147 votes

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

Je suis en train de créer un package NuGet pour une assembly .Net qui appelle une dll native Win32. Je dois packager à la fois l'assembly et la dll native avec l'assembly ajouté aux références du projet (pas de problème à ce niveau) 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 puis-je packager la dll native sans que Visual Studio essaie de l'ajouter à 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 package pour la copier ?

3voto

Ondrej Janacek Points 6467

Il existe une solution purement C# que je trouve plutôt 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é Build Action sur Embedded Resource.

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

private static void UnpackNativeLibrary(string libraryName)
{
    var assembly = Assembly.GetExecutingAssembly();
    string 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 depuis le constructeur statique comme suit UnpackNativeLibrary("win32"); et il décompressera la bibliothèque sur le disque juste avant que vous en ayez besoin. Bien sûr, vous devez vous assurer que vous avez les autorisations d'écriture à cet endroit du disque.

1voto

SERWare Points 357

Ceci est une vieille question, mais j'ai le même problème maintenant, et j'ai trouvé une solution qui est un peu astucieuse mais très simple et efficace : créez dans le dossier de contenu standard Nuget la structure suivante avec un sous-dossier pour chaque configuration :

/Contenu
 /bin
   /Débogage
      bibliothèques natives
   /Production
      bibliothèques natives

Lorsque vous emballez le fichier nuspec, vous recevrez le message suivant pour chaque bibliothèque native dans les dossiers de Débogage et Production :

Problème : Assemblée en dehors du dossier lib. Description : L'assemblée 'Contenu\Bin\Debug\??????.dll' n'est pas à l'intérieur du dossier 'lib' et ne sera donc pas ajoutée comme référence lorsque le package est installé dans un projet. Solution : Déplacez-la dans le dossier 'lib' si elle doit être référencée.

Nous n'avons pas besoin d'une telle "solution" car c'est justement notre objectif : que les bibliothèques natives ne soient pas ajoutées en tant que références d'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 n'importe quel 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 généralement il n'y en a que deux : Débogage et Production, et si vous avez d'autres contenus qui doivent être installés 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 différentes versions 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, les avertissements sont émis au créateur du package au moment de l'empaquetage, et non à l'utilisateur du package au moment de l'installation dans Visual Studio)

0voto

granadaCoder Points 6390

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

Votre exigence clé est : "Et ne pas auto-enregistrer la référence".....

Vous devrez-vous familiariser avec les "éléments de solution"

Voir la référence ici:

Ajout d'éléments au niveau de la solution dans un package NuGet

Vous devrez écrire un peu de pouvoir PowerShell pour copier votre dll native dans son emplacement (encore une fois, parce que vous NE voulez pas que le pouvoir auto-ajouter-référence s'enclenche)

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

Il y a assez là pour que vous puissiez comprendre comment copier votre dll native dans un "home"... sans avoir à partir de 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 "Début Init.ps1" 

<#
L'identifiant unique du package. Il s'agit du nom du package qui est affiché lorsque les packages sont répertoriés en utilisant la console du Gestionnaire de packages. Ceux-ci sont également utilisés lors de l'installation d'un package en utilisant la commande Install-Package dans la console du Gestionnaire de packages. Les ID de package ne peuvent pas contenir d'espaces ni de caractères invalides dans une 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 "nomDuPackageNoVersion:" "${packageNameNoVersion}"
Write-Host " "

<# Recherche récursive d'un fichier .sln à partir du chemin d'installation #>
$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)
<# Fin de la recherche récursive du fichier .sln #>

if ( $parentFolder -ne $null -and $latest -ne $null )
{
    <# Créer un répertoire de base pour stocker les éléments au niveau de la solution #>
    $thirdPartyReferencesDirectory = $parentFolder.FullName + "\ThirdPartyReferences"

    if ((Test-Path -path $thirdPartyReferencesDirectory))
    {
        Write-Host "--Ce chemin existe déjà : $thirdPartyReferencesDirectory-------------------"
    }
    else
    {
        Write-Host "--Création de : $thirdPartyReferencesDirectory-------------------"
        New-Item -ItemType directory -Path $thirdPartyReferencesDirectory
    }

    <# Créer un sous-répertoire uniquement pour ce package. Cela permet une suppression propre et une nouvelle copie. #>
    $thirdPartyReferencesPackageDirectory = $thirdPartyReferencesDirectory + "\${packageNameNoVersion}"

    if ((Test-Path -path $thirdPartyReferencesPackageDirectory))
    {
        Write-Host "--Suppression de : $thirdPartyReferencesPackageDirectory-------------------"
        Remove-Item $thirdPartyReferencesPackageDirectory -Force -Recurse
    }

    if ((Test-Path -path $thirdPartyReferencesPackageDirectory))
    {
    }
    else
    {
        Write-Host "--Création de : $thirdPartyReferencesPackageDirectory-------------------"
        New-Item -ItemType directory -Path $thirdPartyReferencesPackageDirectory
    }

    Write-Host "--Copie de tous les fichiers pour le package : $packageNameNoVersion-------------------"
    Copy-Item $installPath\*.* $thirdPartyReferencesPackageDirectory -recurse
}
else
{
        Write-Host "Un dossier actuel ou parent avec un fichier .sln n'a pas pu être localisé."
}

Write-Host "Fin Init.ps1"

0voto

user1238350 Points 8

J'avais aussi besoin de déplacer certaines bibliothèques natives vers le dossier de sortie. J'ai trouvé cette discussion Comment empaqueter une assembly .net qui encapsule une dll native sur le Nuget CodePlex. C'était exactement ce que je cherchais, peut-être que cela vous aidera aussi.

0voto

Loïc Morvan Points 27

C'est exactement le but du dossier runtimes. Plus d'informations à ce sujet sur Microsoft Learn.

En bref, vous devez placer vos bibliothèques natives spécifiques à l'architecture dans le bon sous-dossier de runtimes:

runtimes/
  win10-x86/
    native/
      my-native.dll
  linux-x64/
    native/
      my-native.so

J'ai réussi à utiliser cette solution avec pinvoke : en fonction de l'exécution de votre programme, le DllImport recherchera la bonne bibliothèque dans le bon sous-dossier de runtimes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X