30 мая 2014 г.

Как MSBuild.Exec определяет ошибки при запуске third-party tools

При подготовке дистрибутива почти любого проекта возникает необходимость выполнить в качестве одного из этапов сборки произвольную программу (минификатор скриптов, архиватор, создание инсталлятора и т.п.), и желательно, чтобы при ошибках в её выполнении сборка останавливалась.

Как это реализовано в 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 стал считать такой текст сообщением об ошибке:

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.

    0 Warning(s)
    2 Error(s)

Time Elapsed 00:00:00.10



Кстати, при этом MSBuild заверяет, что команда якобы завершилась с кодом -1 (наглая ложь), и указывает количество ошибок, равное количеству найденных слов "error" плюс единица, то есть подбивает в итог ещё и свой текст "error MSB3073: The command ... exited with code -1".

Как с этим бороться?

Использовать параметры 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]
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]
  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", и упомянутые опции считал несуществующими.

Комментариев нет:

Отправить комментарий