属性函数

属性函数是对在 MSBuild 属性定义中出现的 .NET 方法的调用。 与任务不同,属性函数可在目标外部使用。 每当展开属性或项时,都会计算属性函数。 因此,对于任何目标外部的属性和项,会在任何目标运行之前计算属性函数。 对于目标中的属性组和项组,会在计算目标时计算属性函数。

可以在生成脚本中读取系统时间、比较字符串、匹配正则表达式及执行其他操作,而无需使用 MSBuild 任务。 MSBuild 将尝试将字符串转换为数字、将数字转换为字符串,并根据需要进行其他转换。

从属性函数返回的字符串值已转义特殊字符。 如果要将这些值视为如同直接置于项目文件中,请使用 $([MSBuild]::Unescape()) 取消转义特殊字符。

属性函数语法

下面列出了三种属性函数;每种函数都有不同的语法:

  • 字符串(实例)属性函数
  • 静态属性函数
  • MSBuild 属性函数

字符串属性函数

所有生成属性值都只是字符串值。 可以使用字符串(实例)方法来操作任何属性值。 例如,可以使用以下代码,从表示完整路径的生成属性中提取驱动器名称(前三个字符):

$(ProjectOutputFolder.Substring(0,3))

静态属性函数

在生成脚本中,可以访问许多系统类的静态属性和方法。 要获取静态属性的值,请使用以下语法,其中 Class 是系统类的名称,Property 是属性的名称。

$([Class]::Property)

例如,可以使用以下代码将生成属性设置为当前日期和时间。

<Today>$([System.DateTime]::Now)</Today>

要调用静态方法,请使用以下语法,其中 Class 是系统类的名称,Method 是方法的名称,(Parameters) 是方法的参数列表:

$([Class]::Method(Parameters))

例如,要将生成属性设置为新的 GUID,可以使用以下脚本:

<NewGuid>$([System.Guid]::NewGuid())</NewGuid>

在静态属性函数中,可以为以下系统类使用在 .NET Standard 2.0 中定义的任何公共静态方法或属性:

注意

在支持 MSBuild 的环境中使用 MSBuild 时,.NET Standard 2.0 中未定义的方法和属性可能可用,但不能保证在所有情况下都可用。 出于兼容性原因,最好避免使用它们。

此外,还可以使用以下静态方法和属性:

System.OperatingSystem 属性函数

System.OperatingSystem 属性函数返回有关运行 MSBuild 的操作系统的信息。 例如,如果项目面向 Linux 并且你在 macOS 上生成项目,则属性函数将返回有关 macOS 的信息。

在 .NET (dotnet build) 上运行的 MSBuild 中,System.OperatingSystem 类的所有静态方法都可以作为静态属性函数进行调用。

在 .NET Framework (MSBuild.exe) 上运行的 MSBuild 中,只有 System.OperatingSystem 的以下方法可作为静态属性函数进行调用。 MSBuild 在内部实现这些方法,因为 System.OperatingSystem 不会在 .NET Framework 上定义它们。 对于没有 .NET SDK 的操作系统(例如 System.OperatingSystem::IsTvOS)不可调用方法。

以下示例演示了这些属性函数的用法。

<IsWindows>$([System.OperatingSystem]::IsWindows())</IsWindows>

对静态属性调用实例方法

如果访问返回对象实例的静态属性,则可以调用该对象的实例方法。 要调用实例方法,请使用以下语法,其中 Class 是系统类的名称,Property 是属性的名称,Method 是方法的名称,(Parameters) 是方法的参数列表:

$([Class]::Property.Method(Parameters))

类的名称必须用命名空间加以完全限定。

例如,使用以下代码可以将生成属性设置为当天的日期:Today。

<Today>$([System.DateTime]::Now.ToString('yyyy.MM.dd'))</Today>

MSBuild 属性函数

可以访问生成中的许多静态方法,以提供算术、按位逻辑和转义字符支持。 可以使用以下语法访问这些方法,其中 Method 是方法的名称,(Parameters) 是方法的参数列表。

$([MSBuild]::Method(Parameters))

例如,要一起添加两个具有数字值的属性,请使用以下代码。

$([MSBuild]::Add($(NumberOne), $(NumberTwo)))

下面列出了 MSBuild 属性函数:

函数签名 描述
double Add(double a, double b) 将两个双精度型值相加。
long Add(long a, long b) 将两个长型值相加。
double Subtract(double a, double b) 将两个双精度型值相减。
long Subtract(long a, long b) 将两个长型值相减。
double Multiply(double a, double b) 将两个双精度型值相乘。
long Multiply(long a, long b) 将两个长型值相乘。
double Divide(double a, double b) 将两个双精度型值相除。
long Divide(long a, long b) 将两个长型值相除。
double Modulo(double a, double b) 对两个双精度型值取模。
long Modulo(long a, long b) 对两个长型值取模。
string Escape(string unescaped) 根据 MSBuild 转义规则对字符串进行转义。
string Unescape(string escaped) 根据 MSBuild 转义规则取消对字符串进行转义。
int BitwiseOr(int first, int second) 对第一个值和第二个值执行按位 OR(第一个值 | 第二个值)。
int BitwiseAnd(int first, int second) 对第一个值和第二个值执行按位 AND(第一个值 & 第二个值)。
int BitwiseXor(int first, int second) 对第一个值和第二个值执行按位 XOR(第一个值 ^ 第二个值)。
int BitwiseNot(int first) 执行按位 NOT(~第一个值)。
bool IsOsPlatform(string platformString) 指定当前 OS 平台是否为 platformStringplatformString 必须属于 OSPlatform
bool IsOSUnixLike() 如果当前 OS 是 Unix 系统,则为 True。
string NormalizePath(params string[] path) 获取指定路径的规范化完整路径,并确保其包含当前操作系统的正确目录分隔符。
string NormalizeDirectory(params string[] path) 获取指定目录的规范化完整路径,确保其包含当前操作系统的正确目录分隔符,并确保其具有尾部反斜杠。
string EnsureTrailingSlash(string path) 如果给定路径没有尾部反斜杠,请添加一个。 如果路径为空字符串,请勿其进行修改。
string GetPathOfFileAbove(string file, string startingDirectory) 在当前生成文件位置所在及上级的目录结构中搜索并返回文件的完整路径,如果指定,则基于 startingDirectory
string GetDirectoryNameOfFileAbove(string startingDirectory, string fileName) 在指定目录或该目录上级的目录结构位置中查找并返回文件目录。
string MakeRelative(string basePath, string path) path 关联到 basePathbasePath 必须是绝对目录。 如果无法关联 path,则会返回逐字字符串。 类似于 Uri.MakeRelativeUri
string ValueOrDefault(string conditionValue, string defaultValue) 仅当参数 conditionValue 为空时在参数 defaultValue 中返回字符串,否则返回值 conditionValue
string ConvertToBase64(string toEncode) 将所有字节转换为 base 64(字母数字字符加 +/)后返回字符串,以一个或两个 = 结尾。
string ConvertFromBase64(string toDecode) 从 base 64 转换(字母数字字符加 +/)后返回字符串,以一个或两个 = 结尾。

嵌套的属性函数

可将属性函数组合在一起,组成更复杂的函数,如下例所示:

$([MSBuild]::BitwiseAnd(32, $([System.IO.File]::GetAttributes(tempFile))))

此示例返回路径 FileAttributes 提供的路径 tempFile 提供的文件的 Archive 位(32 或 0)。 请注意,枚举的数据值在某些上下文中不能按名称显示。 在上一示例中,必须使用数值 (32)。 在其他情况下,根据所调用方法的预期结果,必须使用枚举数据值。 在下面的示例中,必须使用枚举值 RegexOptions.,必须使用 ECMAScript,因为无法按此方法预期那样转换数值。

<PropertyGroup>
    <GitVersionHeightWithOffset>$([System.Text.RegularExpressions.Regex]::Replace("$(PrereleaseVersion)", "^.*?(\d+)$", "$1", "System.Text.RegularExpressions.RegexOptions.ECMAScript"))</GitVersionHeightWithOffset>
</PropertyGroup>

元数据也可以出现在嵌套的属性函数中。 有关详细信息,请参阅批处理

MSBuild DoesTaskHostExist

MSBuild 中的 DoesTaskHostExist 属性函数将返回当前是否针对特定运行时和体系结构值安装了任务宿主。

此属性函数具有以下语法:

$([MSBuild]::DoesTaskHostExist(string theRuntime, string theArchitecture))

MSBuild EnsureTrailingSlash

如果尚不存在尾部反斜杠,MSBuild 中的 EnsureTrailingSlash 属性函数将添加一条。

此属性函数具有以下语法:

$([MSBuild]::EnsureTrailingSlash('$(PathProperty)'))

MSBuild GetDirectoryNameOfFileAbove

MSBuild GetDirectoryNameOfFileAbove 属性函数向上搜索包含指定文件的目录,从指定目录开始(包括指定目录)。 如果找到文件,它将返回包含该文件的最近目录的完整路径,否则返回空字符串。

此属性函数具有以下语法:

$([MSBuild]::GetDirectoryNameOfFileAbove(string startingDirectory, string fileName))

此示例演示了如何在当前文件夹中或更高一级中导入最近的 EnlistmentInfo.props 文件(仅当找到匹配项时):

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), EnlistmentInfo.props))\EnlistmentInfo.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), EnlistmentInfo.props))' != '' " />

请注意,此示例可以通过使用 GetPathOfFileAbove 函数来更简洁地编写:

<Import Project="$([MSBuild]::GetPathOfFileAbove(EnlistmentInfo.props))" Condition=" '$([MSBuild]::GetPathOfFileAbove(EnlistmentInfo.props))' != '' " />

MSBuild GetPathOfFileAbove

MSBuild GetPathOfFileAbove 属性函数向上搜索包含指定文件的目录,从指定目录开始(包括指定目录)。 如果找到文件,它将返回最近匹配文件的完整路径,否则返回空字符串。

此属性函数具有以下语法:

$([MSBuild]::GetPathOfFileAbove(string file, [string startingDirectory]))

其中,file 是要搜索的文件的名称,startingDirectory 是开始搜索的可选目录。 默认情况下,搜索将在当前文件自身的目录中开始。

此示例演示如何在当前目录中或更高一级中导入名为 dir.props 的文件(仅当找到匹配项时):

<Import Project="$([MSBuild]::GetPathOfFileAbove(dir.props))" Condition=" '$([MSBuild]::GetPathOfFileAbove(dir.props))' != '' " />

这在功能上等效于

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))' != '' " />

但是,有时需要在父目录中开始搜索,以避免与当前文件匹配。 此示例演示 Directory.Build.props 文件如何在严格意义上更高级别的树中导入最近的 Directory.Build.props 文件,而不会以递归方式将自己导入 :

<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

这在功能上等效于

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove('$(MSBuildThisFileDirectory)../', 'Directory.Build.props'))/Directory.Build.props" />

MSBuild GetRegistryValue

MSBuild GetRegistryValue 属性函数返回注册表项的值。 此函数采用两个参数(项名称和值名称),并从注册表中返回值。 如果未指定值名称,则返回默认值。

下面的示例演示如何使用此函数:

$([MSBuild]::GetRegistryValue(`HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Debugger`, ``))                                  // default value
$([MSBuild]::GetRegistryValue(`HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\10.0\Debugger`, `SymbolCacheDir`))
$([MSBuild]::GetRegistryValue(`HKEY_LOCAL_MACHINE\SOFTWARE\(SampleName)`, `(SampleValue)`))             // parens in name and value

警告

在 .NET SDK 版本的 MSBuild (dotnet build) 中,不支持此函数。

MSBuild GetRegistryValueFromView

MSBuild GetRegistryValueFromView 属性函数在给定了注册表项、值以及一个或多个经过排序的注册表视图的情况下,获取系统注册表数据。 该属性函数将按顺序在每个注册表视图中搜索注册表项和值,直至找到它们。

该属性函数的语法是:

[MSBuild]::GetRegistryValueFromView(string keyName, string valueName, object defaultValue, params object[] views)

Windows 64 位操作系统维护一个 HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node 注册表项,它表示 32 位应用程序的 HKEY_LOCAL_MACHINE\SOFTWARE 注册表视图。

默认情况下,在 WOW64 上运行的 32 位应用程序将访问 32 位注册表视图,而 64 位应用程序将访问 64 位注册表视图。

以下这些注册表视图是可用的:

注册表视图 定义
RegistryView.Registry32 32 位应用程序注册表视图。
RegistryView.Registry64 64 位应用程序注册表视图。
RegistryView.Default 与应用程序正在其中运行的进程匹配的注册表视图。

下面是一个示例。

$([MSBuild]::GetRegistryValueFromView('HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Silverlight\v3.0\ReferenceAssemblies', 'SLRuntimeInstallPath', null, RegistryView.Registry64, RegistryView.Registry32))

首先在 64 位注册表视图中查找,然后在 32 位注册表视图中查找,以获取 ReferenceAssemblies 项的 SLRuntimeInstallPath 数据。

警告

在 .NET SDK 版本的 MSBuild (dotnet build) 中,不支持此函数。

MSBuild MakeRelative

MSBuild MakeRelative 属性函数将返回第二条路径的相对路径(相对于第一条路径)。 每条路径可以是文件或文件夹。

此属性函数具有以下语法:

$([MSBuild]::MakeRelative($(FileOrFolderPath1), $(FileOrFolderPath2)))

下面的代码是此语法的示例。

<PropertyGroup>
    <Path1>c:\users\</Path1>
    <Path2>c:\users\username\</Path2>
</PropertyGroup>

<Target Name = "Go">
    <Message Text ="$([MSBuild]::MakeRelative($(Path1), $(Path2)))" />
    <Message Text ="$([MSBuild]::MakeRelative($(Path2), $(Path1)))" />
</Target>

<!--
Output:
   username\
   ..\
-->

MSBuild StableStringHash

MSBuild StableStringHash 属性函数接受字符串参数,并返回一个保证稳定的哈希代码,这意味着始终为相同的字符串输入返回相同的代码。 与 .NET 方法 GetHashCode 不同,返回的哈希是相同的(无论使用的是 MSBuild 还是 dotnet build),且在平台体系结构之间保持稳定。 不能保证在不同的 MSBuild 版本之间保持稳定。

此函数在 MSBuild 16.9.0 或更高版本中可用。

下面的示例演示如何使用此函数。

<Project>
   <PropertyGroup>
      <MyHash>$([MSBuild]::StableStringHash("test1"))</MyHash>
   </PropertyGroup>

   <Target Name="WriteHash" AfterTargets="Build">
      <Message Text="Hash: $(MyHash)"/>
   </Target>
</Project>

从 MSBuild 版本 17.10.0 开始,此函数接受请求使用哈希算法的第二个可选参数:

<Project>
   <PropertyGroup>
      <MyHash>$([MSBuild]::StableStringHash("test1", "Sha256"))</MyHash>
   </PropertyGroup>

   <Target Name="WriteHash" AfterTargets="Build">
      <Message Text="Hash: $(MyHash)"/>
   </Target>
</Project>

第二个参数不区分大小写,当前支持以下值:

  • 旧版 - 保持与调用不带第二个参数的函数相同的行为。 使用与 string.GetHashCode 相似的属性返回带符号的 32 位整数。
  • Fnv1a32bit - 返回带符号的 32 位整数,此整数表示给定字符串的版本“1a”的 Fowler–Noll–Vo 哈希
  • Fnv1a64bit - 返回带符号的 64 位整数,此整数表示给定字符串的版本“1a”的 Fowler–Noll–Vo 哈希
  • Sha256 - 返回一个表示给定字符串的 SHA256 哈希的无前缀十六进制字符串。

MSBuild ValueOrDefault

MSBuild ValueOrDefault 属性函数将返回第一个参数,除非它为 null 或空。 如果第一个参数为 null 或空,则该函数将返回第二个参数。

下面的示例演示如何使用此函数。

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

    <PropertyGroup>
        <Value1>$([MSBuild]::ValueOrDefault('$(UndefinedValue)', 'a'))</Value1>
        <Value2>$([MSBuild]::ValueOrDefault('b', '$(Value1)'))</Value2>
    </PropertyGroup>

    <Target Name="MyTarget">
        <Message Text="Value1 = $(Value1)" />
        <Message Text="Value2 = $(Value2)" />
    </Target>
</Project>

<!--
Output:
  Value1 = a
  Value2 = b
-->

MSBuild TargetFramework 和 TargetPlatform 函数

MSBuild 16.7 及更高版本定义了几个用于处理 TargetFramework 和 TargetPlatform 属性的函数。

函数签名 描述
GetTargetFrameworkIdentifier(string targetFramework) 从 TargetFramework 分析 TargetFrameworkIdentifier。
GetTargetFrameworkVersion(string targetFramework, int versionPartCount) 从 TargetFramework 分析 TargetFrameworkVersion。
GetTargetPlatformIdentifier(string targetFramework) 从 TargetFramework 分析 TargetPlatformIdentifier。
GetTargetPlatformVersion(string targetFramework, int versionPartCount) 从 TargetFramework 分析 TargetPlatformVersion。
IsTargetFrameworkCompatible(string targetFrameworkTarget, string targetFrameworkCandidate) 如果候选目标框架(第二个参数)与第一个参数指示的目标框架兼容,则返回 true,否则返回 false。

GetTargetFrameworkVersionGetTargetPlatformVersionversionPartCount 参数的默认值为 2。

下面的示例演示如何使用这些函数。

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

    <PropertyGroup>
        <Value1>$([MSBuild]::GetTargetFrameworkIdentifier('net5.0-windows7.0'))</Value1>
        <Value2>$([MSBuild]::GetTargetFrameworkVersion('net5.0-windows7.0'))</Value2>
        <Value3>$([MSBuild]::GetTargetPlatformIdentifier('net5.0-windows7.0'))</Value3>
        <Value4>$([MSBuild]::GetTargetPlatformVersion('net5.0-windows7.0'))</Value4>
        <Value5>$([MSBuild]::IsTargetFrameworkCompatible('net5.0-windows', 'net5.0'))</Value5>
        <Value6>$([MSBuild]::IsTargetFrameworkCompatible('net5.0', 'net6.0'))</Value6>
        <Value7>$([MSBuild]::IsTargetFrameworkCompatible('net5.0', 'net8.0'))</Value7>
    </PropertyGroup>

    <Target Name="MyTarget">
        <Message Text="Value1 = $(Value1)" />
        <Message Text="Value2 = $(Value2)" />
        <Message Text="Value3 = $(Value3)" />
        <Message Text="Value4 = $(Value4)" />
        <Message Text="Value5 = $(Value5)" />
        <Message Text="Value6 = $(Value6)" />
        <Message Text="Value7 = $(Value7)" />
    </Target>
</Project>
Value1 = .NETCoreApp
Value2 = 5.0
Value3 = windows
Value4 = 7.0
Value5 = True
Value6 = False
Value7 = False

MSBuild 版本比较函数

MSBuild 16.5 及更高版本定义了若干函数,用于比较表示版本的字符串。

注意

条件中的比较运算符可以比较可分析为 System.Version 对象 的字符串,但比较可能会产生意外结果。 首选属性函数。

函数签名 描述
VersionEquals(string a, string b) 如果版本 ab 等效,则根据以下规则返回 true
VersionGreaterThan(string a, string b) 如果版本 a 大于 b,则根据以下规则返回 true
VersionGreaterThanOrEquals(string a, string b) 如果版本 a 大于或等于 b,则根据以下规则返回 true
VersionLessThan(string a, string b) 如果版本 a 小于 b,则根据以下规则返回 true
VersionLessThanOrEquals(string a, string b) 如果版本 a 小于或等于 b,则根据以下规则返回 true
VersionNotEquals(string a, string b) 如果版本 ab 等效,则根据以下规则返回 false

在这些方法中,版本的分析类似于 System.Version,但以下情况例外:

  • 忽略前导 vV,以便可以与 $(TargetFrameworkVersion) 进行比较。

  • 从第一个“-”或“+”到版本字符串末尾的所有内容都将被忽略。 虽然顺序与 SemVer 不同,但允许传递语义版本 (SemVer)。 相反,预发布说明符和生成元数据没有任何排序权重。 例如,若要打开 >= x.y 的功能并使其在 x.y.z-pre 上启动,这很有用。

  • 未指定部分与零值部分相同。 (x == x.0 == x.0.0 == x.0.0.0)。

  • 整数组件中不允许含有空格。

  • 仅主版本有效(3 等于 3.0.0.0

  • + 不允许作为整数组件中的正号(它被视为 SemVer 元数据并被忽略)

提示

要比较 TargetFramework 属性,通常应使用 IsTargetFrameworkCompatible,而不是提取和比较版本。 这允许比较在 TargetFrameworkIdentifier 和版本中不同的 TargetFramework

MSBuild 条件函数

函数 ExistsHasTrailingSlash 不是属性函数。 它们可与 Condition 属性一起使用。 请参阅 MSBuild 条件