Я проснулся рано утром и решил попробовать сделать это сам. Оказалось, довольно быстро, но это может быть из-за моего (неудачного) опыта просмотра файлов MSBuild. Написание этого поста заняло у меня гораздо больше времени, чем написание цели.
Исходя из вашего вопроса, я предполагаю, что вы используете традиционный проект, поскольку проекты в стиле SDK создают только сборку проекта в каталоге bin. Однако я предпочитаю проекты в стиле SDK, потому что пользователь может быстро и легко использовать dotnet cli для создания тестовых проектов, а csproj гораздо проще редактировать. Итак, я расскажу вам, как найти свое решение для проектов в стиле SDK, и вам нужно проделать что-то подобное с традиционным проектом.
Итак, мы хотим изменить место копирования файлов, а это значит, что нам нужно изменить некоторые элементы. Все в MSBuild выполняется в цели, поэтому нам нужно знать, когда нужно запускать нашу собственную цель, какие элементы изменять и, возможно, какие метаданные этих элементов нужно изменить. Я создал новый проект, добавил несколько ссылок NuGet, затем запустил dotnet msbuild -t:publish -bl
и открыл файл msbuild.binlog
.
Какие метаданные изменить
В поисках имени dll, полученного из пакета nuget, я нахожу сообщение, скопированное из ... в ..., поэтому я нажимаю на него, чтобы перейти к записи, и проследовать по дереву обратно к задача, которую я вижу, является встроенной задачей копирования. Целевой путь к задаче - Опубликовать -> _PublishBuildAlternative -> ComputeAndCopyFilesToPublisDirectory -> CopyFilesToPublishDIrectory -> _CopyResolvedFilesToPublishAlways. Двойной щелчок по заданию копирования я вижу
<Copy SourceFiles = "@(_ResolvedFileToPublishAlways)"
DestinationFiles="@(_ResolvedFileToPublishAlways->'$(PublishDir)%(RelativePath)')"
OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"
UseHardlinksIfPossible="$(CreateHardLinksForPublishFilesIfPossible)"
UseSymboliclinksIfPossible="$(CreateSymbolicLinksForPublishFilesIfPossible)">
Итак, я могу предположить, что мне нужно изменить метаданные RelativePath
элемента _ResolvedFileToPublishAlways
.
Какой предмет изменить
Примечание: MSBuild не имеет публичных / приватных модификаций, поэтому вместо этого обычно используется соглашение. Все, что начинается с подчеркивания, должно рассматриваться как деталь реализации, которая может меняться между выпусками, поэтому лучше использовать вещи, которые не начинаются с подчеркивания, и командам, которые поддерживают файл целей, следует стараться не нарушать совместимость.
Итак, поскольку _ResolvedFileToPublishAlways
начинается с подчеркивания, давайте выясним, где оно было создано. В результате поиска он попадает в цель, в которой журнал сообщает, что она была добавлена, в цель с именем _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\
.