Build and Install Packages in Delphi IDE using InnoSetup

Over the years I have written (as all developers out there) a number of packages and components I use in my applications. Visual components need to be installed in the IDE and a reference to the library paths  is required. This last part applies for non-visual packages, as well.

Inspired by commercial component providers, I thought that having an installer for my components would be great and would make my life easy, especially, when I need to use the components in a build server. In this guide, I use InnoSetup to create such an installer.

Folder Structure

Before we start delving into the installation script, we need to do some planning regarding where the source code of the packages will sit and where we will store the compiled units. Personally, I use the following structure as I find easy to keep track of in a version control system.

The structure I follow in this guide meets the following requirements:

  • The package is installed in the Public Documents folder
  • Under Documents, I have a folder with my company’s name, eg. MyCompany
  • Each package is installed in its own folder, eg. MyPackage
  • The BPL files are under a separate folder (BPL folder)
    • For each Delphi installation I need a folder under BPL, eg. D101Berlin for Delphi 10.1 Berlin
    • For each platform, I create separate folders (Win32, Win64, OSX32) under BPL\{Delphi Installation}
  • The source code is in a dedicated folder under MyPackage, eg. SourceCode\Package
    •  For each Delphi installation I have a dedicated folder as before, eg. D101Berlin under SourceCode\Package
    • For each platform, I use the same structure as before (Win32, Win64, OSX32) under SourceCode\Package\{Delphi Installation}
    • I need separate Debug and Release binaries so I have two more folders under each platform, eg. SourceCode\Package\{Delphi Installation}\{Platform}\(Debug or Release)

To summarise, if I have a package named myAwesomePackage, the files I need will be in the following folders:

Public Documents\MyCompany\MyAwesomePackage\BPL\D101Berlin\Win32\myAwesomePackage.bpl
Public Documents\MyCompany\MyAwesomePackage\BPL\D101Berlin\Win64\myAwesomePackage.bpl
Public Documents\MyCompany\MyAwesomePackage\BPL\D101Berlin\OSX32\myAwesomePackage.dylib
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\myAwesomePackage.dproj
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\myAwesomePackage.dpk
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\myAwesomeUnit.pas
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\D101Berlin\Win32\Debug\myAwesomePackage.dcu
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\D101Berlin\Win32\Release\myAwesomePackage.dcu
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\D101Berlin\Win64\Debug\myAwesomePackage.dcu
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\D101Berlin\Win64\Release\myAwesomePackage.dcu
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\D101Berlin\OSX32\Debug\myAwesomePackage.dcu
Public Documents\MyCompany\MyAwesomePackage\SourceCode\Package\D101Berlin\OSX32\Release\myAwesomePackage.dcu

If you wonder why I have a separate folder (Package) under SourceCode, it is because I, usually, have other folders under SourceCode (eg. SupportCode).

The myAwesomePackage Source Code

For this tutorial, I create a package with one unit which has a simple class and one procedure. The code for the myAwesomePackage.bpl is shown below.

unit myAwesomeUnit;
 
interface
 
type
  TmyAwesomeClass = class
    procedure SayHi;
  end;
 
implementation
 
uses
  FMX.Dialogs;
 
{ TmyAwesomeClass }
procedure TmyAwesomeClass.SayHi;
begin
  ShowMessage('Hi from myAwesome class');
end;
 
end.

The Interface of the Installer

I create a new InnoSetup project with a Blank Script in Inno Script Studio. This is not necessary; you can use the wizard to generate the script but here I start from scratch. The very basic script defines a few details of our package.

#define MyAppName "My Awesome Package"
#define MyAppVersion "1.0"
#define MyAppPublisher "MyCompany"
#define MyAppURL "http://www.mycompany.com/"
 
[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{A2A9C2E1-B323-4CEB-AD90-5FC8054C3C23}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
;AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppURL}
AppUpdatesURL={#MyAppURL}
DefaultDirName={commondocs}\MyCompany\myAwesomePackage
DefaultGroupName={#MyAppName}
DisableProgramGroupPage=yes
OutputBaseFilename=myAwesomePackage-setup-1.0
Compression=lzma
SolidCompression=yes
 
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
 
[Files]
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
 
[Icons]
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"

In the script, I have changed the location of the installed files (DefaultDirName) to include the {commondocs} folder and the structure of the folders as I want them to be created. I have, also, changed the name of the installer’s executable file (OutputBaseFilename).

Next, I want to add two new pages in the installer: one that shows the available Delphi installations and one that allows users to select which platforms (Win32, Win64, OSX32) they wish to compile the package.

In the Pascal Code section, I add the following variables:

var
  ChooseDelphiInstallationPage: TInputOptionWizardPage;
  ChooseDelphiTargetsPage: TInputOptionWizardPage;

I add only one option here for Delphi installations (Delphi 10.1 Berlin) but you can expand to more in a similar way. I, also, create options for three platforms (Win32, Win64, OSX32).  The InitializeWizard procedure creates the two pages:

procedure InitializeWizard;
begin
  { Create the pages }
  ChooseDelphiInstallationPage := CreateInputOptionPage(wpWelcome,
    'Choose Delphi Installation', '',
    'Check the Delphi versions you want to install the component for and then click Next.',
    false, False);
 
  ChooseDelphiInstallationPage.Add('Delphi 10.1 Berlin');
 
  ChooseDelphiTargetsPage := CreateInputOptionPage(ChooseDelphiInstallationPage.ID,
    'Choose Targets', '',
    'Check the targets you want to install the component for and then click Next.',
    false, False);
 
  ChooseDelphiTargetsPage.Add('Win32');
  ChooseDelphiTargetsPage.Add('Win64');
  ChooseDelphiTargetsPage.Add('OSX32');
end;

This does the job but we need to make it more sophisticated by checking whether Delphi 10.1 Berlin is available on the system. I define the paths to the Berlin installation. The define lines come before the [Setup] section of the InnoSetup script.

#define DelphiRootDirectory='C:\Program Files (x86)\Embarcadero\Studio\' 
#define Delphi101BaseVersion = '18.0'
#define Delphi101Directory=DelphiRootDirectory+'\'+Delphi101BaseVersion;
#define Delphi101BerlinPath =Delphi101Directory+'\bin\'
#define Delphi101BerlinRsvars = Delphi101BerlinPath+'rsvars.bat' 
#define Delphi101BerlinDCC32 = Delphi101BerlinPath+'dcc32.exe' 
#define Delphi101BerlinBuildDirectory= 'D101Berlin' 
#define Delphi101BerlinBaseRegistryPath ='Software\Embarcadero\BDS\18.0\' 
#define Delphi101BPLPath = Delphi101Directory+'\Bpl'

Back in the Pascal Code section, a couple of variables are defined and I use a type declaration to flag the correct installation.

type
  TAvailableDelphi = (availD101Berlin);
 
var
  IsAvailableDelphi101Berlin: boolean;
 
  ChooseDelphiInstallationPage: TInputOptionWizardPage;
  availableDelphiInstallations: integer;
 
  ChooseDelphiTargetsPage: TInputOptionWizardPage;
  availableDelphiTargets: integer;

Instead of hard-typing the paths, we could have a procedure to locate the actual paths. In addition, the TAvailableDelphi is not strictly necessary here as we have only one installation. The check is taking place in the InitializeSetup procedure.

function InitializeSetup(): Boolean;
begin
  if DirExists(ExpandConstant('{#Delphi101BerlinPath}')) then 
  begin
   IsAvailableDelphi101Berlin:=true;
   result:=true;
  end
  else
  begin
    IsAvailableDelphi101Berlin:=false;
    If MsgBox('Delphi 10.1 Berlin has not been detected in your system.'+#10+#13+
      'If you continue, only the code will be installed.'+#13+#10+
      'If you have another Delphi installation, you need to install the IDE package manually.'+#13+#10+
      'Would you like to continue?', mbConfirmation, mb_YESNO)=IDNO then
      result:=false
    else
      result:=true;
  end;
end;

One more thing is left to do; we need to inform the InstallationPage and the TargetsPage about the available Delphi IDEs. The InitializeWizard checks the IsAvailableDelphi101Berlin, availableDelphiInstallations and availableDelphiTargets and changes the checkboxes in the wizard pages.

procedure InitializeWizard;
var
  i: integer; //<-- add this
begin
  { Create the pages }
  ChooseDelphiInstallationPage := CreateInputOptionPage(wpWelcome,
    'Choose Delphi Installation', '',
    'Check the Delphi versions you want to install the component for and then click Next.',
    false, False);
 
//Add the following lines and the if-begin-end  
  availableDelphiInstallations:=0;
  if IsAvailableDelphi101Berlin then
  begin
    ChooseDelphiInstallationPage.Add('Delphi 10.1 Berlin');
    inc(availableDelphiInstallations);
  end;
 
//Add the following lines
  if availableDelphiInstallations=0 then
    Exit;
 
  for i:=0 to availableDelphiInstallations-1 do
   ChooseDelphiInstallationPage.Values[i]:=true;
 
//This is unchanged
  ChooseDelphiTargetsPage := CreateInputOptionPage(ChooseDelphiInstallationPage.ID,
    'Choose Targets', '',
    'Check the targets you want to install the component for and then click Next.',
    false, False);
 
//Add the following
  availableDelphiTargets:=0;
  ChooseDelphiTargetsPage.Add('Win32');
  inc(availableDelphiTargets);
  ChooseDelphiTargetsPage.Add('Win64');
  inc(availableDelphiTargets);      
  ChooseDelphiTargetsPage.Add('OSX32');
  inc(availableDelphiTargets);
 
  for i:=0 to availableDelphiTargets-1 do
   ChooseDelphiTargetsPage.Values[i]:=true;
 
end;

At this stage, the installer detects the Delphi 10.1 installation and checks the relevant checkbox in the first page. The platforms are all checked by default.
innosetupwizard_1

innosetupwizard_2

 

Improvement: I would like to be able to detect whether the Delphi IDE is properly installed to build for all the platforms and add more target platforms.

Getting Ready for the Package Installation

We have to add the files of the package to the installer. In the [Files] section, I upload myAwesomePackage.dpk, myAwesomeUnit.pas and myAwesomePackage.dproj. According to the folder structure I introduced in the beginning, these files need to land in \SourceCode\Package folder. We configure the installer for this by changing the Destination Folder property (just type in the field) of the files as shown in the following figure.

innosetupwizard_3b

We, also, need to create the BPL folder. We move to the [Directories] section and create a new entry.

innosetupwizard_4

After the user has gone through all the pages of the installer, the installation begins. After the completion of the installation, we need to take charge again and start the build process of the package. We use CurStepChanged to detect this stage.

procedure CurStepChanged(CurStep: TSetupStep);
var
  InstallIDEPackage: boolean;
  IsAnyDelphiChosen: boolean;
  i: integer;
begin
   if (CurStep=ssPostInstall) then 
   begin
     if availableDelphiInstallations=0 then 
       Exit;
     IsAnyDelphiChosen:=false;
 
     for i:=0 to availableDelphiInstallations-1 do
       if ChooseDelphiInstallationPage.Values[i] then 
         IsAnyDelphiChosen:=true;  
 
     if not IsAnyDelphiChosen then
       InstallIDEPackage:=false
     else
       InstallIDEPackage:=true;
 
     if ChooseDelphiInstallationPage.Values[0] then
       InstallPackage(availD101Berlin, InstallIDEPackage);    
   end;
end;

The ssPostInstal flag indicates that the installation is completed. It is time to check whether there are any Delphi installation and, if so, if the user has selected any of them. The idea in the code above is that the user may wish to, only, install the units of the package without asking the installer to build the package or install any design-time packages. The installation of the package begins in InstallPackage procedure.

Installation of the Package

We are going to need a few constant string for the installation of the package, which are declared as defines as previously.

#define MyPackageName "myAwesomePackage.bpl"
#define MyPackageDescription "My Awesome Package"
#define MyPackageProjectName "myAwesomePackage.dproj"

We are going to follow the next steps in InstallPackage:

  1. Remove any old BPL files
  2. Build the package for the target platforms
  3. Install the IDE package (if selected)
  4. Cleanup any leftovers

Removal of old BPL files

In InstallPackage procedure we check and remove old BPL files. You need to put the procedure before CurStepChanged otherwise InnoSetup compiler will complain. In the code below, I provide all the variables I will need for convenience.

procedure InstallPackage(const currPackage: TAvailableDelphi;
  const installIDEPackage: boolean);
var
  BuildFolderID,
  PathFolder,
  runDir, 
  basicParams,
  registryPath, 
  str: string; 
  targetStr: array of string;
  i: integer;
begin
  case currPackage of 
    availD101Berlin:
          begin
            str:='Delphi 10.1 Berlin';
            BuildFolderID:=ExpandConstant('{#Delphi101BerlinBuildDirectory}');
            PathFolder:=ExpandConstant('{#Delphi101BerlinPath}');
            RegistryPath:=ExpandConstant('{#Delphi101BerlinBaseRegistryPath}');
          end;
  else
    Exit;
  end;
 
  WizardForm.StatusLabel.Caption:= 'Installing component for '+str+'...';
 
//Remove old BPL
  WizardForm.StatusLabel.Caption:= 'Removing old BPL files...';
  if FileExists(ExpandConstant('{app}')+'\Bpl\'+BuildFolderID+'\'+ExpandConstant('{#MyPackageName}')) then
   DeleteFile(ExpandConstant('{app}')+'\Bpl\'+BuildFolderID+'\'+ExpandConstant('{#MyPackageName}'));
 
end;

We, first, check whether there is an appropriate Delphi installation before we proceed and, then, we delete any old BPL files.

Build of the package for different platforms

Here is the tricky part; in order to build our package we need to use MsBuild with a number of parameters and environmental variables as they are defined by the Delphi installation. For this purpose, I use CompileSource.bat file which accepts a number of parameters.

@echo off
rem 1:The directory to execute msbuild (project directory)
set runDir=%1
 
rem 2:The full path of rsvars.bat
set rsvarsDir=%2
 
rem 3:The project file
set projFile=%3
 
rem 4:The compile platform
set currPlatform=%4
 
rem 5:BPL output
set BPLDir=%5
echo %BPLDir%
 
rem 6:DCU output
set DCUDir=%6
echo %DCUDir%
 
rem 7:Include path
set IncludeDir=%7
echo %IncludeDir%
 
rem Refresh Delphi Environmental parameters
call %rsvarsDir%
set FullProjPath=%runDir%\%projFile%
echo %FullProjPath%
 
rem
set FullDCUPathDebug=%DCUDir%\%currPlatform%\Debug
echo FullDCUPathDebug
 
set FullDCUPathRelease=%DCUDir%\%currPlatform%\Release
echo FullDCUPathRelease
 
%FrameworkDir%\msbuild.exe %FullProjPath% /p:platform=%currPlatform% /p:config=Debug /p:DCC_BPLOutput=%BPLDir% /p:DCC_DCUOutput=%FullDCUPathDebug% /p:IncludePath=%IncludeDir%
%FrameworkDir%\msbuild.exe %FullProjPath% /p:platform=%currPlatform% /p:config=Release /p:DCC_BPLOutput=%BPLDir% /p:DCC_DCUOutput=%FullDCUPathRelease% /p:IncludePath=%IncludeDir%
 
rem pause

The parameters are:

  1. The project directory (where .dproj file is)
  2. The full path rsvars.bat (this is required as it keeps environmental variables necessary for MsBuild)
  3. The project file
  4. The platform for which we want to build (Win32, Win64, OSX32)
  5. The directory for the BPL files
  6. The directory for the DCU files
  7. Any include directories necessary to build the package

Following the same process as above, we need to add the CompileSource.bat to the files in InnoSetup Studio. The destination is not important as we will extract the file in a temporary directory but we need to make sure that the dontcopy flag is enabled in the (Un)Install tab.

innosetupwizard_5b

In InstallPackage add the following code:

procedure InstallPackage(const currPackage: TAvailableDelphi;
  const installIDEPackage: boolean);
var
  ...
begin
//Remove old BPL
  ...
 
//Prepare to compile
  ExtractTemporaryFile('CompileSource.bat');
  runDir:=ExpandConstant('{app}')+'\SourceCode\Package';
  basicParams:='"'+runDir+'"';
  basicParams:=basicParams+' "'+PathFolder+'rsvars.bat" ';
  basicParams:=basicParams+ExpandConstant('{#MyPackageProjectName}')+' ';
 
  SetArrayLength(targetStr, 0);
 
  if ChooseDelphiTargetsPage.Values[0] then
  begin
    SetArrayLength(targetStr, length(targetStr)+1);
    targetStr[length(targetStr)-1]:='Win32';
  end;
 
  if ChooseDelphiTargetsPage.Values[1] then
  begin
    SetArrayLength(targetStr, length(targetStr)+1);
    targetStr[length(targetStr)-1]:='Win64';
  end;
 
  if ChooseDelphiTargetsPage.Values[2] then
  begin
    SetArrayLength(targetStr, length(targetStr)+1);
    targetStr[length(targetStr)-1]:='OSX32';
  end;
 
//Install - targets/platforms
  for i:=0 to length(targetStr)-1 do
  begin
    WizardForm.StatusLabel.Caption:= 'Compiling for '+targetStr[i]+'...';
    InstallForPlatform(basicParams, BuildFolderID, runDir, targetStr[i], RegistryPath);
  end;
end;

We, first, create the necessary path strings to pass to MsBuild and we check on the selected targets/platforms. Then, we call InstallForPlatform to build the package.

Here is the great moment; InstallForPlatform does all the hard job for us: builds the packages and updates the Library paths (Search, Browsing and Debug DCU paths) in Delphi IDE.

procedure InstallForPlatform(const basicParams: string; const BuildFolderID:
  string; const runDir: string; const currPlatform: string;
  const delphiRegistryPath: string); 
 
var
  Params,
  BPLDir,
  DCUDir,
  cmdLine,
  includeDir,
  fullRegistryLibraryPath: string;
  ResultCode: integer;
begin
  Params:='';
  Params:=basicParams+currPlatform+' ';
  BPLDir:=ExpandConstant('{app}')+'\Bpl\'+BuildFolderID+'\'+currPlatform;
  Params:=Params+'"'+BPLDir+'"';
  DCUDir:=runDir+'\'+BuildFolderID;
  Params:=Params+' "'+DCUDir+'"';
  includeDir:=''; //Here include any folder you need
  Params:=Params +' "'+includeDir+'"';
 
  Exec(ExpandConstant('{tmp}')+'\CompileSource.bat', 
     Params,
     '',
     SW_HIDE, ewWaitUntilTerminated, ResultCode);
  if ResultCode<>0 then 
   MsgBox('Error while compiling for '+currPlatform, mbInformation, mb_OK)
  else
  begin
  //Folders
   fullRegistryLibraryPath:=delphiRegistryPath+'Library\'+currPlatform;
 
   if RegQueryStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Search Path', cmdLine) then
   begin
     if Pos(ExpandConstant('{app}')+'\SourceCode\Package\'+BuildFolderID+'\'+currPlatform+'\Release', cmdLine)=0 then
       cmdLine:=cmdLine+';'+ExpandConstant('{app}')+'\SourceCode\Package\'+BuildFolderID+'\'+currPlatform+'\Release'; 
     RegWriteStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Search Path', cmdLine); 
   end;
 
   if RegQueryStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Browsing Path', cmdLine) then
   begin
     if Pos(ExpandConstant('{app}')+'\SourceCode\Package', cmdLine)=0 then
       cmdLine:=cmdLine+';'+ExpandConstant('{app}')+'\SourceCode\Package'; 
     RegWriteStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Browsing Path', cmdLine); 
   end;
 
  if RegQueryStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Debug DCU Path', cmdLine) then
   begin
     if Pos(ExpandConstant('{app}')+'\SourceCode\Package\'+BuildFolderID+'\'+currPlatform+'\Debug', cmdLine)=0 then 
       RegWriteStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Debug DCU Path',
        cmdLine+';'+ExpandConstant('{app}')+'\SourceCode\Package\'+BuildFolderID+'\'+currPlatform+'\Debug');
   end;
  end;
end;

A couple of comments about the code above:

  1. If you need to include paths for your own packages, edit the includeDir
  2. If you have more folders under SourceCode and you need to include in the Browsing path, add a second if-begin-end statement
  3. If you get any errors with the compilation or want to see what is happening when MsBuild is called, replace the SW_HIDE with SW_SHOW in Exec and uncomment the last line with the pause command in CompileSource.bat

Installation of the IDE Package

Delphi IDE checks the KnownPackages registry key to identify the packages which are installed (or should be) in IDE. We declare InstallIDEPackage as follows:

procedure InstallIDEPackageProcedure(const delphiRegistryPath: string;
           const BuildFolderID: string);
begin
 RegWriteStringValue(HKEY_CURRENT_USER, delphiRegistryPAth+'Known Packages',
    ExpandConstant('{app}')+'\Bpl\'+BuildFolderID+'\Win32\'+ExpandConstant('{#MyPackageName}'), 
    ExpandConstant('{#MyPackageDescription}'));
end;

InstallPackage needs to be updated to call this method.

procedure InstallPackage(const currPackage: TAvailableDelphi;
  const installIDEPackage: boolean);
var
  ...
begin
//Remove old BPL
  ...
//Prepare to compile
  ...
//Install - targets/platforms
  ...
//IDE Package 
  if InstallIDEPackage then 
  begin 
    WizardForm.StatusLabel.Caption:= 'Registering IDE Package'; 
    InstallIDEPackageProcedure(RegistryPath, BuildFolderID); 
  end;
end;

Cleanup

The last part of the installation of the package requires us to cleanup any temporary files. We, therefore, need to delete CompileSource.bat

procedure InstallPackage(const currPackage: TAvailableDelphi;
  const installIDEPackage: boolean);
var
  ...
begin
//Remove old BPL
  ...
//Prepare to compile
  ...
//Install - targets/platforms
  ...
//IDE Package 
  ...
//Delete Temp File 
  DeleteFile(ExpandConstant('{tmp}')+'\CompileSource.bat'); 
end;

(Un)Installation of the Package

InnoSetup takes care of all the files which were copied during the setup. We need, however, to clean the library paths from the Delphi installation. This can be done in the CurUninstallStepChanged.

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
begin
  if (CurUninstallStep=usPostUninstall) then 
  begin
    if DirExists(ExpandConstant('{#Delphi101BerlinPath}')) then
    begin
      UninstallForPlatform(
        ExpandConstant('{#Delphi101BerlinBaseRegistryPath}'), 'Win32', 
        ExpandConstant('{#Delphi101BerlinBuildDirectory}'));
      UninstallForPlatform(
        ExpandConstant('{#Delphi101BerlinBaseRegistryPath}'), 'Win64', 
        ExpandConstant('{#Delphi101BerlinBuildDirectory}'));
      UninstallForPlatform(
        ExpandConstant('{#Delphi101BerlinBaseRegistryPath}'), 'OSX32', 
        ExpandConstant('{#Delphi101BerlinBuildDirectory}'));
    end;
  end;
end;

The UninstallForPlatform deletes the paths from the registry keys.

procedure UninstallForPlatform(const delphiRegistryPath: string; const 
  currPlatform: string; const BuildFolderID: string);
var
  fullStr,
  delStr, 
  fullRegistryLibraryPath: string;
begin
//Registry
  RegDeleteValue(HKEY_CURRENT_USER, delphiRegistryPath+'Known Packages',
    ExpandConstant('{app}')+'\Bpl\'+BuildFolderID+'\'+ExpandConstant('{#MyPackageName}'));
 
//Folders
  fullRegistryLibraryPath:=delphiRegistryPath+'Library\'+currPlatform;
 
  if RegQueryStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath ,'Search Path', fullStr) then
  begin
    delStr:=ExpandConstant('{app}')+'\SourceCode\Package\'+BuildFolderID+'\'+currPlatform+'\Release';
    if Pos(delStr, fullStr)>0 then
    begin 
      Delete(fullStr, Pos(delStr, fullStr), length(delStr)+1);
        RegWriteStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Search Path', fullStr);
     end;
   end;
 
   if RegQueryStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Browsing Path', fullStr) then
   begin
    delStr:=ExpandConstant('{app}')+'\SourceCode\Package';
    if Pos(delStr, fullStr)>0 then
    begin 
     Delete(fullStr, Pos(delStr, fullStr), length(delStr)+1);
     delStr:=ExpandConstant('{app}')+'\SourceCode\SupportCode';
     if Pos(delStr, fullStr)>0 then
     begin 
       Delete(fullStr, Pos(delStr, fullStr), length(delStr)+1);
     end;
     //check if the last character is ';'--if yes, then delete
     if fullstr[length(fullstr)]=';' then 
       Delete(fullstr, length(fullstr),1);
 
     RegWriteStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Browsing Path', fullStr);
    end;
   end;
 
   if RegQueryStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Debug DCU Path', fullStr) then
   begin
    delStr:=ExpandConstant('{app}')+'\SourceCode\Package\'+BuildFolderID+'\'+currPlatform+'\Debug';
    if Pos(delStr, fullStr)>0 then
    begin 
     Delete(fullStr, Pos(delStr, fullStr), length(delStr)+1);
     RegWriteStringValue(HKEY_CURRENT_USER,fullRegistryLibraryPath,'Debug DCU Path', fullStr);
    end;
   end;
end;

Here we go! We, now, have a complete installer/uninstaller for Delphi packages, which are compiled from within the installation and are installed in the IDE.

Source Code

You can download all the files used in this post from here.

Suggestions, Improvements, Errors

If you have suggestions and ideas how to improve this article or have spotted an error, please leave a comment or contact me. Thanks.

Share This:

4 comments

    1. Hi,

      I am not sure how Rad Installer works as I have not tried but I don’t see any reasons why not.

      Are you able to compile and run scripts from within the IDE with that product? If so, then before you try the script keep a backup of the library paths (or the registry keys) as I don’t know how IDE will behave if you change the registry keys while it is open.

      There are no direct dependencies apart from the rsvars.bat and the MSBuild but you need them anyway.

      Thanks for looking at the post.

  1. Well, I’ve got problem with Your demo. Everything looks fine, but after install I have got only package files copied to public/documents.. etc. Delphi can’t find bpl files, because there are NO-files copied by installer

    1. Which version of Delphi are you using? The installer is for 10.1 Berlin.

      You write that no BPL files are copied by the installer. Are they created though? Have you searched for them?

Leave a Reply

Your email address will not be published. Required fields are marked *