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.
Contents
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.
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.
We, also, need to create the BPL folder. We move to the [Directories] section and create a new entry.
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
:
- Remove any old BPL files
- Build the package for the target platforms
- Install the IDE package (if selected)
- 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:
- The project directory (where
.dproj
file is) - The full path
rsvars.bat
(this is required as it keeps environmental variables necessary for MsBuild) - The project file
- The platform for which we want to build (Win32, Win64, OSX32)
- The directory for the BPL files
- The directory for the DCU files
- 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.
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:
- If you need to include paths for your own packages, edit the
includeDir
- If you have more folders under
SourceCode
and you need to include in the Browsing path, add a second if-begin-end statement - If you get any errors with the compilation or want to see what is happening when MsBuild is called, replace the
SW_HIDE
withSW_SHOW
in Exec and uncomment the last line with the pause command inCompileSource.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.
Hello.
Will this tutorial work with RAD & Installer (http://www.rad-installer.com)?
I use it for editing Inno Setup scripts in Delphi IDE so I have everything together.
Are there any dependencies or something?
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.
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
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?
Hi John,
Thanks a lot! I have updated your script, so that the installer also works for newer (and older) Delphi versions. Non-default installation paths are also supported. See https://medium.com/@marcogeuze/inno-setup-for-delphi-packages-6b6d52dba1b9 for the new script.
Hi,
Thank you for the updated one.
Is there a way to make it also for 64 bit installs (win64) besides Win32 only?
Hi Perry,
perhaps you can contact Marco directly in his blog?
Thanks
Hey Marco,
thanks for this and for updating the script.
Regards,
John
Hello Jhon! Thank you very much for the topic, it was a good starting point for me. Finally I ended up rewriting adding several features that would be interesting for you to add in the tutorial, such as:
– support from Delphi 7 to Delphi 11 Alexandria (with automatic detection of installed IDEs)
– support crossplatform (with automatic detection of installed platforms)
– Compilation performed in the setup itself (without any .bat)
– Optional parameters for silent install/uninstall (via command line)
– Open IDE detection during installation (to issue alert)
– Detection of installed FmxLinux (useful for crossplatform fmx packages that compile for linux if FmxLinxux exists.
– Option to generate a setup without embedded files (in this case a Setup.exe is generated that installs files from the same directory, for example)
The source: https://github.com/viniciusfbb/skia4delphi/blob/main/Tools/InnoSetupInstaller/SetupScript.iss
Hi Vinicius,
Thanks for reaching out and for sharing your code. I will look at your link.
By the way, Skia is an amazing project. Well done!