David Heffernan
David Heffernan

Reputation: 612794

How can I automate getting the date of build into a constant visible to my code?

I would like to define in my code a constant holding the date on which the executable was built. I would naturally like to automate that process.

I know that I can write a pre-build script using, for example, Perl, to write out a .inc file containing the date. I would prefer a more lightweight solution using, perhaps, environment variables or build variables. Does msbuild provide any variables that would help? Does anyone know a neater solution to the problem?

Upvotes: 23

Views: 8789

Answers (8)

puiu29
puiu29

Reputation: 11

  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 ());

Upvotes: -1

Dr.eel
Dr.eel

Reputation: 1925

Any version of Delphi, just windows batch to create an .inc file before build. No IDE needed at all.

Here the script:

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"
    )
)

In the source code just include BuildDate.inc file and use consts BUILD_*.

Upvotes: 1

Kromster
Kromster

Reputation: 7387

This code works in newer Delphi versions, combines getting of the time and converting it to local time.

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;

Upvotes: 2

Ondrej Kelle
Ondrej Kelle

Reputation: 37211

You can read the linker timestamp from the PE header of the executable:

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;

For the loaded image of the current module, the following seems to work:

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

Earlier versions of Delphi didn't update it correctly but it has been fixed around Delphi 2010 or so. For the earlier versions, I used an IDE plugin to update it automatically after a successful compile.

Note: The value is stored as UTC so for display purposes you may need to convert it to an appropriate timezone.

Upvotes: 40

mj2008
mj2008

Reputation: 6747

I use FinalBuilder to do all my builds, and with that it is easy to add a utility that will update any source file in advance of compilation to search for and modify the definition of a variable or constant. I do build version numbers, dates, anything that makes sense this way.

Upvotes: 1

philnext
philnext

Reputation: 3402

In fact TCompile is not a Delphi unite. You can find it here, on EDN. And (just a copy of the page) :

Nonvisual component that modifies the unit file "Project.pas". This file has the date and time the project was compiled along with the build number.Drop this component on the TForm, then set the project path. This can be done by placing the caret in the ProjectPath property field, then pressing the ENTER key.

Include "Project" in the USES clause; Save the project, then exit Delphi. The next time the project is opened in Delphi, the unit file "Project.pas" will be created. Then each time you run the program from inside Delphi, the unit file will be updated with the current date, time and the build number.

Upvotes: 1

Uwe Raabe
Uwe Raabe

Reputation: 47694

DDevExtensions has an option to include the compile date and time into the versioninfo resource. I guess I don't have to show you how to extract that from inside the program...

Update Regarding automated builds: FinalBuilder has a similar option.

Upvotes: 5

ain
ain

Reputation: 22749

If you use/have JCL then jclPEImage.PeReadLinkerTimeStamp() does what you're after. Old versions of Delphi didn't set the linker timestamp, but AFAIK new versions do (ie it seems to work OK with D2010).

Upvotes: 9

Related Questions