This site uses cookies for analytics, personalized content and ads. By continuing to browse this site, you agree to this use. Learn more
Microsoft Logo
Gray Pipe
  • Developer Network
    • Downloads
      • Visual Studio
      • SDKs
      • Trial software
    • Programs
      • Subscriptions
      • Students
      • ISV
      • Startups
      • Events
    • Community
      • Magazine
      • Forums
      • Blogs
      • Channel 9
    • Documentation
      • APIs and reference
      • Dev centers
      • Samples
      • Retired content
Developer Network Developer

Subscriber portal

Get tools
magazine
  • Issues and downloads
    • All issues
    • 2019
      • February 2019
      • January 2019
    • 2018
      • Connect(); 2018
      • December 2018
      • November 2018
      • October 2018
      • September 2018
      • August 2018
      • July 2018
      • June 2018
      • May 2018
      • April 2018
      • March 2018
      • February 2018
      • January 2018
    • 2017
      • Connect(); 2017
      • December 2017
      • November 2017
      • October 2017
      • September 2017
      • August 2017
      • July 2017
      • June 2017
      • May 2017
      • April 2017
      • March 2017
      • February 2017
      • January 2017
    • 2016
      • December 2016
      • Connect(); 2016
      • November 2016
      • October 2016
      • September 2016
      • August 2016
      • July 2016
      • June 2016
      • May 2016
      • April 2016
      • March 2016
      • February 2016
      • January 2016
    • 2015
      • December 2015
      • November 2015
      • Windows 10 issue
      • October 2015
      • September 2015
      • August 2015
      • July 2015
      • June 2015
      • May 2015
      • April 2015
      • March 2015
      • February 2015
      • January 2015
    • 2014
      • Special 2014
      • December 2014
      • November 2014
      • October 2014
      • September 2014
      • August 2014
      • July 2014
      • June 2014
      • May 2014
      • April 2014
      • March 2014
      • February 2014
      • January 2014
    • 2013
      • Government 2013
      • December 2013
      • November 2013
      • October 2013
      • September 2013
      • August 2013
      • July 2013
      • June 2013
      • May 2013
      • April 2013
      • March 2013
      • February 2013
      • January 2013
    • 2012
      • December 2012
      • November 2012
      • Windows 8
      • October 2012
      • September 2012
      • August 2012
      • July 2012
      • June 2012
      • May 2012
      • April 2012
      • March 2012
      • February 2012
      • January 2012
    • 2011
      • December 2011
      • November 2011
      • October 2011
      • September 2011
      • August 2011
      • July 2011
      • June 2011
      • May 2011
      • April 2011
      • March 2011
      • February 2011
      • January 2011
    • 2010
      • December 2010
      • November 2010
      • October 2010
      • September 2010
      • August 2010
      • July 2010
      • June 2010
      • May 2010
      • April 2010
      • March 2010
      • February 2010
      • January 2010
    • 2009
      • December 2009
      • November 2009
      • October 2009
      • September 2009
      • August 2009
      • July 2009
      • June 2009
      • May 2009
      • April 2009
      • March 2009
      • February 2009
      • January 2009
  • Subscribe
  • Submit article
search clear
We’re sorry. The content you requested has been removed. You’ll be auto redirected in 1 second.
Issues and downloads 2017 January 2017 Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

January 2017
Volume 32 Number 1

[Essential .NET]

Essential MSBuild: A Build Engine Overview for .NET Tooling

By Mark Michaelis | January 2017

Mark MichaelisThose of you who have been following .NET Core over the past few years (has it been that long?) know all too well that the “build system” has experienced a significant amount of flux, whether it be dropping built-in support for gulp or the demise of Project.json. For me as a columnist, these changes have been challenging as I didn’t want you, my dear readers, to spend too much time learning about features and details that ultimately were only going to be around for a few months. This is why, for example, all my .NET Core-related articles were built on Visual Studio .NET 4.6-based *.CSPROJ files that referenced NuGet packages from .NET Core rather than actually compiled .NET Core projects.

This month, I’m pleased to report, the project file for .NET Core projects has stabilized into (would you believe) an MSBuild file. However, it’s not the same MSBuild file from earlier Visual Studio generations, but rather an improved—simplified—MSBuild file. It’s a file that (without getting into religious wars about curly vs. angle brackets) includes all the features of Project.json but with the accompanying tool support of the traditional MSBuild file we’ve come to know (and perhaps love?) since Visual Studio 2005. In summary, the features include open source, cross-platform compatibility, a simplified and human-editable format and, finally, full modern .NET tool support including wildcard file referencing.

Tooling Support

To be clear, features such as wild cards were always supported in MSBuild, but now the Visual Studio tooling works with them, as well. In other words, the most important news about MSBuild is that it’s tightly integrated as the build system foundation for all the new .NET Tooling—DotNet.exe, Visual Studio 2017, Visual Studio Code and Visual Studio for Mac—and with support for both .NET Core 1.0 and .NET Core 1.1 runtimes.

The big advantage of the strong coupling between .NET Tooling and MSBuild is that any MSBuild file you create is compatible with all the .NET Tooling and can be built from any platform.

The .NET Tooling for MSBuild integration is coupled via the MSBuild API, not just a command-line process. For example, executing the .NET CLI command Dotnet.exe Build doesn’t internally spawn the msbuild.exe process. However, it does call the MSBuild API in process to execute the work (both the MSBuild.dll and Microsoft.Build.* assemblies). Even so, the output—regardless of the tool—is similar across platforms because there’s a shared logging framework with which all the .NET Tools register.

*.CSPROJ/MSBuild File Structure

As I mentioned, the file format itself is simplified down to the bare minimum. It supports wildcards, project and NuGet package references, and multiple frameworks. Furthermore, the project-type GUIDs found in the Visual Studio-created project files of the past are gone.

Figure 1 shows a sample *.CSPROJ/MSBuild file.

Figure 1 A Basic Sample CSProj/MSBuild File
XML
Copy
<Project>
  <PropertyGroup>
    <TargetFramework>netcoreapp1.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <Compile Include="**\*.cs" />
    <EmbeddedResource Include="**\*.resx" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.NETCore.App">
      <Version>1.0.1</Version>
    </PackageReference>
    <PackageReference Include="Microsoft.NET.Sdk">
      <Version>1.0.0-*</Version>
      <PrivateAssets>All</PrivateAssets>
    </PackageReference>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Let’s review the structure and capabilities in detail:

Simplified Header: To begin, notice that the root element is simply a Project element. Gone is the need for even the namespace and version attributes:

XML
Copy
ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"

(Though, they’re still created in the release candidate version tooling.)  Similarly, even the need for importing the common properties is merely optional:

XML
Copy
<Import Project=
  "$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />

Project References: From the project file, you can add entries to the item group elements:

  • NuGet Packages:
XML
Copy
<PackageReference Include="Microsoft.Extensions.Configuration">
  <Version>1.1.0</Version>
</PackageReference>
  • Project References:
XML
Copy
<ProjectReference Include="..\ClassLibrary\ClassLibrary.csproj" />
  • Assembly References:
XML
Copy
<Reference Include="MSBuild">
  <HintPath>...</HintPath>
</Reference>

A direct assembly reference should be the exception as a NuGet reference is generally preferred.

Wildcard Includes: Compiled code files and resource files can all be included via wildcards.

XML
Copy
<Compile Include="**\*.cs" />
<EmbeddedResource Include="**\*.resx" />
<Compile Remove="CodeTemplates\**" />
<EmbeddedResource Remove="CodeTemplates\**" />

However, you can still select specific files to ignore using the remove attribute. (Note that support for wildcards is frequently referred to as globbing.)

Multi-Targeting: To identify which platform you’re targeting, along with the (optional) output type, you can use a property group with the TargetFramework elements:

XML
Copy
<PropertyGroup>
  <TargetFramework>netcoreapp1.0</TargetFramework>
  <TargetFramework>netstandard1.3</TargetFramework>
</PropertyGroup>

Given these entries, the output for each target will be built into the bin\Debug or bin\Release directory (depending on which configuration you specify). If there’s more than one target, the build execution will place the output into a folder corresponding to the target framework name.

No Project Type GUIDs: Note that it’s no longer necessary to include a project-type GUID that identifies the type of project.

Visual Studio 2017 Integration

When it comes to Visual Studio 2017, Microsoft continues to provide a rich UI for editing the CSPROJ/MSBuild project file. Figure 2, for example, shows Visual Studio 2017 loaded with a CSPROJ file listing, slightly modified from Figure 1, that includes target framework elements for netcoreapp1.0 and net45, along with package references for Microsoft.Extensions.Configuration, Microsoft.NETCore.App, and  Microsoft.NET.Sdk, plus an assembly reference to MSBuild, and a project reference to SampleLib.                                                                                                           

Solution Explorer is a Rich UI on Top of a CSProj File
Figure 2 Solution Explorer is a Rich UI on Top of a CSProj File

Notice how each dependency type—assembly, NuGet package or project reference—has a corresponding group node within the Dependencies tree in Solution Explorer.

Furthermore, Visual Studio 2017 supports dynamic reloading of the project and solution. For example, if a new file is added to the project directory—one that matches one of the globbing wild cards—Visual Studio 2017 automatically detects the changes and displays the files within the Solution Explorer. Similarly, if you exclude a file from a Visual Studio project (via the Visual Studio menu option or the Visual Studio properties window), Visual Studio will automatically update the project file accordingly. (For example, it will add a <Compile Remove="CommandLine.cs" /> element to exclude the CommandLine.cs file from compiling within the project.) 

Furthermore, edits to the project file will be automatically detected and reloaded into Visual Studio 2017. In fact, the Visual Studio project node within Solution Explorer now supports a built-in Edit <Project File> menu option that opens the project file within the Visual Studio edit window without first requesting that you unload the project.

There’s built-in migration support within Visual Studio 2017 to convert projects to the new MSBuild format. If you accept its prompt, your project will automatically upgrade from a Project.json/*.XPROJ type to an MSBUILD/*.CSPROJ type. Note that such an upgrade will break backward compatibility with Visual Studio 2015 .NET Core projects, so you can’t have some of your team working on the same .NET Core project in Visual Studio 2017 while others use Visual Studio 2015.

MSBuild

I would be remiss to not point out that back in March 2016, Microsoft released MSBuild as open source on GitHub (github.com/Microsoft/msbuild) and contributed it to the .NET Foundation (dotnetfoundation.org). Establishing MSBuild as open source put it on track for platform portability to Mac and Linux—ultimately allowing it to become the underlying build engine for all the .NET Tooling.

Other than the CSPROJ\MSBuild file PackageReference element identified earlier, MSBuild version 15 doesn’t introduce many additional features beyond open source and cross platform. In fact, comparing the command-line help shows the options are identical. For those not already familiar with it, here’s a list of the most common options you should be familiar with from the general syntax MSBuild.exe [options] [project file]:

/target:<target>:Identifies the build target within the project file to execute along with any dependencies it might have (/t is the abbreviation).

/property:<n>=<v>: Sets or overrides any project properties (identified in the ProjectGroup element of a project file). For example, you can use the property to change the configuration or the output directory, as in /property:Configuration=Release;OutDir=bin\ (/p is the abbreviation).

/maxcpucount[:n]: Specifies the number of CPUs to use. By default, msbuild runs on a single CPU (single-threaded). If synchronization isn’t a problem you can increase that by specifying the level of concurrency. If you specify the /maxcpucount option without providing a value, msbuild will use the number of processors on the computer.

/preprocess[:file]: Generates an aggregated project file by inlining all the included targets. This can be helpful for debugging when there’s a problem.

@file: Provides one (or more) response files that contains options. Such files have each command-line option on a separate line (comments prefixed with “#”). By default, MSBuild will import a file named msbuild.rsp from the first project or solution built. The response file is useful for identifying different build properties and targets depending on which environment (Dev, Test, Production) you’re building, for example.

Dotnet.exe

The dotnet.exe command line for .NET was introduced about a year ago as a cross-platform mechanism for generating, building and running .NET Core-based projects. As already mentioned, it has been updated so that it now relies heavily on MSBuild as the internal engine for the bulk of its work where this makes sense.

Here’s an overview of the various commands:

dotnet new: Creates your initial project. Out of the box this project generator supports the Console, Web, Lib, MSTest and XUnitTest project types. However, in the future you can expect it to allow you to provide custom templates so you can generate your own project types. (As it happens, the new command doesn’t rely on MSBuild for generating the project.)

dotnet restore: Reads through the project dependencies specified in the project file and downloads any missing NuGet packages and tools identified there. The project file itself can either be specified as an argument or be implied from the current directory (if there’s more than one project file in the current directory, specifying which one to use is required). Note that because restore leverages the MSBuild engine for its work, the dotnet command allows for additional MSBuild command-line options.

dotnet build: Calls on the MSBuild engine to execute the build target (by default) within the project file. Like the restore command, you can pass MSBuild arguments to the dotnet build command. For example, a command such as dotnet build /property:configuration=Release will trigger a Release build to be output rather than a Debug build (the default). Similarly, you can specify the MSBuild target using /target (or /t). The dotnet build /t:compile command, for example, will run the compile target.

dotnet clean: Removes all the build output so that a full build rather than an incremental build will execute.

dotnet migrate: Upgrades a Project.json/*.XPROJ-based project into the *.CSPROJ/MSBuild format.

dotnet publish: Combines all the build output along with any dependencies into a single folder, thereby staging it for deployment to another machine. This is especially useful for self-contained deployment that includes not only the compile output and the dependent packages, but even the .NET Core runtime itself. A self-contained application doesn’t have any prerequisites that a particular version of the .NET platform already be installed on the target machine.

dotnet run: Launches the .NET Runtime and hosts the project and/or the compiled assemblies to execute your program. Note that for ASP.NET, compilation isn’t necessary as the project itself can be hosted.

There’s a significant overlap between executing msbuild.exe and dotnet.exe, leaving you with the choice of which one to run. If you’re building the default msbuild target you can simply execute the command “msbuild.exe” from within the project directory and it will compile and output the target for you. The equivalent dotnet.exe command is “dotnet.exe msbuild.” On the other hand, if you’re running a “clean” target the command is “msbuild.exe /t:clean” with MSBuild, versus “dotnet.exe clean” with dotnet. Furthermore, both tools support extensions. MSBuild has a comprehensive extensibility framework both within the project file itself and via .NET assemblies (see bit.ly/2flUBza). Similarly, dotnet can be extended but the recommendation for this, too, essentially involves extending MSBuild plus a little more ceremony.

While I like the idea of dotnet.exe, in the end it doesn’t seem to offer much advantage over MSBuild except for doing the things that MSBuild doesn’t support (of which dotnet new and dotnet run are perhaps the most significant). In the end, I feel that MSBuild allows you to do the simple things easily while still enabling the complicated stuff when needed. Furthermore, even the complicated stuff in MSBuild can be simplified down by providing reasonable defaults. Ultimately, whether dotnet or MSBuild is preferable comes down to preference, and time will tell which the development community generally settles on for the CLI front end.

global.json

While Project.json functionality has migrated to CSPROJ, global.json is still fully supported. The file allows specification of project directories and package directories, and identifies the version of the SDK to use. Here’s a sample global.json file:

JavaScript
Copy
{
  "projects": [ "src", "test" ],
  "packages": "packages",
  "sdk": {
    "version": "1.0.0-preview3",
    "runtime": "clr",
    "architecture": "x64"
  }
}

The three sections correspond to the main purposes of the global.json file:

projects: Identifies the root directories in which .NET projects are located. The projects node is critical for debugging into the .NET Core source code. After cloning the source code, you can add the directory into the projects node and Visual Studio will then automatically load it up as a project within the solution.

packages: Indicates the location of your NuGet packages folder.

sdk: Specifies which version of the runtime to use.

Wrapping Up

In this article, I’ve provided a broad overview of all the places that MSBuild is leveraged within the .NET Tooling suite. Before closing, let me offer one bit of advice from my experience working on projects with thousands of lines of MSBuild. Don’t fall into the trap of scripting too much in a declarative, loosely typed language like the XML MSBuild schema. That’s not its purpose. Rather, the project files should be relatively thin wrappers that identify the order and dependencies among your build targets. If you let your MSBuild project file get too big, it can become a pain to maintain. Don’t wait too long before you refactor it into something like C# MSBuild tasks that can be debugged and easily unit tested.


Mark Michaelis is founder of IntelliTect, where he serves as its chief technical architect and trainer. For nearly two decades he has been a Microsoft MVP, and a Microsoft Regional Director since 2007. Michaelis serves on several Microsoft software design review teams, including C#, Microsoft Azure, SharePoint and Visual Studio ALM. He speaks at developer conferences and has written numerous books including his most recent, “Essential C# 6.0 (5th Edition)” (itl.tc/EssentialCSharp). Contact him on Facebook at facebook.com/Mark.Michaelis, on his blog at IntelliTect.com/Mark, on Twitter: @markmichaelis or via e-mail at mark@IntelliTect.com.

Thanks to the following IntelliTect technical experts for reviewing this article: Kevin Bost, Grant Erickson, Chris Finlayson, Phil Spokas and Michael Stokesbary


Discuss this article in the MSDN Magazine forum

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

Observing reaction of net developers in my company. I think backing from project.json is a mistake. Improving, wouldn't help. Readability and learning curve are not comparable. Also, Java and node programmers who started to pay close attention to Mic...

Jan 24, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

Haha yeah hard to find fault in your reasoning, David.  I too have thought about this a bit and it's amazing how at the end of the day how little feedback there is given the scope and breadth of the market here.  The medium used also matters, as wel...

Jan 19, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

I know people have been going mad over this. But yeah, those that don't engage, it's them that make up the 98%. Not you (I've seen your handle before) or me. Those at their desk doing their job, not following blog posts or twitter or github, not goin...

Jan 19, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

LOL @ 98% David... I invite you to check out the twitters and MSDN blog comments (or even the thread I mention above) to see how divisive this issue is. Unless you mean 98% of the developers don't even engage in these things, which could also be the ...

Jan 17, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

Let's be realistic here. 98% of the developers don't care if it's json or csproj. The 2% are the early adopters. I can agree it wasn't fair to them to change back to xml files.

Jan 17, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

Yeah, this article is all sunny skies and rainbows, but doesn't really speak to the total cluster and community backlash that has occurred from having to switch between different formats and the angst with having to be stuck with a particular format ...

Jan 16, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

In a previous blog post (https://blogs.msdn.microsoft.com/dotnet/2016/12/12/updating-visual-studio-2017-rc-net-core-tooling-improvements/), the PackageReference element in the csproj file was shown to have been simplified such that Version was an XML...

Jan 13, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

Nice article. What will happen to project.json in .net core projects? It will dissapear? We have to use .csproj now?

Jan 12, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

A very good article. Is there any mention of if/when non-coreclr projects will be able to use the new format? I was hoping that VS2017 would import and upgrade my .NET projects to this format, but for now (RC1) it does not. Is an upgrade process poss...

Jan 11, 2017

Essential .NET - Essential MSBuild: A Build Engine Overview for .NET Tooling

The project file for .NET Core projects has finally stabilized into an MSBuild file, but simplified and improved over the old version. Mark Michaelis describes the new MSBuild and provides a broad overview of the places where MSBuild is leveraged wit...

Jan 1, 2017

MSDN Magazine Blog

 

More MSDN Magazine Blog entries >


Current Issue


February 2019

Browse All MSDN Magazines


Subscribe to MSDN Flash newsletter


Receive the MSDN Flash e-mail newsletter every other week, with news and information personalized to your interests and areas of focus.

Follow us
  • https://www.facebook.com/microsoftdeveloper
  • https://twitter.com/msdev
  • https://plus.google.com/111221966647232053570/
Sign up for the MSDN Newsletter
Is this page helpful?
Your feedback about this content is important.
Let us know what you think.
Additional feedback?
1500 characters remaining
Thank you!
We appreciate your feedback.

Dev centers

  • Windows
  • Office
  • Visual Studio
  • Microsoft Azure
  • More...

Learning resources

  • Microsoft Virtual Academy
  • Channel 9
  • MSDN Magazine

Community

  • Forums
  • Blogs
  • Codeplex

Support

  • Self support

Programs

  • BizSpark (for startups)
  • Microsoft Imagine (for students)
United States (English)
  • Newsletter
  • Privacy & cookies
  • Terms of use
  • Trademarks
logo © 2019 Microsoft