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

时间:2011-12-08 20:32:05

标签: delphi delphi-xe2

我想在我的代码中定义一个包含可执行文件构建日期的常量。我当然希望自动化这个过程。

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

7 个答案:

答案 0 :(得分:31)

您可以从可执行文件的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,因此出于显示目的,您可能需要将其转换为适当的时区。

答案 1 :(得分:9)

如果您使用/ JCL,那么jclPEImage.PeReadLinkerTimeStamp()就可以完成您的工作。旧版本的Delphi没有设置链接器时间戳,但AFAIK新版本确实如此(即它似乎与D2010一起正常工作)。

答案 2 :(得分:5)

DDevExtensions可以选择将编译日期和时间包含在versioninfo资源中。我想我不必告诉你如何从程序中提取它......

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

答案 3 :(得分:1)

实际上TCompile并不是Delphi的团结。 你可以找到它here, on EDN。 而且(只是页面的副本):

  

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

     

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

此代码适用于较新的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;

答案 6 :(得分:0)

任何版本的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_*