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 w
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.
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;
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.
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.
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_*
.
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.