vit*_*lex 3 c# msbuild csproj visual-studio nuget
我有一个项目引用了一些 nuget 包。
在输出文件夹(bin\Debug或bin\Release)中,所有引用的库都位于可执行文件旁边。
如何指定库的输出文件夹?
我想要所有 nuget 库bin\Release\Libs并在bin\Release.
I woke up early this morning and decided to have a go at doing it myself. Turned out to be pretty quick, but that may be because of my (unfortunate) experience with looking into MSBuild files. Writing this post took me far longer than writing the target.
From your question, I assume you're using a traditional project, since SDK style projects only create the project's assembly in the bin directory. However, I much prefer SDK style projects because use can quickly and easily use the dotnet cli to create test projects and the csproj is much more easily editable. So, I'll give you my steps to find my solution for SDK style projects, and you need to follow along to do something similar with a traditional project.
So, we want to change where a files are being copied, which means we need to modify some items. Everything in MSBuild runs in a target, so we'll need to know when to run our custom target, what items to modify and probably what metadata of those items to modify. I created a new project, added some NuGet references then ran dotnet msbuild -t:publish -bl and opened the msbuild.binlog file.
Searching for the name of a dll that came from a nuget package, I find a message saying copied from ... to ..., so I click on it to go to the entry, and follow the tree back to the task, which I see is the built-in Copy task. The target path to the task is Publish -> _PublishBuildAlternative -> ComputeAndCopyFilesToPublisDirectory -> CopyFilesToPublishDIrectory -> _CopyResolvedFilesToPublishAlways. Double clicking the copy task I see
<Copy SourceFiles = "@(_ResolvedFileToPublishAlways)"
DestinationFiles="@(_ResolvedFileToPublishAlways->'$(PublishDir)%(RelativePath)')"
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">
Run Code Online (Sandbox Code Playgroud)
So, I can guess I need to modify the RelativePath metadata of an _ResolvedFileToPublishAlways item.
Side note: MSBuild doesn't have public/private modifies, so instead a convention is generally used. Anything starting with an underscore should be considered to be an implementation detail that could change between releases, so it's better to use things that do not start with an underscore, and the teams who maintain the targets file should try harder not to break compatibility.
So, since _ResolvedFileToPublishAlways starts with an underscore, let's find out where it was created. Searching for it takes me to a target where the binlog tells me it was added, in a target called _ComputeResolvedFilesToPublishTypes, and its definition is
<Target Name="_ComputeResolvedFilesToPublishTypes">
<ItemGroup>
<_ResolvedFileToPublishPreserveNewest Include="@(ResolvedFileToPublish)"
Condition="'%(ResolvedFileToPublish.CopyToPublishDirectory)'=='PreserveNewest'" />
<_ResolvedFileToPublishAlways Include="@(ResolvedFileToPublish)"
Condition="'%(ResolvedFileToPublish.CopyToPublishDirectory)'=='Always'" />
</ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)
So, I can see that it's simply copying ResolvedFileToPublish items to new item names. Looking for where those items are created, it's in a target named ComputeFilesToPublish, and expanding the tree to see all the items created and their metadata, I'm going to guess the items I want to modify all have AssetType = runtime, which is perfect for a condition we're going to need to use.
理想情况下我会在之前运行CopyFilesToPublishDirectory,但是看看它的定义我发现
<Target Name="CopyFilesToPublishDirectory"
DependsOnTargets="_CopyResolvedFilesToPublishPreserveNewest;
_CopyResolvedFilesToPublishAlways" />
Run Code Online (Sandbox Code Playgroud)
问题是,当 MSBuild 执行目标时,它按以下顺序运行:
DependsOnTargetsBeforeTargetsAfterTargets因此,虽然我想运行BeforeTargets='CopyFilesToPublishDirectory',但它将DependsOnTargets在我的目标之前运行,所以我不能这样做。所以我会选择跑步AfterTargets="ComputeFilesToPublish"。还有其他目标在这些目标之间运行,其中一个听起来可能会添加ResolvedFileToPublish项目,但在我当前的项目中,目标由于条件而无法运行,因此我的自定义目标可能不够通用,无法适用于所有项目。
现在我们知道我们的目标何时运行、它将修改哪些项目以及我们将如何修改它们的元数据。
<Target Name="RedirectRuntimeFilesToBinDirectory" AfterTargets="ComputeFilesToPublish">
<ItemGroup>
<ResolvedFileToPublish Condition=" '%(ResolvedFileToPublish.AssetType)' == 'runtime' ">
<RelativePath>lib\%(RelativePath)</RelativePath>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)
不幸的是,二进制日志没有显示有关正在修改的元数据的详细信息,这在尝试调试构建问题以及为什么某些项目具有意外值时确实很痛苦,但无论如何我现在已经成功更改了目标NuGet 依赖项,可能还有项目到项目的引用,到一个lib\目录。