Как запустить MSBuild Target ПЕРЕД компиляцией, но только тогда, когда компиляция произойдет

У меня есть проект библиотеки С#, в котором есть некоторые зависимости, созданные в «BeforeBuild» с помощью PowerShell.

Кажется, что MSBuild каждый раз выполняет цель «BeforeBuild», даже если сам проект библиотеки не нуждается в сборке.

Я хотел бы настроить процесс сборки для запуска сценария PowerShell только в том случае, если библиотеке необходимо (повторно) собрать.

Есть ли способ сделать это?

Я пытался использовать цели «BeforeBuild» и «BeforeCompile». Но это, кажется, выполняется каждый раз.


person Ingo Karstein    schedule 05.08.2019    source источник
comment
Привет, друг, какие-нибудь обновления по этой проблеме?   -  person LoLance    schedule 12.08.2019


Ответы (2)


Вы не можете предсказать, произойдет ли компиляция, так как вы не можете знать, что могут или не могут делать цели после запуска вашей цели. Но вы можете предсказать, нужно ли выполнять вашу задачу, исходя из текущего состояния каталога сборки. Именно так вы бы реализовали добавочное построение в MsBuild, и это похоже на то, что вам действительно нужно.

Все цели имеют необязательный параметр Inputs and Outputs. Эти два используются для расчета того, изменились ли какие-либо входные данные с момента последней сборки, и на основе этого MsBuild решает, запускать ли вашу цель или можно ее пропустить.

Это требует некоторых знаний, основанных на текущем состоянии ItemGroups и свойств в вашей сборке, чтобы предсказать результаты вашей цели.

Когда входы и выходы не указаны, MsBuild не может выяснить, повлияет ли ваша цель на что-либо, поэтому она запустит ее, чтобы быть уверенным.

Документация дает хорошее объяснение о том, как включить поэтапное создание ваших пользовательских целей.

Чтобы предсказать результаты, вам нужно написать какое-то преобразование. Или вы можете записать какой-нибудь маркерный файл (например, codeanalysis.lastsucceeded) и использовать его для сравнения. Инкрементная сборка будет смотреть ТОЛЬКО на дату последнего изменения всех входных данных и сравнивать их со всеми выходными данными. Поэтому вам нужно выразить необходимость запуска или нет как операцию над этими данными.

Базовая структура:

<target name="RunMyPowerShell" 
    beforetargets="Compile" Inputs="@(Content)" 
    Outputs"@(Content->'%(Filename).translated.content')">
    ...
    <exec Command="powershell .\mypsfile.ps1" />
    ...
</targets>

В качестве альтернативы BeforeTargets="Build" вы можете перезаписать свойство BuildDependsOn:

<PropertyGroup>
    <CompileDependsOn>
        RunMyPowerShell;
        $(CompileDependsOn)
    </CompileDependsOn>
</PropertyGroup>

Если вы раньше не копались в этой области, все тонкости MsBuild могут быть ошеломляющими, и чтобы заставить эту работу работать, вам нужно освоить довольно много вещей. Также может потребоваться изменить то, что делает сценарий PowerShell, чтобы сделать его вывод достаточно предсказуемым, чтобы MsBuild мог творить чудеса.

Кроме того, вы можете добавить условие к своей цели на основе некоторого выражения, но, учитывая то, как файлы сборки анализируются, вычисляются и выполняются, может быть довольно сложно понять, как это сделать правильно.

Если вы все сделаете правильно, можно добиться значительного прироста производительности.

Смотрите также:

person jessehouwing    schedule 08.08.2019

Кажется, что MSBuild каждый раз выполняет цель «BeforeBuild», как будто сам проект библиотеки не нуждается в сборке.

Что вы имеете в виду, что msbuild каждый раз выполняет цель, даже если сам проект библиотеки не нуждается в сборке? Насколько я знаю, BeforeBuild является одной из необходимых целей в процессе сборки, поэтому, если вы создаете один проект, он всегда будет вызывать эту цель. Если проект библиотеки не нужно собирать, по какой причине вы вызываете msbuild для его сборки? Больше подробностей могло бы быть лучше :)

Я хотел бы настроить процесс сборки для запуска сценария PowerShell только в том случае, если библиотеке необходимо (повторно) собрать.

Есть ли способ сделать это?

Я пытался использовать цели «BeforeBuild» и «BeforeCompile». Но это, кажется, выполняется каждый раз.

BeforeBuild и BeforeCompile — предопределенные цели в системе msbuild. Вы можете перезаписать их, чтобы настроить шаги сборки, но вы не можете избежать их запуска в процессе сборки. Таким образом, они будут выполняться каждый раз, когда запускается сборка, это ожидаемое поведение по дизайну.

Согласно вашему описанию, вы можете перезаписать эти цели и запустить в них сценарий Power Shell. Вот почему на вашей машине всегда будет вызываться скрипт ps.

Вот обходной путь, если вы собираете проект с помощью msbuild.exe вместо VS IDE:

Предполагая, что вы перезаписали BeforeTarget в файле проекта. Вы можете добавить условие к определить, следует ли запускать специальный BeforeTarget(скрипт вызова ps) или оригинальный BeforeTarget.

Определите свойство с именем RunPS и добавьте условие в BeforeTarget, которое вы используете следующим образом:

  <PropertyGroup>
    <RunPS>false</RunPS>
  </PropertyGroup>

  <Target Name="BeforeBuild" Condition="$(RunPS)=='true'">
   <!--Run the ps script here-->  
  </Target>

Затем вы можете контролировать, использовать ли BeforeTarget по умолчанию или пользовательский, установив значение RunPS.

msbuild xx.csproj /p:RunPS=true для запуска скрипта ps во время сборки, msbuild xx.csproj для запуска оригинального BeforeTarget

И если вы создаете проект в VS IDE, вы можете создать новую конфигурацию (CustomDebug), скопированную из Debug или Release, и установить Condition="$(Configuration)=='CustomDebug'" в качестве цели.

Обновление:

Обходной путь, который работает в VS и командной строке:

См. этот документ, в VS мы можем легко создавать новые конфигурации, копируя настройки из конфигураций отладки и выпуска.

1. На мой взгляд, для вашего проекта библиотеки у вас есть обычные конфигурации отладки и выпуска, вы можете следовать советам в документе для создания соответствующих пользовательских конфигураций. Скопируйте параметр из Debug и создайте новую конфигурацию с именем PSDebug, скопируйте параметр из Release и создайте новую конфигурацию с именем PSRelease.

2.Затем отредактируйте xx.csproj, добавьте условие в таком формате:

  <Target Name="BeforeBuild" Condition="'$(Configuration)'=='PSDebug' OR '$(Configuration)'=='PSRelease'">
    <!--Run the ps script here-->
    <Message Text="showsth" Importance="high" />
  </Target>

Затем в VS IDE, если вы строите с обычной конфигурацией Debug or Release, она будет использовать цель по умолчанию BeforeBuild. И только когда вы строите с пользовательским PSDebug or PSRelease, он вызывает вашу пользовательскую цель BeforeBuild. В VS вы можете легко переключаться между этими конфигурациями с помощью этого поля:

введите здесь описание изображения

И для командной строки это тоже работает. Если вы используете такую ​​команду, как msbuild xx.csproj /p:Configuration=Debug, для этого используется предопределенная цель BeforeBuild, а если вы используете команду, подобную msbuild xx.csproj /p:Configuration=PSDebug, она вызывает вашу пользовательскую цель и запускает сценарий PS.

person LoLance    schedule 06.08.2019
comment
Благодарим за ваше предложение! Я понимаю, что это поведение по замыслу! Но я не могу использовать ваше решение, потому что оно должно работать и в VS. - person Ingo Karstein; 06.08.2019
comment
@IngoKarstein Смотрите мое обновление, так как это предусмотрено поведением дизайна, у нас нет возможности VS напрямую управлять им. Но vs позволяет нам создавать новую конфигурацию из Debug и Release, поэтому мы можем добиться поведения, настроив файл проекта. Надеюсь, поможет:) - person LoLance; 07.08.2019