NuGet:两个带有目标文件的包会导致 DLL 被错误地复制 [英] NuGet: Two packages with targets files lead to incorrectly copied DLLs
问题描述
我创建了两个包含本机库的 NuGet 包.第一个有两个子文件夹(amd64 和 x86),第二个包含构建目录下的 DLL flat.第一个包的 NativeLibraries 应该被复制到 OutputPath 的子文件夹中.第二个包中唯一的 DLL 应该被复制到 OutputPath 下.我使用以下 stackoverflow 条目作为创建包的指南:
如果是这样,第一个导入目标文件 flat.targets
文件与您的第一个 nuget 包的目标文件相同:
它将x86
和amd64
文件夹文件包含在output folder\x86
和output folder\amd64
文件夹中,正如我们所排除的那样.
但是,在pakckages.config
下,由于两个target文件在同一个csproj文件中,所以它们可以互相访问(它们在同样CSProj
),当msbuild在flat.targets
文件之后读取faltt.targets
文件时,你也没有改变NativeLibs
> 第二个目标文件的项目,它本身有文件:
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\amd64\Dll1.dllC:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\amd64\Dll2.dllC:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\x86\Dll1.dllC:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\x86\Dll1.dll
当读取flatt.targets
文件时,它还包含C:\Users\xxx\source\repos\flat\packages\flatt.1.0.0\build\x86\Dll1.dll
,
所以它在NativeLibs
项下有五个文件.
然后执行%(FileName)%(Extension)
,第一个nuget包的dll会在第二个nuget包的节点下执行,输出到输出根文件夹.
由于Packages.config
导入目标文件的特殊性,它们是交错的.
你应该注意
在PackageReference nuget管理格式下,导入目标文件存放在obj\xxx.csproj.nuget.g.targets
文件下,如下:
由于内容不在csproj
文件中以及xxx.csproj.nuget.g.targets
文件的特殊性,分别读取每个 nuget 包中的目标文件.每个文件都是一个独立的生命周期,所以不会相互影响.这也是最新PackagesReference nuget 管理格式的优势之一.
抱歉一开始没有告诉你我使用的是packagesReference,我没有注意到packages.config.
所以如果您想通过正确的行为将它们分开,请尝试以下两种方法:
1)不要改变两个nuget包,在安装两个nuget包的主工程的packages.config
文件上右键-->点击将packages.config迁移到PackageReference选项
之后,点击重建选项来获得它.
2) 修改 nuget 包-->将 NativeLibs
项的名称更改为第二个 nuget 包目标文件的另一个名称:
<项目组><NativeLibs1 Include="$(MSBuildThisFileDirectory)*.dll";/><None Include="@(NativeLibs1)"><Link>%(FileName)%(Extension)</Link><CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory></无></项目组></项目>
重新打包项目时,先卸载,删除C:\Users\xxx\.nuget\packages
下的所有缓存文件.然后再次重新安装软件包.
I have created two NuGet packages that contain Native Libraries. The first one has two subfolders (amd64 and x86) the second one includes the DLL flat under the build directory. The NativeLibraries of the the first package are supposed to be copied into subfolders of the OutputPath. The only DLL in the second package should be copied flat under the OutputPath. I used the following stackoverflow entry as a guide for creating the package: https://stackoverflow.com/a/30316946/4496150
The first NuGet package folder structure looks like this (amd64 and x86 subfolder under build):
- build
- amd64
- DLL1.dll
- DLL2.dll
- x86
- DLL1.dll
- DLL2.dll
- packagename.targets
- amd64
First targets file:
<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>
Please notice $(MSBuildThisFileDirectory)** as NativeLibs Include and %(RecursiveDir) as part of Link attribute.
The second NuGet package structure looks like this (no subfolders under build):
- build
- DLL1.dll
- packagename.targets
Second targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs Include="$(MSBuildThisFileDirectory)\*.dll" />
<None Include="@(NativeLibs)">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
Please notice $(MSBuildThisFileDirectory) (without **) as NativeLibs Include and missing %(RecursiveDir) in Link attribute.
If you reference both packages, the DLLs from the first package are additionally copied flat into the output folder, which is not desired. I suspect this is because the second include step also reads the DLLs from the first package, but then does not use %(RecursiveDir).
A workaround is to also include in the second package both. and NativeLibs Include="$(MSBuildThisFileDirectory)***.dll" /> exactly as in the first package.
However, I would prefer to understand why the second package ensures that the DLLs from the first are copied flat.
If I read https://docs.microsoft.com/de-de/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2019 correctly, $(MSBuildThisFileDirectory) points to the folder in my NuGet cache where the targets file of the NuGet package is located. So actually everything would be correct. But still (probably) wrong DLLs are copied.
Edit
I added the following snippet in both targets files to get some output:
<Target Name="OutputPackage1NativeLibs" BeforeTargets="Build">
<Message Text="Package1 %(NativeLibs.Identity)" Importance="high"/>
</Target>
For the seconds targets file I changed the target name to OutputPackage2NativeLibs and started the text Output with Packag2.
When I clean my NuGet Package Cache and rebuild the solution everything is fine. But after the third or forth rebuild operation the DLLs of the first package are copied flat under the Output path and I get the following output:
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL1.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL2.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL1.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL2.dll
Package1 C:\Users\USERNAME\.nuget\packages\PACKAGENAME2\1.0.0.0\build\DLL1.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL1.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\amd64\DLL2.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL1.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME1\1.2.3.4\build\x86\DLL2.dll
Package2 C:\Users\USERNAME\.nuget\packages\PACKAGENAME2\1.0.0.0\build\DLL1.dll
So NativeLibs are added from the other NuGet package apparently after the third or fourth rebuild.
I think you are using Packages.config
nuget package format with non-sdk net framework projects. If so, that could be explained.
For packages.config
nuget management format, it imports the target files directly under csproj file. In my side, I created two nuget packages called flat 1.0.0 and faltt 1.0.0 nuget packages.
You can check my csproj file:
If so, the first import targets file flat.targets
file is the same as your first nuget package's targets file:
It includes the x86
and amd64
folder files into output folder\x86
and output folder\amd64
folder, that is right as we excepted.
However, under pakckages.config
, since the two targets file are in the same csproj file, they can access each other(they have the same life cycle in the same CSProj
), when msbuild reads faltt.targets
file after flat.targets
file, also you did not change NativeLibs
item for the second targets file, itself has the file:
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\amd64\Dll1.dll
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\amd64\Dll2.dll
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\x86\Dll1.dll
C:\Users\xxx\source\repos\flat\packages\flat.1.0.0\build\x86\Dll1.dll
When reads the flatt.targets
file, it also includes C:\Users\xxx\source\repos\flat\packages\flatt.1.0.0\build\x86\Dll1.dll
,
So it has five files under NativeLibs
item.
And then it executes <Link>%(FileName)%(Extension)</Link>
, the first nuget package's dlls will be executed under the second nuget packages's node, output to the output root folder.
Because of the special nature of Packages.config
import targets files, they are interlaced.
You should note that
Under PackageReference nuget management format, the import targets files are stored under obj\xxx.csproj.nuget.g.targets
file, like this:
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)flatt\1.0.0\build\flatt.targets" Condition="Exists('$(NuGetPackageRoot)flatt\1.0.0\build\flatt.targets')" />
<Import Project="$(NuGetPackageRoot)flat\1.0.0\build\flat.targets" Condition="Exists('$(NuGetPackageRoot)flat\1.0.0\build\flat.targets')" />
</ImportGroup>
Since the content is not in csproj
file and the particularity of the file xxx.csproj.nuget.g.targets
, it will read the targets files in each nuget package separately. Each file is a separate life cycle, so it does not Will affect each other. This is also one of the advantages of the latest PackagesReference nuget management format.
Sorry for not telling you at the beginning that I was using packagesReference and I didn't notice packages.config.
So if you want to get the right behavior to separate them, try the two approaches:
1) do not change the two nuget packages, right-click on the packages.config
file of the main project which installs the two nuget packages-->click Migrate packages.config to PackageReference option
After that, click Rebuild option to get that.
2) modify the nuget packages--> change the NativeLibs
item's name to another for the second nuget package targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<NativeLibs1 Include="$(MSBuildThisFileDirectory)*.dll" />
<None Include="@(NativeLibs1)">
<Link>%(FileName)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
When re-pack your project, uninstall them first, delete all cache files under C:\Users\xxx\.nuget\packages
. And then re-install the packages again.
这篇关于NuGet:两个带有目标文件的包会导致 DLL 被错误地复制的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!