【问题标题】:NuGet: Two packages with targets files lead to incorrectly copied DLLsNuGet:带有目标文件的两个包导致错误复制的 DLL
【发布时间】:2021-07-15 18:43:10
【问题描述】:

我创建了两个包含本机库的 NuGet 包。第一个有两个子文件夹(amd64 和 x86),第二个包含构建目录下的 DLL flat。第一个包的 NativeLibraries 应该被复制到 OutputPath 的子文件夹中。第二个包中唯一的 DLL 应该被复制到 OutputPath 下。我使用以下 stackoverflow 条目作为创建包的指南:https://stackoverflow.com/a/30316946/4496150

第一个 NuGet 包文件夹结构如下所示(amd64 和 x86 子文件夹正在构建中):

  • 构建
    • amd64
      • DLL1.dll
      • DLL2.dll
    • x86
      • DLL1.dll
      • DLL2.dll
    • 包名.targets

第一个目标文件:

<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>

请注意 $(MSBuildThisFileDirectory)** 作为 NativeLibs Include 和 %(RecursiveDir) 作为 Link 属性的一部分。

第二个 NuGet 包结构如下所示(没有正在构建的子文件夹):

  • 构建
    • DLL1.dll
    • 包名.targets

第二个目标文件:

<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>

请注意 $(MSBuildThisFileDirectory)(不带 **)作为 NativeLibs Include 并且在 Link 属性中缺少 %(RecursiveDir)。

如果您同时引用这两个包,则第一个包中的 DLL 会额外复制到输出文件夹中,这是不需要的。我怀疑这是因为第二个包含步骤也从第一个包中读取 DLL,但随后不使用 %(RecursiveDir)。

一种解决方法是将两者都包含在第二个包中。 和 NativeLibs Include="$(MSBuildThisFileDirectory)***.dll" /> 与第一个包中的完全相同。

但是,我更愿意理解为什么第二个包确保第一个包中的 DLL 被平面复制。

如果我正确读取了https://docs.microsoft.com/de-de/visualstudio/msbuild/msbuild-reserved-and-well-known-properties?view=vs-2019,$(MSBuildThisFileDirectory) 将指向我的 NuGet 缓存中 NuGet 包的目标文件所在的文件夹。所以实际上一切都是正确的。但仍然(可能)复制了错误的 DLL。

编辑

我在两个目标文件中添加了以下 sn-p 以获得一些输出:

<Target Name="OutputPackage1NativeLibs" BeforeTargets="Build">
    <Message Text="Package1 %(NativeLibs.Identity)" Importance="high"/>
</Target>

对于 seconds 目标文件,我将目标名称更改为 OutputPackage2NativeLibs 并使用 Packag2 开始文本输出。

当我清理我的 NuGet 包缓存并重建解决方案时,一切都很好。但是在第三次或第四次重建操作之后,第一个包的 DLL 被复制到输出路径下,我得到以下输出:

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

所以 NativeLibs 显然是在第三次或第四次重建之后从另一个 NuGet 包添加的。

【问题讨论】:

  • 我认为您正在使用具有 packages.config nuget 包管理格式的非 sdk 网络框架项目,对吧?在我这边,将 PackageReference 与 new-sdk 项目一起使用可以获得正确的行为。事实上,这两个目标文件不应该相互访问,因为它们是两个不同的单个 nuget 包。
  • 我使用的是非 SDK 网络框架项目,但使用了 PackageReference。

标签: nuget target


【解决方案1】:

我认为您正在将Packages.config nuget 包格式与非sdk 网络框架项目一起使用。如果是这样,那可以解释。

对于packages.confignuget管理格式,直接在csproj文件下导入目标文件。在我这边,我创建了两个 nuget 包,分别称为 flat 1.0.0 和 faltt 1.0.0 nuget 包。

您可以查看我的 csproj 文件:

如果如此,则第一个导入目标文件flat.targets 文件与您的第一个 nuget 包的目标文件相同:

它将x86amd64文件夹文件包含到output folder\x86output folder\amd64文件夹中,正如我们所例外的那样。

不过,在pakckages.config下,由于两个targets文件在同一个csproj文件中,所以可以互相访问(在同一个CSProj下的生命周期相同),当msbuild 在flat.targets 文件之后读取faltt.targets 文件,您也没有为第二个目标文件更改NativeLibs 项,它本身就有该文件:

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

读取flatt.targets文件时,还包含C:\Users\xxx\source\repos\flat\packages\flatt.1.0.0\build\x86\Dll1.dll

所以它在NativeLibs item 下有五个文件。

然后执行&lt;Link&gt;%(FileName)%(Extension)&lt;/Link&gt;,第一个nuget包的dll会在第二个nuget包的节点下执行,输出到输出根目录。

由于Packages.config导入目标文件的特殊性,它们是交错的。

你应该注意

PackageReference nuget 管理格式下,导入目标文件存放在obj\xxx.csproj.nuget.g.targets 文件下,如下所示:

 <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>

由于内容不在csproj文件中,而且xxx.csproj.nuget.g.targets文件的特殊性,它会分别读取每个nuget包中的targets文件。每个文件都是一个独立的生命周期,所以不会相互影响。这也是最新的 PackagesReference nuget 管理格式的优势之一。

很抱歉一开始我没有告诉你我正在使用 packagesReference 并且我没有注意到 packages.config。

所以如果您想获得正确的行为来区分它们,请尝试以下两种方法:

1)不要更改这两个nuget包,右键单击安装这两个nuget包的主项目的packages.config文件-->点击Migrate packages.config到 PackageReference 选项

之后,点击Rebuild选项来获得它。

2) 修改 nuget 包--> 将第二个 nuget 包目标文件的 NativeLibs 项目名称更改为另一个:

<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>

重新打包项目时,先将其卸载,删除C:\Users\xxx\.nuget\packages下的所有缓存文件。然后重新安装软件包。

【讨论】:

  • 谢谢你的回复,我现在对这个过程有了更好的理解。但是对于具有 PackageReferences 的非 sdk .NET Framework 项目也存在问题。然而,对我来说,只有在第三次或第四次重建之后(我不知道为什么)。你能重现这种行为吗?
  • 我在最初的问题中添加了一些信息,以便更容易重现和调查。
  • 两个目标OutputPackage1NativeLibsOutputPackage2NativeLibs 无法显示此问题下的内容,因为它们都依赖于构建。当它在构建过程之前执行时,NativeLibs 已经包含五个文件,因为 NativeLibs 项目一直在增加但不是空的。(项目在所有目标之前被读取得很远)。由于它们都依赖于构建,所以它们总是相同的。所以你的所作所为不能作为判断标准。所以他们无法通过使用某些目标来显示文件。
  • 我的意思是msbuild项目读取NativeLibs然后在obj\xxx.csproj.nuget.g.targets期间复制到输出文件夹是分开的,它在所有目标之前执行。当您使用一些目标来获取这些项目时,它已经完成了所有复制步骤并显示了两个包中的所有文件,然后覆盖。在我身边。它显示得很好,并且复制的文件在多次重建时不会相互混淆。不确定您的obj\xxx.csproj.nuget.g.targets 是否仍然是错误的先前文件并且重建(清理+构建)不会删除它。它由还原处理。
  • 因此,我们的判断是根据最终输出文件夹中的文件是否正确。请删除binobj 文件夹。然后,再次重建以检查。更好的是你应该在两个nuget目标文件中将NativeLibs项目更改为两个不同的名称,在这种情况下你不会遇到任何问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-05
  • 2011-02-26
  • 1970-01-01
  • 2017-09-01
相关资源
最近更新 更多