在MSBuild .targets文件中获取Unix时间戳报错,求正确实现方式
问题描述
尝试在C++项目的MSBuild .targets文件中,将构建时的Unix时间戳(自Unix纪元起始的秒数)作为预处理器定义注入。其中BUILD_YEAR可正常工作,但BUILD_DATETIME的写法报错,提示Subtract方法不存在或为静态方法。期望用当前方式实现,而非处理__TIME__的方案。
报错信息
Invalid static method invocation syntax: ".Subtract([System.DateTime]::UnixEpoch).TotalSeconds". Method 'System.DateTime.Subtract' not found. Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(a, b)). Check that all parameters are defined, are of the correct type, and are specified in the right order.
原代码
<Project ToolsVersion="16.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <!-- All Builds --> <BUILD_YEAR>$([System.DateTime]::Now.ToString("yyyy"))</BUILD_YEAR> <BUILD_DATETIME>$([System.DateTime]::Now.Subtract([System.DateTime]::UnixEpoch).TotalSeconds)</BUILD_DATETIME> </PropertyGroup> <ItemGroup> <!-- Note: This will ultimately come from the same path as Version.rc --> <ClCompile> <PreprocessorDefinitions>BUILD_YEAR="$(BUILD_YEAR)";BUILD_DATETIME=$(BUILD_DATETIME);%(PreprocessorDefinitions)</PreprocessorDefinitions> </ClCompile> </ItemGroup> </Project>
问题原因与解决方案
MSBuild的属性函数语法不支持C#式的链式调用,无法直接对方法返回值连续调用其他方法。以下是两种可行的解决方式:
方案1:使用DateTimeOffset直接获取Unix时间戳(推荐)
借助System.DateTimeOffset的ToUnixTimeSeconds()方法可以直接得到秒级Unix时间戳,写法简洁且符合MSBuild语法:
<PropertyGroup> <BUILD_YEAR>$([System.DateTime]::Now.ToString("yyyy"))</BUILD_YEAR> <BUILD_DATETIME>$([System.DateTimeOffset]::Now.ToUnixTimeSeconds())</BUILD_DATETIME> </PropertyGroup>
方案2:拆分链式调用为多步计算(兼容旧版MSBuild)
如果需要兼容不支持DateTimeOffset的旧版MSBuild,可以将计算步骤拆分,通过静态方法调用实现:
<PropertyGroup> <BUILD_YEAR>$([System.DateTime]::Now.ToString("yyyy"))</BUILD_YEAR> <BUILD_NOW>$([System.DateTime]::Now)</BUILD_NOW> <!-- 通过静态方法计算时间差 --> <BUILD_TIMESPAN>$([System.TimeSpan]::FromTicks($([System.DateTime]::Subtract($(BUILD_NOW), [System.DateTime]::UnixEpoch))))</BUILD_TIMESPAN> <!-- 取整得到秒级时间戳 --> <BUILD_DATETIME>$([System.Math]::Floor($(BUILD_TIMESPAN).TotalSeconds))</BUILD_DATETIME> </PropertyGroup>
注意事项
BUILD_DATETIME是数值类型,预定义时无需加引号,原代码中的写法正确。DateTimeOffset.ToUnixTimeSeconds()需要.NET Framework 4.6+或.NET Core环境,对应MSBuild 15.0及以上版本支持。
内容的提问来源于stack exchange,提问作者pjcard

