【问题标题】:How to specify output folder for the referenced nuget packages?如何为引用的 nuget 包指定输出文件夹?
【发布时间】:2019-05-02 05:10:56
【问题描述】:

我有一个项目,其中引用了一些 nuget 包。

在输出文件夹(bin\Debugbin\Release)中,所有引用的库都位于可执行文件旁边。

如何指定库的输出文件夹?
我想要 bin\Release\Libs 中的所有 nuget 库并在 bin\Release 中执行。

【问题讨论】:

  • 您可以在安装nuget包时使用OutputPath属性安装在特定文件夹中
  • 嗨,alex,您的项目类型是什么?请检查docs.microsoft.com/en-us/nuget/consume-packages/… 并安装包到 global-packages 文件夹,然后复制到项目的包文件夹中,构建项目我只能在 bin\Debug 或 bin\Release 下找到 projectname.dll 和 projectname.exe,执行你想改变那些文件的位置吗?如果是这样,请右键单击项目名称并卸载,编辑项目文件如xxx.csproj并修改Debug\Release的'OutputPath'的值,重新加载解决方案。
  • 如果要更改NuGet包的安装位置,我们可以在解决方案文件夹下创建一个项目级配置文件'nuget,config',使用 配置它,请检查这个类似的问题:stackoverflow.com/questions/4092759/…
  • @viter.alex 我不知道有任何内置功能可以更改在构建/发布时将程序集复制到的位置。您是否知道 .NET 默认情况下不会在子目录中加载程序集,因为除了构建文件更改之外,您还需要将应用配置为从该路径加载。
  • 无论如何,如果你决心这样做,我认为你需要成为一个构建专家。 MSBuild Structured Log Viewer 是一个很好的工具,可以帮助你。您需要查看 SDK 如何将 NuGet dll 复制到输出目录,查看是否有任何项目属性可以修改以执行您想要的操作,如果有,请编写一个运行并更新相关项目的 MSBuild 目标以添加/更改需要的属性。

标签: c# visual-studio msbuild nuget csproj


【解决方案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)">

所以,我猜我需要修改 _ResolvedFileToPublishAlways 项目的 RelativePath 元数据。

要更改的项目

旁注:MSBuild 没有公共/私有修改,因此通常使用约定。任何以下划线开头的内容都应被视为可能在不同版本之间更改的实现细节,因此最好使用不以下划线开头的内容,维护目标文件的团队应更加努力地不破坏兼容性。

所以,既然_ResolvedFileToPublishAlways 以下划线开头,让我们找出它是在哪里创建的。搜索它会将我带到 binlog 告诉我添加它的目标,在一个名为 _ComputeResolvedFilesToPublishTypes 的目标中,它的定义是

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

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

所以,我可以看到它只是将ResolvedFileToPublish 项目复制到新项目名称。寻找这些项目的创建位置,它位于一个名为 ComputeFilesToPublish 的目标中,并展开树以查看创建的所有项目及其元数据,我猜我要修改的项目都有 AssetType = runtime,其中非常适合我们需要使用的条件。

何时运行我们的目标

理想情况下,我会在 CopyFilesToPublishDirectory 之前运行,但是看看它的定义,我看到了

  <Target Name="CopyFilesToPublishDirectory"
          DependsOnTargets="_CopyResolvedFilesToPublishPreserveNewest;
                            _CopyResolvedFilesToPublishAlways" />

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

  1. DependsOnTargets 中列出的任何目标
  2. 将当前目标列为BeforeTargets 的任何目标
  3. 当前目标
  4. 将当前目标列为AfterTargets 的任何目标

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

编写我们的自定义目标

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

  <Target Name="RedirectRuntimeFilesToBinDirectory" AfterTargets="ComputeFilesToPublish">
    <ItemGroup>
      <ResolvedFileToPublish Condition=" '%(ResolvedFileToPublish.AssetType)' == 'runtime' ">
        <RelativePath>lib\%(RelativePath)</RelativePath>
      </ResolvedFileToPublish>
    </ItemGroup>
  </Target>

不幸的是,binlog 没有显示有关正在修改的元数据的详细信息,这在尝试调试构建问题以及为什么某些项目具有意外值时确实令人头疼,但无论如何我现在已经成功更改NuGet 依赖项的目的地,可能是项目到项目的引用,指向lib\ 目录。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 嗨,alex,我们很高兴听到您的问题得到解决,请在您有空闲时间时将您的回复标记为答案,这将有助于其他社区成员更轻松地搜索它,在此先感谢: )
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    相关资源
    最近更新 更多