How to define application version in one place for multiple applications?
Asked Answered
G

3

20

We have a system which consists of numerous applications. All applications have their version changed at the same time. Currently, when we release a new version, we have to manually open the project options of each application and change the version one by one. Is there any way to compile all applications on the same version, for example, save it in a global file and upon compilation, read this file and assign that version to the project? I'm just trying to eliminate too many steps, because we plan on changing the version numbers more frequently. I'd like to change it in one place only. Can this be done? and how?

Glyceric answered 2/8, 2012 at 17:0 Comment(3)
I use a home grown tool to create a .rc file for each app, and then compile to resource as part of my build script. In other words I don't rely on any of the built in facilities.Persistence
@MarcusAdams Both the Product Version and the File Version.Glyceric
If you use a build app like FinalBuilder, you can automate the process quite easily. I use a commented name/value pair at the top of each application's main form, such as { buildversion=3.0.1 }, then Finalbuilder reads the name value, increments, and saves while creating my deployment build.Undergarment
A
29

You can create a VERSIONINFO resource, in a plain text file (eg., 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
}
}

Note: The C-style null terminators (\0) are needed at the end of each item as shown in order for the resource compiler to properly terminate the strings. Otherwise, when you use Explorer to display the version information for the executable you may get garbled or partially concatenated values.

Add a line to your project source file:

{$R VersionInfo.res VersionInfo.rc}

I suggest putting the common version info resource into an externals reference in your version control system, and then you can just check it out into each project's folder and update it easily.

Do a Project->Build, and your version info is embedded in the .exe. You can verify by using Windows Explorer and viewing the properties of your app.

There's a couple of posts (one by me and one in a response by Jim Fleming) in the Embarcadero Delphi forums at CodeNewsFast archives. Mine is [here], where, I describe step-by-step how to use a pre-build event in your project to update the version number in the resource script I posted above.

Jim posts a few replies, but about a dozen posts or so down there's source for an executable that can be called from the pre-build event that works for him. (There are some things I'd do differently, like letting the IDE pass the project name and location on the command line; how to do so is described in the step-by-step article. I'd also handle the version parsing and incrementing differently, but the basic app is a good starting location.)

Embarcadero's groups are currently down, but I was able to retrieve Jim's code from CodeNewsFast as well, and can reproduce it here:

Ken,

Thanks to you, I got it to work.

Just in case anyone else wants to implement this solution, Below you will find the necessary steps and auxiliary program.

Jim Fleming

A) Create your Version Info resource file in your project directory or wherever, with the following

contents, and file extension .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) Create a new project in some folder, code of only module should be similar to:

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) In Project Options of the project whose build number should be incremented:

  • Remove the tick "include version info".

  • Add a pre-build event with the following text, as written, including the two pairs of double-quotes, substituting the parts within < >:

"<full file name and path of the auto-increment program exe>" "<full file name and path of the .rc resource file>"

D) Add to the project source, right below the "program" keyword:

{$R '<whatever you called it>.res' '<whatever you called it>.rc'} // I think both names must

be the same here: IIRC, got errors when they were different.

E) Compile, run and enjoy the return of Auto-Increment build numbers, despite Embarcadero's having removed the facility.

End of Jim's content

You could use the pre-build event to, for instance, update the ProductName or FileDescription values, or any others that have to be different from the base script.

Analogous answered 2/8, 2012 at 17:18 Comment(11)
Very promising solution, will try when I'm back at my IDE.Glyceric
I guess you'd need to script the variations of the ProductName.Persistence
True. There's complete source for an app on the EMBT Delphi forums (can't find link, and of course search doesn't work there - will add it if I find it) to use in a pre-build event that would handle that part. I answered pretty much the same question there, with a step by step description, and the person who asked was nice enough to post the code of the app they wrote in their "thank you" response for others to use.Analogous
@David, I addressed your comment in my edit (using a pre-build event with a small external app that does any necessary updates).Analogous
@KenWhite, since Embarcadero are in the process of taking down their old forums (currently inaccessible due to certificate issues), is it possible to summarize the step-by-step process if you still have it written down somewhere else?Holarctic
@BerndLinde: Sure. I was able to retrieve the original posts from CodeNewsFast; I've incorporated both most of the content and provided new links to that archive's versions.Analogous
@JohnKouraklis: It should for Windows applications (Win32/64 targets), but not for other platforms.Analogous
@KenWhite: Thanks Ken. Any idea whether there are any FMX solutions?Abohm
@JohnKouraklis: I have no idea how OS X and iOS handle version info. Android handles it in the Android.manifest.xml file. I think you'll most likely have to let the IDE handle it for you across the various non-Windows platforms.Analogous
Excellent Ken! Just one issue - this line 1252 (multilingual). will cause the .RC unable to be compiled, can you (or anybody) confirm?Weisshorn
@EdwinYip: That line is a continuation of the comment on the line above. You can see that by comparing it with my own copy of the resource above. I've corrected it to make it easier for you.Analogous
R
16

UPDATE: It is not part of RADStudio itself, but comes from Andreas Hausladen's DDevExtensions (which I'm so used to have...!).

You can do it from within the IDE with a ProjectGroup provided you installed the excellent DDevExtensions from Andreas Hausladen.

  • Have a Project Group to contain all of your projects
  • Make sure each of your projects has "Include version information in project" checked in the Options|Version Info page.
  • Use the menu Project|Set Versioninfo... to open the Set Project Versioninfo dialog (only once, the current project does not matter).
  • There, you can specify all the version information and choose which to "Apply to all" or just to the selected projects if you checked "Apply to Selected".

For example, see how I set version to both projects at once: Example where I set version to both projects at once

Then a Build All on the ProjectGroup produced both exes with the version set at 1.1.1.9 and all other details...

Rainbow answered 2/8, 2012 at 18:30 Comment(9)
This is even a better solution, being built-in to RAD StudioGlyceric
We have about 40 applications in production currently. Your suggestion is to put them all in a single project group just for the purpose of not having to check out a version resource script file from your VCS? (Not downvoting; just asking.)Analogous
This is quite nice if you build from the IDE. For most projects it's usually better to script the build process.Persistence
@KenWhite, I'm certainly not suggesting putting 40 files with the same version info to begin with or to be managed from one giant Project Group, but for project where you have a valid use of a Project Group, then you can leverage it to manage the version info from the same central place as well. The VCS will store the changes made to the projects as well as any other change.Rainbow
@DavidHeffernan, and the build can be handled otherwise/later/asynchronously/automatically from what was saved in the VCS. The main point is that you can manage the version info from the IDE. Then you build with whatever is now part of your project: new code, new units, new resources...Rainbow
What version of Delphi is this? I do not have this option in the Project menu.Glyceric
@JerryDodge, XE and XE2 at least. The screenshot is from XE.Rainbow
OOOPS! It is actually not from stock Delphi but from Andreas Hausladen's DDevExtensions. @JerryDodge: good catch! I will update the answer right now.Rainbow
Stock Overthrow? I'm rather surprised RAD Studio doesn't have this in project groups already, sounds like the ideal solution.Glyceric
V
2

This is one of the use cases for dzPrepBuild: http://www.dummzeuch.de/delphi/dzprepbuild/englisch.html

(Note: The project has been moved to sourceforge because berlios was going to be shut down last year. http://sourceforge.net/projects/dzprepbuild/)

Vacillate answered 3/8, 2012 at 12:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.