Eigentlich sollten in der Zwischenzeit alle .NET Entwickler von Nuget gehört haben. Nuget ist wirklich ein sehr schönes Mittel Bibliotheken, Komponenten und Artefakte zu verteilen und für anderen zur Verfügung zu stellen. Für Nuget gibt es eine gute Integration ins Visual Studio 2010, leider nicht in die älteren Versionen, zusätzlich eine Kommandozeilen und Powershell Version. Da ich viel auf Reisen bin, habe ich mir die wichtigsten Pakete, die ich immer wieder brauche in ein lokales Repository gezogen.
nuget install <packagename>
Aus dem heruntergeladenen und installierten Paketen habe ich anschließend alle nupkg-files in einen Ordner kopiert, fertig war mein Repo. Wer mag kann noch eine Nuget-Server (http) daraus machen, dazu muss man nur das Paket “nuget server”
Im Package Manager im VS kann man mittels Add superleicht seine eigenen Quellen hinzufügen.
Ein Paket kann man superleicht aus jedem Projekt erzeugen.
Nuget spec <packageName>
Nuget pack <projectFile>
Erzeugt ein Spec file. Wenn es zusätzlich im Verzeichnis der SLN ausgeführt wird, dann werden die Parameter als Platzhalter übernommen. mit dem 2Kommando wird das nupkg erzeugt und der Output des Projektes hinzugefügt.
Der Teil war einfach. Nun sollte das Paket allerdings mehrere FX-Versionen unterstützen. Dazu muss man im SPEC-file den Output entsprechend in verschiedenen Ordnern einsortieren. Im file-Element kann man mittels des target-Attributes das Verzeichnis im Package definieren. Im Beispiel unten werden die Assemblies für Framework 2.0 in den Ordner “lib\net20” einsortiert.
lib\net20 | .NET Framework 2.0 |
lib\net40 | .NET Framework 4.0 |
lib\sl40 | Silverlight 4.0 |
Nach dem editieren sah mein Spec File so (oder so ähnlich aus):
1 <?xml version="1.0"?> 2 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> 3 <metadata> 4 <id>MyPackageName</id> 5 <version>$version$</version> 6 <authors>Jan Zieschang</authors> 7 <owners>Jan Zieschang</owners> 8 <!--<licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl> 9 <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl> 10 <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>--> 11 <requireLicenseAcceptance>false</requireLicenseAcceptance> 12 <description>Here I should describe my package a bit</description> 13 <tags>Tags For Grouping And Finding</tags> 14 <dependencies> 15 <dependency id="PostSharp" version="2.1"/> 16 <dependency id="log4net" version="1.2" /> 17 </dependencies> 18 </metadata> 19 <files> 20 <file src="bin\Release\myPackageLib.dll" target="lib\net40"/> 21 <file src="bin\Release\myPackageLib.xml" target="lib\net40"/> 22 <file src="bin\Release\myPackageLib.pdb" target="lib\net40"/> 23 <file src="bin\Release2\myPackageLib.dll" target="lib\net20"/> 24 <file src="bin\Release2\myPackageLib.xml" target="lib\net20"/> 25 <file src="bin\Release2\myPackageLib.pdb" target="lib\net20"/> 26 </files> 27 </package>
Nun schlug aber der “Nuget pack” Aufruf fehlt. Nach etwas probieren musste ich feststellen, dass der Aufruf nun das Spec file direkt benötigt.
1 "%nugetPath%" pack "MyPackageName.nuspec" -Version %1 -OutputDirectory "%output%" -symbols -Properties Configuration=Release
Wie man ein Package erstellt kann man noch viel detaillierter unter http://docs.nuget.org/docs/creating-packages/creating-and-publishing-a-package nachlesen. Hier noch einige Tipps für die Paketierung - http://lostechies.com/joshuaflanagan/2011/06/23/tips-for-building-nuget-packages/. Publishen von Packages ist mehr als simpel. Paket in den Ordner kopieren und oder mittels nuget push auf den Paket Server schieben. Ich kam nun aber zu meinem eigentlich Problem.
Da ich für mein Projekt mehrere Ausgaben für unterschiedliche Frameworks erzeugen wollte, musste ich irgendwie meine Projekt-Referenzen austauschen. Letztendlich habe ich mein Project files editiert und die Hint-Paths für die Assemblies verändert.
1 <Reference Include="log4net"> 2 <HintPath>..\packages\log4net.1.2.10\$(Log4NetHintPath)\log4net.dll</HintPath> 3 </Reference> 4 <Reference Include="PostSharp, Version=2.1.0.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7, processorArchitecture=MSIL"> 5 <HintPath>..\packages\PostSharp.2.1.2.8\$(PostSharpHintPath)\PostSharp.dll</HintPath> 6 </Reference>
Die beiden Hint-Paths für die Referenzen PostSharp und Nunit werden mittels einer Variable dynamisiert. In einer PropertyGroup mit einem Condition-Element können die Referenzen ausgetauscht werden.
1 <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release3.5|AnyCPU'"> 2 <PostSharpHintPath>lib\net20</PostSharpHintPath> 3 <Log4NetHintPath>lib\2.0</Log4NetHintPath> 4 <TargetFrameworkVersion>v3.5</TargetFrameworkVersion> 5 </PropertyGroup>
Achtung: Sollte man die Referenzen aktualisieren, zum Beispiel weil es neue Versionen als Nuget-Package gibt, muss ggf. der Hint-Pfad erneut korrigiert werden.
Außer dem Hint-Path muss die Property “TargetFrameworkVersion” gesetzt werden. Dadurch kann man mit MSBuild 4.0 alle Fx Versionen bauen. (Ich glaube, es gebe auch schon in MSBuild 3.5)
Das war die minimal Version. Aber ich habe schon seit Wochen TeamCity auf meinem Rechner gehabt und CI Builds von meinem Paket zu erzeugen. Außerdem wollte ich auch eine minimal Validierung meines Setups haben.
Aus bauen und Paket erstellen ist sind 4 Schritte geworden. Baue Fx4 Version, Validiere diese Version, Baue Fx3.5 Version Validiere diese und anschließend erstelle mein Nuget-Paket. Natürlich übernimmt der CI Server auch die Versionierung der Assemblies und des Paketes und checkt die Version immer sauber aus.