Partager via


Getting Started with MSBuild

I have had several customers asking me about MSBuild and how to get started learning the language and using it. A little over a year and a half ago, I joined the MSBuild team. When I got here, I did not even know what MSBuild was, and I have been a Visual Studio user for many years. This article is to help you understand what MSBuild is, how it relates to Visual Studio, and why every developer should know how to use it.

MSBuild ships as part of the .NET Framework, starting with v2.0 in Visual Studio 2005 and updated in v3.5 with Visual Studio 2008. It is a declarative language (XML) that allows you to quickly and easily write complex build systems. It is highly extensible, making customizations to an existing build process a simple task.

It has been the basis for the managed project systems that ship as part of Visual Studio. Over the years, the number of project types that MSBuild supports has grown. In Visual Studio 2010 (MSBuild v4.0) we add support for Visual C++. In addition, MSBuild v4.0 introduces several new language features, a new object model, and many performance improvements; including scheduler enhancements and the new FileTracker technology that provides reliable and fast incremental builds.

To the developer, there is tremendous benefit to understanding MSBuild. This is the same benefit Visual Studio utilizes to build the various project types we support. The developer can hook into this system to perform repetitive tasks, add custom tooling, and to help guarantee accuracy of the build. These are tasks that most developers face every day in trying to get their work done. And these benefits can also be leveraged on a build server. For example, Team Foundation Build also uses MSBuild to build its projects.

Learning MSBuild is like learning any new language. There are some basics that you need to understand to get started. For example, the language is declarative, as opposed to imperative languages such as C# and VB. Hence, the traditional flow of execution may seem strange at first.

clip_image002

One great exercise to start is to take a look at the contents of a new C# or VB project in Visual Studio. To do so, right-click on a project node in “Solution Explorer” and click "Unload Project" in the context menu that appears. The project will unload and become grayed out.

clip_image003

Next, right-click on the same project node and select "Edit {ProjectName}". The project file will be loaded up in the Visual Studio XML editor.

clip_image004

Take a look at the XML: You will notice that there are PropertyGroups that contain child nodes (known in MSBuild as “properties”) which define the build settings for various configurations. These are the global switches you might provide to tools to determine whether you want optimized or debug code, or whether or not to generate symbols.

<?xml version="1.0" encoding="utf-8"?>

<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

<Platform Condition=" '$(Platform)' == '' ">x86</Platform>

<ProductVersion>8.0.30703</ProductVersion>

<SchemaVersion>2.0</SchemaVersion>

<ProjectGuid>{8076DCA7-8C27-44D9-9A0F-7CD5C091C6E1}</ProjectGuid>

<OutputType>Exe</OutputType>

<AppDesignerFolder>Properties</AppDesignerFolder>

<RootNamespace>Sample</RootNamespace>

<AssemblyName>Sample</AssemblyName>

<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>

<TargetFrameworkProfile>Client</TargetFrameworkProfile>

<FileAlignment>512</FileAlignment>

</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">

<PlatformTarget>x86</PlatformTarget>

<DebugSymbols>true</DebugSymbols>

<DebugType>full</DebugType>

<Optimize>false</Optimize>

<OutputPath>bin\Debug\</OutputPath>

<DefineConstants>DEBUG;TRACE</DefineConstants>

<ErrorReport>prompt</ErrorReport>

<WarningLevel>4</WarningLevel>

</PropertyGroup>

...

Closer to the bottom of the file, you will see ItemGroups gathering sets of MSBuild “items”, used to represent concepts such as References (Imports in VB), Compile items (these are your source code files you have added to the project), resources, etc. Items are used to keep track of lists, while properties are key-value pairs.


<ItemGroup>

<Reference Include="System" />

<Reference Include="System.Core" />

<Reference Include="System.Xml.Linq" />

<Reference Include="System.Data.DataSetExtensions" />

<Reference Include="Microsoft.CSharp" />

<Reference Include="System.Data" />

<Reference Include="System.Xml" />

</ItemGroup>

<ItemGroup>

<Compile Include="Program.cs" />

<Compile Include="Properties\AssemblyInfo.cs" />

</ItemGroup>

<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

</Project>

You are also bound to see an Import element or two. These allow you to pull in MSBuild code located in other files (like a C/C++ #include). By placing your MSBuild code outside the project in imports you keep the individual project files small. The things specific to that project are located in the project, and settings of broader interest are located in the reusable components (targets), which are generally located where lots of projects can easily point to them. Typically, imports have the ".props" or ".targets" extension. MSBuild does not really care what the extension is, and considers these to all be just regular MSBuild project files. By convention, the extensions imply that a ".props" file contains property definitions (and/or items and definitions), and a ".targets" file contains target definitions.

<Target Name="PrintMyMessage">

<PropertyGroup>

<MyMessage>MSBuild Rocks!</MyMessage>

</PropertyGroup>

<Message Text="$(MyMessage)" />

</Target>

A target is something like a sub-routine or procedure in a traditional language. They are built with XML, but act more like imperative code. The example target above, named PrintMyMessage, sets the property MyMessage to “MSBuild Rocks!”, and then prints it to the output. The <Message /> element above is what calls the Message task to actually print the property. MSBuild ships with a wealth of targets for building the projects you use in Visual Studio.

Tasks are like MSBuild library functions. We deliver a large set with MSBuild, including tasks to make directories, copy/delete files, invoke the compilers, etc. In fact, almost all of the command line build tools in the .NET Framework that are needed to build most applications are wrapped by a corresponding MSBuild task. If none of the pre-existing tasks suit, you can look at an existing library like the comprehensive MSBuild Extensions Pack. Or, you can write your own tasks. Tasks can be built from any of the managed languages, or with v4.0, you can write inline tasks in VB or C#. (More on that in a later post!)

<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="https://schemas.microsoft.com/developer/msbuild/2003">

...

</Project>

To hook all of this together, you need a place to start. In MSBuild, there are several ways to get started. In the project file you have open, look at the top opening <Project> element. You will see a DefaultTargets attribute. This defines the targets that will be executed if you run MSBuild on the file. In a standard project, this is set to "Build", which just happens to be the name we gave the all-encompassing build for that project type. If you want to run something other than the default target(s), you can also tell MSBuild explicitly which target you want to execute in the project. For example, the Rebuild target runs the Clean target followed by the Build target to rebuild your project. To run the Rebuild target instead, in a standard Visual Studio command prompt you can type:

msbuild Sample.csproj /t:Rebuild

This will run MSBuild on the Sample.csproj project, and initiate the "Rebuild" target. To specify multiple targets, list them in a semicolon-delimited list, e.g.

msbuild Sample.csproj /t:Clean;Build

Once the process has been kicked off by specifying a starting target(s), MSBuild will then build the target(s) and any others that have been chained to it by the various means MSBuild has to specify dependencies. If a target depends on another target(s) (as specified by the DependsOnTargets attribute on the target element), then MSBuild will run the other target(s) prior to running the target specified. In MSBuild v4.0, you can also use the BeforeTargets and AfterTargets attributes to specify the targets this target needs to run before or after respectively.

Unlike the flexibility afforded to you by calling MSBuild on the command line, Visual Studio only explicitly supports the use of four targets: Build, Clean, Rebuild, and Publish.

clip_image006

When you are done exploring your project, right-click on the project name in the Solution Explorer and choose "Reload Project" from the context menu. This will load the project back up in Visual Studio.

Now, when you click build, or F5 to debug (which first requests a build under the covers), all this is set in motion. Visual Studio kicks off an execution of the desired MSBuild target, and the items in your project are built according to the definitions provided by the project and any other targets files or property sheets.

And this is where you come in: you can leverage this same system to do all sorts of cool things when you build. You can use MSBuild to deploy files to a server, automatically increment your build number, or even send yourself a text message upon completion of a nightly build. And it is so simple to do and to use.

chuckeng

Chuck England
Visual Studio Platform
Program Manager – MSBuild

Want to learn more? Check our online documentation…

Or listen to the CodeCast I recently did with Ken Levy: https://www.code-magazine.com/codecast/

And join us at the MSBuild forums: https://social.msdn.microsoft.com/Forums/en-US/msbuild/threads

Here’s a pack of ready-made MSBuild tasks that go beyond the ones you get in the box: https://www.codeplex.com/MSBuildExtensionPack