Добавить исходные файлы из пакета NuGet в каталог вывода проекта

Я пытаюсь создать пакет NuGet для сборки .NET, которая выполняет pinvoke в родной dll win32. Мне нужно собрать как сборку, так и родную DLL с сборкой, добавленной в ссылки на проект (без проблем в этой части), а родная DLL должна быть скопирована в выходной каталог проекта или какой-либо другой относительный каталог.

Мои вопросы:

  • Как упаковать родную dll без визуальной студии, пытаясь добавить ее в список ссылок?
  • Должен ли я писать install.ps1 для копирования родной DLL? Если да, то как я могу получить доступ к содержимому пакета для его копирования?

Ответ 1

Использование целевого объекта Copy в файле целей для копирования требуемых библиотек не будет копировать эти файлы в другие проекты, которые ссылаются на проект, что приводит к DllNotFoundException. Это можно сделать с помощью гораздо более простого файла целей, используя элемент None, поскольку MSBuild скопирует все файлы None в ссылки на проекты.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
    <None Include="@(NativeLibs)">
      <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>

Добавьте файл целей в каталог build пакета nuget вместе с необходимыми родными библиотеками. Файл целей будет содержать все dll файлы во всех дочерних каталогах каталога build. Поэтому для добавления версии x86 и x64 собственной библиотеки, используемой управляемой ассемблером Any CPU, вы получите структуру каталогов, похожую на следующую:

  • сборки
    • x86
      • NativeLib.dll
      • NativeLibDependency.dll
    • 64
      • NativeLib.dll
      • NativeLibDependency.dll
    • MyNugetPackageID.targets
  • Lib
    • net40
      • ManagedAssembly.dll

Те же директории x86 и x64 будут созданы в каталоге вывода проекта при построении. Если вам не нужны подкаталоги, то можно удалить ** и %(RecursiveDir) и вместо этого включить нужные файлы в каталог build. Другие необходимые файлы содержимого также могут быть добавлены таким же образом.

Файлы, добавленные как None в файл целей, не будут отображаться в проекте при открытии в Visual Studio. Если вам интересно, почему я не использую папку Content в nupkg, потому что нет способа установить элемент CopyToOutputDirectory без использования powershell script (который будет выполняться только внутри Visual Studio, а не из командной строки, на серверах сборки или в других IDE и не поддерживается в project.json/xproj DNX), и я предпочитаю использовать Link для файлов, а не иметь дополнительную копию файлов в проекте.

Update: Хотя это также должно работать с Content, а не None, появляется ошибка в msbuild, поэтому файлы не будут скопированы для ссылок на проекты, удаленные более чем на один шаг (например, proj1 → proj2 → proj3, proj3 won ' t получить файлы из пакета proj1 NuGet, но proj2 будет).

Ответ 2

У меня была та же самая проблема, когда я попытался создать пакет EmuCV NuGet, включая как управляемые сборки, так и не управляемые общие лириеры (которые также должны были быть помещены в подкаталог x86), который пришлось автоматически копировать в выходной файл сборки после каждой сборки.

Вот решение, с которым я столкнулся, полагается только на NuGet и MSBuild:

  • Поместите управляемые сборки в каталог /lib пакета (очевидная часть) и не управляемые совместно используемые библиотеки и связанные файлы (например, пакеты .pdb) в /build (как описано в Документах NuGet).

  • Переименуйте все неподдерживаемые окончания файлов *.dll на что-то другое, например *.dl_, чтобы NuGet не застонал от предполагаемых сборок, размещенных на неправильное место ( "Проблема: сборка вне папки lib".).

  • Добавьте <PackageName>.targets файл <PackageName>.targets в подкаталог /build с чем-то вроде следующего содержания (см. ниже описание):

    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <ItemGroup>
        <AvailableItemName Include="NativeBinary" />
      </ItemGroup>
      <ItemGroup>
        <NativeBinary Include="$(MSBuildThisFileDirectory)x86\*">
          <TargetPath>x86</TargetPath>
        </NativeBinary>
      </ItemGroup>
      <PropertyGroup>
        <PrepareForRunDependsOn>
          $(PrepareForRunDependsOn);
          CopyNativeBinaries
        </PrepareForRunDependsOn>
      </PropertyGroup>
      <Target Name="CopyNativeBinaries" DependsOnTargets="CopyFilesToOutputDirectory">
        <Copy SourceFiles="@(NativeBinary)"
              DestinationFiles="@(NativeBinary->'$(OutDir)\%(TargetPath)\%(Filename).dll')"
              Condition="'%(Extension)'=='.dl_'">
          <Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
        </Copy>
        <Copy SourceFiles="@(NativeBinary)"
              DestinationFiles="@(NativeBinary->'$(OutDir)\%(TargetPath)\%(Filename).%(Extension)')"
              Condition="'%(Extension)'!='.dl_'">
          <Output TaskParameter="DestinationFiles" ItemName="FileWrites" />
        </Copy>
      </Target>
    </Project>
    

Вышеприведенный .targets файл будет инъецирован при установке пакета NuGet в файл целевого проекта и отвечает за копирование собственных библиотек в выходной каталог.

  • <AvailableItemName Include="NativeBinary" /> добавляет новый элемент "Действие сборки" для проекта (который также становится доступным в раскрывающемся меню "Построение действия" внутри Visual Studio).

  • <NativeBinary Include="... добавляет собственные библиотеки, размещенные в /build/x86, к текущему проекту и делает их доступными для настраиваемой цели, которая копирует эти файлы в выходной каталог.

    /li >
  • <TargetPath>x86</TargetPath> добавляет пользовательские метаданные к файлам и сообщает настраиваемой цели копировать собственные файлы в подкаталог x86 фактического каталога вывода.

  • Блок <PrepareForRunDependsOn ... добавляет пользовательскую цель в список целей, на которые зависит сборка, подробнее см. в файле Microsoft.Common.targets.

  • Пользовательская цель CopyNativeBinaries содержит две задачи копирования. Первый отвечает за копирование любых файлов *.dl_ в выходной каталог при изменении их расширения до исходного *.dll. Второй экземпляр просто копирует остальные (например, любые файлы *.pdb) в одно и то же место. Это может быть заменено заданием на одну копию и install.ps1 script, которому пришлось переименовать все файлы *.dl_ на *.dll во время установки пакета.

Однако это решение по-прежнему не копирует исходные двоичные файлы в выходной каталог другого проекта, ссылаясь на тот, который первоначально включает пакет NuGet. Вам все равно придется ссылаться на пакет NuGet в вашем "окончательном" проекте.

Ответ 3

Вот альтернатива, которая использует .targets to для вставки собственной DLL в проект со следующими свойствами.

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

Основное преимущество этого метода заключается в том, что встроенная DLL копируется в папку bin/ зависимых проектов транзитивно.

См. макет файла .nuspec:

Screen capture of NuGet Package Explorer

Вот файл .targets:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <None Include="$(MSBuildThisFileDirectory)\..\MyNativeLib.dll">
            <Link>MyNativeLib.dll</Link>
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </None>
    </ItemGroup>
</Project>

Вставляет MyNativeLib.dll, как если бы он был частью исходного проекта (но любопытно, что файл не виден в Visual Studio).

Обратите внимание на элемент <Link>, который устанавливает имя целевого файла в папке bin/.

Ответ 4

Немного поздно, но для этого я создал для него nuget-пакет.

Идея состоит в том, чтобы иметь дополнительную специальную папку в вашем пакете nuget. Я уверен, что вы уже знаете Lib и Content. Пакет nuget, который я создал, ищет папку с именем Output и копирует все, что находится там, в папку вывода проектов.

Единственное, что вам нужно сделать, это добавить зависимость от nuget для пакета http://www.nuget.org/packages/Baseclass.Contrib.Nuget.Output/

Я написал сообщение в блоге об этом: http://www.baseclass.ch/blog/Lists/Beitraege/Post.aspx?ID=6&mobile=0

Ответ 5

Если кто-то еще наткнулся на это.

.targets имя_файла ДОЛЖЕН равный идентификатору пакета NuGet

Все остальное не будет работать.

Кредиты идут: https://sushihangover.github.io/nuget-and-msbuild-targets/

Мне следовало бы прочитать более подробно, поскольку его на самом деле отметили здесь. Взял меня в покое.

Добавьте пользовательский <PackageName>.targets

Ответ 6

Там чистое решение С#, которое я считаю довольно простым в использовании, и мне не нужно беспокоиться об ограничениях NuGet. Выполните следующие действия:

Включите исходную библиотеку в свой проект и установите для свойства Build Action значение Embedded Resource.

Вставьте следующий код в класс, где вы PInvoke этой библиотеки.

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());
    }
}

Вызовите этот метод из статического конструктора, как показано ниже UnpackNativeLibrary("win32");, и он распакует библиотеку на диск незадолго до ее возникновения. Конечно, вы должны быть уверены, что у вас есть права на запись на эту часть диска.

Ответ 7

Я не могу решить вашу точную проблему, но я могу дать вам предложение.

Ваше ключевое требование: "И не нужно авторегистрацию ссылки".....

Итак, вам нужно будет ознакомиться с "элементами решения"

См. здесь:

Добавление элементов уровня решения в пакете NuGet

Вам нужно будет написать voodoo с powershell, чтобы получить копию вашей родной DLL в свой дом (опять же, потому что вы НЕ хотите, чтобы огонь с добавлением ссылок был добавлен)

Вот файл ps1, который я написал....., чтобы поместить файлы в папку сторонних ссылок.

Существует достаточно для вас, чтобы выяснить, как скопировать вашу родную DLL в какой-нибудь "домашний"... без необходимости запускать с нуля.

Опять же, это не прямой удар, но лучше, чем ничего.

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" 

Ответ 8

Это старый вопрос, но сейчас у меня такая же проблема, и я нашел поворот, который немного сложный, но очень простой и эффективный: создайте в стандартной папке содержимого Nuget следующую структуру с одной подпапкой для каждой конфигурации:

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

Когда вы упаковываете файл nuspec, вы получите следующее сообщение для каждой родной библиотеки в папках Debug и Release:

Проблема: сборка вне папки lib. Описание: сборка 'Content\Bin\Debug \??????. Dll' не находится в папке 'lib' и поэтому он не будет добавлен в качестве ссылки при установке пакета в проект. Решение. Переместите его в папку "lib", если это необходимо. ссылаться.

Нам не нужно такое "решение", потому что это только наша цель: встроенные библиотеки не добавляются как ссылки NET Assemblies.

Преимущества:

  • Простое решение без громоздких скриптов со странными эффектами, которые трудно выполнить reset при удалении пакета.
  • Nuget управляет родными библиотеками как любой другой контент при установке и удалении.

Недостатки:

  • Вам нужна папка для каждой конфигурации (но обычно есть только две: Debug и Release, и если у вас есть другой контент, который должен быть установлен в каждой папке конфигурации, это либо путь)
  • Собственные библиотеки должны дублироваться в каждой папке конфигурации (но если у вас есть разные версии родных библиотек для каждой конфигурации, это либо путь)
  • Предупреждения для каждой родной DLL в каждой папке (но, как я уже сказал, предупреждения они выдаются создателю пакета во время пакета, а не пользователю пакета на время установки VS)

Ответ 9

Поместите это папка содержимого

команда nuget pack [projfile].csproj сделает это автоматически, если вы помечаете файлы как содержимое.

затем отредактируйте файл проекта, как указано здесь, добавив элементы ItemGroup и NativeLibs и None

<ItemGroup>
    <NativeLibs Include="$(MSBuildThisFileDirectory)**\*.dll" />
    <None Include="@(NativeLibs)">
      <Link>%(RecursiveDir)%(FileName)%(Extension)</Link>
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
</ItemGroup>

работал у меня