How to check if a MSBuild-Task fails if using ContinueOnError=true

☆樱花仙子☆ 提交于 2019-12-20 18:25:27

问题


I am running the MSBuild task with ContinueOnError=true:

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

So my build always succeeds.

Is there a way to find out if any error occurs?

I could not find any Output of the MSBuild task containing this information. The only way I know is to parse the log file for errors but it looks like a workaround for me.

(I am using 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...

This was indeed the first question arose when I was reading your comment with the suggestion to use Log.HasLoggedErrors: "Was is the scope of the Log?".
Unfortunately I was not be able to finde a proper documentation. MSND does not help much...
Why did you know it is scoped to the task?
I'm not in doubt about your statement at all! I'm just wondering if there is a proper documentation somewhere.. (I haven't been using MSBuild for years ;-)

In any case, what are you building as project?

My test projects are very simple.
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>

The CopyNotExistingFile.proj just tries to copy a file that does not exist:

<?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>

And this is my custom task 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;
        }
    }
}

If I build my MyTest.proj the HasLoggedErrorswill be set to false although an error (MSB3021) was logged(?) to the console logger:

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.

My expectation was HasLoggedErrors would be set to 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

I've already tried it (that were my investigations I meant in my post). Unfortunately it doesn't work either :-(
(I am aware you said in theory). My new single project looks like this:

<?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>

If I build this project HasLoggedErrors will still be set to false.
(Furthermore, my "real" build I'm currently maintaining is much complexer containing several project files with targets... so I can't pack them all in a single project file ).

or writing custom logger and passing it through command line

That was my last hope!
My "real" build has a custom logger passed through the command line (I didn't use it for my test project for the sake of simplicity). That is actually producing the log (a XML file) I'm going to parse to find out if any errors have been logged.
BTW, I thought the console logger is a kind of "global" logger. Am I wrong?

Anyway, the custom logger does not help neither, the Log.HasLoggedErrors is still set to 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?

It really looks like Log is scoped to individual targets.

Hmm... if the reflection on the buildengine instance is the last resort I would still prefer parsing the log.
(Don't blame me! :-) )


My decision
After some investigations I've decided to stick with my initial solution: parse the log to find out if the build failed.

Check my comments to see why I prefer that to the suggestions have been provided so far.

If someone has some other ideas do not hesitate to share :-)

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


回答1:


The MSBuildLastTaskResult reserved property will be set to True if the last task succeeded and False if the last task failed:

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

I believe this was introduced with MSBuild v4.0.




回答2:


I know this thread is a bit old, but another possible solution, as I presume you needed to know that build failed in order to execute some "final task", is to use:

<OnError ExecuteTargets="FinalReportTarget;CleanupTarget" />

That would fail the build in case of error, but execute the "FinalReportTarget" and "CleanupTarget".

ContinueOnError="true" is not needed in this case.




回答3:


You could capture TargetOutputs and check them for error conditions afterwards, but that's still quite hackish.




回答4:


If you only want to check if MSBuild task failed, use Exec task. Set IgnoreExitCode to true and check ExitCode output value. If not zero, something is wrong.

If you need the list of build errors, use /fileloggerparameters command line switch to log errors only to some specific file:

/flp1:logfile=errors.txt;errorsonly




回答5:


But if another task inside some target (e.g. Copytask) raised an error the Log.HasLoggedErrors returns false.

Didn't know comments have length limits...

Log is scoped to individual targets or to be more specific tasks, and (as far as I'm aware) there is no way to get a "global" one, may be through reflection on the buildengine instance, or writing custom logger and passing it through command line. In any case, what are you building as project? HasLoggedErrors works as expected (and has been working unchanged for years), it shows if project being built logged any errors. It doesn't, and shouldn't, have any control over logging of other tasks (that might use other types of loggers). If you want a global one, 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, etc, in theory it should simulate a global HasLoggedErrors...



来源:https://stackoverflow.com/questions/10796733/how-to-check-if-a-msbuild-task-fails-if-using-continueonerror-true

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!