Text in an MSBuild file is expanded before it is used: an attribute value, the text of a
property or of item metadata, a task parameter. Expansion replaces three kinds of
expression and one kind of escape. $(Name) reads a property,
@(Name) reads an item list, %(Name) reads item metadata (and makes
the surrounding element run once for each distinct value, which is called batching), and
%3B is a character written by its code. Properties can call
functions, and item lists can be transformed.
The pages of the explorer show every expression as a node of its own below the attribute or text that holds it, with its kind and meaning.
The forms
| Form | Reads | Result |
|---|---|---|
$(Name) | A property | Its value, or the empty string when nothing has set it. Environment variables are properties too. Names are letters, digits, _ and -, not starting with a digit; they are not case-sensitive. |
$(Name.Method(args)) | A string method on a property | The result of the .NET String method; chained and indexed forms work: $(V.Split('.')[1]). |
$([Class]::Member(args)) | A static method or property of an allowed .NET class | The result as a string. $([MSBuild]::Function(…)) are MSBuild’s own functions. |
$(registry:HIVE\Key@Value) | A registry value (Windows) | The value, or the key’s default value when no @Name is given. |
@(Type) | An item list | The items of the type, joined with semicolons. |
@(Type, 'sep') | An item list with a separator | The items joined with sep. |
@(Type->'template') | A transform | One text per item: the template with every %(Metadata) in it replaced for that item. |
@(Type->Function(args)) | An item function | See item functions; functions chain with more ->. |
%(Name), %(Type.Name) | Item metadata | The metadata of the current item in a batch (see batching) or in a transform. |
%XX | An escape | The character with hexadecimal code XX. |
A malformed expression in a value is not an error: $(, @(,
$() and $(a b) are left as the text they are. Only a function
MSBuild cannot find or call is an error (MSB4184 for a string method that
does not exist, MSB4186 for a static method that does not, MSB4212
for a class that may not be used).
Escapes
The characters MSBuild gives a meaning to are written as % and two
hexadecimal digits when they should be taken literally: this is what
[MSBuild]::Escape produces and [MSBuild]::Unescape reverses.
| Character | Escape | Meant by it |
|---|---|---|
% | %25 | Starts an escape or %(…). |
$ | %24 | Starts $(…). |
@ | %40 | Starts @(…). |
' | %27 | Quotes in conditions and transforms. |
( ) | %28 %29 | Delimit expressions. |
; | %3B | Separates items in a list. |
* ? | %2A %3F | Wildcards in an item’s Include. |
Reserved and well-known properties
MSBuild defines these itself. A project that sets one is refused (MSB4004),
except the extension paths and a few the build tasks may override.
| Property | Value |
|---|---|
MSBuildProjectFullPath, MSBuildProjectDirectory, MSBuildProjectDirectoryNoRoot, MSBuildProjectFile, MSBuildProjectName, MSBuildProjectExtension | The project file being built: its full path, folder (with and without the drive), file name with extension, name without extension, and extension with its dot. |
MSBuildThisFile, MSBuildThisFileName, MSBuildThisFileExtension, MSBuildThisFileFullPath, MSBuildThisFileDirectory, MSBuildThisFileDirectoryNoRoot | The same for the file the expression is written in: the import, not the project. $(MSBuildThisFileDirectory) is how a props file refers to its neighbours. |
MSBuildBinPath, MSBuildToolsPath | The folder of the MSBuild binaries in use. |
MSBuildToolsVersion, MSBuildVersion, MSBuildAssemblyVersion, MSBuildFileVersion, MSBuildSemanticVersion | The version of MSBuild. |
MSBuildRuntimeType | Core, Full or Mono. |
MSBuildStartupDirectory | The folder MSBuild was started in. |
MSBuildNodeCount | The number of parallel build nodes. |
MSBuildLastTaskResult | true when the task before it succeeded, false when it failed. |
MSBuildProjectDefaultTargets | The project’s DefaultTargets. |
MSBuildProgramFiles32 | The 32-bit Program Files folder. |
MSBuildExtensionsPath, MSBuildExtensionsPath32, MSBuildExtensionsPath64, MSBuildUserExtensionsPath, MSBuildSDKsPath | Where the MSBuild extensions, user extensions and bundled SDKs are. These may be overridden. |
OS | Windows_NT or Unix. |
LocalAppData | The local application data folder. |
Property functions
[MSBuild]:: functions
MSBuild’s own functions, written $([MSBuild]::Name(args)). A name that is not
in this table is an error (MSB4186).
| Function | Arguments | Result |
|---|---|---|
Add | a, b | The sum of two numbers: $([MSBuild]::Add(1, 2)) is 3. |
Subtract | a, b | a minus b. |
Multiply | a, b | The product. |
Divide | a, b | a divided by b. |
Modulo | a, b | The remainder of the division. |
Escape | text | The text with its special characters written as %XX. |
Unescape | text | The text with %XX turned back into characters. |
BitwiseOr, BitwiseAnd, BitwiseXor | a, b | The bitwise operation on two integers. |
BitwiseNot | a | The bitwise complement. |
LeftShift, RightShift, RightShiftUnsigned | operand, count | The integer shifted (the last fills with zeros). |
GetRegistryValue | key, value [, default] | A registry value (Windows). |
GetRegistryValueFromView | key, value, default, views… | A registry value read from the 32-bit or 64-bit view. |
MakeRelative | base, path | The path relative to the base: MakeRelative('/a/b/', '/a/b/c/d.txt') is c/d.txt. |
GetDirectoryNameOfFileAbove | start folder, file name | The folder that holds the file, searching from the start folder upward. |
GetPathOfFileAbove | file [, start folder] | The full path of the file found going upward (how Directory.Build.props is found). |
ValueOrDefault | value, default | The value, or the default when the value is empty. |
ConvertToBase64, ConvertFromBase64 | text | Base-64 text of a string, and the string of base-64 text. |
StableStringHash | text [, algorithm] | A hash of the string that is the same on every run. |
DoesTaskHostExist | runtime, architecture | Whether a task host exists for the runtime and architecture. |
EnsureTrailingSlash | path | The path with a trailing directory separator. |
NormalizeDirectory | paths… | The paths combined, made full and given a trailing slash. |
NormalizePath | paths… | The paths combined and made full. |
FileExists, DirectoryExists | path | Whether it exists. |
IsOSPlatform | name | Whether the OS is that platform: Windows, OSX, Linux, FreeBSD… |
IsOsUnixLike | Whether the OS is Unix-like. | |
VersionEquals, VersionNotEquals, VersionGreaterThan, VersionGreaterThanOrEquals, VersionLessThan, VersionLessThanOrEquals | a, b | Compares two versions. |
GetTargetFrameworkIdentifier | moniker | The framework of a target framework moniker: net8.0 gives .NETCoreApp. |
GetTargetFrameworkVersion | moniker [, parts] | Its version: 8.0. |
GetTargetPlatformIdentifier | moniker | Its platform: net8.0-windows10.0.19041 gives windows. |
GetTargetPlatformVersion | moniker [, parts] | Its platform version: 10.0.19041. |
IsTargetFrameworkCompatible | target, candidate | Whether a project for the target can use the candidate. |
FilterTargetFrameworks | list, filter | The monikers of the list that the filter allows. |
AreFeaturesEnabled | wave | Whether the features of an MSBuild change wave are on. |
CheckFeatureAvailability | name | Whether this MSBuild has the named feature. |
SubstringByAsciiChars | text, start, length | A substring counted in ASCII characters. |
GetCurrentToolsDirectory, GetToolsDirectory32, GetToolsDirectory64 | The folder of the MSBuild tools in use, and the 32-bit and 64-bit ones. | |
GetMSBuildSDKsPath, GetMSBuildExtensionsPath, GetProgramFiles32, GetVsInstallRoot | The folder of the bundled SDKs, of the MSBuild extensions, of 32-bit Program Files and of the Visual Studio installation. | |
IsRunningFromVisualStudio | Whether MSBuild is running inside Visual Studio. | |
RegisterBuildCheck | project, assembly | Registers a build check (analyzer) assembly. |
Static .NET classes
A static method or property of these classes may be called as
$([System.IO.Path]::Combine('a', 'b')); a class that is not on the list is
refused (MSB4212), and a method that does not exist is an error
(MSB4186).
System.Byte, System.Char, System.Convert,
System.DateTime, System.Decimal, System.Double,
System.Enum, System.Guid, System.Int16,
System.Int32, System.Int64, System.IO.Path,
System.Math, System.UInt16, System.UInt32,
System.UInt64, System.SByte, System.Single,
System.String, System.StringComparer,
System.TimeSpan, System.Text.RegularExpressions.Regex,
System.Version, System.Environment (selected members),
System.IO.Directory and System.IO.File (existence, time and
read members), System.Globalization.CultureInfo,
System.Runtime.InteropServices.RuntimeInformation and
OSPlatform, Microsoft.Build.Utilities.ToolLocationHelper, and
MSBuild (the table above). The explorer marks a class outside this list as a
note, not an error, because this is the documented list and MSBuild itself may accept
more.
String methods on a property
$(Name.Method(args)) calls a System.String method on the value:
Contains, EndsWith, StartsWith, IndexOf,
LastIndexOf, IndexOfAny, LastIndexOfAny,
Insert, PadLeft, PadRight, Remove,
Replace, Split, Substring, ToLower,
ToLowerInvariant, ToUpper, ToUpperInvariant,
Trim, TrimStart, TrimEnd, CompareTo,
Equals, Normalize, IsNormalized,
ToCharArray, GetHashCode, ToString and the
Length property. $(P.ToUpper()) on hello gives
HELLO, $(P.Substring(1)) gives ello, and
$(V.Split('.')[1]) on 1.2.3 gives 2.
Item lists
<ItemGroup>
<A Include="src/a.cs;src/b.cs;lib/c.dll"><Kind>Code</Kind></A>
</ItemGroup>
<Target Name="Show">
<Message Text="@(A)" /> <!-- src/a.cs;src/b.cs;lib/c.dll -->
<Message Text="@(A, ' | ')" /> <!-- src/a.cs | src/b.cs | lib/c.dll -->
<Message Text="@(A->'%(Filename)%(Extension)', ';')" /> <!-- a.cs;b.cs;c.dll -->
<Message Text="@(A->Count())" /> <!-- 3 -->
<Message Text="@(A->'%(RelativeDir)'->Distinct())" /> <!-- src/;lib/ -->
<Message Text="@(A->Reverse())" /> <!-- lib/c.dll;src/b.cs;src/a.cs -->
</Target>
dotnet msbuild.Item functions
@(Type->Function(args)) maps a list to a list or a value; functions chain. A name
that is not in this table is tried as a System.String method on each item.
| Function | Result |
|---|---|
Count() | The number of items. |
Exists() | The items whose file or folder exists. |
Combine('path') | Each item joined to a path. |
GetPathsOfAllDirectoriesAbove() | Every folder above each item. |
DirectoryName() | The full folder of each item. |
Metadata('name') | The value of a metadata, as items. |
Distinct() | The items without duplicates, ignoring case. |
DistinctWithCase() | The items without duplicates, case sensitive. |
Reverse() | The items in the opposite order. |
ClearMetadata() | The items without their custom metadata. |
HasMetadata('name') | The items that have the metadata. |
WithMetadataValue('name', 'value') | The items whose metadata has the value. |
WithoutMetadataValue('name', 'value') | The items whose metadata does not. |
AnyHaveMetadataValue('name', 'value') | true when any item has it. |
Item metadata
Well-known metadata
Every item has these, whatever its type; they are derived from the item’s value and are read-only. They are not written in the file.
| Name | Value |
|---|---|
Identity | The item as written in Include. |
FullPath | The full path. |
RootDir | The root of the full path (a drive or share; / on Unix). |
Filename | The file name without the extension. |
Extension | The extension, with its dot. |
RelativeDir | The folder part as written. |
Directory | The folder part of the full path, without the root. |
RecursiveDir | For an item from a ** wildcard: what the ** matched. |
ModifiedTime, CreatedTime, AccessedTime | The file’s times. |
DefiningProjectFullPath, DefiningProjectDirectory, DefiningProjectName, DefiningProjectExtension | The file that defined the item: useful when a props file adds items to the projects that import it. |
Batching
A %(…) in a task’s attributes makes MSBuild call the task once for every
different value of that metadata among the items it reads (task batching); in a
target’s Inputs and Outputs it batches the target (target
batching). %(Type.Name) names the item type when several are read.
Inside a transform it only picks the metadata of the item being transformed.
<Target Name="ByFolder">
<!-- runs once per folder, with only that folder’s items in @(A) -->
<Message Text="%(A.RelativeDir): @(A->'%(Filename)')" />
</Target>
Not described
Known, not yet covered
- Reference: the individual members of each allowed .NET class (hundreds); the allow list MSBuild applies beyond the documented classes; the metadata and property functions that Visual Studio and individual SDKs add.
Sources
MSBuild source (dotnet/msbuild):
IntrinsicFunctions.cs (every [MSBuild]:: function above),
Expander.ItemExpander.cs (the item functions),
ItemSpecModifiers.cs (the well-known metadata) and
ReservedPropertyNames.cs (the reserved properties). The result of each
example was produced with dotnet msbuild of the .NET SDK. Microsoft Learn,
property
functions, item
functions, well-known
item metadata and reserved
and well-known properties.