如何自动将构建日期转换为对我的代码可见的常量?

Posted

技术标签:

【中文标题】如何自动将构建日期转换为对我的代码可见的常量?【英文标题】:How can I automate getting the date of build into a constant visible to my code? 【发布时间】:2012-01-16 06:17:18 【问题描述】:

我想在我的代码中定义一个常量来保存可执行文件的构建日期。我自然希望自动化该过程。

我知道我可以使用例如 Perl 编写预构建脚本来写出包含日期的 .inc 文件。我更喜欢使用环境变量或构建变量的更轻量级的解决方案。 msbuild 是否提供任何有用的变量?有谁知道这个问题的更简洁的解决方案?

【问题讨论】:

我自然会查看 EXE 文件的 Date Modified 和/或 Date Created 来确定这一点,但不确定这是否与官方构建日期不同 - 所以我是在评论而不是回答. 即使用Application.ExeName获取文件的属性,读取必要的日期。 @jerry 可以修改。我想要构建 exe 的日期,作为常量 在最新的 Delphi 版本中没有可以使用 TCompile 单元吗? @Rob 我想我正试图避免需要一个 .inc 文件,但我觉得这是不可能的。我希望有一种巧妙的方法可以将构建变量放入源代码中。 【参考方案1】:
  TDateTime     date_time;
  long          dt_sec;
  LOADED_IMAGE  LI;

  MapAndLoad   (ParamStr (0). c_str (), NULL, &LI, False, True);
  dt_sec = ((LI.FileHeader)-> FileHeader). TimeDateStamp;
  UnMapAndLoad (&LI);

  display_memo ("%x", dt_sec);

  date_time = EncodeDate (1970, 1, 1) + (double) dt_sec / 24. / 60. / 60.;

  display_memo ("%s", FormatDateTime ("yyyy-mm-dd hh:nn:ss", date_time). c_str ());

【讨论】:

这如何回答这个问题?特别是因为这似乎是 C 代码并且问题被标记为 Delphi?【参考方案2】:

任何版本的 Delphi,只是 windows 批处理在构建之前创建一个 .inc 文件。根本不需要 IDE。

这里是脚本:

REM CommandInterpreter: $(COMSPEC)
@echo off

setlocal enabledelayedexpansion  
setlocal
rem determine project top level directory from command file name
set PRJDIR=%~dp0
cd %PRJDIR%

set BUILD_DATE_FILE=%PRJDIR%Source\BuildDate.inc

call :GetGurrentDateTime&set BUILD_YEAR=!current_year!&set BUILD_MONTH=!current_month!&set BUILD_DAY=!current_day!&set BUILD_TIME=!current_time!

echo const BUILD_YEAR = %BUILD_YEAR%;> "%BUILD_DATE_FILE%"
:: 3 letter name Apr for April
echo const BUILD_MONTH = '%BUILD_MONTH%';>> "%BUILD_DATE_FILE%"
echo const BUILD_DAY = %BUILD_DAY%;>> "%BUILD_DATE_FILE%"
echo const BUILD_TIME = '%BUILD_TIME%';>> "%BUILD_DATE_FILE%"

goto :EOF
echo Done
exit /b 0

:GetGurrentDateTime
rem GET CURRENT DATE
echo.>"%TEMP%\~.ddf"
makecab /D RptFileName="%TEMP%\~.rpt" /D InfFileName="%TEMP%\~.inf" -f "%TEMP%\~.ddf">nul
for /f "tokens=4,5,6,7" %%a in ('type "%TEMP%\~.rpt"') do (
    if not defined current_date ( 
        set "current_date=%%d-%%a-%%b"
        set "current_time=%%c"   
    set "current_year=%%d"
    set "current_month=%%a"
    set "current_day=%%b"
    )
)

在源代码中只包含 BuildDate.inc 文件并使用 consts BUILD_*

【讨论】:

【参考方案3】:

此代码适用于较新的 Delphi 版本,结合了获取时间并将其转换为本地时间。

function GetExeBuildTime: TDateTime;
var
  LI: TLoadedImage;
  $IF CompilerVersion >= 26.0
  m: TMarshaller;
  $IFEND
  timeStamp: Cardinal;
  utcTime: TDateTime;
begin
  $IF CompilerVersion >= 26.0 //XE7 requires TMarshaller to convert to PAnsiChar
  Win32Check(MapAndLoad(PAnsiChar(m.AsAnsi(ParamStr(0)).ToPointer), nil, @LI, False, True));
  $ELSE
  Win32Check(MapAndLoad(PAnsiChar(AnsiString(ParamStr(0))), nil, @LI, False, True));
  $IFEND
  timeStamp := LI.FileHeader.FileHeader.TimeDateStamp;
  UnMapAndLoad(@LI);

  utcTime := UnixToDateTime(timeStamp);
  Result := TTimeZone.Local.ToLocalTime(utcTime);
end;

【讨论】:

【参考方案4】:

您可以从可执行文件的PE header 中读取链接器时间戳

uses
  ImageHlp;

function LinkerTimeStamp(const FileName: string): TDateTime; overload;
var
  LI: TLoadedImage;
begin
  Win32Check(MapAndLoad(PChar(FileName), nil, @LI, False, True));
  Result := LI.FileHeader.FileHeader.TimeDateStamp / SecsPerDay + UnixDateDelta;
  UnMapAndLoad(@LI);
end;

对于当前模块的加载图像,以下似乎可行:

function LinkerTimestamp: TDateTime; overload;
begin
  Result := PImageNtHeaders(HInstance + Cardinal(PImageDosHeader(HInstance)^._lfanew))^.FileHeader.TimeDateStamp / SecsPerDay + UnixDateDelta;
end;

早期版本的 Delphi 没有正确更新它,但它已在 Delphi 2010 左右得到修复。对于早期版本,我使用IDE plugin 编译成功后自动更新。

注意:该值存储为 UTC,因此出于显示目的,您可能需要将其转换为适当的时区。

【讨论】:

非常感谢。我不认为这是可能的。我很高兴我现在问了! :-) 欢迎,我很高兴为您提供帮助! 太棒了!您可以使用 DateUtils 例程转换为本地时间:TTimeZone.Local.ToLocalTime() 请注意PImageDosHeader(HInstance)^._lfanew 是有符号整数,而HInstance 是无符号整数。为防止出现警告,请将其转换为无符号整数,如下所示:Cardinal(PImageDosHeader(HInstance)^._lfanew)【参考方案5】:

我使用 FinalBuilder 进行所有构建,并且很容易添加一个实用程序,该实用程序将在编译之前更新任何源文件,以搜索和修改变量或常量的定义。我确实会构建版本号、日期以及任何有意义的东西。

【讨论】:

【参考方案6】:

事实上TCompile 不是Delphi 的联合体。 你可以找到它here, on EDN。 并且(只是页面的副本):

修改单元文件“Project.pas”的非可视组件。该文件包含项目编译的日期和时间以及内部版本号。将此组件放在 TForm 上,然后设置项目路径。这可以通过将插入符号放在 ProjectPath 属性字段中,然后按 ENTER 键来完成。

在USES子句中包含“项目”;保存项目,然后退出 德尔福。下次在Delphi中打开项目时,单元文件 将创建“Project.pas”。然后每次运行程序时 在 Delphi 中,单元文件将更新为当前日期, 时间和版本号。

【讨论】:

【参考方案7】:

DDevExtensions 可以选择将编译日期和时间包含在 versioninfo 资源中。我想我不必向您展示如何从程序内部提取它...

更新关于自动构建:FinalBuilder 有一个类似的选项。

【讨论】:

我使用自己的版本资源。 DDevExtensions 工具是否依赖于您使用内置工具? 我不知道。你应该直接问安迪以获得一个体面的答案。他通常很有帮助。【参考方案8】:

如果您使用/拥有JCL,那么jclPEImage.PeReadLinkerTimeStamp() 会满足您的需求。旧版本的 Delphi 没有设置链接器时间戳,但 AFAIK 新版本设置了(即它似乎与 D2010 一起工作)。

【讨论】:

以上是关于如何自动将构建日期转换为对我的代码可见的常量?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用php将日期格式转换为日期时间格式[重复]

如何使用以下代码在输入表单中自动选择当前日期

如何使用以下代码在输入表单中自动选择当前日期

如何将 13 位 Unix 时间戳转换为日期和时间?

如何将 Javascript 日期转换为 iOS 日期格式

将 iOS 应用设置为对 App Store 上的选定用户可见