devxlogo

Master Complex Builds with MSBuild

Master Complex Builds with MSBuild

he Microsoft Build Engine (MSBuild) is the new build platform for Microsoft and Visual Studio. MSBuild is completely transparent with regards to how it processes and builds software, enabling developers to orchestrate and build products in build lab environments where Visual Studio is not installed. This article provides brief overviews of:

  • The basic elements of an MSBuild project file.
  • How MSBuild is used to build projects.
  • The advanced features of MSBuild.
  • How Visual Studio uses MSBuild to build projects.
  • Walkthrough Sample application / Logger.

Basic MSBuild Project Elements
MSBuild introduces a new XML-based project file format that is simple to understand, easy to extend, and fully supported (see Figure 1). The MSBuild project file format lets developers describe what items need to be built as well as how they need to be built with different platforms and configurations. In addition, developers can author reusable build rules that can be factored into separate files, which can help make builds perform consistently across different projects.

Figure 1. Project Files in Solution Explorer: In this figure, you can see the MSBuild project (.proj) files for the sample MSBuildLogger project.

You declare items in the project file by creating an element with the name of the item collection as a child of an ItemGroup element. For example, the following code creates an item collection named Compile, which includes two files:

                    

That code fragment is typical of a build file: Individual items represent inputs to the build system. Items are grouped into item collections in elements with a collection name that you define (e.g. “Compile” in the preceding code). You can then use these item collections as parameters for tasks, which use the individual items contained in the collection to perform the steps of the build process.

?
Figure 2. Various MSBuild Project Items: The circled code in the figure shows several MSBuild project items.

You reference item collections throughout the project file using the syntax @(ItemCollectionName). You can create items using wildcards, and items may contain additional metadata for more advanced build scenarios (see Figure 2).

An MSBuild file can also hold properties, which represent key/value pairs that you can use to configure builds. You declare a property by creating an element with the property name as a child of a PropertyGroup element (see Figure 3). For example, the following code creates a property named BuildDir with a value of TestBuildDir:

          TestBuildDir   
?
Figure 3. MSBuild Properties: The circled code in the figure shows a property named “appname” with a value of “TestBuildApp.”

You can reference properties throughout the project file using the syntax $(PropertyName).

There are several differences in the way MSBuild treats properties and items. Items are stored in collections where each item had the same element name, while property names are unique. Properties contain a single scalar value. You cannot remove items from item collections, but properties can have their values changed after they are defined. Items can contain metadata and can use the %(ItemMetadata) notation, while properties cannot.

Tasks are reusable units of executable code used by MSBuild projects to perform build operations. For example, a task might compile input files, or run an external tool. Tasks can be stored, shared, and reused by different developers in different projects.

You can write your own task by authoring a managed type that implements the ITask interface. You can then map the custom task to MSBuild with the Using task element. MSBuild ships with many common tasks such as Copy, which copies files, MakeDir, which creates directories, and Csc, which compiles Visual C# source code files

?
Figure 4. Executing a Task: This circled code executes the CSC task, passing the attributes as parameters to create an executable.

To execute a task in an MSBuild project file, create an element with the name of the task as a child of a Target element. Tasks usually accept parameters, which you specify as attributes of the element. You can use MSBuild item collections and properties as parameters. For example, the following code calls the MakeDir task and passes it the value of the BuildDir property declared in the previous example. Figure 4 shows the CSC task creating an executable that uses attributes to pass several values.

             

Targets group tasks together in a specific order and expose sections of the project file as entry points into the build process. It’s generally best to group targets into logical sections to allow for expansion and increase readability. By separating the build steps into many targets you can call the grouped steps from other targets without having to copy that section of code into each target.

You declare targets in the project file with the Target element. For example, the following code creates a target named Compile.

             

Using Wildcards to Build All Files in a Directory
You can declare multiple files within a single item as shown below:

       

You can also use wildcards to recursively include all files or only specific files from subdirectories as build inputs. The double asterisk matches all files and folders. For the following example, assume you have a project that contains graphics files in the following directories and subdirectories:

   ProjectImagesBestJpgs    ProjectImagesImgJpgs    ProjectImagesImgJpgsImg1 

Assume that the project file itself is stored in the Project directory shown above. Given that structure, you could then write:

   Include="Images\***.jpg"    Include="Images**img*.jpg"   Include="Images***jpgs*.*"   Include="Images***jpgs*"

Using MSBuild to Build Projects
You can run MSBuild from the command line by passing a project file to MSBuild.exe with the appropriate command line options for setting properties, executing specific targets, and specifying loggers. For example, the following command line builds the file TestProj.proj with the Configuration property set to Debug:

   MSBuild.exe TestProj.proj /property:Configuration=Debug
Author’s Note: The default path of MSBuild.exe is C:WINNTMicrosoft .NETFrameworkFramework Version (see Figure 5).

?
Figure 5. MSBuild Default Path: The figure shows the default path for MSBuild.exe.

From the command line, you can view help and reference information for MSBuild commands using:

   MSBuild.exe /help

The following steps show how to create an MSBuild Project file and create a build from it.

Step 1
Add a Project element at the top of your XML configuration file. The Project element is the root element of an MSBuild project file. It specifies the default set of targets for the project as well as the initial target to run every time the project is built:

          ...   

Step 2
Add a PropertyGroup element. You use this element to group Property elements, which may contain values that are referenced several times in the project file, or that set values for properties used in several configurations:

          TestAppName   

Step 3
Add the ItemGroup element used to group Item elements, which define inputs into the build system:

             

For a VB project, you may want to name the item collection something more descriptive, such as VBFile, for example:

             

Step 4
Add Target elements, which list tasks in a particular order, and expose sections of the project file as entry points into the build process. The Target element defines each target, and contains a set of tasks that MSBuild executes sequentially:

   ...

Next add tasks, which are units of executable code used by MSBuild to perform build operations. The Task element specifies a particular task to run and the parameters to pass to the task:

   ...

The following example compiles a Visual Basic project, passing the VBFile item collection to the Sources parameter of the task:

   ...

You could define some task attributes for the task outputs, so that those outputs can be referenced later in the project file. You use the Output element to specify task outputs. You can assign the output to either an item collection or a property:

             

The following example compiles a Visual Basic project, passing the VBFile item collection to the Sources parameter of the task, and stores the value of the OutputAssembly parameter in the EXEFile item collection:

             

MSBuild automatically logs status information such as the current target and task as a build progresses. Beyond that, you can use the Message task to provide additional information:

   

You should comment your build files. In MSBuild you write project file comments in the standard XML comment format; in other words, type to end the comment, as shown below:

   

To build a project file, you use MSBuild.exe. Navigate to the directory that contains the project file and type the command:

   msbuild .proj

If this command doesn’t work (because MSBuild.exe isn’t in your path), navigate to the default MSBuild.exe directory (C:WINNTMicrosoft .NETFrameworkFramework Version) and run MSBuild.exe from that path. Listing 1 shows a sample MSBuild Project file.

Figure 6 shows the output when you build the file in Listing 1.

?
Figure 6. MsBuild Output (with Error): Sample output showing a build error.
?
Figure 7. Checking MSBuild Version: Here’s the type of output you’ll see when you check your MSbuild version from the command line.

If you’re not sure which version of MSBuild you’re running, type “msbuild /version” at the command prompt, and you’ll see output similar to Figure 7.

Now that you know the basic steps involved in using MSBuild, you can explore some more advanced features, such as logging, task batching, and transforms, and you’ll see how MSBuild integrates with Visual Studio.

Advanced MSBuild: Logging
MSBuild loggers provide a way to customize the reporting of build events, messages, warnings, and errors. Loggers relay information from build events, messages, warnings, and errors to a log that can be read and interpreted easily. Loggers can display information in the console window, write to XML or a text file, or enter build data into a database. You write logger logic in a managed language project that implements the ILogger interface.

When you run MSBuild.exe from the command line, it uses the default console logger to display build event information in the console window. You can select the level of detail that you want the logger to report by using the /verbosity command line option.

The default console logger accepts additional parameters that can modify the output to the console window. These parameters are specified with the /consoleloggerparameters switch on the command line. If you are using a custom logger, you may want to hide the information displayed from the default console logger. Use the /noconsolelogger switch on the command line to disable the default console logger.

?
Figure 8. Msbuild with Logger Option: This sample logger output shows a “Detailed” level of verbosity.

For custom loggers, use the /logger switch at the MSBuild command line to specify the logger location. As an example, Listing 2 contains the code for a complete custom logger in C#:

The following command line builds the project with the same custom logger, but with a Verbosity level of Detailed (see Figure 8).

   MSBuild /nologo /noconsolelogger       /logger:SimpleLogger.dll /verbosity:Detailed 

Advanced MSBuild: Task Batching
MSBuild has the ability to divide item collections into different categories, or batches, based on item metadata, and run a target or task one time with each batch.

Task batching simplifies project files by providing a way to divide item collections into different batches, letting you pass each batched collection to a task separately. This means that a project file needs to have the task and its attributes declared only once, even though that task may be run several times.

You control task batching by using the %(ItemMetaDataName) syntax in one of the task attributes. The following example splits the TestExample item collection into batches based on the Color item metadata value, and passes each batch to the MyTestTask task separately.

                                          Blue                                    Red                                              

Here’s another example showing a Target element that contains an Outputs attribute with the %(ItemMetaDataName) notation. MSBuild divides the TestExample item collection into batches based on the Color item metadata, and analyzes the timestamps of the output files for each batch. During the build, if the outputs from a batch are not up-to-date, it runs the target; otherwise, it skips that target.

                                           Blue                                     Red                                                 

Advanced MSBuild: Transforms
A transform is a one-to-one conversion of one item collection into another. In addition to letting a project convert item collections, transforms allow a target to identify a direct mapping between its inputs and outputs.

The syntax for transforms is similar to that for batches. All transform modifiers must be in the format %(ItemMetaDataName). You can use any item metadata as a transform modifier, including the well-known item metadata assigned to every item upon creation.

The following example shows an MSBuild project file using transforms. This example assumes a single .xsd file in the c:sub0sub1sub2sub3 directory, and a working directory of c:sub0.

                                                                                                                                  

The preceding example produces the following output.

   rootdir: C:    fullpath: C:xmakesub1sub2sub3myfile.xsd    rootdir + directory + filename + extension: C:xmakesub1sub2sub3myfile.xsd    identity: sub1sub2sub3myfile.xsd    filename: myfile    directory: xmakesub1sub2sub3    relativedir: sub1sub2sub3    extension: .xsd 

MSBuild and Visual Studio Integration
Visual Studio 2005 and 2008 can host MSBuild to load and build managed projects. Because MSBuild is responsible for the project, you can use almost any project in the MSBuild format successfully in Visual Studio, even if the project was authored by a different tool and has a customized build process.

Visual Studio does not have a language-neutral MSBuild based project system. MSBuild.exe recognizes any project file name extension matching the pattern *Proj. However, Visual Studio recognizes only a subset of these project file name extensions, which determine the language-specific project system that will load the project.

For example, the Visual C# project system loads .csproj files?but Visual Studio is not able to load an .xxproj file. A project file for source files in an arbitrary language must use the same extension as Visual Basic, Visual C#, or Visual J# project files to be loaded in Visual Studio.

Clicking the Build command in Visual Studio executes the default target in the project. Often, this target is also named Build. Choosing the Rebuild or Clean command will attempt to execute a target of the same name in the project. Clicking Publish will execute a target named PublishOnly in the project.

Configurations are represented in MSBuild projects by properties grouped in a PropertyGroup element that contains a Condition attribute. To successfully extract this list, the conditions must have a format similar to the following:

   Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "   Condition=" '$(Configuration)' == 'Release' "    Condition=" '$(Something)|$(Configuration)|$(SomethingElse)' ==       'xxx|Debug|yyy' "

Visual Studio looks at the conditions on PropertyGroup, ItemGroup, Import, Property, and Item elements for this purpose.

For example, adding the following to your project file will add the custom type JScript to this menu for all projects that import it:

            

When possible, Visual Studio will attempt to use the in-process versions of the Visual Basic or Visual C# compilers for increased performance; however, for this to work correctly, the following conditions must be met:

  • In a target of the project, there must be a task named Csc (for Visual C# projects) or Vbc (for Visual Basic projects)
  • The UseHostCompilerIfAvailable parameter of the task must be set to true.
  • Only supported parameter values must be specified. All parameters specified on the task are supported by the in-process compiler, but some parameter values are unsupported.

The following Csc task parameter values are not supported by the Visual C# in-process compiler:

  • NoConfig: false and empty values are unsupported.
  • ResponseFiles: non-empty values are unsupported.
  • AdditionalLibPaths: non-empty values are unsupported.
  • AddModules: non-empty values are unsupported.
  • CodePage: non-zero values are unsupported.
  • GenerateFullPaths: true is unsupported.
  • LinkResources: non-empty values are unsupported.

If these conditions are not met, the project will compile using the command line compiler instead of the in-process compiler.

When you’re working inside Visual Studio, it controls the solution file and project build ordering. When building a solution from the command line, msbuild.exe parses the solution file and orders the project builds. In both cases the projects are built individually, in dependency order, and project-to-project references are not traversed. In contrast, when you build individual projects with msbuild.exe, project-to-project references are traversed.

When building inside Visual Studio, the property $(BuildingInsideVisualStudio) is set to true. You can use this value in your project or .targets files to cause the build to behave differently.

Visual Studio recognizes certain property names and values. For example, the following property in a project will cause “Windows Application” to appear in the Application Type box in the Project Designer:

   WinExe

Items defined in the project with arbitrary item collection names are by default displayed in the Solution Explorer under their project node. To hide an item from display, set the Visible metadata to false. For example, the following item will participate in the build process but Solution Explorer will not display it:

                     false          

Items declared in files imported into the project are not displayed by default. Items created during the build process are never displayed in Solution Explorer.

You’ll find several sample MSBuild projects in the downloadable code that will help you understand MSBuild’s functionality better.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist