指定 PackageReference 依赖项 DLL 的构建输出位置

Zev*_*itz 1 .net nuget .net-core

我有一个带有一些 NuGet 依赖项的项目,使用PackageReference

<ItemGroup>
    <PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
    <PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.19" />
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

我不希望将依赖项直接安装在bin\${buildConfiguration}\${framework}文件夹中(括号中的文件( )):

bin
    Debug
        net472
            (MyLibrary.dll)
            (Microsoft.CSharp.dll)
            (Microsoft.Xaml.Behaviors.Wpf.dll)
Run Code Online (Sandbox Code Playgroud)

相反,我希望每个构建的依赖项都位于更深的子文件夹中,如下所示:

bin
    Debug
        net472
            (MyLibrary.dll)
            MyLibrary
                (Microsoft.CSharp.dll)
                (Microsoft.Xaml.Behaviors.Wpf.dll)
Run Code Online (Sandbox Code Playgroud)

我知道我可以使用nuget.config文件来控制解决方案的包应下载到的位置,但 NuGet 依赖项的构建输出保持不变—— bin/release/framework 文件夹。

请注意,我想要的不仅仅是移动文件,这可以通过构建后步骤完成,并且不会很有用。我需要在构建时更改对依赖 DLL 的引用以引用子文件夹而不是根文件夹;所以我可以将根文件夹的全部内容作为一个整体复制到不同的位置,并且仍然可以工作。

MyLibrary.dll使用 SDK 格式的项目构建,该项目使用PackageReference; 它可以是 .NET Framework、.NET Core 或 .NET Standard。

我怎样才能做到这一点?


一些背景

为表达式编写了一个 Visual Studio调试可视化工具。调试可视化工具是手动复制的单个 DLL - 连同它们的依赖项 - 到 Documents 下的特定子文件夹 - 例如Visual Studio 2019\Visualizers- 或到VisualizersVS 安装文件夹的子文件夹。

如果有两个可视化器依赖于同一个库的不同版本,那么一个很容易被破坏。删除可视化工具是删除不需要的依赖项的事。

在为 .NET Core 或 .NET Standard 编写可视化工具时,需要创建多个 DLL 使情况更加复杂;每个 DLL 可能都有自己的依赖项。

如果可以将依赖项输出到具有相同名称的子文件夹,那将是朝着正确方向迈出的一步。

开发人员社区功能请求和(现已关闭)请求记录此问题的更好解决方案

ziv*_*kan 5

我只在最基本的场景中测试过这个,所以也许多目标项目需要对此进行修改,并且非 sdk 风格的项目可能与 sdk 风格的项目不同,但是:

调查

在调查任何 MSBuild 时,您需要了解的最重要的一点是二进制日志输出,可以使用MSBuild 结构化日志查看器查看

所以,我运行了dotnet new console, 和dotnet add package NuGet.Versioning,因为我真的需要在控制台应用程序中进行 SemVer2 比较。现在,我跑dotnet build -blstart msbuild.binlog

在 MSBuild 日志查看器中,从包中搜索程序集名称和单词 copy。就我而言,我搜索了“copy nuget.versioning.dll”并找到了一个结果。单击它,我看到消息是由名为“Copy”的任务输出的,该任务在名为“_CopyFilesMarkedCopyLocal”的目标中运行。单击树中的 Task Copy,它会在运行 Copy 任务的行上打开 Microsoft.Common.CurrentVersion.targets 的文本视图,我看到:

    <Copy
        SourceFiles="@(ReferenceCopyLocalPaths)"
        DestinationFiles="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')"
        SkipUnchangedFiles="$(SkipCopyUnchangedFiles)"
        OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
        Retries="$(CopyRetryCount)"
        RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
        UseHardlinksIfPossible="$(CreateHardLinksForCopyLocalIfPossible)"
        UseSymboliclinksIfPossible="$(CreateSymbolicLinksForCopyLocalIfPossible)"
        Condition="'$(UseCommonOutputDirectory)' != 'true'"
            >
Run Code Online (Sandbox Code Playgroud)

注意目的地DestinationFiles="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')"。好的,DestinationSubDirectory听起来很有希望。检查复制任务的参数,DestinationFiles 项没有任何DestinationSubDirectory设置,所以看来我可以将它设置为我想要的任何相对路径。

让我们搜索ReferenceCopyLocalPaths项目的定义位置。我看到“AddItem ReferenceCopyLocalPaths”的两个搜索结果,但检查它们的“调用堆栈”,我看到它们都在名为“ResolveReferences”的目标下。

最后一件事,由于这个问题是关于来自 PackageReference 的程序集,我想格外小心,所以我查看了 ReferenceCopyLocalPaths 项,并注意到它有一个名为 NuGetPackageId 的元数据项。

所以现在:

  • 我想在 ResolveReferences 目标之后运行我自己的目标
  • 我希望它更新 ReferenceCopyLocalPaths 项目
  • 定义 NuGetPackageId 元数据的地方
  • 将 DestinationSubDirectory 设置为某个路径

解决方案

将此目标添加到您的 csproj 中:

  <Target Name="CopyPackageAssembliesToSubFolder" AfterTargets="ResolveReferences">
    <ItemGroup>
      <ReferenceCopyLocalPaths Condition=" '%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' "
        Update="%(ReferenceCopyLocalPaths)"
        DestinationSubDirectory="libs\" />
    </ItemGroup>
  </Target>
Run Code Online (Sandbox Code Playgroud)

现在,当我运行时dotnet clean ; dotnet build,我看到 bin 目录中有一个libs/包含 NuGet.Versioning.dll的文件夹。