Ajouter des fichiers natifs du package NuGet au répertoire de sortie du projet

J’essaie de créer un paquetage NuGet pour un assemblage .Net qui correspond à une DLL native win32. Je dois emballer l’assembly et la DLL native avec l’assembly ajouté aux références du projet (pas de problème à ce stade) et la DLL native doit être copiée dans le répertoire de sortie du projet ou dans un autre répertoire relatif.

Mes questions sont:

  1. Comment emballer le dll natif sans Visual Studio pour essayer de l’append à la liste des références?
  2. Dois-je écrire un install.ps1 pour copier la DLL native? Si oui, comment puis-je accéder au contenu du paquet pour le copier?

L’utilisation de la cible Copy dans le fichier cible pour copier les bibliothèques requirejses ne copiera pas ces fichiers vers d’autres projets faisant référence au projet, entraînant une DllNotFoundException . Cela peut être fait avec un fichier cible beaucoup plus simple, en utilisant un élément None , car MSBuild copiera tous les fichiers None dans les projets de référence.

     %(RecursiveDir)%(FileName)%(Extension) PreserveNewest    

Ajoutez le fichier cible au répertoire de génération du package nuget avec les bibliothèques natives requirejses. Le fichier de cibles inclura tous les fichiers dll dans tous les répertoires enfants du répertoire de build . Donc, pour append une version x86 et x64 d’une bibliothèque native utilisée par un assembly géré Any CPU , vous devriez vous retrouver avec une structure de répertoire similaire à celle-ci:

  • construire
    • x86
      • NativeLib.dll
      • NativeLibDependency.dll
    • x64
      • NativeLib.dll
      • NativeLibDependency.dll
    • MyNugetPackageID.targets
  • lib
    • net40
      • ManagedAssembly.dll

Les mêmes répertoires x86 et x64 seront créés dans le répertoire de sortie du projet lors de sa construction. Si vous n’avez pas besoin de sous-répertoires, vous pouvez supprimer le ** et le %(RecursiveDir) et inclure directement les fichiers requirejs dans le répertoire de build . D’autres fichiers de contenu requirejs peuvent également être ajoutés de la même manière.

Les fichiers ajoutés en tant que None dans le fichier cible ne seront pas affichés dans le projet lorsqu’ils seront ouverts dans Visual Studio. Si vous vous demandez pourquoi je n’utilise pas le dossier Content dans nupkg, c’est qu’il est impossible de définir l’élément CopyToOutputDirectory sans utiliser un script powershell (qui ne sera exécuté que dans Visual Studio, pas à partir de l’invite de commande, sur les serveurs de génération). ou dans d’autres IDE, et n’est pas pris en charge dans les projets DNX project.json / xproj ) et je préfère utiliser un Link vers les fichiers plutôt que d’avoir une copie supplémentaire des fichiers dans le projet.

Mise à jour: Bien que cela devrait également fonctionner avec Content plutôt qu’avec None il semble qu’il y ait un bogue dans msbuild pour que les fichiers ne soient pas copiés sur les projets référencés plus d’une étape (par exemple, proj1 -> proj2 -> proj3 les fichiers du package NuGet de proj1 mais proj2 le seront).

J’ai récemment eu le même problème lorsque j’ai essayé de créer un paquet EmguCV NuGet comprenant à la fois des assemblys gérés et des liraries partagés non gérés (qui devaient également être placés dans un sous-répertoire x86 ) qui devaient être copiés automatiquement après chaque construire.

Voici une solution que j’ai imaginée, qui repose uniquement sur NuGet et MSBuild:

  1. Placez les assemblys gérés dans le répertoire /lib du package (partie évidente) et les bibliothèques partagées non gérées et les fichiers associés (par exemple les packages .pdb) dans le sous-répertoire /build (comme décrit dans les documents NuGet ).

  2. Renommez toutes les terminaisons de fichiers *.dll non gérées en quelque chose de différent, par exemple *.dl_ pour empêcher NuGet de se plaindre des assemblys présumés placés au mauvais endroit ( “Problème: Assemblage en dehors du dossier lib” ).

  3. Ajoutez un .targets dans le sous-répertoire /build avec le contenu suivant (voir ci-dessous pour une description):

            x86     $(PrepareForRunDependsOn); CopyNativeBinaries            

Le fichier .targets ci-dessus sera injecté sur une installation du package NuGet dans le fichier de projet cible et est responsable de la copie des bibliothèques natives dans le répertoire de sortie.

  • ajoute un nouvel élément “Build Action” pour le projet (qui devient également disponible dans la liste déroulante “Build Action” dans Visual Studio).

Voici une alternative qui utilise les .targets pour injecter la DLL native dans le projet avec les propriétés suivantes.

  • Build action = None
  • Copy to Output Directory = Copy if newer

Le principal avantage de cette technique est que la DLL native est copiée de manière transitoire dans le dossier bin/ des projets dépendants .

Voir la présentation du fichier .nuspec :

Capture d'écran de NuGet Package Explorer

Voici le fichier .targets :

     MyNativeLib.dll PreserveNewest    

Cela insère le MyNativeLib.dll comme si il faisait partie du projet d’origine (mais curieusement, le fichier n’est pas visible dans Visual Studio).

Notez l’élément qui définit le nom du fichier de destination dans le dossier bin/ .

C’est un peu tard mais j’ai créé un paquet nuget exaclty pour cela.

L’idée est d’avoir un dossier spécial supplémentaire dans votre package nuget. Je suis sûr que vous connaissez déjà Lib et Content. Le paquet nuget que j’ai créé recherche un dossier nommé Output et copiera tout ce qui s’y trouve dans le dossier de sortie des projets.

La seule chose à faire est d’append une dépendance nuget au package http://www.nuget.org/packages/Baseclass.Consortingb.Nuget.Output/

J’ai écrit un article de blog à ce sujet: http://www.baseclass.ch/blog/Lists/Beitraege/Post.aspx?ID=6&mobile=0

Si quelqu’un trébuche à ce sujet.

Le .targets fichier .targets DOIT être égal à l’ID de package NuGet

Tout le rest ne fonctionnera pas.

Crédits à: https://sushihangover.github.io/nuget-and-msbuild-targets/

J’aurais dû lire plus en détail comme cela a été noté ici. M’a pris des âges ..

Ajouter un .targets

Il y a une solution C # pure que je trouve assez facile à utiliser et je n’ai pas à me soucier des limitations de NuGet. Suivez ces étapes:

Incluez la bibliothèque native dans votre projet et définissez sa propriété Action de Embedded Resource sur Embedded Resource .

Collez le code suivant dans la classe où vous invoquez cette bibliothèque.

 private static void UnpackNativeLibrary(ssortingng libraryName) { var assembly = Assembly.GetExecutingAssembly(); ssortingng resourceName = $"{assembly.GetName().Name}.{libraryName}.dll"; using (var stream = assembly.GetManifestResourceStream(resourceName)) using (var memoryStream = new MemoryStream(stream.CanSeek ? (int)stream.Length : 0)) { stream.CopyTo(memoryStream); File.WriteAllBytes($"{libraryName}.dll", memoryStream.ToArray()); } } 

Appelez cette méthode à partir du constructeur statique comme suit: UnpackNativeLibrary("win32"); et il déballera la bibliothèque sur le disque juste avant que vous en ayez besoin. Bien entendu, vous devez vous assurer que vous disposez des droits en écriture sur cette partie du disque.

C’est une vieille question, mais j’ai le même problème maintenant, et j’ai trouvé un délai un peu délicat mais très simple et efficace: créer dans le dossier de contenu standard de Nuget la structure suivante avec un sous-dossier pour chaque configuration:

 /Content /bin /Debug native libraries /Release native libraries 

Lorsque vous compressez le fichier nuspec, vous recevrez le message suivant pour chaque bibliothèque native dans les dossiers Debug et Release:

Problème: Assemblage en dehors du dossier lib. Description: L’assembly ‘Content \ Bin \ Debug \ ??????. Dll’ ne se trouve pas dans le dossier ‘lib’ et ne sera donc pas ajouté comme référence lorsque le package est installé dans un projet. Solution: Déplacez-le dans le dossier ‘lib’ s’il doit être référencé.

Nous n’avons pas besoin d’une telle “solution” car ce n’est que notre objective: les bibliothèques natives ne sont pas ajoutées en tant que références aux assemblages NET.

Les avantages sont:

  1. Solution simple sans scripts encombrants avec des effets étranges difficiles à réinitialiser lors de la désinstallation du package.
  2. Nuget gère les bibliothèques natives comme tout autre contenu lors de l’installation et de la désinstallation.

Les inconvénients sont:

  1. Vous avez besoin d’un dossier pour chaque configuration (mais en général, il n’y en a que deux: Debug et Release, et si vous avez d’autres contenus à installer dans chaque dossier de configuration, c’est la voie à suivre)
  2. Les bibliothèques natives doivent être dupliquées dans chaque dossier de configuration (mais si vous avez des versions différentes des bibliothèques natives pour chaque configuration, c’est la voie à suivre)
  3. Les avertissements pour chaque DLL native dans chaque dossier (mais comme je l’ai dit, ils sont envoyés au créateur du paquet au moment du pack, et non à l’utilisateur du package au moment de l’installation)

Je ne peux pas résoudre votre problème exact, mais je peux vous donner une suggestion.

Votre exigence principale est la suivante: “Et ne pas enregistrer automatiquement la référence” …..

Vous devrez donc vous familiariser avec les “éléments de solution”

Voir la référence ici:

Ajout d’éléments de niveau solution dans un package NuGet

Vous devrez écrire un peu de voodoo PowerShell pour obtenir la copie de votre DLL native dans sa maison (encore une fois, parce que vous ne voulez PAS que le vaudou à référence automatique soit déclenché)

Voici un fichier ps1 que j’ai écrit ….. pour placer des fichiers dans un dossier de références tierces.

Il y a suffisamment de place pour vous permettre de copier votre dll natif dans un certain “home” … sans avoir à recommencer à zéro.

Encore une fois, ce n’est pas un coup direct, mais c’est mieux que rien.

 param($installPath, $toolsPath, $package, $project) if ($project -eq $null) { $project = Get-Project } Write-Host "Start Init.ps1" <# The unique identifier for the package. This is the package name that is shown when packages are listed using the Package Manager Console. These are also used when installing a package using the Install-Package command within the Package Manager Console. Package IDs may not contain any spaces or characters that are invalid in an URL. #> $separator = " " $packageNameNoVersion = $package -split $separator | select -First 1 Write-Host "installPath:" "${installPath}" Write-Host "toolsPath:" "${toolsPath}" Write-Host "package:" "${package}" <# Write-Host "project:" "${project}" #> Write-Host "packageNameNoVersion:" "${packageNameNoVersion}" Write-Host " " <# Recursively look for a .sln file starting with the installPath #> $parentFolder = (get-item $installPath) do { $parentFolderFullName = $parentFolder.FullName $latest = Get-ChildItem -Path $parentFolderFullName -File -Filter *.sln | Select-Object -First 1 if ($latest -ne $null) { $latestName = $latest.name Write-Host "${latestName}" } if ($latest -eq $null) { $parentFolder = $parentFolder.parent } } while ($parentFolder -ne $null -and $latest -eq $null) <# End recursive search for .sln file #> if ( $parentFolder -ne $null -and $latest -ne $null ) { <# Create a base directory to store Solution-Level items #> $thirdPartyReferencesDirectory = $parentFolder.FullName + "\ThirdPartyReferences" if ((Test-Path -path $thirdPartyReferencesDirectory)) { Write-Host "--This path already exists: $thirdPartyReferencesDirectory-------------------" } else { Write-Host "--Creating: $thirdPartyReferencesDirectory-------------------" New-Item -ItemType directory -Path $thirdPartyReferencesDirectory } <# Create a sub directory for only this package. This allows a clean remove and recopy. #> $thirdPartyReferencesPackageDirectory = $thirdPartyReferencesDirectory + "\${packageNameNoVersion}" if ((Test-Path -path $thirdPartyReferencesPackageDirectory)) { Write-Host "--Removing: $thirdPartyReferencesPackageDirectory-------------------" Remove-Item $thirdPartyReferencesPackageDirectory -Force -Recurse } if ((Test-Path -path $thirdPartyReferencesPackageDirectory)) { } else { Write-Host "--Creating: $thirdPartyReferencesPackageDirectory-------------------" New-Item -ItemType directory -Path $thirdPartyReferencesPackageDirectory } Write-Host "--Copying all files for package : $packageNameNoVersion-------------------" Copy-Item $installPath\*.* $thirdPartyReferencesPackageDirectory -recurse } else { Write-Host "A current or parent folder with a .sln file could not be located." } Write-Host "End Init.ps1" 

Mettez le dossier de contenu

la commande nuget pack [projfile].csproj fera automatiquement pour vous si vous marquez les fichiers en tant que contenu.

puis éditez le fichier de projet comme mentionné ici en ajoutant l’élément ItemGroup & NativeLibs & None

    %(RecursiveDir)%(FileName)%(Extension) PreserveNewest   

travaillé pour moi