如何为引用的nuget包指定输出文件夹?

时间:2019-05-02 05:10:56

标签: c# visual-studio msbuild nuget csproj

我有一个引用了一些nuget包的项目。在输出文件夹bin \ Debug或bin \ Release中,所有引用的库都位于可执行文件的旁边。如何为库指定输出文件夹?我想要bin \ Release \ Libs中的所有nuget库和bin \ Release

中的可执行文件

2 个答案:

答案 0 :(得分:1)

我今天一大早醒来,决定自己去尝试一下。原来很快,但这可能是因为我(不幸的)体验了MSBuild文件。写这篇文章花了我比写目标更长的时间。

根据您的问题,我假设您使用的是传统项目,因为SDK样式的项目仅在bin目录中创建项目的程序集。但是,我更喜欢SDK风格的项目,因为使用它可以快速轻松地使用dotnet cli来创建测试项目,而csproj则更容易编辑。因此,我将为您找到针对SDK样式项目的解决方案的步骤,您需要遵循与传统项目相似的方法。

因此,我们想更改文件的复制位置,这意味着我们需要修改一些项目。 MSBuild中的所有内容都在目标中运行,因此我们需要知道何时运行我们的自定义目标,要修改的项目以及可能要修改的项目的元数据。我创建了一个新项目,添加了一些NuGet引用,然后运行dotnet msbuild -t:publish -bl并打开了msbuild.binlog文件。

要更改哪些元数据

搜索来自nuget程序包的dll的名称,我发现一条消息,说从...复制到...,因此我单击它以转到条目,然后按照树回到目录任务,我看到的是内置的复制任务。任务的目标路径是Publish-> _PublishBuildAlternative-> ComputeAndCopyFilesToPublisDirectory-> CopyFilesToPublishDIrectory-> _CopyResolvedFilesToPublishAlways。双击我看到的复制任务

    <Copy SourceFiles = "@(_ResolvedFileToPublishAlways)"
          DestinationFiles="@(_ResolvedFileToPublishAlways->'$(PublishDir)%(RelativePath)')"
          OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
          Retries="$(CopyRetryCount)"
          RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
          UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
          UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">

因此,我猜想我需要修改RelativePath项目的_ResolvedFileToPublishAlways元数据。

要更改的项目

侧面说明:MSBuild没有进行公共/私有修改,因此通常使用约定。任何以下划线开头的内容都应视为在发行版之间可能会更改的实现细节,因此最好使用不以下划线开头的内容,维护目标文件的团队应更努力地避免破坏兼容性。 / p>

因此,由于_ResolvedFileToPublishAlways以下划线开头,因此让我们找出它的创建位置。搜索它会将我带到一个目标,在该目标中二进制日志告诉我它已添加到一个名为_ComputeResolvedFilesToPublishTypes的目标中,其定义为

  <Target Name="_ComputeResolvedFilesToPublishTypes">
    <ItemGroup>
      <_ResolvedFileToPublishPreserveNewest Include="@(ResolvedFileToPublish)"
                                             Condition="'%(ResolvedFileToPublish.CopyToPublishDirectory)'=='PreserveNewest'" />

      <_ResolvedFileToPublishAlways Include="@(ResolvedFileToPublish)"
                                     Condition="'%(ResolvedFileToPublish.CopyToPublishDirectory)'=='Always'" />
    </ItemGroup>
  </Target>

因此,我可以看到它只是将ResolvedFileToPublish个项目复制到新的项目名称中。寻找在何处创建这些项目,它位于名为ComputeFilesToPublish的目标中,并展开树以查看所有创建的项目及其元数据,我想我要修改的所有项目都有{{1 }},非常适合我们需要使用的条件。

何时运行目标

理想情况下,我会在AssetType = runtime之前运行,但是看一下它的定义

CopyFilesToPublishDirectory

问题在于,当MSBuild执行目标时,它将按以下顺序运行:

  1. <Target Name="CopyFilesToPublishDirectory" DependsOnTargets="_CopyResolvedFilesToPublishPreserveNewest; _CopyResolvedFilesToPublishAlways" /> 中列出的任何目标
  2. 将当前目标列为DependsOnTargets的任何目标
  3. 当前目标
  4. 任何将当前目标列为BeforeTargets的目标

因此,尽管我想运行AfterTargets,但BeforeTargets='CopyFilesToPublishDirectory'将在我的目标之前运行,所以我不能这样做。因此,我将选择运行DependsOnTargets。在它们之间还有其他目标,听起来像它可能会添加AfterTargets="ComputeFilesToPublish"个项目,但由于当前条件,我的项目无法运行,因此我的自定义目标可能不够通用为所有项目工作。

编写我们的自定义目标

现在我们知道目标何时运行,它将修改哪些项目以及如何修改其元数据。

ResolvedFileToPublish

不幸的是,binlog并未显示有关正在修改的元数据的详细信息,这在尝试调试构建问题以及某些项目具有意外值的原因时确实是个麻烦,但是无论如何我现在都成功地进行了更改NuGet依赖项的目的地(可能是项目引用)到 <Target Name="RedirectRuntimeFilesToBinDirectory" AfterTargets="ComputeFilesToPublish"> <ItemGroup> <ResolvedFileToPublish Condition=" '%(ResolvedFileToPublish.AssetType)' == 'runtime' "> <RelativePath>lib\%(RelativePath)</RelativePath> </ResolvedFileToPublish> </ItemGroup> </Target> 目录。

答案 1 :(得分:0)

宽容zivkaninvestigation,我找到了答案。传统项目的目标CopyFilesToOutputDirectory取决于_CopyFilesMarkedCopyLocal目标。在这最后一个中,我们有任务Copy

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

在这里,我发现元数据DestinationSubDirectory正是我需要更改的。

所以最后

首先,我们需要更改csproj文件并添加以下行:

  <ItemDefinitionGroup>
    <ReferenceCopyLocalPaths>
      <DestinationSubDirectory>lib\</DestinationSubDirectory>
    </ReferenceCopyLocalPaths>
  </ItemDefinitionGroup>

第二,我们需要更改app.config文件以使程序集知道库的路径:

  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="lib;libs" />
    </assemblyBinding>
  </runtime>

仅此而已。所有引用的库将被复制到子文件夹lib