MSBuild: By Example—Dealing with MSBuild Errors

Microsoft .NET Framework, ASP.NET, Visual C# (CSharp, C Sharp, C-Sharp) Developer Training, Visual Studio


Jump to: navigation, search
CSharp-Online.NET:Articles
Visual Studio Articles

MSBuild: By Example

© 2006 Hashimi & Hashimi

Dealing with MSBuild Errors

As you create new MSBuild targets, you are bound to encounter situations where errors occur. How should you deal with errors, and more important, how can you protect the integrity of the build when one occurs? MSBuild has two elements related to errors: the OnError element and the Error element. OnError specifies what to do in the case of an error, that is, what targets to execute. The Error call actually throws an error and calls any registered error-handling mechanisms. Let’s examine the simple case of handling an error. Refer to the following SimpleError.proj file:

<Project xmlns= "http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="ThrowError">
        <Error Text="Error in ThrowError target"/>
        <OnError ExecuteTargets="MessageErrorHandler"/>
    </Target>
 
    <Target Name="MessageErrorHandler">
        <Message Text="An error has occurred, build will be halted"/>
    </Target>
</Project>

In this project file, you have two targets: one that throws the error, ThrowError, and one that handles errors, MessageErrorHandler. Previously we mentioned that targets can have error handlers associated with them. The OnError element creates this association. This element has only two possible attributes: ExecuteTargets and Condition. The ExecuteTargets attribute is a semicolon-separated list of targets to execute in the case of an error. These targets will be executed in the order they are defined. The Condition attribute is the same here as it is for every other MSBuild element. If the condition is true, then the error handler listed in ExecuteTargets will be registered; if not, then they won’t. Now we’ll demonstrate the execution of the ThrowError target by invoking it with >msbuild SimpleError.proj /t:ThrowError. Figure 3-21 shows the output from executing this target.



Figure 3-21. Output from the ThrowError target


From Figure 3-21 you can see that the ThrowError target was invoked and an error was thrown. Following this, the MessageErrorHandler was invoked to deal with the error. From the output you can see the following line: C:\MSBuild\MSBuildExamples\ErrorExamples\SimpleError.proj(3,9): error : Error in ThrowError target. This tells you four facts:

The file that the error occurred in: SimpleError.proj

Where in that file it occurred: (3,9) = Line 3, position 9

What type of error it was: error (more on this in a bit)

The error message: Error in ThrowError target

When the error is logged, you can use this information to determine what caused it and how to resolve it. Let’s quickly examine the full syntax for the Error element before proceeding. The Error element has five attributes, as summarized in Table 3-4.

Table 3-4. Error Element Attributes

Name Description
Text Text that is sent to the error handler. If this is not present, then there will be no location information attached with the error; that is, you won’t know where in the project file to start looking.
Code This determines the type of error and will be sent to the logger. See the following example.
Condition Condition to determine whether to raise the error.
ContinueOnError If this is true, then the error will be converted to a warning, and the build will continue.
HelpKeyword A help keyword that will be sent to the logger.

We will demonstrate how to use the Code attribute in the next error-handling example. The previous example was simple but also usable for small projects. For larger, team-based applications, you may want to have a more flexible approach to error handling. For a better example, see the following Error1.proj project:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 
    <Import Project="ErrorHandler.targets"/>
    <PropertyGroup>
        <ErrorEmails>$(ErrorEmails);deployError@sedodream.com</ErrorEmails>
    </PropertyGroup>
    <!-- Uncomment for normal execution
    <PropertyGroup>
        <WebURL>sedodream.com</WebURL>
    </PropertyGroup>
    -->
 
    <Target Name="DeployToWebServer">
 
        <Error Text="Unable to connect to webserver" Code="Deploy" ~CCC
Condition=" '$(WebURL)' == '' "/>
        <!--
           contents to deploy to your Web server here
        -->
        <Message Text="Deployed project to Project $(MSBuildProjectName) ~CCC
to webserver at: $(WebURL)"/>
 
        <OnError ExecuteTargets="$(ErrorHandlers)"/>
    </Target>
 
    <Target Name="ProjectErrorHandler">
        <Message Text="An error has occurred, build stopped."/>
    </Target>
 
</Project>

This project has two targets, one property, and one import statement. As for the two targets, one is for execution, and the other is for error handling. The property defines the ErrorEmails value. The idea of the imported file is that you could have a project file, or set of files, that every project in your organization will import. These imported files could contain the standard error-handling mechanisms. The ErrorHandler.targets file is as follows:

<Project 
  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <ErrorHandlers>
            ProjectErrorHandler;
            SolutionErrorHandler;
            ErrorLogger;
            ErrorMailer;
        </ErrorHandlers>
        <ErrorEmails>errorHandler@sayedhashimi.com</ErrorEmails>
    </PropertyGroup>
 
    <Target Name="SolutionErrorHandler">
        <Message Text="An error has occurred in the solution; build failed"/>
    </Target>
    <Target Name="ErrorLogger">
        <Message Text="A build error has occurred and has been logged"/>
    </Target>
    <Target Name="ErrorMailer">
        <Message Text="An error has occurred and has been emailed ~CCC
to: $(ErrorEmails)"/>
    </Target>
</Project>

The ErrorHandler.targets file has three targets that relate to the error handling defined and two properties. The targets don’t do anything interesting, so we will not discuss them. Let’s look at the properties defined here and see how they provide the flexibility for which you are looking. The two properties defined here are ErrorHandlers and ErrorEmails. The ErrorHandlers property contains a semicolon-separated list of targets. This property is used in the OnError element to specify which targets to execute in the case of an error. From the four targets in that property, three are defined in the ErrorHandler.targets file; the other is implemented in the Error1.proj file. Because of this technique, you can completely change the way errors are handled across the organization without actually changing a single source project. Now you will move on to the remaining property.

The ErrorEmails property defined in the ErrorHandler.targets file provides an e-mail address that should be notified when an error occurs. Now let’s look at how the Error1.proj file adds to this; the snippet is as follows:

</CSO_Source>xml <PropertyGroup>

  <ErrorEmails>$(ErrorEmails);deployError@sedodream.com</ErrorEmails>

</PropertyGroup> </CSO_Source>

In this file, you are redefining the ErrorEmails property based on the previous definition of it. You are actually appending the e-mail deployError@sedodream.com to the list of e-mails to be notified in case of errors. Once again, if your organization needed to change who received e-mail notifications of errors, this could happen in one place as opposed to inside every project. To demonstrate the error handling, execute the DeployToWebServer target on the Error1.proj project file by with the following: >msbuild Error1.proj /t:DeployToWebServer. Figure 3-22 shows the output.



Figure 3-22. Output from DeployToWebServer target


From this output you can see that the error was raised inside DeployToWebServer. This was raised from the <Error Text="Unable to connect to webserver" Code="Deploy" Condition=" '$(WebURL)' == "/> element. This error was raised because the WebURL property has not been initialized. From this error you can see that the targets ProjectErrorHandler, SolutionErrorHandler, ErrorLogger, and ErrorMailer were executed in that order. This is the order in which they were defined in the ErrorHandler.targets file. Notice in this element the use of the Code attribute; once used, this information is passed to the loggers and may assist with determining how the error occurred. In the output the error type is error Deploy. Many organizations will use only integer values for this field, but you can use any string, as shown here.

When you approach a build, you must be careful to avoid errors and to easily detect when errors have occurred. You can use the MSBuild error-handling mechanisms to assist in this critical task. The previous target simply exposed the MSBuild error-handling mechanisms. This sample has shown how your organization can approach MSBuild error handling to increase flexibility and simplicity.


Previous_Page_.gif Next_Page_.gif

Personal tools