2016-01-05 17 views
11

Come si impacchetta una libreria di piattaforma Windows universale scritta in C# che offre solo build dipendenti dall'architettura? Per fare un esempio, diciamo che ho un codice specifico per l'architettura compilato in modo condizionale per ogni architettura (usando #if ARM e equivalenti).Come impacchettare una libreria .NET multi-architettura destinata alla Universal Windows Platform?

Per essere chiari, non esiste alcuna build AnyCPU per la mia libreria - solo x86, x64 e ARM.

Una situazione equivalente e potenzialmente più comune è quella in cui ho una dipendenza da una libreria esterna che viene fornita solo come build specifici dell'architettura (ad esempio Win2D). Per mantenere il contesto semplice, supponiamo che non ci siano dipendenze e che sia coinvolto solo il mio codice: la soluzione dovrebbe ridurre allo stesso modo la stessa cosa.

Si tratta di una serie di domande e risposte che documentano le mie scoperte sul tema del pacchetto di authoring moderna NuGet, concentrandosi in particolare sulle modifiche introdotte con NuGet 3. Si può anche essere interessati a alcune domande correlate:

risposta

10

Questa risposta costruisce sulla principles of .NET Framework library packaging e principles of Universal Windows Platform library packaging. Leggi le risposte collegate per capire meglio quanto segue.

Suppongo che tutte le build specifiche dell'architettura espongano la stessa superficie API, con solo l'implementazione di tali API diverse.

La principale complicazione di questo scenario è che la toolchain di build richiede un assembly AnyCPU per la risoluzione di riferimento in fase di compilazione, anche se questo assembly non viene mai utilizzato in fase di esecuzione. Poiché lo scenario non ha output di compilazione AnyCPU, è necessario trovare una soluzione alternativa. Il concetto che si applica qui è di assiemi di riferimento: gli assembly AnyCPU vengono utilizzati solo in fase di compilazione per la convalida del riferimento. Pertanto, per pubblicare la libreria, è necessario creare un assembly di riferimento e raggruppare le risorse come descritto di seguito.

Per semplicità, assumerò che la libreria non abbia dipendenze da altri pacchetti NuGet. Non è probabile che ciò avvenga in pratica, ma la gestione delle dipendenze è già coperta dalle altre risposte sopraindicate e viene quindi omessa da questa risposta.

La struttura desiderata del pacchetto NuGet è la seguente:

+---ref 
| \---uap10.0 
|  | MultiArchitectureUwpLibrary.dll 
|  | MultiArchitectureUwpLibrary.pri 
|  | MultiArchitectureUwpLibrary.XML 
|  | 
|  \---MultiArchitectureUwpLibrary 
|    ArchitectureControl.xaml 
|    MultiArchitectureUwpLibrary.xr.xml 
| 
+---runtimes 
| +---win10-arm 
| | \---lib 
| |  \---uap10.0 
| |    MultiArchitectureUwpLibrary.dll 
| |    MultiArchitectureUwpLibrary.pdb 
| | 
| +---win10-x64 
| | \---lib 
| |  \---uap10.0 
| |    MultiArchitectureUwpLibrary.dll 
| |    MultiArchitectureUwpLibrary.pdb 
| | 
| \---win10-x86 
|  \---lib 
|   \---uap10.0 
|     MultiArchitectureUwpLibrary.dll 
|     MultiArchitectureUwpLibrary.pdb 

Se preso confidenza con le risposte legate sopra, i file dovrebbero essere noti a voi già, anche se la struttura delle directory è piuttosto insolito in questo caso. La directory ref contiene l'assembly di riferimento, la documentazione XML ei file di risorse, mentre le risorse specifiche dell'architettura sono strutturate nella directory runtime.

La maggior parte di questo è abbastanza semplice e può essere realizzato utilizzando un file nuspec creato sulla base della seguente configurazione:

<?xml version="1.0"?> 
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> 
    <metadata minClientVersion="3.2"> 
     <id>Example.MultiArchitectureUwpLibrary</id> 
     <version>1.0.0</version> 
     <authors>Firstname Lastname</authors> 
     <description>Example of library that is published as a set of architecture-specific assmeblies for the UWP platform.</description> 
    </metadata> 
    <files> 
     <!-- Architecture-independent reference library for use at compile-time; generated by the PowerShell script. --> 
     <file src="..\bin\Reference\Release\MultiArchitectureUwpLibrary.dll" target="ref\uap10.0" /> 

     <!-- XML documentation file goes together with the reference library. --> 
     <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.xml" target="ref\uap10.0" /> 

     <!-- Resource files go together with the reference library. --> 
     <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.pri" target="ref\uap10.0" /> 
     <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary\*" target="ref\uap10.0\MultiArchitectureUwpLibrary" /> 

     <!-- The architecture-specific files go in architecture-specific directories. --> 
     <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-x86\lib\uap10.0" /> 
     <file src="..\bin\x86\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-x86\lib\uap10.0" /> 

     <file src="..\bin\x64\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-x64\lib\uap10.0" /> 
     <file src="..\bin\x64\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-x64\lib\uap10.0" /> 

     <file src="..\bin\arm\Release\MultiArchitectureUwpLibrary.dll" target="runtimes\win10-arm\lib\uap10.0" /> 
     <file src="..\bin\arm\Release\MultiArchitectureUwpLibrary.pdb" target="runtimes\win10-arm\lib\uap10.0" /> 
    </files> 
</package> 

Il pezzo mancante, naturalmente, è il gruppo di riferimento. Fortunatamente, questo è piuttosto semplice da risolvere: un assembly di riferimento è un assembly AnyCPU che definisce le stesse classi e metodi che contengono gli assembly runtime. Il suo scopo principale è fornire al compilatore un riferimento a cui lavorare, in modo che il compilatore possa verificare che tutte le chiamate di metodo stiano effettivamente facendo riferimento a metodi che esisteranno in fase di runtime. Il codice effettivo nell'assembly di riferimento (se presente) non viene utilizzato per nulla.

Poiché tutte le build specifiche dell'architettura espongono la stessa superficie API, possiamo semplicemente prendere qualcuna di esse e chiedere al compilatore di usarla come assembly di riferimento. Windows SDK contiene un'utilità denominata CorFlags.exe che può essere utilizzata per convertire un assembly x86 in un assembly AnyCPU, rendendo possibile ciò.

Di seguito è riportato uno script di creazione del pacchetto che crea gli assembly di riferimento richiesti prima di impacchettare la libreria. Presuppone che l'SDK di Windows sia installato nella posizione standard. Per comprendere i dettagli della logica, vedere i commenti in linea.

# Any assembly matching this filter will be transformed into an AnyCPU assembly. 
$referenceDllFilter = "MultiArchitectureUwpLibrary.dll" 

$programfilesx86 = "${Env:ProgramFiles(x86)}" 
$corflags = Join-Path $programfilesx86 "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6 Tools\x64\CorFlags.exe" 

If (!(Test-Path $corflags)) 
{ 
    Throw "Unable to find CorFlags.exe" 
} 

$solutionRoot = Resolve-Path ..\.. 
$topLevelDirectories = Get-ChildItem $solutionRoot -Directory 
$binDirectories = $topLevelDirectories | %{ Get-ChildItem $_.FullName -Directory -Filter "bin" } 

# Create reference assemblies, because otherwise the NuGet packages cannot be used. 
# This creates them for all outputs that match the filter, in all output directories of all projects. 
# It's a bit overkill but who cares - the process is very fast and keeps the script simple. 
Foreach ($bin in $binDirectories) 
{ 
    $x86 = Join-Path $bin.FullName "x86" 
    $any = Join-Path $bin.FullName "Reference" 

    If (!(Test-Path $x86)) 
    { 
     Write-Host "Skipping reference assembly generation for $($bin.FullName) because it has no x86 directory." 
     continue; 
    } 

    if (Test-Path $any) 
    { 
     Remove-Item -Recurse $any 
    } 

    New-Item $any -ItemType Directory 
    New-Item "$any\Release" -ItemType Directory 

    $dlls = Get-ChildItem "$x86\Release" -File -Filter $referenceDllFilter 

    Foreach ($dll in $dlls) 
    { 
     Copy-Item $dll.FullName "$any\Release" 
    } 

    $dlls = Get-ChildItem "$any\Release" -File -Filter $referenceDllFilter 

    Foreach ($dll in $dlls) 
    { 
     Write-Host "Converting to AnyCPU: $dll" 

     & $corflags /32bitreq- $($dll.FullName) 
    } 
} 

# Delete any existing output. 
Remove-Item *.nupkg 

# Create new packages for any nuspec files that exist in this directory. 
Foreach ($nuspec in $(Get-Item *.nuspec)) 
{ 
    .\NuGet.exe pack "$nuspec" 
} 

Potrebbe essere necessario regolare i percorsi nello script in modo che corrispondano alle convenzioni utilizzate nella soluzione.

Eseguire questo script per creare un pacchetto NuGet che consente di utilizzare la libreria in tutte le varianti specifiche dell'architettura! Ricordarsi di creare la soluzione utilizzando la configurazione di rilascio per tutte le architetture prima di creare il pacchetto NuGet.

Una libreria di esempio e i relativi file di imballaggio sono available on GitHub. La soluzione corrispondente a questa risposta è MultiArchitectureUwpLibrary.

+0

Se si verifica questo, assicurarsi di modificare la versione. L'ho provato e prima non ha funzionato. Perché le giunture di nuget nascondono che non è possibile installare il pacchetto. – lokimidgard

Problemi correlati