Подтвердить что ты не робот

Как я могу автоматизировать получение даты сборки в константу, видимую для моего кода?

Я хотел бы определить в своем коде константу, содержащую дату, на которой был создан исполняемый файл. Я, естественно, хотел бы автоматизировать этот процесс.

Я знаю, что я могу написать pre-build script, используя, например, Perl, для записи файла .inc, содержащего дату. Я бы предпочел более легкое решение, используя, возможно, переменные окружения или встроенные переменные. Предоставляет ли msbuild какие-либо переменные, которые помогут? Кто-нибудь знает, как решить проблему?

4b9b3361

Ответ 1

Вы можете прочитать метку времени компоновщика из PE-заголовка исполняемого файла:

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, чтобы автоматически обновлять его после успешного компиляции.

Примечание: Значение сохраняется в формате UTC, поэтому для отображения вам может потребоваться преобразовать его в соответствующий часовой пояс.

Ответ 2

Если вы используете /have JCL, то jclPEImage.PeReadLinkerTimeStamp() выполняет то, что вам нужно. Старые версии Delphi не устанавливали временную метку компоновщика, но новые версии AFAIK (т.е., похоже, работают с D2010).

Ответ 3

DDevExtensions имеет возможность включать дату и время компиляции в ресурс versioninfo. Думаю, мне не нужно показывать вам, как извлечь изнутри программу...

Обновление Что касается автоматизированных сборок: FinalBuilder имеет аналогичную возможность.

Ответ 4

Фактически, TCompile не является объединением Delphi. Вы можете найти здесь, в EDN. И (просто копия страницы):

Невизуальный компонент, который изменяет единичный файл "Project.pas". Этот файл имеет дату и время, когда проект был скомпилирован вместе с номером сборки. Добавьте этот компонент в TForm, а затем установите путь к проекту. Это можно сделать, поместив каретку в поле свойства ProjectPath, а затем нажав клавишу ENTER.

Включить "Проект" в предложение USES; Сохраните проект, затем выйдите Delphi. В следующий раз, когда проект откроется в Delphi, файл блока Будет создан "Project.pas". Затем каждый раз, когда вы запускаете программу из внутри Delphi файл модуля будет обновляться с текущей датой, время и номер сборки.

Ответ 5

Я использую FinalBuilder для выполнения всех своих сборников, и с этим легко добавить утилиту, которая будет обновлять любой исходный файл перед компиляцией для поиска и изменения определения переменной или константы. Я строю номера версий, даты, все, что имеет смысл таким образом.

Ответ 6

Этот код работает в новых версиях 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;

Ответ 7

Любая версия Delphi, просто пакет для создания файла.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_*.