Moving beyond the simple case, here is what I did to package the Cavity log4net trace listener which I'm sharing because I encountered some frustration points.
First, the .nuspec
file:
<package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>Cavity.Diagnostics.Log4Net</id> <version>1.1.0.444</version> <title>Cavity log4net trace listener</title> <authors>Alan Dean</authors> <owners /> <licenseUrl>http://www.opensource.org/licenses/mit-license.php</licenseUrl> <projectUrl>http://code.google.com/p/cavity/</projectUrl> <iconUrl>http://www.alan-dean.com/nuget.png</iconUrl> <requireLicenseAcceptance>false</requireLicenseAcceptance> <description>A trace listener for log4net, allowing provider-agnostic tracing.</description> <summary /> <copyright>Copyright © 2010 - 2011 Alan Dean</copyright> <language /> <tags>Diagnostics</tags> <releaseNotes>Switched off license acceptance dialog.</releaseNotes> <dependencies> <dependency id="log4net" version="1.2.10" /> </dependencies> </metadata> <files> <file src="content\app.config.transform" target="content\app.config.transform" /> <file src="content\log4net.config.transform" target="content\log4net.config.transform" /> <file src="content\web.config.transform" target="content\web.config.transform" /> <file src="content\Properties\log4net.cs" target="content\Properties\log4net.cs" /> <file src="lib\net35\Cavity.Diagnostics.Log4Net.dll" target="lib\net35\Cavity.Diagnostics.Log4Net.dll" /> <file src="lib\net40\Cavity.Diagnostics.Log4Net.dll" target="lib\net40\Cavity.Diagnostics.Log4Net.dll" /> <file src="tools\Install.ps1" target="tools\Install.ps1" /> </files> </package>
Right off the bat. you can see that more is going on here. The trace listener is dependent on log4net and I also have a bunch of content in additional to my two assemblies.
The target project will need to have either its' app.config
or web.config
extended so there is a transform for each. The target will need an assembly attribute applied, so I took the simple expedient of dropping log4net.cs
into Properties alongside AssemblyInfo.cs
. However, log4net also requires XmlConfigurator.Configure() to be called. This call needs to be in either Main()
or Application_Start()
and therefore existing code needs to be edited (rather than a new code file dropped in). To accomplish this, we have to use EnvDTE, as exposed in the Install.ps1
PowerShell script (I also mark the log4net.config
file to copy to output during build):
param($installPath, $toolsPath, $package, $project) $project.ProjectItems.Item("log4net.config").Properties.Item("CopyToOutputDirectory").Value = 1 try { $item = $project.ProjectItems.Item("Program.cs") } catch [System.Management.Automation.MethodInvocationException] { } if (!$item) { $item = $project.ProjectItems.Item("global.asax").ProjectItems.Item("global.asax.cs") } $terminator = "" if ($item.FileCodeModel.Language -eq "{B5E9BD34-6D3E-4B5D-925E-8A43B79820B4}") { $terminator = ";" } $win = $item.Open("{7651A701-06E5-11D1-8EBD-00A0C90F26EA}") $text = $win.Document.Object("TextDocument"); $namespace = $item.FileCodeModel.CodeElements | where-object {$_.Kind -eq 5} $class = $namespace.Children | where-object {$_.Kind -eq 1} $methods = $class.Children | where-object {$_.Name -eq "Main"} if (!$methods) { $methods = $class.Children | where-object {$_.Name -eq "Application_Start"} if (!$methods) { [system.windows.forms.messagebox]::show("methods is null") } } $edit = $methods.StartPoint.CreateEditPoint(); $edit.LineDown() $edit.CharRight(1) $edit.Insert([Environment]::NewLine) $edit.Insert(" log4net.Config.XmlConfigurator.Configure()") $edit.Insert($terminator)
I have to say that the PowerShell + EnvDTE experience was rather less than enjoyable but I got the basics of what I needed to work.