title: How to check if a MSBuild-Task fails if using ContinueOnError=true
body: <p>I am running the <em>MSBuild</em> task with <strong><em>ContinueOnError</strong>=true</em>:</p>
  
  <pre class="lang-xml prettyprint-override"><code>&lt;MSBuild Projects="@(ComponentToDeploy)"
      Targets="$(DeploymentTargets)"
      Properties="$(CommonProperties);%(AdditionalProperties)"
      ContinueOnError="true" 
      Condition="%(Condition)"/&gt;
  </code></pre>
  
  <p>So my build always succeeds.</p>
  
  <p>Is there a way to find out if any error occurs?</p>
  
  <p>I could not find any <em>Output</em> of the <em>MSBuild</em> 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.</p>
  
  <p>(I am using MSBuild 4.0)</p>
  
  <hr>
  
  <p><em>This is an answer to the last feedback of @Ilya.</em> <br />
  <em>I'm using feedback/answer because of the length and formatting restrictions of the comments.</em></p>
  
  <blockquote>
    <p>Log is scoped to individual targets or to be more specific tasks...</p>
  </blockquote>
  
  <p>This was indeed the first question arose when I was reading your comment with the suggestion to use <code>Log.HasLoggedErrors</code>: "<strong>Was is the scope of the Log?</strong>". <br />
  Unfortunately I was not be able to finde a proper documentation. MSND does not help much...<br />
  Why did you know it is scoped to the task?<br/>
  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 <em>MSBuild</em> for years ;-) 
  <br />
  <br /></p>
  
  <blockquote>
    <p>In any case, what are you building as project?</p>
  </blockquote>
  
  <p>My test projects are very simple.<br />
  <em>MyTest.project</em></p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
  &lt;Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ElenasTarget" ToolsVersion="4.0"&gt;
  
      &lt;UsingTask AssemblyFile="$(MSBuildProjectDirectory)\MyCompany.Tools.MSBuild.Tasks.dll" TaskName="MSBuildWithHasLoggedErrors" /&gt;
  
      &lt;ItemGroup&gt;
          &lt;MyProjects Include="CopyNotExistingFile.proj" /&gt;
      &lt;/ItemGroup&gt;
  
      &lt;Target Name="ElenasTarget"&gt;
          &lt;MSBuildWithHasLoggedErrors Projects="@(MyProjects)" ContinueOnError="true" &gt;
              &lt;Output TaskParameter="HasLoggedErrors" PropertyName="BuildFailed" /&gt;
           &lt;/MSBuildWithHasLoggedErrors&gt;
  
           &lt;Message Text="BuildFailed=$(BuildFailed)" /&gt;
    &lt;/Target&gt;
  &lt;/Project&gt;
  </code></pre>
  
  <p>The <em>CopyNotExistingFile.proj</em> just tries to copy a file that does not exist:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
  &lt;Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Target1" ToolsVersion="4.0"&gt;
      &lt;Target Name="Target1"&gt; 
           &lt;Copy SourceFiles="C:\lalala.bum" DestinationFiles="C:\tralala.bam" /&gt;
    &lt;/Target&gt;
  &lt;/Project&gt;
  </code></pre>
  
  <p>And this is my custom task <em>MSBuildWithHasLoggedErrors</em></p>
  
  <pre class="lang-csh prettyprint-override"><code>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;
          }
      }
  }
  </code></pre>
  
  <p>If I build my MyTest.proj the <code>HasLoggedErrors</code>will be set to <code>false</code> although an error (<em>MSB3021</em>) was logged(?) to the console logger:</p>
  
  <pre><code>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.
  </code></pre>
  
  <p>My expectation was <code>HasLoggedErrors</code> would be set to <code>true</code>.</p>
  
  <p><br />
  <br /></p>
  
  <blockquote>
    <p>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</p>
  </blockquote>
  
  <p>I've already tried it (that were my investigations I meant in my post). Unfortunately it doesn't work either :-(<br/>
  (I am aware you said <em>in theory</em>).
  My new <strong>single</strong> project looks like this:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="utf-8" ?&gt;
  &lt;Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="ElenasTarget" ToolsVersion="4.0"&gt;
  
      &lt;UsingTask AssemblyFile="$(MSBuildProjectDirectory)\MyCompany.Tools.MSBuild.Tasks.dll" TaskName="MSBuildWithHasLoggedErrors" /&gt;
  
      &lt;Target Name="ElenasTarget"&gt;
          &lt;MSBuildWithHasLoggedErrors Projects="$(MSBuildProjectFile)" Targets="CopyNotExistingFile" ContinueOnError="true" &gt;
              &lt;Output TaskParameter="HasLoggedErrors" PropertyName="BuildFailed" /&gt;
           &lt;/MSBuildWithHasLoggedErrors&gt;
  
           &lt;Message Text="BuildFailed=$(BuildFailed)" /&gt;
    &lt;/Target&gt;
  
    &lt;Target Name="CopyNotExistingFile" &gt;
           &lt;Copy SourceFiles="C:\lalala.bum" DestinationFiles="C:\tralala.bam" /&gt;
    &lt;/Target&gt;
  &lt;/Project&gt;
  </code></pre>
  
  <p>If I build this project <code>HasLoggedErrors</code> will still be set to <code>false</code>. <br />
  (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 ).
  <br/>
  <br/></p>
  
  <blockquote>
    <p>or writing custom logger and passing it through command line</p>
  </blockquote>
  
  <p>That was my last hope!<br/>
  My "real" build <strong>has</strong> 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.<br/>
  <strong>BTW, I thought the console logger is a kind of "global" logger.</strong> Am I wrong?</p>
  
  <p>Anyway, the custom logger does not help neither, the <code>Log.HasLoggedErrors</code> is still set to <code>false</code>.<br/>
  <strong>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?</strong></p>
  
  <p>It really looks like <code>Log</code> is scoped to individual targets.</p>
  
  <p>Hmm... if the reflection on the buildengine instance is the last resort I would still prefer parsing the log.<br/>
  (Don't blame me! :-) )</p>
  
  <hr>
  
  <p><strong>My decision</strong> <br />
  After some investigations I've decided to stick with my initial solution: parse the log to find out if the build failed.</p>
  
  <p>Check my comments to see why I prefer that to the suggestions have been provided so far.</p>
  
  <p>If someone has some other ideas do not hesitate to share :-)</p>
  
  <p><em>(Otherwise this question can be closed, I suppose...)</em></p>