我们拥有一个由众多应用程序组成的系统。所有应用程序都同时更改其版本。目前,当我们发布新版本时,我们必须手动打开每个应用程序的项目选项并逐个更改版本。是否有一种方法可以编译同一版本的所有应用程序,例如将其保存在全局文件中,在编译时读取此文件并将该版本分配给项目?我只是想消除太多的步骤,因为我们计划更频繁地更改版本号。我希望只在一个地方进行更改。这可以实现吗?如何实现?
我们拥有一个由众多应用程序组成的系统。所有应用程序都同时更改其版本。目前,当我们发布新版本时,我们必须手动打开每个应用程序的项目选项并逐个更改版本。是否有一种方法可以编译同一版本的所有应用程序,例如将其保存在全局文件中,在编译时读取此文件并将该版本分配给项目?我只是想消除太多的步骤,因为我们计划更频繁地更改版本号。我希望只在一个地方进行更改。这可以实现吗?如何实现?
VERSIONINFO
资源,保存在一个纯文本文件中(例如,Versioninfo.rc
)。1 VERSIONINFO
FILEVERSION 2,0,0,0
PRODUCTVERSION 2,0,0,0
FILEOS 0x4
FILETYPE 0x1
{
BLOCK "StringFileInfo"
{
BLOCK "040904E4"
{
VALUE "CompanyName", "Your Company Name Here\0"
VALUE "FileDescription", "Your File Description Here\0"
VALUE "FileVersion", "2.0.0.0\0"
VALUE "InternalName", "Your Internal Name\0"
VALUE "LegalCopyright", "© Your Copyright Notice\0"
VALUE "LegalTrademarks", "Your Trademark Notice\0"
VALUE "OriginalFilename", "YourExeName\0"
VALUE "ProductName", "Your Product Name\0"
VALUE "ProductVersion", "2.0.0.0\0"
VALUE "Comments", "No Comments\0"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0409 0x04E4
}
}
\0
)。否则,当您使用资源管理器显示可执行文件的版本信息时,可能会出现乱码或部分连接的值。{$R VersionInfo.res VersionInfo.rc}
我建议将常见的版本信息资源放入版本控制系统中的外部引用中,然后您可以将其检出到每个项目的文件夹中并轻松更新。
进行项目->构建,您的版本信息将嵌入.exe文件中。您可以使用Windows资源管理器查看应用程序的属性来验证。
在Embarcadero Delphi论坛的CodeNewsFast档案中有几篇文章(其中一篇是我写的,另一篇是Jim Fleming的回复)。我的文章在[这里],我描述了如何在项目中使用预构建事件来更新上面发布的资源脚本中的版本号。
Jim发表了一些回复,但是在大约十二个帖子之后,他发布了一个可从预构建事件中调用的可执行文件的源代码,该代码对他有效。(有一些事情我会做得不同,例如让IDE通过命令行传递项目名称和位置;如何执行在步骤说明中有描述。我也会以不同的方式处理版本解析和增量,但基本应用程序是一个很好的起点。)
Embarcadero的群组目前无法使用,但我已经从CodeNewsFast中检索到Jim的代码,并在此处重现:
Ken,
多亏了你,我让它正常工作了。
以防其他人想要实施这个解决方案,下面是必要的步骤和辅助程序。
Jim Fleming
A)在您的项目目录或任何地方创建版本信息资源文件,具有以下内容和文件扩展名.rc:
// Note the \000 !!!! Here and elsewhere below !!!!
// C string terminator !!!
#define CONST_VERSION "4.1.1.1003\000"
1 VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEOS 0x4
FILETYPE 0x1
{
BLOCK "StringFileInfo"
{
BLOCK "040904E4" // Will need changing if your language is not English and char-set not 1252 (multilingual).
{
VALUE "CompanyName", "Whatever\000"
VALUE "FileDescription", "Whatever\000"
VALUE "FileVersion", CONST_VERSION
VALUE "InternalName", "My Internal Name\000"
VALUE "LegalCopyright", "Copyright © whoever\000"
VALUE "LegalTrademarks", "\000"
VALUE "OriginalFileName", "If you wish\000"
VALUE "ProductName", "What pleases you\000"
VALUE "ProductVersion", CONST_VERSION
VALUE "Comments", "Anything you wish to add\000"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x0409 0x04E4
}
}
B) 在某个文件夹中创建一个新项目,仅模块的代码应类似于:
unit FormIncrementBuildNumber;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, System.IOUtils, System.StrUtils;
type
TIncrementBuildNumber = class(TForm)
IncrementingBuildNumberLabel: TLabel;
procedure FormShow (Sender: TObject);
procedure FormActivate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
IncrementBuildNumber: TIncrementBuildNumber;
implementation
{$R *.dfm}
procedure TIncrementBuildNumber.FormShow (Sender: TObject);
var
Resource_File_Contents: TStringList;
Full_File_Name_And_Path: string;
First_Line_Of_File: string;
Position_First_Dot: Integer;
Position_Second_Dot: Integer;
Position_Third_Dot: Integer;
Position_Trailing_Backslash: Integer;
Start_of_Build_Number: Integer;
Length_of_Build_Number: Integer;
Build_Number_In_ASCII: string;
Build_Number_Numeric: Integer;
Old_Resource_File_Name: string;
Success: Boolean;
begin
if (System.ParamCount <> 1) then
begin
ShowMessage ('Resource File name not in first command-line parameter.');
Exit;
end;
Full_File_Name_And_Path := System.ParamStr(1);
if (not TFile.Exists(Full_File_Name_And_Path, False)) then
begin
ShowMessage ('Resource file ' + Full_File_Name_And_Path +
' not found.');
Exit;
end;
Resource_File_Contents := TStringList.Create;
try
Resource_File_Contents.LoadFromFile(Full_File_Name_And_Path);
First_Line_Of_File := Resource_File_Contents.Strings[0];
if (Copy(First_Line_Of_File, 1, 21) <> '#define CONST_VERSION') then
begin
ShowMessage ('First line of Version Info must start with "#define CONST_VERSION".' +
#13 + 'Version not incremented.');
Exit;
end;
Position_First_Dot := Pos('.', First_Line_Of_File);
if (Position_First_Dot = 0) then
begin
ShowMessage ('Version must have format "a.b.c.d".' + #13 +
'Build Number not incremented.');
Exit;
end;
Position_Second_Dot := PosEx('.', First_Line_Of_File,
Position_First_Dot+1);
if (Position_Second_Dot = 0) then
begin
ShowMessage ('Version must have format "a.b.c.d".' + #13 +
'Build Number not incremented.');
Exit;
end;
Position_Third_Dot := PosEx('.', First_Line_Of_File,
Position_Second_Dot+1);
if (Position_Third_Dot = 0) then
begin
ShowMessage ('Version must have format "a.b.c.d".' + #13 +
'Build Number not incremented.');
Exit;
end;
Position_Trailing_Backslash := PosEx('\', First_Line_Of_File,
Position_Third_Dot+1);
if (Position_Trailing_Backslash = 0) then
begin
ShowMessage ('Version must have format "a.b.c.d\000".' + #13 +
'Build Number not incremented.');
Exit;
end;
Start_of_Build_Number := Position_Third_Dot + 1;
Length_of_Build_Number := Position_Trailing_Backslash -
Start_of_Build_Number;
if (Length_of_Build_Number < 1) then
begin
ShowMessage ('Build Number must be present.' + #13 +
'Build Number not incremented.');
Exit;
end;
Build_Number_In_ASCII := Copy (First_Line_Of_File,
Start_of_Build_Number,
Length_of_Build_Number);
Success := TryStrToInt (Build_Number_In_ASCII, Build_Number_Numeric);
if (not Success) then
begin
ShowMessage ('Build Number must be numeric integer.' + #13 +
'Build Number not incremented.');
Exit;
end;
Build_Number_Numeric := Build_Number_Numeric + 1;
Build_Number_In_ASCII := IntToStr(Build_Number_Numeric);
Resource_File_Contents.Strings[0] := Copy(First_Line_Of_File, 1,
Position_Third_Dot) +
Build_Number_In_ASCII +
'\000"';
Old_Resource_File_Name := Full_File_Name_And_Path;
Old_Resource_File_Name := TPath.ChangeExtension(Old_Resource_File_Name, '~rc');
if TFile.Exists(Old_Resource_File_Name, False) then
TFile.Delete(Old_Resource_File_Name);
Success := RenameFile(Full_File_Name_And_Path, Old_Resource_File_Name);
if (not Success) then
begin
ShowMessage ('Error renaming old resource file to have extension "~rc".' + #13 +
'Build Number not incremented.');
Exit;
end;
Resource_File_Contents.SaveToFile(Full_File_Name_And_Path);
finally
Resource_File_Contents.Free;
end;
end;
procedure TIncrementBuildNumber.FormActivate (Sender: TObject);
begin
Close;
end;
end.
C) 在应该增加版本号的项目的“项目选项”中:
取消“包括版本信息”的勾选。
添加一个预构建事件,文本如下,按原样书写,包括两对双引号,用< >替换其中的部分:
"<自动增量程序exe的完整文件名和路径>" "<.rc资源文件的完整文件名和路径>"
D) 在项目源代码中,在“program”关键字的正下方添加:
{$R '<whatever you called it>.res' '<whatever you called it>.rc'} // I think both names must
在这里保持一致:如果它们不同,我记得会出现错误。
E)编译、运行并享受自动递增构建号的返回,尽管 Embarcadero 已经删除了该功能。
Jim 内容结束
您可以使用预构建事件来更新 ProductName
或 FileDescription
值,或任何其他必须与基本脚本不同的值。
更新: 这个功能不是RADStudio自带的,而是来自于Andreas Hausladen的DDevExtensions(我已经习惯使用它了...!)。
如果你安装了Andreas Hausladen出色的 DDevExtensions,你可以在IDE中使用ProjectGroup实现这个功能。
Options|Version Info page
中勾选了"Include version information in project"Project|Set Versioninfo...
打开"Set Project Versioninfo"对话框(只需要一次,当前项目无关紧要)。例如,看下面如何同时将版本设置为两个项目的示例:
然后,在ProjectGroup上执行Build All
就能生成具有版本号1.1.1.9和其他详细信息的两个exe文件了...