Artykuł

freeimages.com freeimages.com
lut 04 2016
0

Nuget - budowanie paczek dla różnych konfiguracji i architektur

Gdy tworzymy kod jakiejś własnej paczki nugetowej, bardzo często wykorzystujemy kompilację w wariancie Any CPU. Dzięki temu docelowa DLLka zadziała zarówno na x86 jak i x64, ale również i na ARM w sytuacji gdy mamy do czynienia z systemami mobilnymi. Niestety nie zawsze jest tak kolorowo i prosto, i czasem Any CPU po prostu nie zadziała - np. w sytuacji gdy mamy składowe DLLki przygotowane pod określone platformy/architektury...

W takim przypadku musimy skompilować osobne DLLki pod każdą architekturę. Wszystko byłoby proste, gdyby nie trzeba było ich umieścić wspólnie w jednej paczce Nugeta, która standardowo zakłada jedną DLLkę o określonej nazwie. Czy można sobie zatem z tym jakoś poradzić i serwować różne biblioteki w zależności od systemu i architektury? Okazuje się, że tak - wystarczy tylko posiadać odpowiedniego nowego Nugeta i w prawidłowy sposób spreparować plik nuspec:)

Co dokładnie jest problemem?

Ostatnio w pracy, musiałem stworzyć bibliotekę, która zawierałaby rozszerzenia do popularnego odtwarzacza Player Framework, o którym co nieco więcej, pisałem już tutaj. Już na wstępie pojawiło się jednak kilka problemów:

  • Player Framework dostarczony jest w postaci rozszerzenia Visual Studio vsix, a nie w postaci paczek nugetowych
  • Wykorzystując odtwarzacz, należy docelowy projekt kompilować zarówno pod określony system (Windows, Windows Phone), jak również architekturę
  • Konieczne jest logiczne ułożenie kilku DLLek w jednej paczce Nugeta

Jak widać już na starcie nie był to trywialny problem, a wcale nie doszliśmy do programowania:) Na szczęście tak jak wspomniałem we wstępie, możemy zawrzeć pewne zapisy w pliku nuspec, które wrzucą do naszej paczki wszystkie DLLki, które zostaną później odpowiednio wpięte podczas budowania docelowego projektu.

Przykładowa struktura projektu

Przykładowa struktura projektu może wyglądać następująco:

- MyProject
    - MyProject.Phone
        - bin
        - build 
            - MyProject.targets
        - ...
    - MyProject.Shared
        - bin
        - ...
    - MyProject.Windows
        - bin
        - ...
    - MyProject.sln
    - MyProject.nuspec
    - ...

W tym przypadku mamy do czynienia z prostym projektem przeznaczonym dla aplikacji uniwersalnej, jednak nie od kod dziś chodzi.

Plik nuspec

Sercem każdej paczki nugetowej jest plik nuspec, dlatego właśnie tutaj wprowadzimy pierwsze zmiany, a konkretniej w jego sekcji files. Przede wszystkim umieścimy tutaj pliki w odpowiednich lokalizacjach w zależności od targetów oraz konfiguracji. W moim przypadku musiałem rozmieścić:

  • Domyślną DLLkę dla WPA81, z której korzystać będzie choćby Visual podczas developmentu i która zostanie dodana jako bazowa referencja
  • Plik targetów dla Windows Phone dla MSBuild - szczegóły za chwilę
  • DLLki dla WP dla ARM i x86 w katalogu build
  • DLLkę x86 dla Windowsa

Poniżej mój plik nuspec:

<?xml version="1.0"?>
<package>
	<metadata>
		<id>MyProject</id>
		<version>$version$</version>
		<title>MyProject plugins package for Windows UA 8.1</title>
		<authors>Jerzy Piechowiak</authors>
		<owners>Jerzy Piechowiak</owners>
		<requireLicenseAcceptance>false</requireLicenseAcceptance>
		<description>MyProject description</description>
		<releaseNotes/>
		<copyright>Copyright 2016</copyright>
		<tags>project test plugins myproject</tags>
		<dependencies />
	</metadata>
	<files>
		<!-- Base wpa81 -->
		<file src="MyProject.Phone\bin\x86\$configuration$\MyProject.dll" target="lib\wpa81\" />
		
		<!-- Build targets for conditional compilation for wpa81 -->
		<file src="MyProject.Phone\build\MyProject.targets" target="build\wpa81\" />
		<file src="MyProject.Phone\bin\ARM\$configuration$\MyProject.dll" target="build\wpa81\ARM\" />
		<file src="MyProject.Phone\bin\x86\$configuration$\MyProject.dll" target="build\wpa81\x86\" />
		
		<!-- win8 -->
		<file src="MyProject.Windows\bin\x86\$configuration$\MyProject.dll" target="lib\win8\" />
	</files>
</package>

Jak widać, część plików (np. ten od Windowsa) od razu trafia do docelowego katalogu lib. DLLki oraz plik targets dla Windows Phone, trafiają do tymczasowego folderu build.

Plik nuspec to jednak tylko część sukcesu. W obecnej sytuacji będziemy w stanie poprawnie użyć paczkę przy kompilacji projektu Windows oraz Windows Phone w wersji x86. Wciąż problemem pozostanie jednak ARM dla WP. I chociaż odpowiednie DLLki będą w paczce, to docelowy projekt nie będzie wiedział jak z nich skorzystać. Tym zajmie się jednak plik targets.

Plik targets

Plik targets powinien mieć dokładnie taką samą nazwę jak id paczki nugetowej (w moim przypadku po prostu MyProject). Jego głównym założeniem, jest utworzenie dwóch targetów dla MSBuilda, które pozwolą na dynamiczne dorzucanie DLLki do folderu bin podczas kompilacji.

Pierwszy z targetów musi sprawdzić z jaką platformą mamy obecnie do czynienia (x86, x64, ARM) i w przypadku braku obsługi aktualnej platformy, przerywa on proces budowania.

Drugi target wywoływany jest tylko w sytuacji gdy pierwszy się powiedzie i dorzuca odpowiednią DLLkę do wynikowego folderu budowania.

Poniżej kod pliku targets, który realizuje powyższe zadania:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

	<Target Name="PlatformCheck" BeforeTargets="InjectReference"
			Condition=" ( ('$(Platform)' != 'x86') AND ('$(Platform)' != 'ARM') AND  ('$(Platform)' != 'x64') )">
		<Error  Text="$(MSBuildThisFileName) does not work correctly on '$(Platform)' 
                     platform. You need to specify platform (x86 / x64 or ARM)." />
	</Target>

	<Target Name="InjectReference" BeforeTargets="ResolveAssemblyReferences">
		<ItemGroup Condition=" '$(Platform)' == 'x86' or '$(Platform)' == 'x64' or '$(Platform)' == 'ARM'">
			<Reference Include="MyProject">
                <HintPath>$(MSBuildThisFileDirectory)$(Platform)\MyProject.dll</HintPath>
                <Private>True</Private>
			</Reference>
		</ItemGroup>
	</Target>
</Project>

Jeśli poprawnie zbudujecie paczkę i zainstalujecie w projekcie docelowym, to powyższe targety powinny się pojawić w pliku csproj.

Cały proces może wyglądać trochę na małe hakowanie, ale w praktyce całość działa naprawdę sensownie. Przez cały czas pracy z projektem, mamy referencję do naszej DLLki, działający IntelliSense oraz wsparcie dla każdej architektury i platformy:-)

Data ostatniej modyfikacji: 09.12.2016, 12:57.

Podoba Ci się ten wpis? Powiedz o tym innym!

Send to Kindle

Komentarze

blog comments powered by Disqus