Вопрос по – Как проверить, если MSBuild-Task не удается при использовании ContinueOnError = true

12

Я бегуMSBuild задача сContinueOnError=true:

<MSBuild Projects="@(ComponentToDeploy)"
    Targets="$(DeploymentTargets)"
    Properties="$(CommonProperties);%(AdditionalProperties)"
    ContinueOnError="true" 
    Condition="%(Condition)"/>

Так что моя сборка всегда удалась.

Есть ли способ узнать, происходит ли какая-либо ошибка?

Я не мог найти ни одногоOutput изMSBuild задание, содержащее эту информацию. Единственный способ, который я знаю, - это проанализировать файл журнала на наличие ошибок, но для меня это выглядит как обходной путь.

(Я использую MSBuild 4.0)

This is an answer to the last feedback of @Ilya.
I'm using feedback/answer because of the length and formatting restrictions of the comments.

Log is scoped to individual targets or to be more specific tasks...

Это был действительно первый вопрос, когда я читал ваш комментарий с предложением использоватьLog.HasLoggedErrors: & quot;Was is the scope of the Log?& Quot ;.
К сожалению, я не смог найти правильную документацию. MSND не сильно помогает ...
Почему вы знали, что это относится к задаче?
Я совсем не сомневаюсь в вашем утверждении! Мне просто интересно, есть ли где-нибудь надлежащая документация .. (Я не использовалMSBuild годами ;-)

In any case, what are you building as project?

Мои тестовые проекты очень просты.
MyTest.project

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ElenasTarget" ToolsVersion="4.0">

    <UsingTask AssemblyFile="$(MSBuildProjectDirectory)\MyCompany.Tools.MSBuild.Tasks.dll" TaskName="MSBuildWithHasLoggedErrors" />

    <ItemGroup>
        <MyProjects Include="CopyNotExistingFile.proj" />
    </ItemGroup>

    <Target Name="ElenasTarget">
        <MSBuildWithHasLoggedErrors Projects="@(MyProjects)" ContinueOnError="true" >
            <Output TaskParameter="HasLoggedErrors" PropertyName="BuildFailed" />
         </MSBuildWithHasLoggedErrors>

         <Message Text="BuildFailed=$(BuildFailed)" />
  </Target>
</Project>

CopyNotExistingFile.proj просто пытается скопировать файл, который не существует:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Target1" ToolsVersion="4.0">
    <Target Name="Target1"> 
         <Copy SourceFiles="C:\lalala.bum" DestinationFiles="C:\tralala.bam" />
  </Target>
</Project>

И это мое заданиеMSBuildWithHasLoggedErrors

namespace MyCompany.Tools.MSBuild.Tasks
{
    public class MSBuildWithHasLoggedErrors : Microsoft.Build.Tasks.MSBuild
    {
        [Output]
        public bool HasLoggedErrors { get; private set; }

        public override bool Execute()
        {
            try
            {
                base.Execute();
                HasLoggedErrors = Log.HasLoggedErrors;
            }
            catch (Exception e)
            {
                Log.LogErrorFromException(e, true);
                return false;
            }

            return true;
        }
    }
}

Если я создаю свой MyTest.projHasLoggedErrorsбудет установлен вfalse хотя ошибка (MSB3021) был зарегистрирован (?) в консоли журнала:

Project "C:\Users\elena\mytest.proj" on node 1 (default targets).
Project "C:\Users\elena\mytest.proj" (1) is building "C:\Users\elena\CopyNotExistingFile.proj" (2) on node 1 (default targets).
Target1:
  Copying file from "C:\lalala.bum" to "C:\tralala.bam".
C:\Users\elena\CopyNotExistingFile.proj(5,4): error MSB3021: Unable to copy file "C:\lalala.bum" to "C:\tralala.bam". Could not find file 'C:\lalala.bum'.
Done Building Project "C:\Users\elena\CopyNotExistingFile.proj" (default targets) -- FAILED.
ElenasTarget:
  BuildFailed=False
Done Building Project "C:\Users\elena\mytest.proj" (default targets).

Build succeeded.

Мое ожидание былоHasLoggedErrors будет установлен наtrue.



one way is to build self but with different target, for example your DefaultTargets one launches your custom MSBuildWrapper task pointing to itself (ie $(MSBuildProjectFile)) but with a different target that does other builds, copies

Я уже пробовал это (это были мои исследования, которые я имел в виду в своем посте). К сожалению, это тоже не работает :-(
(Я знаю, что вы сказали,in theory). My new single Проект выглядит так:

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ElenasTarget" ToolsVersion="4.0">

    <UsingTask AssemblyFile="$(MSBuildProjectDirectory)\MyCompany.Tools.MSBuild.Tasks.dll" TaskName="MSBuildWithHasLoggedErrors" />

    <Target Name="ElenasTarget">
        <MSBuildWithHasLoggedErrors Projects="$(MSBuildProjectFile)" Targets="CopyNotExistingFile" ContinueOnError="true" >
            <Output TaskParameter="HasLoggedErrors" PropertyName="BuildFailed" />
         </MSBuildWithHasLoggedErrors>

         <Message Text="BuildFailed=$(BuildFailed)" />
  </Target>

  <Target Name="CopyNotExistingFile" >
         <Copy SourceFiles="C:\lalala.bum" DestinationFiles="C:\tralala.bam" />
  </Target>
</Project>

Если я построю этот проектHasLoggedErrors все еще будет установлен вfalse.
(Более того, моя «настоящая» сборка, которую я сейчас поддерживаю, намного сложнее, содержит несколько файлов проекта с целями ... поэтому я не могу упаковать их все в один файл проекта).

or writing custom logger and passing it through command line

Это была моя последняя надежда!
Мой "настоящий" строитьhas пользовательский регистратор прошел через командную строку (я не использовал его для моего тестового проекта ради простоты). Это на самом деле создает журнал (файл XML), который я собираюсь проанализировать, чтобы выяснить, не были ли зарегистрированы какие-либо ошибки.
BTW, I thought the console logger is a kind of "global" logger. Я ошибся?

Во всяком случае, пользовательский регистратор не помогает ни,Log.HasLoggedErrors все еще настроен наfalse.
Is there some way I am not aware of to reference a particular logger (e.g. my custom logger) to ask if it has logged any errors?

Это действительно выглядитLog относится к отдельным целям.

Хм ... если бы отражение в экземпляре builddengine было последним средством, я все равно предпочел бы разобрать бревно.
(Не вини меня! :-))

My decision
После некоторых исследований я решил придерживаться своего первоначального решения: проанализировать журнал, чтобы выяснить, не удалась ли сборка.

Проверьте мои комментарии, чтобы понять, почему я предпочитаю, чтобы предложения были предоставлены до сих пор.

Если у кого-то есть другие идеи, не стесняйтесь поделиться :-)

(Otherwise this question can be closed, I suppose...)

Ваш Ответ

5   ответов
20

TheMSBuildLastTaskResult зарезервированное свойство будет установлен наTrue если последнее задание выполнено успешно иFalse если последняя задача не удалась:

<MSBuild Projects="@(ComponentToDeploy)"
         Targets="$(DeploymentTargets)"
         Properties="$(CommonProperties);%(AdditionalProperties)"
         ContinueOnError="true" 
         Condition="%(Condition)" />
<Message Text="MSBuild failed!" Condition="'$(MSBuildLastTaskResult)' == 'False'" />

Я считаю, что это было введено в MSBuild v4.0.

+ 1 за подсказку о новом зарезервированном свойстве, которое все еще отсутствует на сайте MSDN! Я не знал об этом. Это решение, которое я искал, спасибо! Elena
Если вы посмотрите внизу страницы зарезервированных свойств MSDN, вы увидите, что кто-то из сообщества любезно задокументировал несколько отсутствующих свойств. Удивительно, что официальная документация никогда не обновлялась. Aaron Jensen
Да, я прочитал этот комментарий только сегодня, и у меня даже есть последний выпуск книги Саида Ибрагима Хашими на моем столе ;-), но я не знал об этом свойстве, спасибо снова! Elena
3

что этот поток немного староват, но другое возможное решение, поскольку, как я полагаю, вам нужно было знать, что сборка не удалась для выполнения какой-то «последней задачи», заключается в использовании:

<OnError ExecuteTargets="FinalReportTarget;CleanupTarget" />

В случае ошибки сборка завершится неудачно, но выполните «FinalReportTarget» и «CleanupTarget».

ContinueOnError = "истинный" не требуется в этом случае.

Aleksey, спасибо за ваше предложение, но я хочу использовать "ContinueOnError =" true "". У меня есть несколько задач MSBuild в моем проекте, и я хочу выполнить их все, независимо от того, не справятся ли некоторые из них. Но в конце я хочу проверить, не удалось ли некоторым из них. Используя ваш подход, сборка будет прервана немедленно, когда одна из задач завершится неудачей. Elena
Это может быть не самым элегантным решением, но вы можете связать OnError в каждом отдельном теге, где вы ожидаете, что ошибка произойдет тогда. Кроме того, у вас может быть какая-то общая задача PrintError, например, <OnError ExecuteTargets = "PrintError; NextTask">, где в PrintError вы должны проверить MSBuildLastTaskResult. Alex M
0

TargetOutputs и потом проверяйте их на наличие ошибок, но это все еще довольно хакерски.

Хмм ... если нет лучшего способа, я лучше создаю пользовательскую задачу, анализируя мой лог-файл и подсчитывая ошибки (и предупреждения). Делает мой файл проекта более ясным .. @skolima в любом случае спасибо за быстрый ответ! Elena
Нет необходимости анализировать журнал, просто наследуйте от Microsoft.Build.Tasks.MSBuild и предоставьте вывод, который возвращает Log.HasLoggedErrors Ilya Kozhevnikov
Отличная идея, спасибо @IlyaKozhevnikov! Elena
Хорошо ... Я реализовал простой пример, и он работает не так, как ожидалось. Если ошибка была записанаMSBuild само задание (например, если файл проекта не может быть найдеLog.HasLoggedErrors возвращаетtrue. Но если другая задача внутри какой-либо цели (например,Copytask) вывел ошибкуLog.HasLoggedErrors возвращаетfalse. Что еще говорит MSDN оLogсвойство "Этот API поддерживает инфраструктуру .NET Framework и не предназначен для использования непосредственно из вашего кода.". Это заставляет меня нервничать ... звучит так, как будто его можно удалить в любое время ... Elena
0

Exec задача. Установите для IgnoreExitCode значение true и проверьте выходное значение ExitCode. Если не ноль, значит что-то не так.

Если вам нужен список ошибок сборки, используйте / fileloggerparameters переключатель командной строки чтобы регистрировать ошибки только в каком-то определенном файле:

/ Flp1: файл_журнал = errors.txt; errorsonly

Я только хочу проверить, еслиMSBuild задача не выполнена, но я не хочу использоватьExec задание вместоMSBuild задача из-за преимуществ последней. Elena
0

Copytask) вызвала ошибку, Log.HasLoggedErrors возвращает false.

Не знал, что комментарии имеют ограничения по длине ...

Log ограничен индивидуальными целями или более конкретными задачами, и (насколько я знаю) нет способа получить «глобальный», возможно, путем размышлений об экземпляре builddengine или написания собственного logger и передавая это через командную строку. В любом случае, что вы строите как проект? HasLoggedErrors работает должным образом (и работает без изменений в течение многих лет), он показывает, регистрируются ли в процессе сборки какие-либо ошибки. Он не имеет и не должен иметь никакого контроля над регистрацией других задач (которые могут использовать другие типы регистраторов). Если вам нужна глобальная, один из способов - это создать себя, но с другой целью, например, ваши DefaultTargets запускают вашу собственную задачу MSBuildWrapper, указывающую на себя (т. Е. $ (MSBuildProjectFile)), но с другой целью, которая выполняет другие сборки, копирует, так далее,в теори должно имитировать глобальные ошибки HasLoggedErrors ...

Я признаю, что должен подробно описать свои «расследования». Я только что сделал это, см. Мой ответ. В любом случае, я очень ценю ваши предложения и желание помочь! : -) Elena

Похожие вопросы