Using my buildtools

My buildtools for Delphi have been available on SourceForge for quite a while. I use them in all my projects, including – slightly modified – in GExperts. Just in case somebody else is interested, I’ll outline how to use them in a project.

Requirements

First, your project has to follow the following structure:

Project
  src
    Project.dpr
    Project.dproj  

Notice that the main project directory should have the same name as your project, so if the project is called MyProject, the directory should have the same name (Exceptions are possible, see below). Also, the project files should be in a subdirectory called src (this cannot easily be changed).

Preparations

If these requirements are met, you can just add the build tools as an svn:external to a subversion project like this:

buildtools https://svn.code.sf.net/p/dzlib/code/buildtools/trunk

This will create a subdirectory buildtools to the project’s main directory.

Optional:
If your main project directory name is different from your project name, create a __SetProjectName.cmd script that sets the environment variable PROJECT to your project name:

set PROJECT=MyProject

All scripts will try to call this in order to get the project name. If it does not exist, the project name will be assumed to be the same as the main project directory.

The first step should now be to execute the script __CopyTemplates.cmd located in buildtools\templates. It will do the following:

  • Copy _BuildProject.cmd, _OpenInIde.cmd and _AutoBuildCheck.cmd to the project directory
  • Copy template.manifest.in to src\%project%.manifest.in
  • Copy template_icon.rc to src\%project%_icon.rc if it does not exist
  • Copy template_version.ini to src\%project%_version.ini if it does not exist.
  • open src\%project%_version.ini in notepad to be adjusted for the given project

src\%project%_version.ini is an ini file that maintains the version info of the project. The Delphi IDE (since Delphi 2005 and up until recently) notoriously mishandled the version information, so at some point I grew tired of it and moved the handling of version information to an external tool. More on that later. The format of the INI file should be familiar to all who have used Delphi 7 or earlier. The Version info in the .dof file there was in the same format.

The build tools assume that the project uses Delphi 2007 since that is the version with which I (have to) do most of my development work. If your project uses a different version, create a __DelphiVersion.cmd file with the following content:

set DelphiVer=XE2 

Replace the XE2 with your Delphi version:
6, 7, 2005, 2006, 2007, 2009, 2010, XE, XE2 … XE8 (I have yet to add Delphi 10 Seattle)

Now you should be able to open the project in the Delphi IDE by executing the _OpenInIDE.cmd script, do a commandline build by executing the _BuildProject.cmd script.

That’s the general preparation, now for the actual advantages of using these tools:

Version Information, Icon and Manifest
Up to Delphi 7 you could enter the version information into the projects settings dialog, check the “auto inc build number” option and let the IDE do everything else. The information was stored in the project.dof file. There was the disadvantage that the project.res file changed with every build but most people didn’t mind that much. If you deleted the .res file, it would be recreated with the correct version info, all you lost was the icon. Unfortunately a command line build using dcc32 did not increment the build number. Then Borland jumped on the dotNET wagon and created a new IDE. This new IDE botched the handling of the version info vs. the project.res file completely. Now, the master version information was stored in the project.res file and the IDE updated the project.bdsproj file (and later the project.dproj file) from there. The command line compiler still did not increment the build number.

At that point I got fed up and wrote dzPrepBuild. It is a small tool that can read and modify .dof, .bdsproj, .dproj and .ini files. But the most important function is creating a .rc file with the version information that can be compiled to a .res file which in turn can be used by Delphi to add version information to the executable. It also creates a manifest file for Windows Vista and later, containing the version information.

So, in order to use it, do the following:

  • Disable version information in your project
  • Add the following Pre-Build event (Delphi 2007 and up):
    call ..\buildtools\prebuild.cmd $(PROJECTPATH)
    
  • Add the following Post-Build event (Delphi 2007 and up):
    call ..\buildtools\postbuild.cmd $(OUTPUTDIR)$(OUTPUTNAME)
    
  • In the project.dpr file remove the {$ *.res} line
  • and add the following lines
      {$R *_version.res}
      {$R *_icon.res}
      {$R *_manifest.res}
    

If you use a Delphi version older than Delphi 2007, you must find another way to call the prebuild and postbuild scripts, because Pre- and Post-Build events were only introduce with Delphi 2007.

So, what does prebuild.cmd do?

  • It increments the build number in src\%project%_version.ini
  • Writes a src\%project%_version.rc from this information
  • If src\%project%.manifest.in exists, writes a src\%project%.manifest and a corresponding src\%project%_manifest.rc
  • Calls brcc32 to compile src\%project%_version.rc to a .res file
  • If src\%project%_manifest.rc exists, calls brcc32 to compile it to a .res file
  • If src\%project%_icon.rc exists, calls brcc32 to compile it to a .res file

Oh, I haven’t mentioned the %project%_icon.rc file yet: It’s a text file that only contains one line:

MAINICON ICON LOADONCALL MOVEABLE DISCARDABLE IMPURE "../buildtools/dz.ico"

You should adapt it to point to the .ico file you want to use for your project. Alternatively you can delete the .rc file if you don’t want to have an icon. In this case you also must remove the {$R *_icon.res} line from the project.dpr file. Here is a hint on creating icons from multiple png files.

Regarding the manifest: Newer versions of Windows (starting with Vista) have the (I think) annoying habit of virtualizing access to the registry and some file system folders to which a program does not have write access. If you don’t tell Windows to not do that, you will not even notice your programming errors. To tell Windows to go and play somewhere else, you need a manifest. A manifest is an xml file that is added to the executable as a resource. That’s what the project_manifest.rc file does. Windows looks into that xml file in order to find out whether the program is compatible to a particular Windows version. The template.manifest.in file in buildtools\templates contains the following text:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
  This manifest tells Windows Vista (and Windows 7/8) not to virtualize any file
  or registry access. Also, it disables themes support.
 -->
  <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity version="1.0.0.0"
    processorArchitecture="*"
    name="template from dzlib build tools"
    type="win32"/>
<!-- We do not want themes support
  <dependency>
    <dependentassembly>
    <assemblyidentity type="win32"
      name="Microsoft.Windows.Common-Controls"
      version="6.0.0.0"
      publickeytoken="6595b64144ccf1df"
      language="*" processorarchitecture="*">
      </assemblyidentity>
      </dependentassembly>
  <dependency>
 -->
  <description>This application was built using buildtools from dzlib</description>
  <!-- COMPATIBILITY SECTION SPECIFIES IF APP IS COMPLIANT 
       DISABLES PCA IF SPECIFIED -->
  <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
    <application>
      <!-- We support Windows Vista -->
      <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
      <!-- We support Windows 7 -->
      <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
      <!-- We support Windows 8 -->
      <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
      <!-- We support Windows 8.1 -->
      <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
      <!-- We support Windows 10 -->
      <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    </application>
  </compatibility>
    
  <!-- TRUSTINFO SECTION SPECIFIES REQUESTED PERMISSIONS AND 
       UIPI DISABLEMENT (SPECIAL CONDITIONS APPLY TO UIPI DISABLEMENT)-->
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
        </requestedPrivileges>
       </security>
  </trustInfo>
</assembly>

Apart from claiming compatibility to Windows Vista, 7, 8, 8.1 and 10 this manifest also disables theming (you might want to change that) and tells Windows to not run with any elevated privileges. MSDN has some more information on manifests.

Please note that the manifest also contains the version information. The prepbuild tool will read src\%project%.manifest.in and create src\%project%.manifest, overwriting the latter if it exists. So in order to change the manifest, you should edit src\%project%.manifest.in.

JclDebug Information and Translation

The postbuild.cmd file’s job is to do the following:

  • Append JclDebug information to the executable by calling the AppendJclDebug.cmd script, which in turn calls the makejcldbg executable
  • Append translations for dxGetText to the executable by calling AppendTranslations.cmd which in turn calls the dxgettext tools.

I have written a separate blog post on Using JclDebug in your program. See there for an introduction on how it works. For the above to work, you should enable a full map file in the project’s Linker options.

AppendTranslation.cmd assumes that you use GNU Gettext for Delphi and C++ Builder (dxgettext) for translating your projects. It expects translations for German, English and French in subdirectories of translations:

project
  buildtools
  src
  translations
    de
    en
    fr

The translation files themselves are called default.po and can be created with either the explorer extensions that come with dxgettext or by copying the _ExtractTranslations.cmd script from builtools to the main project directory and executing it there.

The .po files are compiled to binary .mo files which are created in the corresponding directory under locale:

project
  locale
    de
      LC_MESSAGES
    en
      LC_MESSAGES
    fr
      LC_MESSAGES

You must create these directories for the AppendTranslation.cmd script to work. (Edit: A new script in buildtools PrepareForTranslation.cmd can do that for you. — 2015-10-4 twm)

That’s it for now. I hope this short overview isn’t too confusing for somebody who hasn’t used the buildtools before. If you like them, feel free to use them. If you have suggestions, please contact me through my Google+ profile.