我正在尝试为确实拼凑到本地win32 dll的.Net程序集创建NuGet程序包。我需要将程序集和本机dll打包在一起,并将程序集添加到项目引用中(此部分没有问题),本机dll应复制到项目输出目录或其他相对目录中。
我的问题是:
- 如何在不使用Visual Studio尝试将其添加到引用列表的情况下打包本机dll?
- 我必须编写一个install.ps1来复制本机dll吗?如果是这样,我如何访问包装内容以进行复制?
我正在尝试为确实拼凑到本地win32 dll的.Net程序集创建NuGet程序包。我需要将程序集和本机dll打包在一起,并将程序集添加到项目引用中(此部分没有问题),本机dll应复制到项目输出目录或其他相对目录中。
我的问题是:
Answers:
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
和x64
目录将在项目的输出目录生成时创建。如果你不需要子目录,则**
和%(RecursiveDir)
可以被删除,而不是包括在所要求的文件build
直接目录。其他所需的内容文件也可以以相同的方式添加。
None
在Visual Studio中打开时,在目标文件中添加为目标文件的文件将不会显示在项目中。如果您想知道为什么我不使用Content
nupkg中的文件夹,那是因为没有设置Powershell脚本就无法设置CopyToOutputDirectory
元素(该脚本只能在Visual Studio中运行,而不能在构建服务器或内部的命令提示符下运行)其他IDE,并且在project.json / xproj DNX项目中不受支持),我更喜欢对Link
文件使用,而不是在项目中包含文件的额外副本。
更新:
尽管这应该也可以解决,Content
而不是None
看起来msbuild中存在错误,所以文件不会被复制到引用项目中,而已删除一个步骤(例如proj1-> proj2-> proj3,proj3不会获取文件)从proj1的NuGet包中获取,但是proj2可以)。
'$(MSBuildThisFileDirectory)' != '' And HasTrailingSlash('$(MSBuildThisFileDirectory)')
需要这种条件?我认为MSBuildThisFileDirectory
总是这样。什么时候不是这样?
**\*.dll
吗?那就是复制所有.dll
目录中的所有文件。您可以轻松**\*.*
地复制整个目录树。
最近,当我尝试构建一个EmguCV NuGet程序包时遇到了同样的问题,该程序包包括托管程序集和非托管共享库(也必须放置在x86
子目录中),每次构建后都必须将它们自动复制到构建输出目录中。 。
这是我想出的一种解决方案,仅依赖于NuGet和MSBuild:
将托管程序集放在/lib
程序包的目录(上图)中,并将非托管共享库和相关文件(例如.pdb程序包)放在/build
子目录中(如NuGet docs中所述)。
将所有非托管*.dll
文件结尾重命名为不同的名称,例如,*.dl_
以防止NuGet抱怨抱怨被放在错误位置的程序集(“问题:lib文件夹外的程序集。”)。
<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" />
为项目添加一个新项目“ Build Action”(也可以在Visual Studio的“ Build Action”下拉列表中找到)。
<NativeBinary Include="...
将放置在/build/x86
当前项目中的本机库添加到并使自定义目标可以访问的本地库中,这些目标将这些文件复制到输出目录中。
<TargetPath>x86</TargetPath>
将自定义元数据添加到文件,并告诉自定义目标将本机文件复制到x86
实际输出目录的子目录。
该<PrepareForRunDependsOn ...
块将自定义目标添加到构建所依赖的目标列表中,有关详细信息,请参见Microsoft.Common.targets文件。
定制目标CopyNativeBinaries
包含两个复制任务。第一个负责将任何*.dl_
文件复制到输出目录,同时将其扩展名更改回原始文件*.dll
。第二个简单地将其余*.pdb
文件(例如任何文件)复制到同一位置。可以用一个复制任务和一个install.ps1脚本代替该脚本,该脚本必须在软件包安装期间将所有*.dl_
文件重命名为*.dll
。
但是,此解决方案仍然不会将本机二进制文件复制到引用最初包含NuGet包的另一个项目的输出目录。您仍然还必须在“最终”项目中引用NuGet包。
DllNotFoundException
抛出。
<NoWarn>NU5100</NoWarn>
到项目文件
下面是使用替代.targets
以项目注入机DLL具有以下属性。
Build action
= None
Copy to Output Directory
= Copy if newer
此技术的主要好处是将本机DLL复制到相关项目的bin/
文件夹中传递地。
查看.nuspec
文件的布局:
这是.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/
夹中设置目标文件名的元素。
有点晚了,但是我为此创建了一个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
有一个纯粹的C#解决方案,我觉得它很容易使用,并且不必担心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");
,它将在需要之前将库解压缩到磁盘。当然,您需要确保对磁盘的该部分具有写权限。
这是一个老问题,但是我现在遇到了同样的问题,并且发现了一个棘手的问题,但是非常简单有效:在Nuget标准内容文件夹中,为每个配置创建以下结构和一个子文件夹:
/Content
/bin
/Debug
native libraries
/Release
native libraries
打包nuspec文件时,对于Debug和Release文件夹中的每个本机库,您将收到以下消息:
问题:在lib文件夹之外的程序集。说明:程序集“ Content \ Bin \ Debug \ ??????。dll”不在“ lib”文件夹中,因此,将程序包安装到项目中时,不会将其添加为引用。解决方案:如果需要引用,请将其移至“ lib”文件夹。
我们不需要这种“解决方案”,因为这只是我们的目标:不将本机库添加为NET程序集引用。
优点是:
缺点是:
我无法解决您的确切问题,但是我可以给您一个建议。
您的主要要求是:“并且不要让它自动注册参考”。
因此,您必须熟悉“解决方案项”
请参阅此处的参考:
您必须编写一些powershell voodoo才能将本机dll的副本放到主目录中(再次,因为您不希望触发自动添加引用的voodoo)
这是我写的一个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"
把它放在内容文件夹中
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>
为我工作