При подготовке дистрибутива почти любого проекта возникает необходимость выполнить в качестве одного из этапов сборки произвольную программу (минификатор скриптов, архиватор, создание инсталлятора и т.п.), и желательно, чтобы при ошибках в её выполнении сборка останавливалась.
Кстати, при этом MSBuild заверяет, что команда якобы завершилась с кодом -1 (наглая ложь), и указывает количество ошибок, равное количеству найденных слов "error" плюс единица, то есть подбивает в итог ещё и свой текст "error MSB3073: The command ... exited with code -1".
<Target Name="Target1">
<Exec Command="build-step-2.cmd" />
</Target>
</Project>
Вроде всё просто - запускается сценарий, который просто пишет что-то в консоль. Конец немного предсказуем:
исправленный билд-файл:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Target1" ToolsVersion="4.0">
<Target Name="Target1">
<Exec Command="build-step-2.cmd" IgnoreStandardErrorWarningFormat="true" CustomWarningRegularExpression=" WARN: " CustomErrorRegularExpression="Error in script" />
</Target>
</Project>
Ещё одна особенность - лучше явно указывать версию ToolsVersion в билд-файле, иначе у меня MSBuild по умолчанию считал ToolsVersion="2.0", и упомянутые опции считал несуществующими.
Как это реализовано в MSBuild: Exec Task - Runs the specified program or command by using the specified arguments.
У Exec есть одна особенность, о которой в документации прямо не упомянуто: "умное" определение ошибок выполнения. Если выполненная программа завершилась с ненулевым кодом, то Exec считает это ошибкой, но если программа завершилась с нулевым кодом, то Exec дополнительно сканирует её вывод (stdout, stderr) на наличие ключевых слов.
Само по себе завершение программы с ненулевым кодом не всегда считается ошибкой (например, у RoboCopy) - для этого у Exec есть параметр IgnoreExitCode="true".
Гораздо интереснее видеть, как warning при минификации JavaScript валит всю сборку:
C:\temp\msbuild>build.cmd
Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.18063]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 30-May-14 14:42:55.
Project "C:\temp\msbuild\build.msbuild" on node 1 (default targets).
Target1:
build-step-2.cmd
uglifyjs2 WARN : Dropping unused function argument error [amanda.js.src.js: 395,62] [C:\temp\msbuild\build.msbuild]
C:\temp\msbuild\build.msbuild(4,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
Done Building Project "C:\temp\msbuild\build.msbuild" (default targets) -- FAILED.
Build FAILED.
"C:\temp\msbuild\build.msbuild" (default target) (1) ->
(Target1 target) ->
uglifyjs2 WARN : Dropping unused function argument error [amanda.js.src.js: 395,62] [C:\temp\msbuild\build.msbuild]
C:\temp\msbuild\build.msbuild(4,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
0 Warning(s)
2 Error(s)
Time Elapsed 00:00:00.10
Это происходит потому, что хоть минификация и завершилась с нулевым кодом ошибки (с точки зрения минификатора, всё хорошо), но в тексте предупреждения, выданного минификатором, MSBuild всё-таки нашёл слово "error" - для него это достаточная причина считать задачу невыполненной.
При этом паттерны для поиска ошибок/предупреждений в разных версиях MSBuild немного отличаются. Например, строка " Push: Error: Empty database name.", которую выводил makensis в процессе формирования инсталлятора в формате NSIS (ну есть в инсталляторе проверки правильности настроек, соответственно есть и сообщения об ошибках), у MSBuild долгие годы не вызывала подозрений, и только после явного указания в билд-файле атрибута ToolVersion="4.0" тот же MSBuild стал считать такой текст сообщением об ошибке:
При этом паттерны для поиска ошибок/предупреждений в разных версиях MSBuild немного отличаются. Например, строка " Push: Error: Empty database name.", которую выводил makensis в процессе формирования инсталлятора в формате NSIS (ну есть в инсталляторе проверки правильности настроек, соответственно есть и сообщения об ошибках), у MSBuild долгие годы не вызывала подозрений, и только после явного указания в билд-файле атрибута ToolVersion="4.0" тот же MSBuild стал считать такой текст сообщением об ошибке:
C:\temp\msbuild>build.cmd
Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.18063]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 30-May-14 15:03:53.
Project "C:\temp\msbuild\build.msbuild" on node 1 (default targets).
Target1:
build-step-2.cmd
Push : error : Empty database name. [C:\temp\msbuild\build.msbuild]
C:\temp\msbuild\build.msbuild(6,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
Done Building Project "C:\temp\msbuild\build.msbuild" (default targets) -- FAILED.
Build FAILED.
"C:\temp\msbuild\build.msbuild" (default target) (1) ->
(Target1 target) ->
Push : error : Empty database name. [C:\temp\msbuild\build.msbuild]
C:\temp\msbuild\build.msbuild(6,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
C:\temp\msbuild\build.msbuild(6,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
0 Warning(s)
2 Error(s)
Time Elapsed 00:00:00.10
Как с этим бороться?
Использовать параметры CustomErrorRegularExpression, CustomWarningRegularExpression, IgnoreStandardErrorWarningFormat.Пример: вот такой билд-файл:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Target1" ToolsVersion="4.0"><Target Name="Target1">
<Exec Command="build-step-2.cmd" />
</Target>
</Project>
вот такой build-step-2.cmd:
@echo off
echo Error: file not found
echo Push: Error: Empty database name.
echo uglifyjs2 WARN: Dropping unused function argument error [amanda.js.src.js: 395,62]
C:\temp\msbuild>build.cmd
Microsoft (R) Build Engine version 4.0.30319.17929
[Microsoft .NET Framework, version 4.0.30319.18063]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 30-May-14 15:29:38.
Project "C:\temp\msbuild\build.msbuild" on node 1 (default targets).
Target1:
build-step-2.cmd
EXEC : error : file not found [C:\temp\msbuild\build.msbuild]
Push : error : Empty database name. [C:\temp\msbuild\build.msbuild]
uglifyjs2 WARN : Dropping unused function argument error [amanda.js.src.js: 395,62] [C:\temp\msbuild\build.msbuild]
Push : error : Empty database name. [C:\temp\msbuild\build.msbuild]
uglifyjs2 WARN : Dropping unused function argument error [amanda.js.src.js: 395,62] [C:\temp\msbuild\build.msbuild]
C:\temp\msbuild\build.msbuild(6,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
Done Building Project "C:\temp\msbuild\build.msbuild" (default targets) -- FAILED.
Build FAILED.
"C:\temp\msbuild\build.msbuild" (default target) (1) ->
(Target1 target) ->
EXEC : error : file not found [C:\temp\msbuild\build.msbuild]
Push : error : Empty database name. [C:\temp\msbuild\build.msbuild]
uglifyjs2 WARN : Dropping unused function argument error [amanda.js.src.js: 395,62] [C:\temp\msbuild\build.msbuild]
(Target1 target) ->
EXEC : error : file not found [C:\temp\msbuild\build.msbuild]
Push : error : Empty database name. [C:\temp\msbuild\build.msbuild]
uglifyjs2 WARN : Dropping unused function argument error [amanda.js.src.js: 395,62] [C:\temp\msbuild\build.msbuild]
C:\temp\msbuild\build.msbuild(6,3): error MSB3073: The command "build-step-2.cmd" exited with code -1.
0 Warning(s)
4 Error(s)
Time Elapsed 00:00:00.06
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Target1" ToolsVersion="4.0">
<Target Name="Target1">
<Exec Command="build-step-2.cmd" IgnoreStandardErrorWarningFormat="true" CustomWarningRegularExpression=" WARN: " CustomErrorRegularExpression="Error in script" />
</Target>
</Project>
- IgnoreStandardErrorWarningFormat="true" - заставляет MSBuild отказаться от своих убеждений и не сканировать вывод на наличие своих паттернов
- CustomWarningRegularExpression=" WARN: " - паттерн для поиска warning-ов в UglifyJS2
- CustomErrorRegularExpression="Error in script" - паттерн для поиска warning-ов в makensis
Ещё одна особенность - лучше явно указывать версию ToolsVersion в билд-файле, иначе у меня MSBuild по умолчанию считал ToolsVersion="2.0", и упомянутые опции считал несуществующими.
Комментариев нет:
Отправить комментарий