今天早上我很早就醒了,决定自己尝试一下。事实证明速度相当快,但这可能是因为我在查看 MSBuild 文件方面有(不幸的)经验。写这篇文章比写目标花了我更长的时间。
根据您的问题,我假设您使用的是传统项目,因为 SDK 样式项目仅在 bin 目录中创建项目的程序集。不过,我更喜欢 SDK 风格的项目,因为可以快速轻松地使用 dotnet cli 创建测试项目,并且 csproj 更容易编辑。因此,我将向您提供找到 SDK 样式项目解决方案的步骤,您需要按照传统项目执行类似的操作。
因此,我们想要更改文件的复制位置,这意味着我们需要修改一些项目。 MSBuild 中的所有内容都在目标中运行,因此我们需要知道何时运行自定义目标、要修改哪些项目以及可能要修改这些项目的哪些元数据。我创建了一个新项目,添加了一些 NuGet 引用,然后运行dotnet msbuild -t:publish -bl
并打开了msbuild.binlog
file.
要更改哪些元数据
搜索来自 nuget 包的 dll 的名称,我发现一条消息说复制自 ... 到 ...,因此我单击它转到条目,然后按照树返回到任务,该任务我看到是内置的复制任务。该任务的目标路径是 Publish -> _PublishBuildAlternative -> ComputeAndCopyFilesToPublisDirectory -> CopyFilesToPublishDIRECory -> _CopyResolvedFilesToPublishAlways。双击我看到的复制任务
<Copy SourceFiles = "@(_ResolvedFileToPublishAlways)"
DestinationFiles="@(_ResolvedFileToPublishAlways->'$(PublishDir)%(RelativePath)')"
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">
所以,我猜我需要修改RelativePath
的元数据_ResolvedFileToPublishAlways
item.
要更改什么项目
旁注: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'
, the DependsOnTargets
将在我的目标之前运行,所以我不能这样做。所以我会选择跑步AfterTargets="ComputeFilesToPublish"
。还有其他目标在这些目标之间运行,其中一个听起来可能会添加ResolvedFileToPublish
项目,但对于我当前的项目,目标由于条件而无法运行,因此我的自定义目标可能不够通用,无法适用于所有项目。
编写我们的自定义目标
现在我们知道我们的目标何时运行、它将修改哪些项目以及我们将如何修改它们的元数据。
<Target Name="RedirectRuntimeFilesToBinDirectory" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Condition=" '%(ResolvedFileToPublish.AssetType)' == 'runtime' ">
<RelativePath>lib\%(RelativePath)</RelativePath>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
不幸的是,二进制日志没有显示有关正在修改的元数据的详细信息,这在尝试调试构建问题以及为什么某些项目具有意外值时确实很痛苦,但无论如何,我现在已经成功更改了目标NuGet 依赖项,可能还有项目到项目引用、到lib\
目录。