Automated build of a Delphi application

Quite often, I came across the fact that Delphi developers (one can say traditionally) compile their applications with β€œpens”, which is far from a production solution, but from the outside looks like scrub and β€œdo it on the knee” , although the products are very serious and sellable . This probably went back to the time when for automation it was necessary to come up with your own batch files that started the command line compiler dcc32with the necessary parameters. Some even made their own β€œPublisher” - Delphi-expert, which does the work of the assembly server: compiles the project (though open in the IDE), exposing it with an incremented version number taken from some database, writes some changelog and copies it somewhere into network directory .


I will not go into the historical excursion as it was before . I will tell you how to eat / can now, and how to use it to increase the efficiency of my work.


The project file of the modern version of Delphi is a .dprojfile (hereinafter I will focus on Delphi 10 Rio, but with slight differences this is true for all earlier versions of Delphi, starting from 2007). It stores all the project settings, which are usually changed in the IDE (menu Project - Options (Ctrl+Shift+F11)). In this article, I will concentrate on the β€œcore” ones that will be needed to demonstrate the general principles: this Configis the configuration, the Platformplatform, the OutputDirectorypath of the output file, and ConditionalDefines(conditional compilation directives). The rest of the settings, if any need to be changed during assembly, I propose to identify yourself. This same .dprojfile, if you look into it with a regular text editor, is nothing more than an MSBuild build script(let's create a simple console application and call it DelphiAutomatedBuild ):


<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <ProjectGuid>{6880AD8E-6CB3-47B9-B8E3-7304CF6E9735}</ProjectGuid>
        <ProjectVersion>18.1</ProjectVersion>
        <FrameworkType>None</FrameworkType>
        <MainSource>DelphiAutomatedBuild.dpr</MainSource>
        <Base>True</Base>
        <Config Condition="'$(Config)'==''">Debug</Config>
        <Platform Condition="'$(Platform)'==''">Win32</Platform>
        <TargetedPlatforms>1</TargetedPlatforms>
        <AppType>Console</AppType>
    </PropertyGroup>
    ...

MSBuild build scripts are also used to describe projects, such as Visual Studio. I will touch on some details of MSBuild, but I suggest the reader to master its basics on its own . What does this give us? This allows us to build a Delphi project from the command line with one line ( which, in turn, allows us to automate the assembly of the project )


msbuild /t:build DelphiAutomatedBuild.dproj

MSBuild? Delphi, MSBuild , Delphi . , %WINDIR%\Microsoft.Net\Framework\v3.5, .Net 4.0/4.5/4.6. Microsoft. MSBuild 4.0, ,


(hint: , () IDE β€” Show in Explorer, β€” ), :


...>msbuild /t:build DelphiAutomatedBuild.dproj
"msbuild"     
,     .

. , MSBuild- PATH . :


set PATH=%WINDIR%\Microsoft.Net\Framework\v3.5;%PATH%

:


...>msbuild /t:build DelphiAutomatedBuild.dproj
Microsoft (R) Build Engine  12.0.21005.1
[Microsoft .NET Framework  4.0.30319.42000]
(C)   (Microsoft Corporation).   .

  24.11.2018 0:12:14.
 "Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj"   1 (  build).
Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj : error MSB4057:      "build".
  "Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj"  (  build)  .

 .

"Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj" (  build ) (1) ->
  Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj : error MSB4057:      "build".

    : 0
    : 1

 : 00:00:00.04

, . ? build?


.dproj-, :


...
    <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/>
...

Delphi
c:\Program Files\Embarcadero\Studio\20.0\Bin\CodeGear.Delphi.Targets, MSBuild-, Build:


<Target Name="Build"...

.. BDS ($(VAR) MSBuild (Property) VAR, , ), Delphi, (-, Delphi, BDS). $(BDS), .Targets Delphi Build.
:


set BDS=c:\Program Files\Embarcadero\Studio\20.0

...>msbuild /t:build DelphiAutomatedBuild.dproj
Microsoft (R) Build Engine  12.0.21005.1
[Microsoft .NET Framework  4.0.30319.42000]
(C)   (Microsoft Corporation).   .

  24.11.2018 0:20:40.
 "Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj"   1 (  build).
CreateProjectDirectories:
    ".\Win32\Debug".
BuildVersionResource:
  C:\Program Files\Embarcadero\Studio\20.0\bin\cgrc.exe -c65001 DelphiAutomatedBuild.vrc -foDelphiAutomatedBuild.res 
  CodeGear Resource Compiler/Binder
  Version 1.2.2 Copyright (c) 2008-2012 Embarcadero Technologies Inc.

  Microsoft (R) Windows (R) Resource Compiler Version 6.0.5724.0

  Copyright (C) Microsoft Corporation.  All rights reserved.

   "DelphiAutomatedBuild.vrc" .
_PasCoreCompile:
  C:\Program Files\Embarcadero\Studio\20.0\bin\dcc32.exe -$O- -$W+ --no-config -B -Q -TX.exe -AGenerics.Collections=System.Generics.Collections;Generics.Defaults=System.Generics.Defaults;WinTypes=Winapi.Windows;WinProcs=Winapi.Windows;DbiTypes=BDE;DbiProcs=BDE;DbiErrs=BDE -DDEBUG -E.\Win32\Debug -I"c:\program files\embarcadero\studio\20.0\lib\Win32\debug";"c:\program files\embarcadero\studio\20.0\lib\Win32\release";C:\Users\USER\Documents\Embarcadero\Studio\20.0\Imports;"C:\Program Files\Embarcadero\Studio\20.0\Imports";"C:\Users\Public\Documents\RAD Studio\5.0\Dcp";"C:\Program Files\Embarcadero\Studio\20.0\include";C:\Users\USER\AppData\Local\Programs\TestInsight\Source -LE"C:\Users\Public\Documents\RAD Studio\5.0\Bpl" -LN"C:\Users\Public\Documents\RAD Studio\5.0\Dcp" -NU.\Win32\Debug -NSWinapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;System;Xml;Data;Datasnap;Web;Soap; -O"c:\program files\embarcadero\studio\20.0\lib\Win32\release";C:\Users\USER\Documents\Embarcadero\Studio\20.0\Imports;"C:\Program Files\Embarcadero\Studio\20.0\Imports";"C:\Users\Public\Documents\RAD Studio\5.0\Dcp";"C:\Program Files\Embarcadero\Studio\20.0\include";C:\Users\USER\AppData\Local\Programs\TestInsight\Source -R"c:\program files\embarcadero\studio\20.0\lib\Win32\release";C:\Users\USER\Documents\Embarcadero\Studio\20.0\Imports;"C:\Program Files\Embarcadero\Studio\20.0\Imports";"C:\Users\Public\Documents\RAD Studio\5.0\Dcp";"C:\Program Files\Embarcadero\Studio\20.0\include";C:\Users\USER\AppData\Local\Programs\TestInsight\Source -U"c:\program files\embarcadero\studio\20.0\lib\Win32\debug";"c:\program files\embarcadero\studio\20.0\lib\Win32\release";C:\Users\USER\Documents\Embarcadero\Studio\20.0\Imports;"C:\Program Files\Embarcadero\Studio\20.0\Imports";"C:\Users\Public\Documents\RAD Studio\5.0\Dcp";"C:\Program Files\Embarcadero\Studio\20.0\include";C:\Users\USER\AppData\Local\Programs\TestInsight\Source -CC -V -VN -NB"C:\Users\Public\Documents\RAD Studio\5.0\Dcp" -NH"C:\Users\Public\Documents\RAD Studio\5.0\hpp\Win32" -NO.\Win32\Debug   DelphiAutomatedBuild.dpr   
  Embarcadero Delphi for Win32 compiler version 30.0
  Copyright (c) 1983,2015 Embarcadero Technologies, Inc.
  19 lines, 0.27 seconds, 100748 bytes code, 26044 bytes data.
  "Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj"  (  build).

  .
    : 0
    : 0

 : 00:00:01.32

-! . Win32\Debug DelphiAutomatedBuild.exe.


( , Debug-), Release- ( ). IDE , , , , . .dproj-,


...
        <Config Condition="'$(Config)'==''">Debug</Config>
...

, , / Config, , Debug. , IDE ( IDE Release β€”


...
        <Config Condition="'$(Config)'==''">Release</Config>
...

:


    {$IFDEF RELEASE}
    WriteLn('This is RELEASE build');
    {$ENDIF RELEASE}
    {$IFDEF DEBUG}
    WriteLn('This is DEBUG build');
    {$ENDIF DEBUG}

, conditional defines Release Debug- RELEASE DEBUG,


Config , :


...>msbuild /t:build DelphiAutomatedBuild.dproj /p:Config=Release
Microsoft (R) Build Engine  12.0.21005.1
[Microsoft .NET Framework  4.0.30319.42000]
(C)   (Microsoft Corporation).   .

  24.11.2018 0:48:30.
 "Z:\habr\delphi-automate-build\DelphiAutomatedBuild.dproj"   1 (  build).
CreateProjectDirectories:
    ".\Win32\Release".
...

...>Win32\Debug\DelphiAutomatedBuild.exe
This is DEBUG build

...>Win32\Release\DelphiAutomatedBuild.exe
This is RELEASE build

!


( ) - , , , β€” - . , . .dproj:


...
        <DCC_ExeOutput>.\$(Platform)\$(Config)</DCC_ExeOutput>
...

? ( ), , ?


...>msbuild /t:build DelphiAutomatedBuild.dproj /p:DCC_ExeOutput=binaries

-! binaries, β€” DelphiAutomatedBuild.exe. ? , MSBuild, , , MSBuild-, , . . ...


. , (, ). , Config β€” Debug, β€” β€” Release, , -, (, DCC_ExeOutput ( β€” )), -, , ( , MSBuild- ).


...>msbuild /t:build DelphiAutomatedBuild.dproj /p:DCC_ExeOutput=binaries

. ? !
Build. - , IDE ( ; , MMX: DProjNormalizer, DProjSplitter β€” ProjectMagician β€” .dproj-), . DAB.ciproj (CI-project, CI β€” Continuous Integration):


<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Build">
        <MSBuild Projects="DelphiAutomatedBuild.dproj"
            Targets="Build"
            Properties="Config=Debug"/>
        <MSBuild Projects="DelphiAutomatedBuild.dproj"
            Targets="Build"
            Properties="Config=Release"/>
    </Target>
</Project>


...>msbuild /t:build DAB.ciproj /p:DCC_ExeOutput=binaries

… DelphiAutomatedBuild.exe binaries, , :


...>binaries\DelphiAutomatedBuild.exe
This is RELEASE build

DCC_Exeoutput MSBuild β€” , . :


<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Build">
        <MSBuild Projects="DelphiAutomatedBuild.dproj"
            Targets="Build"
            Properties="Config=Debug;DCC_Exeoutput=$(DCC_ExeOutput)\Debug"/>
        <MSBuild Projects="DelphiAutomatedBuild.dproj"
            Targets="Build"
            Properties="Config=Release;DCC_Exeoutput=$(DCC_ExeOutput)\Release"/>
    </Target>
</Project>


...>msbuild /t:build DAB.ciproj /p:DCC_ExeOutput=binaries


binaries\Debug\DelphiAutomatedBuild.exe binaries\Release\DelphiAutomatedBuild.exe.


...>binaries\Debug\DelphiAutomatedBuild.exe
This is DEBUG build

...>binaries\Release\DelphiAutomatedBuild.exe
This is RELEASE build

, / conditional define (, -, , TRIAL )


-


    {$IFDEF TRIAL}
    WriteLn('This is TRIAL version');
    {$ENDIF TRIAL}

Debug- conditional define TRIAL , .dproj:


        <DCC_Define>DEBUG;TRIAL;$(DCC_Define)</DCC_Define>

, .. /p:DCC_Define=TRIAL,


...>msbuild /t:build DAB.ciproj /p:DCC_ExeOutput=binaries /p:DCC_Define=TRIAL


...>binaries\Debug\DelphiAutomatedBuild.exe
This is TRIAL version

...>binaries\Release\DelphiAutomatedBuild.exe
This is TRIAL version

, - , - DEBUG RELEASE, , .. define-.
, , . .
DCC_Define:


...>set DCC_Define=TRIAL
...>msbuild /t:build DAB.ciproj /p:DCC_ExeOutput=binaries
...

...>binaries\Debug\DelphiAutomatedBuild.exe
This is DEBUG build
This is TRIAL version
...>binaries\Release\DelphiAutomatedBuild.exe
This is RELEASE build
This is TRIAL version

, , ( Git, SVN ) , , , , .


, . ? , , β€” - "-", , ( " " β€” ).


Windows/Delphi- Major.Minor.Release.Build, , , Release, , , . , , .dproj-


        <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys>

, . FileVersion=0.0.0.0, , FileVersion=$(Version), Version β€” , , , IDE β€” , .. "" . . , , β€” CSV-, .
, . MSBuild 4.0 C#, ( , PATH ).


Delphi.Version.Targets
<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="12.0">
  <UsingTask TaskName="__SetFileVersion" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll">
    <ParameterGroup>
      <VerInfoKeys ParameterType="System.String" Required="true" />
      <VerInfoProperties ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />
      <Out ParameterType="System.String" Output="true" />
    </ParameterGroup>
    <Task>
      <Code Type="Fragment" Language="cs"><![CDATA[
        // split values as CSV (by ";")
        String[] verInfoKeysList = VerInfoKeys.Split(';');
        Dictionary<String, String> d = new Dictionary<String, String>();
        foreach (String verInfoValue in verInfoKeysList) {
            // split values as "key=value"
            if (! String.IsNullOrEmpty(verInfoValue)) {
                String[] kv = verInfoValue.Split('=');
                d.Add(kv[0], kv[1]);
            }
        }
        if (VerInfoProperties.Length > 0) {
          foreach (ITaskItem item in VerInfoProperties) {
            String value = item.GetMetadata("Value");
            if (value.Length > 0) {
              Log.LogMessage("{0}: {1}", item.ItemSpec, value);
              d.Remove(item.ItemSpec);
              d.Add(item.ItemSpec, value);
            }
          }
        }

        List<String> L = new List<String>();
        foreach (KeyValuePair<String, String> kv in d) {
            L.Add(kv.Key + "=" + kv.Value);
        }
        _Out = String.Join(";", L.ToArray());
]]></Code>
    </Task>
  </UsingTask>

  <Target Name='_SetVersionCode_Name'>
    <Message Text="$(VerInfo_Keys)" />

    <ItemGroup>
      <VerInfoProperties Include="FileVersion">
        <Value>$(FileVersion)</Value>
      </VerInfoProperties>
    </ItemGroup>
    <__SetFileVersion VerInfoKeys="$(VerInfo_Keys)" VerInfoProperties="@(VerInfoProperties)">
      <Output PropertyName="VerInfo_Keys" TaskParameter="Out" />
    </__SetFileVersion>
    <Message Text="$(VerInfo_Keys)" />
  </Target>

  <Target Name='_SetFileVersion' BeforeTargets="_BuildRCFile"
      Condition="'$(FileVersion)' != ''">
     <CallTarget Targets='_SetVersionCode_Name'/>
  </Target>
</Project> 

.
DelphiAutomatedBuild.dproj


...
<Import Project="Delphi.Version.Targets" Condition="$(MSBuildToolsVersion) >= 4.0" />
...

( "$(MSBuildToolsVersion) >= 4.0" , IDE, , , MSBuild 3.5, UsingTask)


, _SetFileVersion, _BuildRCFile ( : , β€” . $(BDS)\bin\Codegear.Common.Targets), FileVersion. CSV- VerInfo_Keys, -, , VerInfo_Keys.


"Include version information in project" ( ), :


...>msbuild /t:build DAB.ciproj /p:DCC_ExeOutput=binaries /p:FileVersion=4.3.2.1

...>binaries\Debug\DelphiAutomatedBuild.exe
This is RELEASE build
This is TRIAL version
This file version is 4.3.2.1
...>binaries\Release\DelphiAutomatedBuild.exe
This is DEBUG build
This is TRIAL version
This file version is 4.3.2.1

Profit!



Delphi- , , , , , , . , ( ), .



  1. ( FixInsight, !)
  2. unit- Delphi (, ))). )
  3. "" Delphi- GitLab CI
  4. as well as how you can use the WinDbg debugger, for example, to find the reasons for applications crashing / crashing due to libraries written in Delphi (well, of course, how to integrate the formation of PDB files necessary for this into auto-assembly)

Z.Y. I will be happy to answer any questions, including in a telegram, both in PM and in the @Delphi_Lazarus and @DelphiCommunity chats


All Articles