我目前有一个应用程序显示构建号在其标题窗口。这很好,除了它对大多数用户没有任何意义,他们想知道他们是否有最新的版本-他们倾向于将其称为“上周四的”而不是1.0.8.4321版本。
计划是把构建日期放在那里,例如“App构建于21/10/2009”。
我正在努力寻找一种程序化的方法,将构建日期作为文本字符串提取出来,以便像这样使用。
对于版本号,我使用:
Assembly.GetExecutingAssembly().GetName().Version.ToString()
在定义了这些是怎么来的之后。
我希望编译日期(和时间,为了加分)也像这样。
非常感谢这里的指示(如果合适的话,借口双关语),或者更整洁的解决方案……
在2018年,上面的一些解决方案不再工作,或者不能与。net Core一起工作。
我使用下面的方法,它很简单,适用于我的。net Core 2.0项目。
将以下内容添加到PropertyGroup中的.csproj中:
<Today>$([System.DateTime]::Now)</Today>
这定义了一个PropertyFunction,您可以在预构建命令中访问它。
您的预构建看起来像这样
echo $(today) > $(ProjectDir)BuildTimeStamp.txt
将BuildTimeStamp.txt的属性设置为嵌入式资源。
现在你可以像这样读时间戳
public static class BuildTimeStamp
{
public static string GetTimestamp()
{
var assembly = Assembly.GetEntryAssembly();
var stream = assembly.GetManifestResourceStream("NamespaceGoesHere.BuildTimeStamp.txt");
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
另一种不同的pcl友好的方法是使用MSBuild内联任务将构建时间替换为应用程序上的属性返回的字符串。我们在一个具有Xamarin的应用程序中成功地使用了这种方法。形式,Xamarin的。Android和Xamarin。iOS项目。
编辑:
通过将所有逻辑移动到SetBuildDate来简化。目标文件,并使用Regex而不是简单的字符串替换,这样文件可以在每次构建时修改而不“重置”。
MSBuild内联任务定义(保存在SetBuildDate中。目标文件本地到Xamarin。本例中的表单项目):
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="12.0">
<UsingTask TaskName="SetBuildDate" TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
<ParameterGroup>
<FilePath ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs"><![CDATA[
DateTime now = DateTime.UtcNow;
string buildDate = now.ToString("F");
string replacement = string.Format("BuildDate => \"{0}\"", buildDate);
string pattern = @"BuildDate => ""([^""]*)""";
string content = File.ReadAllText(FilePath);
System.Text.RegularExpressions.Regex rgx = new System.Text.RegularExpressions.Regex(pattern);
content = rgx.Replace(content, replacement);
File.WriteAllText(FilePath, content);
File.SetLastWriteTimeUtc(FilePath, now);
]]></Code>
</Task>
</UsingTask>
</Project>
在Xamarin中调用上述内联任务。在目标BeforeBuild中形成csproj文件:
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. -->
<Import Project="SetBuildDate.targets" />
<Target Name="BeforeBuild">
<SetBuildDate FilePath="$(MSBuildProjectDirectory)\BuildMetadata.cs" />
</Target>
FilePath属性被设置为Xamarin中的BuildMetadata.cs文件。表单项目,包含一个简单的类,具有字符串属性BuildDate,构建时间将被替换为:
public class BuildMetadata
{
public static string BuildDate => "This can be any arbitrary string";
}
将此文件BuildMetadata.cs添加到项目中。它将在每次构建时被修改,但是以一种允许重复构建的方式(重复替换),因此您可以根据需要在源代码控制中包含或省略它。
对于. net Core(。NET 5+),可以这样做。它的优点在于不需要添加或嵌入文件,没有T4,也没有预构建脚本。
在你的项目中添加这样一个类:
namespace SuperDuper
{
[AttributeUsage(AttributeTargets.Assembly)]
public class BuildDateTimeAttribute : Attribute
{
public string Date { get; set; }
public BuildDateTimeAttribute(string date)
{
Date = date;
}
}
}
更新项目的.csproj以包含如下内容:
<ItemGroup>
<AssemblyAttribute Include="SuperDuper.BuildDateTime">
<_Parameter1>$([System.DateTime]::Now.ToString("s"))</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
注意,_Parameter1是一个神奇的名字——它意味着BuildDateTime属性类的构造函数的第一个(也是唯一一个)参数。
这就是在程序集中记录构建日期时间所需的全部内容。
然后读取程序集的构建日期时间,执行如下操作:
private static DateTime? getAssemblyBuildDateTime()
{
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var attr = Attribute.GetCustomAttribute(assembly, typeof(BuildDateTimeAttribute)) as BuildDateTimeAttribute;
if (DateTime.TryParse(attr?.Date, out DateTime dt))
return dt;
else
return null;
}
注1(根据评论中的Flydog57):如果你的.csproj有属性GenerateAssemblyInfo在其中列出并设置为false,构建将不会生成程序集信息,你将不会在程序集中获得BuildDateTime信息。因此,要么不要在.csproj中提到GenerateAssemblyInfo(这是新项目的默认行为,如果没有特别设置为false, GenerateAssemblyInfo默认为true),要么显式地将其设置为true。
注2(根据Teddy在评论中的说法):在给出的_Parameter1示例中,我们使用::Now来使用DateTime。现在,这是您的计算机上的本地日期和时间,如果适用日光节约时间和您的本地时区。如果你想使用::UtcNow来使用DateTime,你可以这样做。UtcNow以便将构建日期和时间记录为UTC/GMT。
这里有很多很棒的答案,但我觉得我可以添加我自己的,因为简单,性能(与资源相关的解决方案相比)跨平台(也适用于Net Core)和避免任何第三方工具。只需将msbuild目标添加到csproj。
<Target Name="Date" BeforeTargets="BeforeBuild">
<WriteLinesToFile File="$(IntermediateOutputPath)gen.cs" Lines="static partial class Builtin { public static long CompileTime = $([System.DateTime]::UtcNow.Ticks) %3B }" Overwrite="true" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)gen.cs" />
</ItemGroup>
</Target>
现在你有了内置。编译时间在这个项目中,例如:
var compileTime = new DateTime(Builtin.CompileTime, DateTimeKind.Utc);
ReSharper不会喜欢的。你可以忽略他,也可以向项目中添加一个partial类,但无论如何都可以工作。
UPD:现在ReSharper在选项的第一页有一个选项:“MSBuild访问”,“每次编译后从MSBuild获取数据”。这有助于生成代码的可见性。