今天早上我起得很早,决定自己动手。结果很快,但这可能是因为我(不幸)查看 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 执行目标时,它会按以下顺序运行:
-
DependsOnTargets 中列出的任何目标
- 将当前目标列为
BeforeTargets 的任何目标
- 当前目标
- 将当前目标列为
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\ 目录。