属性函数

属性函数是对 MSBuild 属性定义中显示的 .NET 方法的调用。 通常使用它们来构造需要更复杂的逻辑的属性定义。

与任务不同,属性函数可以在目标外部使用。 每当属性或项展开时,都会评估属性函数。 因此,对于任何目标之外的属性和项,将在任何目标运行之前评估属性函数。 对于目标中的属性组和项组,在执行目标时会评估属性函数。

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

从属性函数返回的字符串值中的特殊字符被转义。 如果希望将值视为直接放入项目文件中,则用于 $([MSBuild]::Unescape()) 取消设置特殊字符。

属性函数语法

这些是三种类型的属性函数:每个函数都有不同的语法:

  • String (instance) 属性函数
  • 静态属性函数
  • 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 上运行的 System.OperatingSystem MSBuild 中,dotnet build类的所有静态方法都可以作为静态属性函数调用。

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

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

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

对静态属性调用实例方法

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

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

类名必须使用命名空间进行完整限定。

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

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

MSBuild 属性函数

在您的项目构建中,有若干静态方法可访问,以便于提供算术、按位逻辑和转义字符的支持。 使用以下语法访问这些方法,其中方法 Method 的名称是 (Parameters) 该方法的参数列表。

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

例如,若要将具有数值的两个属性相加,请使用以下代码。

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

下面是 MSBuild 属性函数的列表:

函数签名 DESCRIPTION
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 系统。
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) 将所有字节转换为 Base64(字母数字字符加 +/),最后以一个或两个 = 结尾,然后返回字符串。
string ConvertFromBase64(string toDecode) 从 base 64(字母数字字符加上+/)转换后返回字符串,以一个或两个=结尾。

嵌套属性函数

可以组合属性函数以形成更复杂的函数,如以下示例所示:

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

此示例返回值 FileAttributesArchive 文件路径tempFile所示文件的第(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 中的属性函数返回任务主机当前是否为指定的运行时和体系结构值安装。

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

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

MSBuild EnsureTrailingSlash

EnsureTrailingSlash MSBuild 中的属性函数添加尾部斜杠(如果尚不存在)。

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

$([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 获取注册表值

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

警告

在 MSBuild 的 .NET SDK 版本中,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.默认 与应用程序正在运行的进程匹配的注册表视图。

下面是一个示例。

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

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

警告

在 MSBuild 的 .NET SDK 版本中,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 属性函数接受字符串参数,并返回一个保证稳定哈希代码,这意味着始终为同一字符串输入返回相同的代码。 无论是使用 MSBuild 还是 dotnet build,返回的哈希值都是相同的,并且在跨平台架构中保持稳定,这一点不同于 .NET 方法 GetHashCode。 不能保证在不同的 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>

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

  • 旧版 - 保留与在没有第二个参数的情况下调用函数相同的行为。 返回带符号的 32 位整数,其属性与 string.GetHashCode 相似。
  • Fnv1a32bit - 返回表示给定字符串的 Fowler–Noll–Vo 版本“1a”哈希的有符号32位整数。
  • 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 属性的函数。

函数签名 DESCRIPTION
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。

versionPartCountGetTargetFrameworkVersionGetTargetPlatformVersion参数默认值为 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 对象的字符串,但比较可能会产生意外的结果。 首选属性函数。

函数签名 DESCRIPTION
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,而不是提取和比较版本。 可以比较 TargetFrameworkTargetFrameworkIdentifier 和版本上的变化。

MSBuild 条件函数

函数 ExistsHasTrailingSlash 不是属性函数。 它们可用于特性 Condition 。 请参阅 MSBuild 条件