Добавить исходные файлы из пакета 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
Те же директории 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>
работал у меня