MSBuild is a process automation tool with emphasis on building .NET projects.
- property - simple string data (configuration, target architecture, output directory)
- item - list with things (files, references, resources, other)
- task - operation that can be performed (copy files, create folders, zip files, compile a dll)
- target - set of tasks that accomplishes something (Build, Rebuild, Clean)
Project
is the root element:
<?xml version="1.0" encoding="utf-8" ?>
<Project
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
DefaultTargets="Build;..."
ToolsVersion="12.0">
<!-- ... -->
</Project>
Additional project files can be imported:
<Import Project="path/to/project.targets" />
Standard projects that need to be imported for .NET development:
- at the beginning:
$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props
- at the end:
$(MSBuildToolsPath)\Microsoft.CSharp.targets
or$(MSBuildToolsPath)\Microsoft.VisualBasic.targets
Can be added in Project
but have to be in a PropertyGroup
:
<PropertyGroup>
<OutputDirectory>bin\Debug\</OutputDirectory>
<ZipOutputDir>dist\Debug\</ZipOutputDir>
<!-- ... -->
</PropertyGroup>
Properties provided by MSBuild: https://msdn.microsoft.com/en-us/library/ms164309.aspx
Properties defined in default .NET targets: https://msdn.microsoft.com/en-us/library/bb629394.aspx
Can be added in Project
but have to be in an ItemGroup
:
<ItemGroup>
<Compile Include="**/*.cs" Exclude="obj/**/*.cs" />
</ItemGroup>
Name of the node defines the type of the item (Compile
, Reference
, EmbeddedResource
etc.).
Use Include
attribute to define the elements that will be a part of the list (supports basic glob patterns).
Use Exclude
attribute to not include some elements that would normally be according to the Include
attribute.
Item types used in default .NET targets: https://msdn.microsoft.com/en-us/library/bb629388.aspx
Each item has a customizable set of metadata. To define metadata add a child node to the item:
<ItemGroup>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
</ItemGroup>
Item type can have some default metadata defined if needed:
<ItemDefinitionGroup>
<Compile>
<Author>Me</Author>
</Compile>
</ItemDefinitionGroup>
A list of metadata available on each item: https://msdn.microsoft.com/en-us/library/ms164313.aspx
Target is a list of operations that needs to performed to achieve something.
Example targets: Compile
, CopyToOutput
. A target could look like this:
<ItemGroup>
<Copyright Include="COPYRIGHT.txt" />
<Copyright Include="LICENSE.txt" />
</ItemGroup>
<!-- ... -->
<Target Name="OutputCopyright">
<Message Text="Starting 'OutputCopyright' target." />
<Copy
SourceFiles="@(Copyright)"
DestinationFolder="$(OutputPath)" />
<Message Text="'OutputCopyright' target finished." />
</Target>
The execution is governed by many factors. The most important ones:
/t:TargetName
switch on command-line MSBuild execution - tells MSBuild which targets to runDefaultTargets
attribute onProject
node - used when targets not specified on command-lineDependsOnTargets
attribute onTarget
node - lists targets which should be executed before this oneBeforeTargets
attribute onTarget
node - a list of targets before which this one should be executedAfterTargets
attribute onTarget
node - a list of targets after which this one should be executed
Task are operations that are used in targets (Message
, Copy
, Csc
).
Tasks can take items and values as inpus as well as output them:
<SomeTaskWithOutputs>
<Output
TaskParameter="Output1"
PropertyName="OutputProperty" />
<Output
TaskParameter="Output2"
ItemName="OutputItem" />
</SomeTaskWithOutputs>
List of tasks included with MSBuild: https://msdn.microsoft.com/en-us/library/7z253716.aspx
MSBuild Extension Pack: http://www.msbuildextensionpack.com/
MSBuild Community Tasks: https://github.com/loresoft/msbuildtasks
Almost every XML node can have a Condtition
attribute, which signifies wheter this node will be taken into account:
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DefineConstants>DEBUG;TRACE</DefineConstants>
<!-- ... -->
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<DefineConstants>TRACE</DefineConstants>
<!-- ... -->
</PropertyGroup>
With conditions you can make the project more flexible by adjusting the behaviour to the state of the project or introduce build variants. One such example is differentiating between Debug and Release build, other might be performing optional deployment based on a property flag.
For more info about conditions' sytax see: https://msdn.microsoft.com/en-us/library/7szfhaft.aspx
The Choose
node is a conditional structure that helps to deal with "one or the other" scenarios for top level elements (items, properties, targets).
The previous example could be rewritten:
<Choose>
<When Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<PropertyGroup>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<!-- ... -->
</PropertyGroup>
</When>
<When Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<PropertyGroup>
<DefineConstants>TRACE</DefineConstants>
<!-- ... -->
</PropertyGroup>
</When>
<!--<Otherwise>
<PropertyGroup>
<DefineConstants>WTF</DefineConstants>
</PropertyGroup>
</Otherwise>-->
</Choose>
MSBuild allows for various expressions to be used as values:
- property expression:
$(Expression)
- item expression:
@(Expression)
- metadata expression:
%(Expressions)
Most commonly the property expressions are used to read value of some property:
<PropertyGroup>
<DeployFolder>deploy\</DeployFolder>
<DeployZipPath>$(DeployFolder)deploy.zip</DeployZipPath>
</PropertyGroup>
Since properties are just strings some methods can be used on them in the expressions:
<PropertyGroup>
<OutputPath>bin\$(Configuration.Substring(0,3))\</OutputPath>
</PropertyGroup>
It's also possible to access some static members:
$([Namespace.To.Class]::StaticProperty)
$([Namespace.To.Class]::StaticMethod(parameters))
<ItemGroup Condition="$([System.Text.RegularExpressions.Regex]::IsMatch($(TargetFramework), 'net(45|451)'))">
<Reference Include="Microsoft.Build.Framework" />
</ItemGroup>
For more info about what is allowed see: https://msdn.microsoft.com/en-us/library/dd633440.aspx
Use item expressions to... use the item lists as input for other items/targets/tasks:
<ItemGroup>
<Compile Include="**\*.cs" />
<Page Include="**\*.xaml" />
<StuffToShowInTheEditor Include="@(Compile);@(Page)" />
</ItemGroup>
The item expression syntax can be used to map the item list to some other transformed list or even to a scalar value:
@(Compile->Count())
- amount of the Compile
items
@(Compile->Distinct())
- filters the items and selects only the ones that do not repeat
To find out more about what's possible see: https://msdn.microsoft.com/en-us/library/ee886422.aspx
Metadata expressions are used (as the name implies) to access metadata.
<ItemGroup>
<Reference Include="Newtonsoft.Json">
<Version>9.0.1</Version>
</Reference>
</ItemGroup>
<Target Name="Build">
<Message Text="For referece '%(Reference.Identity)' version '%(Version)' is used." />
</Target>
HINT: usage of metadata experssions in targets or tasks triggers batching.
Metadata expressions are commonly used inside item transform expressions:
<ItemGroup>
<Page Include="**\*.xaml" />
<Compile Include="@(Page -> '%(RelativeDir)%(Filename).cs')">
<DependentUpon>%(Filename).xaml</DependentUpon>
</Compile>
</ItemGroup>
- batching: https://msdn.microsoft.com/en-us/library/ms171473.aspx
- custom tasks
- compiled tasks: https://msdn.microsoft.com/en-us/library/t9883dzc.aspx
- inline tasks: https://msdn.microsoft.com/en-us/library/dd722601.aspx
- visual studio integration: https://msdn.microsoft.com/en-us/library/ms171468.aspx
- evaluation and execution order: https://msdn.microsoft.com/en-us/library/dd997067.aspx#Anchor_2
- multitargeting https://msdn.microsoft.com/en-us/library/hh264223.aspx
Special thanks to:
- Semantic Versioning for the format of headers
- Adam P for the Markdown Cheatsheet
- Visual Studio Code for helping me write this
- MSDN documentation for being quite good
- Vim extension for VSCode for being a great help when editing