diff --git a/.gitignore b/.gitignore index ff46fd9..7893907 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,10 @@ Examples/OSX32/ *.lnk +/Source/Packages/Android +/Source/Packages/Linux64 +/Source/Packages/Android64 +/Source/Packages/iOSDevice64 +/Source/Packages/iOSSimARM64 +/Source/Packages/OSX64 +/Source/Packages/OSXARM64 diff --git a/DUnitX.groupproj b/DUnitX.groupproj new file mode 100644 index 0000000..eecc804 --- /dev/null +++ b/DUnitX.groupproj @@ -0,0 +1,48 @@ + + + {FD2B2356-90B5-43EF-93E6-1881F2A6022A} + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Installer/DUnitX-Setup.iss b/Installer/DUnitX-Setup.iss new file mode 100644 index 0000000..ba5a50e --- /dev/null +++ b/Installer/DUnitX-Setup.iss @@ -0,0 +1,969 @@ +; *************************************************************************** +; +; DUnitX IDE Expert InnoSetup Installer +; +; Copyright (c) 2026 Jim McKeeth +; Licensed under the Apache License, Version 2.0 +; +; Compile with Inno Setup 6.x from the Installer\ sub-folder: +; iscc DUnitX-Setup.iss +; +; Command-line parameters accepted at run time: +; /DIR= Override the default installation directory +; /VERSIONS= Non-interactive version selection: +; all - every detected Delphi version +; 12,13 - comma-separated generation numbers +; (matched against registry version or +; name substring, e.g. "13" matches +; "Delphi 13 Florence") +; /TASKS= Comma-separated list of tasks to perform: +; expert - build and register the IDE Expert BPL +; updatepaths - update IDE library/search/DCU paths +; buildvcl - build VCL runtime packages and units +; buildfmx - build FMX runtime packages and units +; backup - back up Delphi registry before changes +; testinsight - check/offer to install TestInsight +; Default (no /TASKS): all six tasks are performed. +; Example: /TASKS="expert,updatepaths,buildvcl" +; /SKIPBACKUP Backward-compat: equivalent to omitting 'backup' from +; /TASKS; overrides /TASKS even if backup is listed. +; /SKIPTS Backward-compat: equivalent to omitting 'testinsight' +; from /TASKS; overrides /TASKS even if listed. +; /PLATFORMS= Non-interactive platform selection (ignored when +; neither buildvcl nor buildfmx task is selected): +; all - every platform (default) +; Win32,Win64 - comma-separated platform names +; Valid names: Win32 Win64 Win64x Android Android64 +; OSX64 OSXARM64 iOSDevice64 +; iOSSimARM64 Linux64 +; +; A log is always written to %TEMP%\Setup Log *.txt +; /SILENT Unattended install; selects all detected versions +; /VERYSILENT Same as /SILENT with no progress window +; +; *************************************************************************** + +#define AppName "DUnitX" +#define AppVersion "v0.4.2" +#define AppPublisher "VSoftTechnologies" +#define AppURL "https://github.com/VSoftTechnologies/DUnitX" + +; --------------------------------------------------------------------------- +[Setup] +; --------------------------------------------------------------------------- +AppId={{B3F2A1D0-C4E5-4F67-89AB-CD1234567EF0} +AppName={#AppName} +AppVersion={#AppVersion} +AppPublisher={#AppPublisher} +AppPublisherURL={#AppURL} +AppSupportURL={#AppURL}/issues +AppUpdatesURL={#AppURL}/releases +DefaultDirName={autopf}\DUnitX +DisableProgramGroupPage=yes +LicenseFile=..\LICENSE.txt +OutputDir=. +OutputBaseFilename=DUnitX-Setup +Compression=lzma2 +SolidCompression=yes +WizardStyle=modern +; No elevation needed - all registry writes go to HKCU +PrivilegesRequired=lowest +MinVersion=6.1sp1 +; Write a log to %TEMP%\Setup Log *.txt on every run +SetupLogging=yes + +; --------------------------------------------------------------------------- +[Files] +; --------------------------------------------------------------------------- + +; Expert project files (one .dproj per supported Delphi version) +Source: "..\Expert\*.pas"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.dfm"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.dpk"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.dproj"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.res"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.dres"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.rc"; DestDir: "{app}\Expert"; Flags: ignoreversion +Source: "..\Expert\*.ico"; DestDir: "{app}\Expert"; Flags: ignoreversion + +; DUnitX source files +Source: "..\source\*.pas"; DestDir: "{app}\source"; Flags: ignoreversion +Source: "..\source\*.inc"; DestDir: "{app}\source"; Flags: ignoreversion +Source: "..\source\*.fmx"; DestDir: "{app}\source"; Flags: ignoreversion +Source: "..\source\*.dfm"; DestDir: "{app}\source"; Flags: ignoreversion +Source: "..\source\*.vlb"; DestDir: "{app}\source"; Flags: ignoreversion + +; Runtime package project files +; Note: the VCL package filename is DUnitX_VLC (not VCL) - that is the +; actual name in the repository. +Source: "..\source\Packages\DUnitX_VLC.dpk"; DestDir: "{app}\source\Packages"; Flags: ignoreversion +Source: "..\source\Packages\DUnitX_VLC.dproj"; DestDir: "{app}\source\Packages"; Flags: ignoreversion +Source: "..\source\Packages\DUnitX_FMX.dpk"; DestDir: "{app}\source\Packages"; Flags: ignoreversion +Source: "..\source\Packages\DUnitX_FMX.dproj"; DestDir: "{app}\source\Packages"; Flags: ignoreversion + +; --------------------------------------------------------------------------- +[Run] +; --------------------------------------------------------------------------- + +; Offer to launch the newest installed Delphi from the Finish page. +; GetBdsExe / ShouldOfferLaunch are defined in [Code] below. +; postinstall - adds a labelled checkbox to the Finish page (ticked by default) +; nowait - don't hold up Setup waiting for the IDE to close +; skipifsilent - suppressed in /SILENT and /VERYSILENT mode +Filename: "{code:GetBdsExe}"; Description: "Launch Delphi after installation"; Flags: postinstall nowait skipifsilent; Check: ShouldOfferLaunch +Filename: "notepad.exe"; Parameters: """{code:GetLogFile}"""; Description: "View installation log"; Flags: postinstall nowait skipifsilent + +; --------------------------------------------------------------------------- +[Tasks] +; --------------------------------------------------------------------------- + +Name: backup; Description: "Back up Delphi registry settings before making changes"; GroupDescription: "Options:"; Flags: checkedonce +Name: expert; Description: "Build and register the DUnitX IDE Expert BPL"; GroupDescription: "Components:"; +Name: updatepaths; Description: "Update IDE library, search, and debug DCU paths"; GroupDescription: "Components:"; +Name: buildvcl; Description: "Build VCL runtime packages (Win32 and Win64) and units"; GroupDescription: "Components:"; +Name: buildfmx; Description: "Build FireMonkey (FMX) runtime packages and units"; GroupDescription: "Components:"; +Name: testinsight; Description: "Check for TestInsight and offer to install it if missing"; GroupDescription: "Options:"; Flags: checkedonce + +; --------------------------------------------------------------------------- +[Code] + +// -------------------------------------------------------------------------- +// Type declarations +// -------------------------------------------------------------------------- + +type + TDelphiVersion = record + RegVer: String; // registry subkey, e.g. '23.0' + Name: String; // display name, e.g. 'Delphi 12 Athens' + OldBplSuffix: String; // legacy BPL suffix, e.g. '360' + DprojFile: String; // Expert .dproj filename + RootDir: String; // populated by DetectVersions + Detected: Boolean; + end; + +// -------------------------------------------------------------------------- +// Global state +// -------------------------------------------------------------------------- + +var + VersionTable: array of TDelphiVersion; + DetectedIndices: array of Integer; // indices into VersionTable + VersionPage: TWizardPage; + VersionCheckList: TNewCheckListBox; + PlatformPage: TWizardPage; + PlatformCheckList: TNewCheckListBox; + NewestInstalledBdsExe: String; // set during ssPostInstall; used by [Run] + SetupInitDir: String; // working directory at startup; used to resolve relative /log= paths + +const + BDS_KEY = 'Software\Embarcadero\BDS'; + FMX_PLAT_COUNT = 10; + +// -------------------------------------------------------------------------- +// String / list helpers +// -------------------------------------------------------------------------- + +// Remove one entry (exact, case-insensitive) from a ';'-separated list. +function SemiListRemove(const S, Entry: String): String; +var Src, Part: String; + p: Integer; +begin + Result := ''; + Src := S; + while Src <> '' do begin + p := Pos(';', Src); + if p = 0 then begin Part := Src; Src := ''; end + else begin Part := Copy(Src, 1, p-1); Src := Copy(Src, p+1, MaxInt); end; + if not SameText(Part, Entry) then begin + if Result <> '' then Result := Result + ';'; + Result := Result + Part; + end; + end; +end; + +// Check whether an entry already exists in a ';'-separated list. +function SemiListContains(const S, Entry: String): Boolean; +var Src, Part: String; + p: Integer; +begin + Result := False; + Src := S; + while Src <> '' do begin + p := Pos(';', Src); + if p = 0 then begin Part := Src; Src := ''; end + else begin Part := Copy(Src, 1, p-1); Src := Copy(Src, p+1, MaxInt); end; + if SameText(Part, Entry) then begin Result := True; Exit; end; + end; +end; + +// -------------------------------------------------------------------------- +// Registry helpers (HKCU only) +// -------------------------------------------------------------------------- + +procedure RegAddToSemiList(const SubKey, ValueName, ToAdd: String); +var Current, New_: String; +begin + if not RegQueryStringValue(HKCU, SubKey, ValueName, Current) then Current := ''; + if SemiListContains(Current, ToAdd) then Exit; + if Current = '' then New_ := ToAdd else New_ := Current + ';' + ToAdd; + RegWriteStringValue(HKCU, SubKey, ValueName, New_); +end; + +procedure RegRemoveFromSemiList(const SubKey, ValueName, ToRemove: String); +var Current, Cleaned: String; +begin + if not RegQueryStringValue(HKCU, SubKey, ValueName, Current) then Exit; + Cleaned := SemiListRemove(Current, ToRemove); + if Cleaned <> Current then + RegWriteStringValue(HKCU, SubKey, ValueName, Cleaned); +end; + +// -------------------------------------------------------------------------- +// Version table +// -------------------------------------------------------------------------- + +procedure BuildVersionTable; +var i: Integer; +begin + SetArrayLength(VersionTable, 17); + i := 0; + VersionTable[i].RegVer:='7.0'; VersionTable[i].Name:='Delphi 2010'; + VersionTable[i].OldBplSuffix:='210'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_2010.dproj'; Inc(i); + VersionTable[i].RegVer:='8.0'; VersionTable[i].Name:='Delphi XE'; + VersionTable[i].OldBplSuffix:='220'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE.dproj'; Inc(i); + VersionTable[i].RegVer:='9.0'; VersionTable[i].Name:='Delphi XE2'; + VersionTable[i].OldBplSuffix:='230'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE2.dproj'; Inc(i); + VersionTable[i].RegVer:='10.0'; VersionTable[i].Name:='Delphi XE3'; + VersionTable[i].OldBplSuffix:='240'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE3.dproj'; Inc(i); + VersionTable[i].RegVer:='11.0'; VersionTable[i].Name:='Delphi XE4'; + VersionTable[i].OldBplSuffix:='250'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE4.dproj'; Inc(i); + VersionTable[i].RegVer:='12.0'; VersionTable[i].Name:='Delphi XE5'; + VersionTable[i].OldBplSuffix:='260'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE5.dproj'; Inc(i); + VersionTable[i].RegVer:='13.0'; VersionTable[i].Name:='Delphi XE6'; + VersionTable[i].OldBplSuffix:='270'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE6.dproj'; Inc(i); + VersionTable[i].RegVer:='14.0'; VersionTable[i].Name:='Delphi XE7'; + VersionTable[i].OldBplSuffix:='280'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE7.dproj'; Inc(i); + VersionTable[i].RegVer:='15.0'; VersionTable[i].Name:='Delphi XE8'; + VersionTable[i].OldBplSuffix:='290'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_XE8.dproj'; Inc(i); + VersionTable[i].RegVer:='17.0'; VersionTable[i].Name:='Delphi 10 Seattle'; + VersionTable[i].OldBplSuffix:='300'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D10Seattle.dproj'; Inc(i); + VersionTable[i].RegVer:='18.0'; VersionTable[i].Name:='Delphi 10.1 Berlin'; + VersionTable[i].OldBplSuffix:='310'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D10Berlin.dproj'; Inc(i); + VersionTable[i].RegVer:='19.0'; VersionTable[i].Name:='Delphi 10.2 Tokyo'; + VersionTable[i].OldBplSuffix:='320'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D10Tokyo.dproj'; Inc(i); + VersionTable[i].RegVer:='20.0'; VersionTable[i].Name:='Delphi 10.3 Rio'; + VersionTable[i].OldBplSuffix:='330'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D10Rio.dproj'; Inc(i); + VersionTable[i].RegVer:='21.0'; VersionTable[i].Name:='Delphi 10.4 Sydney'; + VersionTable[i].OldBplSuffix:='340'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_Sydney.dproj'; Inc(i); + VersionTable[i].RegVer:='22.0'; VersionTable[i].Name:='Delphi 11 Alexandria'; + VersionTable[i].OldBplSuffix:='350'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D11Alexandria.dproj';Inc(i); + VersionTable[i].RegVer:='23.0'; VersionTable[i].Name:='Delphi 12 Athens'; + VersionTable[i].OldBplSuffix:='360'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D12Athens.dproj'; Inc(i); + VersionTable[i].RegVer:='37.0'; VersionTable[i].Name:='Delphi 13 Florence'; + VersionTable[i].OldBplSuffix:='370'; VersionTable[i].DprojFile:='DUnitX_IDE_Expert_D13Florence.dproj'; +end; + +function DetectVersions: Integer; +var i, n: Integer; + RootDir: String; +begin + BuildVersionTable; + n := 0; + SetArrayLength(DetectedIndices, 0); + for i := 0 to GetArrayLength(VersionTable) - 1 do begin + VersionTable[i].Detected := False; + VersionTable[i].RootDir := ''; + if RegQueryStringValue(HKCU, BDS_KEY + '\' + VersionTable[i].RegVer, + 'RootDir', RootDir) then begin + RootDir := RemoveBackslash(RootDir); + if (RootDir <> '') and DirExists(RootDir) then begin + VersionTable[i].Detected := True; + VersionTable[i].RootDir := RootDir; + SetArrayLength(DetectedIndices, n + 1); + DetectedIndices[n] := i; + Inc(n); + end; + end; + end; + Result := n; +end; + +// -------------------------------------------------------------------------- +// Build helpers +// -------------------------------------------------------------------------- + +// Run rsvars.bat and capture the BDSCOMMONDIR environment variable. +function GetBDSCommonDir(const RsvarsBat: String; + var BDSCommonDir: String): Boolean; +var TempBat, TempOut, Line: String; + Content: AnsiString; + ResultCode, p: Integer; +begin + Result := False; + TempBat := ExpandConstant('{tmp}\dunitx_rsvars.bat'); + TempOut := ExpandConstant('{tmp}\dunitx_rsvars.txt'); + + SaveStringToFile(TempBat, + '@echo off' + #13#10 + + 'call "' + RsvarsBat + '" >nul 2>&1' + #13#10 + + 'echo BDSCOMMONDIR=%BDSCOMMONDIR%' + #13#10, + False); + + Exec(ExpandConstant('{cmd}'), + '/c ""' + TempBat + '" > "' + TempOut + '" 2>&1"', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + + if LoadStringFromFile(TempOut, Content) then begin + Line := String(Content); + p := Pos('BDSCOMMONDIR=', Line); + if p > 0 then begin + Line := Copy(Line, p + Length('BDSCOMMONDIR='), MaxInt); + p := Pos(#13, Line); if p > 0 then Line := Copy(Line, 1, p - 1); + p := Pos(#10, Line); if p > 0 then Line := Copy(Line, 1, p - 1); + BDSCommonDir := Trim(Line); + Result := BDSCommonDir <> ''; + end; + end; + + DeleteFile(TempBat); + DeleteFile(TempOut); +end; + +// Log each non-empty line from a multi-line string with an optional prefix. +procedure LogLines(const Prefix, S: String); +var Rest, Line: String; + p: Integer; +begin + Rest := S; + while Rest <> '' do begin + p := Pos(#10, Rest); + if p = 0 then begin Line := Rest; Rest := ''; end + else begin Line := Copy(Rest, 1, p - 1); + Rest := Copy(Rest, p + 1, MaxInt); end; + // Strip trailing CR + if (Length(Line) > 0) and (Line[Length(Line)] = #13) then + Line := Copy(Line, 1, Length(Line) - 1); + if Trim(Line) <> '' then + Log(Prefix + Line); + end; +end; + +// Build a .dproj via msbuild, loading the Delphi environment from rsvars.bat. +// Returns True on success (msbuild exit code 0). Output is captured to the +// Setup log file. +function BuildDproj(const RsvarsBat, DprojPath, Config, Plat: String): Boolean; +var TempBat, TempOut: String; + Content: AnsiString; + ResultCode: Integer; +begin + TempBat := ExpandConstant('{tmp}\dunitx_build.bat'); + TempOut := ExpandConstant('{tmp}\dunitx_build_out.txt'); + + SaveStringToFile(TempBat, + '@echo off' + #13#10 + + 'call "' + RsvarsBat + '"' + #13#10 + + 'msbuild "' + DprojPath + '"' + + ' /p:Config=' + Config + ' /p:Platform=' + Plat + + ' /nologo /v:minimal' + #13#10, + False); + + Exec(ExpandConstant('{cmd}'), + '/c ""' + TempBat + '" > "' + TempOut + '" 2>&1"', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + + if LoadStringFromFile(TempOut, Content) then + LogLines(' [msbuild] ', String(Content)); + + DeleteFile(TempBat); + DeleteFile(TempOut); + Result := (ResultCode = 0); +end; + +// Return the i-th FMX platform name (0-based). +function FmxPlatform(const i: Integer): String; +begin + case i of + 0: Result := 'Win32'; + 1: Result := 'Win64'; + 2: Result := 'Win64x'; + 3: Result := 'Android'; + 4: Result := 'Android64'; + 5: Result := 'OSX64'; + 6: Result := 'OSXARM64'; + 7: Result := 'iOSDevice64'; + 8: Result := 'iOSSimARM64'; + 9: Result := 'Linux64'; + else + Result := ''; + end; +end; + +// -------------------------------------------------------------------------- +// Platform selection resolution +// -------------------------------------------------------------------------- + +function GetSelectedPlatforms: array of String; +var PlatformsParam: String; + n, i, p: Integer; + Token, Src: String; +begin + SetArrayLength(Result, 0); + n := 0; + PlatformsParam := Trim(ExpandConstant('{param:PLATFORMS|}')); + + // Silent without /PLATFORMS, or /PLATFORMS=all → every platform + if (WizardSilent and (PlatformsParam = '')) or SameText(PlatformsParam, 'all') then begin + SetArrayLength(Result, FMX_PLAT_COUNT); + for i := 0 to FMX_PLAT_COUNT - 1 do + Result[i] := FmxPlatform(i); + Exit; + end; + + // /PLATFORMS=Win32,Win64 → parse comma-separated list + if PlatformsParam <> '' then begin + Src := PlatformsParam; + while Src <> '' do begin + p := Pos(',', Src); + if p = 0 then begin Token := Trim(Src); Src := ''; end + else begin Token := Trim(Copy(Src, 1, p-1)); + Src := Trim(Copy(Src, p+1, MaxInt)); end; + for i := 0 to FMX_PLAT_COUNT - 1 do + if SameText(FmxPlatform(i), Token) then begin + SetArrayLength(Result, n + 1); + Result[n] := FmxPlatform(i); + Inc(n); + Break; + end; + end; + Exit; + end; + + // Interactive: read checklist + if Assigned(PlatformCheckList) then + for i := 0 to FMX_PLAT_COUNT - 1 do + if PlatformCheckList.Checked[i] then begin + SetArrayLength(Result, n + 1); + Result[n] := FmxPlatform(i); + Inc(n); + end; +end; + +// -------------------------------------------------------------------------- +// Per-version installation +// -------------------------------------------------------------------------- + +function ShouldDoBackup: Boolean; +begin + if Trim(ExpandConstant('{param:SKIPBACKUP|}')) <> '' then + Result := False + else + Result := WizardIsTaskSelected('backup'); +end; + +procedure InstallForVersion(const VerIdx: Integer; const AppDir: String); +var + Ver: TDelphiVersion; + RsvarsBat, ExpertDproj, SourceDir, PkgDir: String; + BDSCommonDir, BplName, BplPath: String; + KnownPkgsKey, OldBplName: String; + EnvVarsKey, LibKey: String; + BackupFile: String; + SubkeyNames: TArrayOfString; + VclDproj, FmxDproj: String; + Platforms: array of String; + ResultCode, i: Integer; +begin + Ver := VersionTable[VerIdx]; + RsvarsBat := AddBackslash(Ver.RootDir) + 'bin\rsvars.bat'; + ExpertDproj:= AddBackslash(AppDir) + 'Expert\' + Ver.DprojFile; + SourceDir := RemoveBackslash(AppDir) + '\source'; + PkgDir := SourceDir + '\Packages'; + + WizardForm.StatusLabel.Caption := + 'Preparing ' + Ver.Name + '...'; + + // ---- Registry backup -------------------------------------------------- + // Skipped when /SKIPBACKUP is passed on the command line or the checkbox + // is unticked in the interactive wizard. + if ShouldDoBackup then begin + BackupFile := Ver.RegVer; + StringChangeEx(BackupFile, '.', '_', False); + BackupFile := + ExpandConstant('{tmp}') + '\DUnitX_BDS_' + BackupFile + '_' + + GetDateTimeString('yyyymmdd_hhnnss', '-', ':') + '.reg'; + Exec(ExpandConstant('{sys}') + '\reg.exe', + 'export "HKCU\Software\Embarcadero\BDS\' + Ver.RegVer + + '" "' + BackupFile + '" /y', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + if ResultCode = 0 then + Log(Ver.Name + ': registry backup saved to ' + BackupFile) + else + Log(Ver.Name + ': registry backup failed (continuing)'); + end else + Log(Ver.Name + ': registry backup skipped'); + + // ---- Validate rsvars.bat (needed for any BPL build) ------------------ + if WizardIsTaskSelected('expert') or WizardIsTaskSelected('buildvcl') or + WizardIsTaskSelected('buildfmx') then begin + if not FileExists(RsvarsBat) then begin + Log('SKIP ' + Ver.Name + ': rsvars.bat not found: ' + RsvarsBat); Exit; + end; + end; + + // ---- Expert BPL build + Known Package + DUNITX env var --------------- + if WizardIsTaskSelected('expert') then begin + if not FileExists(ExpertDproj) then begin + Log('SKIP ' + Ver.Name + ': .dproj not found: ' + ExpertDproj); Exit; + end; + + if not GetBDSCommonDir(RsvarsBat, BDSCommonDir) then begin + Log('SKIP ' + Ver.Name + ': could not read BDSCOMMONDIR from rsvars.bat'); + Exit; + end; + + WizardForm.StatusLabel.Caption := + 'Building Expert BPL for ' + Ver.Name + '...'; + + BplName := ChangeFileExt(Ver.DprojFile, '.bpl'); + BplPath := AddBackslash(BDSCommonDir) + 'dcp\Win32\Release\' + BplName; + + if not BuildDproj(RsvarsBat, ExpertDproj, 'Release', 'Win32') then + Log('WARN ' + Ver.Name + ': Expert BPL msbuild returned non-zero'); + + if not FileExists(BplPath) then begin + Log('FAIL ' + Ver.Name + ': BPL not found at expected path: ' + BplPath); + Exit; + end; + Log(Ver.Name + ': Expert BPL built: ' + BplPath); + + // ---- Register Known Package ----------------------------------------- + KnownPkgsKey := BDS_KEY + '\' + Ver.RegVer + '\Known Packages'; + OldBplName := '$(BDS)\bin\DUnitXIDEExpert' + Ver.OldBplSuffix + '.bpl'; + RegDeleteValue(HKCU, KnownPkgsKey, OldBplName); + RegDeleteValue(HKCU, KnownPkgsKey, + '$(BDSBIN)\DUnitXIDEExpert' + Ver.OldBplSuffix + '.bpl'); + RegWriteStringValue(HKCU, KnownPkgsKey, BplPath, 'DUnitX - IDE Expert'); + Log(Ver.Name + ': Known Package registered'); + + // ---- Set DUNITX IDE environment variable ---------------------------- + EnvVarsKey := BDS_KEY + '\' + Ver.RegVer + '\Environment Variables'; + RegWriteStringValue(HKCU, EnvVarsKey, 'DUNITX', SourceDir); + Log(Ver.Name + ': DUNITX = ' + SourceDir); + end; + + Platforms := GetSelectedPlatforms; + + // ---- Build VCL package ------------------------------------------------ + // The repository file is named DUnitX_VLC.dproj (not VCL — that is the + // actual filename in the repo). VCL only supports Win32 and Win64. + if WizardIsTaskSelected('buildvcl') then begin + VclDproj := PkgDir + '\DUnitX_VLC.dproj'; + if FileExists(VclDproj) then begin + WizardForm.StatusLabel.Caption := + 'Building DUnitX VCL packages for ' + Ver.Name + '...'; + for i := 0 to GetArrayLength(Platforms) - 1 do + if SameText(Platforms[i], 'Win32') or SameText(Platforms[i], 'Win64') then begin + if not BuildDproj(RsvarsBat, VclDproj, 'Release', Platforms[i]) then + Log('WARN ' + Ver.Name + ': VCL ' + Platforms[i] + '/Release failed'); + if not BuildDproj(RsvarsBat, VclDproj, 'Debug', Platforms[i]) then + Log('WARN ' + Ver.Name + ': VCL ' + Platforms[i] + '/Debug failed'); + end; + end else + Log(Ver.Name + ': DUnitX_VLC.dproj not found, skipping VCL build'); + end else + Log(Ver.Name + ': VCL build skipped (task not selected)'); + + // ---- Build FMX packages ----------------------------------------------- + if WizardIsTaskSelected('buildfmx') then begin + FmxDproj := PkgDir + '\DUnitX_FMX.dproj'; + if FileExists(FmxDproj) then begin + for i := 0 to GetArrayLength(Platforms) - 1 do begin + WizardForm.StatusLabel.Caption := + 'Building DUnitX FMX/' + Platforms[i] + ' for ' + Ver.Name + '...'; + // Failures on non-Windows platforms are expected if SDKs are absent + BuildDproj(RsvarsBat, FmxDproj, 'Release', Platforms[i]); + BuildDproj(RsvarsBat, FmxDproj, 'Debug', Platforms[i]); + end; + end else + Log(Ver.Name + ': DUnitX_FMX.dproj not found, skipping FMX build'); + end else + Log(Ver.Name + ': FMX build skipped (task not selected)'); + + // ---- Update library paths for every platform subkey ------------------- + if WizardIsTaskSelected('updatepaths') then begin + WizardForm.StatusLabel.Caption := + 'Updating library paths for ' + Ver.Name + '...'; + + LibKey := BDS_KEY + '\' + Ver.RegVer + '\Library'; + if RegGetSubkeyNames(HKCU, LibKey, SubkeyNames) then begin + for i := 0 to GetArrayLength(SubkeyNames) - 1 do begin + // Remove the legacy $(BDS)\source\DUnitX browsing path entry + RegRemoveFromSemiList(LibKey + '\' + SubkeyNames[i], + 'Browsing Path', '$(BDS)\source\DUnitX'); + RegAddToSemiList(LibKey + '\' + SubkeyNames[i], + 'Search Path', '$(DUNITX)\Packages\$(Platform)\Release'); + RegAddToSemiList(LibKey + '\' + SubkeyNames[i], + 'Browsing Path', '$(DUNITX)\Source'); + RegAddToSemiList(LibKey + '\' + SubkeyNames[i], + 'Debug DCU Path', '$(DUNITX)\Packages\$(Platform)\Debug'); + end; + end; + end; + + Log(Ver.Name + ': installation complete'); +end; + +// -------------------------------------------------------------------------- +// TestInsight helpers +// -------------------------------------------------------------------------- + +function HasTestInsight(const RegVer: String): Boolean; +var ExpertsKey: String; + ValueNames: TArrayOfString; + i: Integer; +begin + Result := False; + ExpertsKey := BDS_KEY + '\' + RegVer + '\Experts'; + if RegGetValueNames(HKCU, ExpertsKey, ValueNames) then + for i := 0 to GetArrayLength(ValueNames) - 1 do + if Pos('TestInsight', ValueNames[i]) > 0 then begin + Result := True; Exit; + end; +end; + +procedure DownloadAndRunTestInsight; +var ZipUrl, ZipPath, ExtractDir, SetupExe: String; + ResultCode: Integer; +begin + ZipUrl := 'https://files.spring4d.com/TestInsight/latest/TestInsightSetup.zip'; + ZipPath := ExpandConstant('{tmp}\TestInsightSetup.zip'); + ExtractDir := ExpandConstant('{tmp}\TestInsightSetup'); + + WizardForm.StatusLabel.Caption := 'Downloading TestInsight...'; + + // Use PowerShell to download (no extra plugin required) + Exec('powershell.exe', + '-NoProfile -NonInteractive -Command ' + + '"Invoke-WebRequest -Uri ''' + ZipUrl + + ''' -OutFile ''' + ZipPath + ''' -UseBasicParsing"', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + + if not FileExists(ZipPath) then begin + MsgBox( + 'Could not download the TestInsight installer.' + #13#10 + + 'Please install TestInsight manually from:' + #13#10 + + 'https://bitbucket.org/sglienke/testinsight', + mbInformation, MB_OK); + Exit; + end; + + WizardForm.StatusLabel.Caption := 'Extracting TestInsight...'; + + if DirExists(ExtractDir) then + DelTree(ExtractDir, True, True, True); + + Exec('powershell.exe', + '-NoProfile -NonInteractive -Command ' + + '"Expand-Archive -Path ''' + ZipPath + + ''' -DestinationPath ''' + ExtractDir + ''' -Force"', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + + SetupExe := ExtractDir + '\TestInsightSetup.exe'; + if not FileExists(SetupExe) then begin + MsgBox( + 'TestInsightSetup.exe was not found in the downloaded archive.' + #13#10 + + 'Please install TestInsight manually.', + mbInformation, MB_OK); + DeleteFile(ZipPath); + Exit; + end; + + WizardForm.StatusLabel.Caption := 'Running TestInsight installer...'; + Exec(SetupExe, '', '', SW_SHOWNORMAL, ewWaitUntilTerminated, ResultCode); + Log('TestInsight installer exited with code: ' + IntToStr(ResultCode)); + DeleteFile(ZipPath); +end; + +// -------------------------------------------------------------------------- +// Version selection resolution +// -------------------------------------------------------------------------- + +// Returns an array of VersionTable indices to install, resolved from either +// the /VERSIONS command-line parameter, silent mode, or the wizard checklist. +function GetSelectedVersionIndices: array of Integer; +var VersionsParam: String; + n, i, j, p: Integer; + Token, Src: String; +begin + SetArrayLength(Result, 0); + n := 0; + VersionsParam := Trim(ExpandConstant('{param:VERSIONS|}')); + + // /SILENT or /VERYSILENT without explicit /VERSIONS → select all detected + if WizardSilent and (VersionsParam = '') then begin + SetArrayLength(Result, GetArrayLength(DetectedIndices)); + for i := 0 to GetArrayLength(DetectedIndices) - 1 do + Result[i] := DetectedIndices[i]; + Exit; + end; + + // /VERSIONS=all → select all detected + if SameText(VersionsParam, 'all') then begin + SetArrayLength(Result, GetArrayLength(DetectedIndices)); + for i := 0 to GetArrayLength(DetectedIndices) - 1 do + Result[i] := DetectedIndices[i]; + Exit; + end; + + // /VERSIONS=12,13 → match each comma-separated token against name or regver + if VersionsParam <> '' then begin + Src := VersionsParam; + while Src <> '' do begin + p := Pos(',', Src); + if p = 0 then begin Token := Trim(Src); Src := ''; end + else begin Token := Trim(Copy(Src, 1, p-1)); + Src := Trim(Copy(Src, p+1, MaxInt)); end; + for i := 0 to GetArrayLength(DetectedIndices) - 1 do begin + j := DetectedIndices[i]; + // Match registry version ('37.0') or name substring ('Florence', '13') + if SameText(VersionTable[j].RegVer, Token) or + (Pos(Token, VersionTable[j].Name) > 0) then begin + SetArrayLength(Result, n + 1); + Result[n] := j; + Inc(n); + Break; + end; + end; + end; + Exit; + end; + + // Interactive mode: read the version selection checklist + if Assigned(VersionCheckList) then + for i := 0 to GetArrayLength(DetectedIndices) - 1 do + if VersionCheckList.Checked[i] then begin + SetArrayLength(Result, n + 1); + Result[n] := DetectedIndices[i]; + Inc(n); + end; +end; + +// -------------------------------------------------------------------------- +// [Run] helpers — launch newest Delphi on the Finish page +// -------------------------------------------------------------------------- + +function GetBdsExe(Param: String): String; +begin + Result := NewestInstalledBdsExe; +end; + +function ShouldOfferLaunch: Boolean; +begin + Result := NewestInstalledBdsExe <> ''; +end; + +// Return an absolute path to the Setup log file for the [Run] "View log" entry. +// {log} expands to the path as given on /log=, which may be a bare filename. +// Notepad resolves relative paths against its own working directory (System32), +// so we pre-resolve any relative path using the directory captured at startup. +function GetLogFile(Param: String): String; +var Path: String; +begin + Path := ExpandConstant('{log}'); + if (Length(Path) >= 2) and (Path[2] = ':') then + Result := Path // already absolute: C:\... + else if (Length(Path) >= 2) and (Path[1] = '\') and (Path[2] = '\') then + Result := Path // UNC path + else if (Length(Path) >= 1) and (Path[1] = '\') then + Result := Copy(SetupInitDir, 1, 2) + Path // drive-rooted: \foo -> C:\foo + else + Result := SetupInitDir + '\' + Path; // bare filename -> absolute +end; + +// -------------------------------------------------------------------------- +// Process helpers +// -------------------------------------------------------------------------- + +// Returns True if bds.exe is currently running. +function IsBdsRunning: Boolean; +var + TempFile: String; + Content: AnsiString; + ResultCode: Integer; +begin + TempFile := ExpandConstant('{tmp}\dunitx_bds_check.txt'); + Exec(ExpandConstant('{sys}\cmd.exe'), + '/c tasklist /FI "IMAGENAME eq bds.exe" /NH > "' + TempFile + '" 2>&1', + '', SW_HIDE, ewWaitUntilTerminated, ResultCode); + Result := False; + if LoadStringFromFile(TempFile, Content) then + Result := Pos('bds.exe', String(Content)) > 0; + DeleteFile(TempFile); +end; + +// -------------------------------------------------------------------------- +// Wizard event handlers +// -------------------------------------------------------------------------- + +function InitializeSetup: Boolean; +begin + Result := True; + SetupInitDir := GetCurrentDir; + + // Require the Delphi IDE to be closed before installation proceeds. + // In silent mode the check runs once; on failure the installer aborts. + while IsBdsRunning do begin + if WizardSilent or + (MsgBox( + 'The Delphi IDE (bds.exe) is currently running.' + #13#10#13#10 + + 'Please close Delphi before installing DUnitX, then click Retry.' + #13#10 + + 'Click Cancel to abort the installation.', + mbError, MB_RETRYCANCEL) = IDCANCEL) then begin + Result := False; + Exit; + end; + end; + + if DetectVersions = 0 then begin + MsgBox( + 'No supported Delphi installation was found in the registry.' + #13#10 + + 'Install Delphi 2010 or later and try again.', + mbError, MB_OK); + Result := False; + end; +end; + +procedure InitializeWizard; +var i: Integer; +begin + VersionPage := CreateCustomPage( + wpSelectTasks, + 'Select Delphi Versions', + 'Choose the Delphi versions to install DUnitX into.'); + + VersionCheckList := TNewCheckListBox.Create(VersionPage); + VersionCheckList.Parent := VersionPage.Surface; + VersionCheckList.Left := 0; + VersionCheckList.Top := 0; + VersionCheckList.Width := VersionPage.SurfaceWidth; + VersionCheckList.Height := VersionPage.SurfaceHeight; + + for i := 0 to GetArrayLength(DetectedIndices) - 1 do + VersionCheckList.AddCheckBox( + VersionTable[DetectedIndices[i]].Name + + ' (' + VersionTable[DetectedIndices[i]].RootDir + ')', + '', 0, True, True, False, True, nil); + + // ---- Platform selection page ------------------------------------------ + PlatformPage := CreateCustomPage( + VersionPage.ID, + 'Select Target Platforms', + 'Choose the platforms to compile DUnitX units for:'); + + PlatformCheckList := TNewCheckListBox.Create(PlatformPage); + PlatformCheckList.Parent := PlatformPage.Surface; + PlatformCheckList.Left := 0; + PlatformCheckList.Top := 0; + PlatformCheckList.Width := PlatformPage.SurfaceWidth; + PlatformCheckList.Height := PlatformPage.SurfaceHeight; + + for i := 0 to FMX_PLAT_COUNT - 1 do + PlatformCheckList.AddCheckBox(FmxPlatform(i), '', 0, True, True, False, True, nil); +end; + +// Skip custom pages when their CLI equivalent is supplied or in silent mode. +function ShouldSkipPage(PageID: Integer): Boolean; +begin + Result := False; + if PageID = VersionPage.ID then + Result := WizardSilent or + (Trim(ExpandConstant('{param:VERSIONS|}')) <> ''); + if PageID = PlatformPage.ID then + Result := WizardSilent or + (Trim(ExpandConstant('{param:PLATFORMS|}')) <> '') or + (not WizardIsTaskSelected('buildvcl') and not WizardIsTaskSelected('buildfmx')); +end; + +// Require at least one version to be ticked before proceeding. +function NextButtonClick(CurPageID: Integer): Boolean; +var i, n: Integer; +begin + Result := True; + if CurPageID = VersionPage.ID then begin + n := 0; + for i := 0 to GetArrayLength(DetectedIndices) - 1 do + if VersionCheckList.Checked[i] then Inc(n); + if n = 0 then begin + MsgBox('Please select at least one Delphi version.', mbError, MB_OK); + Result := False; + end; + end; +end; + +// Main post-install step: build BPLs and configure the IDE registry. +procedure CurStepChanged(CurStep: TSetupStep); +var + Selected: array of Integer; + AppDir: String; + i: Integer; + SkipTS: Boolean; + MissingTS: String; + Ver: TDelphiVersion; + BdsExe: String; +begin + if CurStep <> ssPostInstall then Exit; + + AppDir := WizardDirValue; + Selected := GetSelectedVersionIndices; + + if GetArrayLength(Selected) = 0 then begin + Log('DUnitX installer: no versions selected.'); + Exit; + end; + + // --- Build and register each selected Delphi version --- + for i := 0 to GetArrayLength(Selected) - 1 do + InstallForVersion(Selected[i], AppDir); + + // --- Find newest installed bds.exe for the Finish-page launch checkbox --- + NewestInstalledBdsExe := ''; + for i := GetArrayLength(Selected) - 1 downto 0 do begin + BdsExe := AddBackslash(VersionTable[Selected[i]].RootDir) + 'bin\bds.exe'; + if FileExists(BdsExe) then begin + NewestInstalledBdsExe := BdsExe; + Break; + end; + end; + + // --- TestInsight check ------------------------------------------------- + // /SKIPTS or the testinsight task being unselected suppresses the check. + SkipTS := (Trim(ExpandConstant('{param:SKIPTS|}')) <> '') + or not WizardIsTaskSelected('testinsight'); + + if not SkipTS then begin + MissingTS := ''; + for i := 0 to GetArrayLength(Selected) - 1 do begin + Ver := VersionTable[Selected[i]]; + if not HasTestInsight(Ver.RegVer) then begin + if MissingTS <> '' then MissingTS := MissingTS + ', '; + MissingTS := MissingTS + Ver.Name; + end; + end; + + if MissingTS <> '' then begin + if WizardSilent then begin + // Silent mode without /SKIPTS → install automatically + DownloadAndRunTestInsight; + end else begin + if MsgBox( + 'TestInsight is not installed for: ' + MissingTS + '.' + #13#10#13#10 + + 'TestInsight displays real-time test results inside the IDE.' + #13#10#13#10 + + 'Download and run the TestInsight installer now?', + mbConfirmation, MB_YESNO) = IDYES then + DownloadAndRunTestInsight; + end; + end; + end; + + WizardForm.StatusLabel.Caption := 'DUnitX installation complete.'; +end; diff --git a/Installer/Installer.md b/Installer/Installer.md new file mode 100644 index 0000000..e36cf9e --- /dev/null +++ b/Installer/Installer.md @@ -0,0 +1,332 @@ +# DUnitX IDE Expert Installer + +`DUnitX-Setup.iss` is an [Inno Setup 6](https://jrsoftware.org/isinfo.php) project that automates the installation of [DUnitX](https://github.com/VSoftTechnologies/DUnitX/) into one or more versions of Delphi/RAD Studio. + +The installer is based on the [manual steps described in the wiki](https://github.com/VSoftTechnologies/DUnitX/wiki/Wizard-Installation). + +1. Detects installed Delphi versions, allows user selection +2. Copies the DUnitX source tree to the chosen installation directory +3. _(Optional)_ backup the registry settings for the selected Delphi version +4. Removes the old Embarcadero version of the DUnitX package if it exists +5. Removes `$(BDS)\source\DUnitX` from the IDE Browsing path +6. Builds the Expert BPL for the selected Delphi versions using the command-line compiler +7. Uses VCL and FMX packages to build debug and release versions of the units for all appropriate compilers +8. Sets the `$(DUNITX)` IDE environment variable +9. Updates the IDE's Search paths to the precompiled release units for each platform `$(DUNITX)\packages\$(Platform)\Release` +10. Updates the IDE's Debug DCU paths to the precompiled debug units for each platform `$(DUNITX)\packages\$(Platform)\Debug` +11. Adds the source to the IDE's Browsing path to the DUnitX source code `$(DUNITX)\source` +12. _(Optional)_ download and install TestInsight if it isn't already installed +13. _(Optional)_ launch the newest version of Delphi + +--- + +## Requirements + +- [Inno Setup 6.x](https://jrsoftware.org/isdl.php) (to compile the installer) +- One or more supported Delphi versions installed (but _not running_) +- No elevated privileges required — all registry writes go to `HKCU` + +--- + +## Building the Installer EXE + +Compile `DUnitX-Setup.iss` from the `Installer\` sub-folder: + +```cmd +iscc DUnitX-Setup.iss +``` + +This produces `Installer\DUnitX-Setup.exe`. +The script references source files via relative paths (`..\..\Expert\`, `..\source\`, etc.) so it **must** be compiled from within the repository. + +--- + +## Running the Installer + +### Interactive (GUI) + +Double-click `DUnitX-Setup.exe`. The wizard guides you through: + +1. Accepting the license +2. Choosing an installation directory +3. Selecting which detected Delphi versions to install into +4. Installation (file copy + build + registry configuration) +5. Optional TestInsight download + +### Unattended / Command-line + +```cmd +DUnitX-Setup.exe [/DIR=] [/VERSIONS=] [/SKIPTS] [/SKIPBACKUP] [/SILENT | /VERYSILENT] +``` + +| Parameter | Description | +| ------------------ | ------------------------------------------------------------------------------ | +| `/DIR=` | Override the default installation directory. | +| `/VERSIONS=` | Non-interactive version selection (see below). Skips the selection page. | +| `/SKIPTS` | Skip the TestInsight check entirely. | +| `/SKIPBACKUP` | Skip the per-version registry backup. | +| `/SILENT` | Run with a progress window but no wizard pages; selects all detected versions. | +| `/VERYSILENT` | Run completely silently; selects all detected versions. | + +### `/VERSIONS` values + +| Value | Meaning | +| ------- | ---------------------------------------------------------------------------- | +| `all` | Install into every detected Delphi version. | +| `13` | Install into the version whose name contains `13` (e.g. Delphi 13 Florence). | +| `12,13` | Install into Delphi 12 Athens and Delphi 13 Florence. | +| `37.0` | Match by registry version string exactly. | + +### Examples + +```cmd +REM Interactive — wizard prompts for everything +DUnitX-Setup.exe + +REM All detected versions, no prompts, no TestInsight +DUnitX-Setup.exe /VERSIONS=all /SKIPTS /VERYSILENT + +REM Delphi 13 Florence only, interactive TestInsight prompt +DUnitX-Setup.exe /VERSIONS=13 + +REM Custom install location, Athens + Florence, skip TestInsight +DUnitX-Setup.exe /DIR=C:\Libraries\DUnitX /VERSIONS=12,13 /SKIPTS + +REM Skip registry backup (faster CI installs) +DUnitX-Setup.exe /VERSIONS=all /SKIPBACKUP /VERYSILENT +``` + +--- + +## How It Works + +### 1. Detect Installed Versions + +On startup the installer scans `HKCU:\Software\Embarcadero\BDS\` for subkeys matching the known version table. For each subkey it: + +- Reads the `RootDir` registry value +- Verifies that the directory exists on disk +- Skips with a warning if the version is unrecognised or the directory is missing + +If no recognised Delphi installation is found, setup aborts immediately with an error message. + +### 2. Version Selection + +When running interactively, a wizard page lists every detected installation as a checked checkbox (all ticked by default). The user can untick versions to skip. + +When `/VERSIONS=` is supplied or `/SILENT`/`/VERYSILENT` is used, this page is skipped and the selection is resolved programmatically. + +### 3. File Installation + +All necessary source files are extracted from the installer to the chosen `{app}` directory: + +```text +{app}\ +├── Expert\ ← .dproj/.dpk/.pas/.dfm/resources for every version +└── source\ + ├── *.pas / *.inc ← DUnitX core source + └── Packages\ + ├── DUnitX_VLC.dproj ← VCL runtime package + └── DUnitX_FMX.dproj ← FMX runtime package +``` + +### 4. Registry Backup (optional) + +Before making any changes for a given Delphi version the installer exports its entire BDS registry key to a timestamped `.reg` file in `%TEMP%`. Pass `/SKIPBACKUP` to suppress this step. + +```cmd +%TEMP%\DUnitX_BDS_23_0_20250101_120000.reg +``` + +To restore, double-click the `.reg` file or run: + +```cmd +reg import %TEMP%\DUnitX_BDS_23_0_20250101_120000.reg +``` + +### 5. Remove Old Known Package Entry + +The Embarcadero-shipped stub package is removed before registering the new one: + +```text +HKCU\Software\Embarcadero\BDS\{ver}\Known Packages + → delete value: $(BDS)\bin\DUnitXIDEExpert{OldSuffix}.bpl +``` + +### 6. Remove Legacy Browsing Path Entry + +The old source path that Embarcadero's installer added is removed from every platform's Browsing Path: + +```text +HKCU\Software\Embarcadero\BDS\{ver}\Library\{platform}\Browsing Path + → remove: $(BDS)\source\DUnitX +``` + +### 7. Build the Expert BPL + +For each selected version: + +```text +{app}\Expert\{DprojFile} → msbuild /p:Config=Release /p:Platform=Win32 +``` + +The build is driven by `rsvars.bat` (found at `{RootDir}\bin\rsvars.bat`), which sets up the Delphi build environment including `BDSCOMMONDIR`. The installer captures `BDSCOMMONDIR` and uses it to locate the built BPL: + +```cmd +{BDSCOMMONDIR}\dcp\Win32\Release\{PackageName}.bpl +``` + +The new BPL path is then registered as a Known Package: + +```text +HKCU\Software\Embarcadero\BDS\{ver}\Known Packages + {full path to built .bpl} = "DUnitX - IDE Expert" +``` + +If the BPL is not found after a successful build, the version is skipped and the failure is logged. + +### 8. Build VCL and FMX Runtime Packages + +```text +{app}\source\Packages\DUnitX_VLC.dproj → Win32 + Win64 × Release + Debug +{app}\source\Packages\DUnitX_FMX.dproj → all enabled platforms × Release + Debug +``` + +> **Note:** the VCL package filename in the repository is `DUnitX_VLC.dproj` (not `VCL`) — that is the actual name on disk. + +FMX platforms attempted: `Win32`, `Win64`, `Win64x`, `Android`, `Android64`, `OSX64`, `OSXARM64`, `iOSDevice64`, `iOSSimARM64`, `Linux64`. + +Build failures (e.g. missing mobile SDK) are reported as warnings and do not abort the overall installation. + +### 9. Set the `DUNITX` Environment Variable + +```text +HKCU\Software\Embarcadero\BDS\{ver}\Environment Variables + DUNITX = {app}\source +``` + +This makes `$(DUNITX)` available as an IDE macro in all project and library path settings, resolving to the `source\` folder of the installed DUnitX tree. + +### 10. Update Search Paths + +For every platform subkey under `HKCU\Software\Embarcadero\BDS\{ver}\Library\{platform}\`: + +```text +Search Path += $(DUNITX)\Packages\$(Platform)\Release +``` + +This gives the IDE (and MSBuild) the compiled `.dcp` files for each target platform. + +### 11. Update Debug DCU Paths + +```text +Debug DCU Path += $(DUNITX)\Packages\$(Platform)\Debug +``` + +Allows the debugger to step into DUnitX source code using the debug-configuration DCUs. + +### 12. Update Browsing Paths + +```text +Browsing Path += $(DUNITX) +``` + +`$(DUNITX)` and `$(Platform)` are IDE macros written literally to the registry and expanded by the IDE at runtime. Each entry is only appended if not already present. + +### 13. TestInsight Check (optional) + +After all selected versions are processed the installer checks whether [TestInsight](https://bitbucket.org/sglienke/testinsight/wiki/Home) is registered in the `Experts` key for each successfully installed version: + +```reg +HKCU\Software\Embarcadero\BDS\{ver}\Experts +``` + +If any version is missing a value whose name contains `TestInsight`, the installer: + +1. Lists the affected versions +2. Prompts `[Yes / No]` to download and run the installer (interactive mode), or installs automatically (silent mode) +3. Downloads `TestInsightSetup.zip` from `https://files.spring4d.com/TestInsight/latest/` +4. Extracts it to `%TEMP%\TestInsightSetup\` +5. Runs `TestInsightSetup.exe` and waits for it to finish + +Pass `/SKIPTS` to skip this step entirely. + +### 14. Launch Delphi (optional) + +After installation the Finish page shows a **"Launch Delphi after installation"** checkbox (ticked by default). If left ticked, clicking **Finish** launches `bds.exe` for the newest Delphi version that was successfully installed. + +- The checkbox only appears when at least one `bds.exe` was found on disk. +- The IDE is launched without waiting for it to close (`nowait`). +- In `/SILENT` or `/VERYSILENT` mode the checkbox is suppressed (`skipifsilent`). + +--- + +## Version Table + +| Delphi name | Reg ver | BPL suffix | Expert `.dproj` | +| -------------------- | ------- | ---------- | --------------------------------------- | +| Delphi 2010 | 7.0 | 210 | `DUnitX_IDE_Expert_2010.dproj` | +| Delphi XE | 8.0 | 220 | `DUnitX_IDE_Expert_XE.dproj` | +| Delphi XE2 | 9.0 | 230 | `DUnitX_IDE_Expert_XE2.dproj` | +| Delphi XE3 | 10.0 | 240 | `DUnitX_IDE_Expert_XE3.dproj` | +| Delphi XE4 | 11.0 | 250 | `DUnitX_IDE_Expert_XE4.dproj` | +| Delphi XE5 | 12.0 | 260 | `DUnitX_IDE_Expert_XE5.dproj` | +| Delphi XE6 | 13.0 | 270 | `DUnitX_IDE_Expert_XE6.dproj` | +| Delphi XE7 | 14.0 | 280 | `DUnitX_IDE_Expert_XE7.dproj` | +| Delphi XE8 | 15.0 | 290 | `DUnitX_IDE_Expert_XE8.dproj` | +| Delphi 10 Seattle | 17.0 | 300 | `DUnitX_IDE_Expert_D10Seattle.dproj` | +| Delphi 10.1 Berlin | 18.0 | 310 | `DUnitX_IDE_Expert_D10Berlin.dproj` | +| Delphi 10.2 Tokyo | 19.0 | 320 | `DUnitX_IDE_Expert_D10Tokyo.dproj` | +| Delphi 10.3 Rio | 20.0 | 330 | `DUnitX_IDE_Expert_D10Rio.dproj` | +| Delphi 10.4 Sydney | 21.0 | 340 | `DUnitX_IDE_Expert_Sydney.dproj` | +| Delphi 11 Alexandria | 22.0 | 350 | `DUnitX_IDE_Expert_D11Alexandria.dproj` | +| Delphi 12 Athens | 23.0 | 360 | `DUnitX_IDE_Expert_D12Athens.dproj` | +| Delphi 13 Florence | 37.0 | 370 | `DUnitX_IDE_Expert_D13Florence.dproj` | + +Any detected registry version not in this table is skipped with a warning in the Inno Setup log. + +--- + +## Verification + +After running the installer: + +1. Open **Registry Editor** and confirm: + - `HKCU\...\Known Packages` — new value whose name is the full BPL path and whose data is `DUnitX - IDE Expert` + - `HKCU\...\Environment Variables` — `DUNITX` = `{app}\source` + - `HKCU\...\Library\Win32\Search Path` — contains `$(DUNITX)\Packages\$(Platform)\Release` + - `HKCU\...\Library\Win32\Browsing Path` — contains `$(DUNITX)`, does **not** contain `$(BDS)\source\DUnitX` + - `HKCU\...\Library\Win32\Debug DCU Path` — contains `$(DUNITX)\Packages\$(Platform)\Debug` +2. Launch RAD Studio and confirm the DUnitX wizard appears under **File > New**. + +--- + +## Installed Folder Layout + +```text +{app}\ ← chosen installation directory +├── Expert\ +│ ├── DUnitX_IDE_Expert_2010.dproj +│ │ ... (one .dproj/.dpk per supported version) +│ └── DUnitX_IDE_Expert_D13Florence.dproj +└── source\ ← $(DUNITX) points here + ├── DUnitX.TestFramework.pas + ├── ... + └── Packages\ + ├── DUnitX_VLC.dproj + ├── DUnitX_FMX.dproj + ├── Win32\ + │ ├── Release\ ← VCL/FMX DCUs/BPLs (Search Path) + │ └── Debug\ ← VCL/FMX DCUs (Debug DCU Path) + └── [other platforms]\ +``` + +--- + +## License + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://www.apache.org/licenses/LICENSE-2.0 diff --git a/Source/DUnitX.Exceptions.pas b/Source/DUnitX.Exceptions.pas index f48628c..2bdd325 100644 --- a/Source/DUnitX.Exceptions.pas +++ b/Source/DUnitX.Exceptions.pas @@ -36,7 +36,8 @@ interface {$ELSE} SysUtils, {$ENDIF} - DUnitX.ComparableFormat; + + DUnitX.ComparableFormat; type ETestFrameworkException = class(Exception); diff --git a/Source/Packages/DUnitX_FMX.dpk b/Source/Packages/DUnitX_FMX.dpk new file mode 100644 index 0000000..1a8eb3e --- /dev/null +++ b/Source/Packages/DUnitX_FMX.dpk @@ -0,0 +1,95 @@ +package DUnitX_FMX; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'DUnitX FMX/Multiplatform'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + fmx; + +contains + DUNitX.Loggers.GUIX in '..\DUNitX.Loggers.GUIX.pas' {GUIXTestRunner}, + DUNitX.Loggers.MobileGUI in '..\DUNitX.Loggers.MobileGUI.pas' {MobileGUITestRunner}, + DUnitX.WeakReference in '..\DUnitX.WeakReference.pas', + DUnitX.Utils.XML in '..\DUnitX.Utils.XML.pas', + DUnitX.Utils in '..\DUnitX.Utils.pas', + DUnitX.Types in '..\DUnitX.Types.pas', + {$IFDEF MSWINDOWS} + DUnitX.Timeout, + DUnitX.Windows.Console, + {$ENDIF} + DUnitX.TestRunner in '..\DUnitX.TestRunner.pas', + DUnitX.TestResult in '..\DUnitX.TestResult.pas', + DUnitX.TestNameParser in '..\DUnitX.TestNameParser.pas', + DUnitX.TestFramework in '..\DUnitX.TestFramework.pas', + DUnitX.TestFixture in '..\DUnitX.TestFixture.pas', + DUnitX.TestDataProvider in '..\DUnitX.TestDataProvider.pas', + DUnitX.Test in '..\DUnitX.Test.pas', + DUnitX.ServiceLocator in '..\DUnitX.ServiceLocator.pas', + DUnitX.RunResults in '..\DUnitX.RunResults.pas', + DUnitX.ResStrs in '..\DUnitX.ResStrs.pas', + DUnitX.OptionsDefinition in '..\DUnitX.OptionsDefinition.pas', + DUnitX.MemoryLeakMonitor.Default in '..\DUnitX.MemoryLeakMonitor.Default.pas', + DUnitX.MacOS.Console in '..\DUnitX.MacOS.Console.pas', + DUnitX.Loggers.XML.xUnit in '..\DUnitX.Loggers.XML.xUnit.pas', + DUnitX.Loggers.XML.NUnit in '..\DUnitX.Loggers.XML.NUnit.pas', + DUnitX.Loggers.XML.JUnit in '..\DUnitX.Loggers.XML.JUnit.pas', + DUnitX.Loggers.Text in '..\DUnitX.Loggers.Text.pas', + DUnitX.Loggers.Null in '..\DUnitX.Loggers.Null.pas', + DUnitX.Loggers.Console in '..\DUnitX.Loggers.Console.pas', + DUnitX.Linux.Console in '..\DUnitX.Linux.Console.pas', + DUnitX.InternalInterfaces in '..\DUnitX.InternalInterfaces.pas', + DUnitX.InternalDataProvider in '..\DUnitX.InternalDataProvider.pas', + DUnitX.Init in '..\DUnitX.Init.pas', + DUnitX.Helpers in '..\DUnitX.Helpers.pas', + DUnitX.Generics in '..\DUnitX.Generics.pas', + DUnitX.FixtureResult in '..\DUnitX.FixtureResult.pas', + DUnitX.FixtureProvider in '..\DUnitX.FixtureProvider.pas', + DUnitX.FixtureBuilder in '..\DUnitX.FixtureBuilder.pas', + DUnitX.Filters in '..\DUnitX.Filters.pas', + DUnitX.FilterBuilder in '..\DUnitX.FilterBuilder.pas', + DUnitX.Extensibility in '..\DUnitX.Extensibility.pas', + DUnitX.Exceptions in '..\DUnitX.Exceptions.pas', + DUnitX.DUnitCompatibility in '..\DUnitX.DUnitCompatibility.pas', + DUnitX.Constants in '..\DUnitX.Constants.pas', + DUnitX.ConsoleWriter.Base in '..\DUnitX.ConsoleWriter.Base.pas', + DUnitX.ComparableFormat.Xml in '..\DUnitX.ComparableFormat.Xml.pas', + DUnitX.ComparableFormat in '..\DUnitX.ComparableFormat.pas', + DUnitX.ComparableFormat.Csv in '..\DUnitX.ComparableFormat.Csv.pas', + DUnitX.CommandLine.Parser in '..\DUnitX.CommandLine.Parser.pas', + DUnitX.CommandLine.Options in '..\DUnitX.CommandLine.Options.pas', + DUnitX.CommandLine.OptionDef in '..\DUnitX.CommandLine.OptionDef.pas', + DUnitX.CategoryExpression in '..\DUnitX.CategoryExpression.pas', + DUnitX.Banner in '..\DUnitX.Banner.pas', + DUnitX.AutoDetect.Console in '..\DUnitX.AutoDetect.Console.pas', + DUnitX.Attributes in '..\DUnitX.Attributes.pas', + DUnitX.Assert in '..\DUnitX.Assert.pas', + DUnitX.Assert.Ex in '..\DUnitX.Assert.Ex.pas'; + +end. diff --git a/Source/Packages/DUnitX_FMX.dproj b/Source/Packages/DUnitX_FMX.dproj new file mode 100644 index 0000000..8d1d57d --- /dev/null +++ b/Source/Packages/DUnitX_FMX.dproj @@ -0,0 +1,264 @@ + + + True + Package + Release + FMX + DUnitX_FMX.dpk + Win32 + {5B9C1441-943C-4EF2-95B5-19CBC6BD857A} + DUnitX_FMX + 20.3 + 693395 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + DUnitX_FMX + All + .\$(Platform)\$(Config) + DUnitX FMX/Multiplatform + .\$(Platform)\$(Config) + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + true + true + true + + + Debug + None + activity-1.7.2.dex.jar;annotation-experimental-1.4.1.dex.jar;annotation-jvm-1.8.1.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-7.1.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-jvm-1.4.2.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.15.0.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.15.0.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.2.dex.jar;lifecycle-livedata-2.6.2.dex.jar;lifecycle-livedata-core-2.6.2.dex.jar;lifecycle-runtime-2.6.2.dex.jar;lifecycle-service-2.6.2.dex.jar;lifecycle-viewmodel-2.6.2.dex.jar;lifecycle-viewmodel-savedstate-2.6.2.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.5.0.dex.jar;play-services-basement-18.4.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.2.0.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.2.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=35 + + + Debug + None + activity-1.7.2.dex.jar;annotation-experimental-1.4.1.dex.jar;annotation-jvm-1.8.1.dex.jar;annotations-13.0.dex.jar;appcompat-1.2.0.dex.jar;appcompat-resources-1.2.0.dex.jar;billing-7.1.1.dex.jar;biometric-1.1.0.dex.jar;browser-1.4.0.dex.jar;cloud-messaging.dex.jar;collection-jvm-1.4.2.dex.jar;concurrent-futures-1.1.0.dex.jar;core-1.15.0.dex.jar;core-common-2.2.0.dex.jar;core-ktx-1.15.0.dex.jar;core-runtime-2.2.0.dex.jar;cursoradapter-1.0.0.dex.jar;customview-1.0.0.dex.jar;documentfile-1.0.0.dex.jar;drawerlayout-1.0.0.dex.jar;error_prone_annotations-2.9.0.dex.jar;exifinterface-1.3.6.dex.jar;firebase-annotations-16.2.0.dex.jar;firebase-common-20.3.1.dex.jar;firebase-components-17.1.0.dex.jar;firebase-datatransport-18.1.7.dex.jar;firebase-encoders-17.0.0.dex.jar;firebase-encoders-json-18.0.0.dex.jar;firebase-encoders-proto-16.0.0.dex.jar;firebase-iid-interop-17.1.0.dex.jar;firebase-installations-17.1.3.dex.jar;firebase-installations-interop-17.1.0.dex.jar;firebase-measurement-connector-19.0.0.dex.jar;firebase-messaging-23.1.2.dex.jar;fmx.dex.jar;fragment-1.2.5.dex.jar;google-play-licensing.dex.jar;interpolator-1.0.0.dex.jar;javax.inject-1.dex.jar;kotlin-stdlib-1.8.22.dex.jar;kotlin-stdlib-common-1.8.22.dex.jar;kotlin-stdlib-jdk7-1.8.22.dex.jar;kotlin-stdlib-jdk8-1.8.22.dex.jar;kotlinx-coroutines-android-1.6.4.dex.jar;kotlinx-coroutines-core-jvm-1.6.4.dex.jar;legacy-support-core-utils-1.0.0.dex.jar;lifecycle-common-2.6.2.dex.jar;lifecycle-livedata-2.6.2.dex.jar;lifecycle-livedata-core-2.6.2.dex.jar;lifecycle-runtime-2.6.2.dex.jar;lifecycle-service-2.6.2.dex.jar;lifecycle-viewmodel-2.6.2.dex.jar;lifecycle-viewmodel-savedstate-2.6.2.dex.jar;listenablefuture-1.0.dex.jar;loader-1.0.0.dex.jar;localbroadcastmanager-1.0.0.dex.jar;okio-jvm-3.4.0.dex.jar;play-services-ads-22.2.0.dex.jar;play-services-ads-base-22.2.0.dex.jar;play-services-ads-identifier-18.0.0.dex.jar;play-services-ads-lite-22.2.0.dex.jar;play-services-appset-16.0.1.dex.jar;play-services-base-18.5.0.dex.jar;play-services-basement-18.4.0.dex.jar;play-services-cloud-messaging-17.0.1.dex.jar;play-services-location-21.0.1.dex.jar;play-services-maps-18.1.0.dex.jar;play-services-measurement-base-20.1.2.dex.jar;play-services-measurement-sdk-api-20.1.2.dex.jar;play-services-stats-17.0.2.dex.jar;play-services-tasks-18.2.0.dex.jar;print-1.0.0.dex.jar;profileinstaller-1.3.0.dex.jar;room-common-2.2.5.dex.jar;room-runtime-2.2.5.dex.jar;savedstate-1.2.1.dex.jar;sqlite-2.1.0.dex.jar;sqlite-framework-2.1.0.dex.jar;startup-runtime-1.1.1.dex.jar;tracing-1.2.0.dex.jar;transport-api-3.0.0.dex.jar;transport-backend-cct-3.1.8.dex.jar;transport-runtime-3.1.8.dex.jar;user-messaging-platform-2.0.0.dex.jar;vectordrawable-1.1.0.dex.jar;vectordrawable-animated-1.1.0.dex.jar;versionedparcelable-1.1.1.dex.jar;viewpager-1.0.0.dex.jar;work-runtime-2.7.0.dex.jar + package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=;minSdkVersion=23;targetSdkVersion=35 + + + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing the Bluetooth interface + + + Debug + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing the Bluetooth interface + + + Debug + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + Debug + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + Debug + None + $(MSBuildProjectName) + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + + + None + true + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + + + true + true + DEBUG;$(DCC_Define) + true + true + false + true + true + + + false + + + 0 + RELEASE;$(DCC_Define) + false + 0 + + + true + 1033 + + + + MainSource + + + + +
GUIXTestRunner
+
+ +
MobileGUITestRunner
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + +
+ + Delphi.Personality.12 + Package + + + + DUnitX_FMX.dpk + + + + + True + True + True + True + True + True + True + False + True + True + + + 12 + + + + +
diff --git a/Source/Packages/DUnitX_FMX_nonshared.a b/Source/Packages/DUnitX_FMX_nonshared.a new file mode 100644 index 0000000..e6d45bc Binary files /dev/null and b/Source/Packages/DUnitX_FMX_nonshared.a differ diff --git a/Source/Packages/DUnitX_VLC.dpk b/Source/Packages/DUnitX_VLC.dpk new file mode 100644 index 0000000..df54c80 --- /dev/null +++ b/Source/Packages/DUnitX_VLC.dpk @@ -0,0 +1,95 @@ +package DUnitX_VLC; + +{$R *.res} +{$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} +{$ALIGN 8} +{$ASSERTIONS ON} +{$BOOLEVAL OFF} +{$DEBUGINFO OFF} +{$EXTENDEDSYNTAX ON} +{$IMPORTEDDATA ON} +{$IOCHECKS ON} +{$LOCALSYMBOLS OFF} +{$LONGSTRINGS ON} +{$OPENSTRINGS ON} +{$OPTIMIZATION ON} +{$OVERFLOWCHECKS OFF} +{$RANGECHECKS OFF} +{$REFERENCEINFO OFF} +{$SAFEDIVIDE OFF} +{$STACKFRAMES OFF} +{$TYPEDADDRESS OFF} +{$VARSTRINGCHECKS ON} +{$WRITEABLECONST OFF} +{$MINENUMSIZE 1} +{$IMAGEBASE $400000} +{$DEFINE RELEASE} +{$ENDIF IMPLICITBUILDING} +{$DESCRIPTION 'DUnitX VCL/Windows'} +{$RUNONLY} +{$IMPLICITBUILD ON} + +requires + rtl, + vcl, + vclactnband; + +contains + DUnitX.Loggers.GUI.VCL in '..\DUnitX.Loggers.GUI.VCL.pas' {GUIVCLTestRunner}, + DUnitX.Loggers.GUI.VCL.RichEdit in '..\DUnitX.Loggers.GUI.VCL.RichEdit.pas', + DUnitX.Windows.Console in '..\DUnitX.Windows.Console.pas', + DUnitX.WeakReference in '..\DUnitX.WeakReference.pas', + DUnitX.Utils.XML in '..\DUnitX.Utils.XML.pas', + DUnitX.Utils in '..\DUnitX.Utils.pas', + DUnitX.Types in '..\DUnitX.Types.pas', + DUnitX.Timeout in '..\DUnitX.Timeout.pas', + DUnitX.TestRunner in '..\DUnitX.TestRunner.pas', + DUnitX.TestResult in '..\DUnitX.TestResult.pas', + DUnitX.TestNameParser in '..\DUnitX.TestNameParser.pas', + DUnitX.TestFramework in '..\DUnitX.TestFramework.pas', + DUnitX.TestFixture in '..\DUnitX.TestFixture.pas', + DUnitX.TestDataProvider in '..\DUnitX.TestDataProvider.pas', + DUnitX.Test in '..\DUnitX.Test.pas', + DUnitX.ServiceLocator in '..\DUnitX.ServiceLocator.pas', + DUnitX.RunResults in '..\DUnitX.RunResults.pas', + DUnitX.ResStrs in '..\DUnitX.ResStrs.pas', + DUnitX.OptionsDefinition in '..\DUnitX.OptionsDefinition.pas', + DUnitX.MemoryLeakMonitor.FastMM4 in '..\DUnitX.MemoryLeakMonitor.FastMM4.pas', + DUnitX.MemoryLeakMonitor.Default in '..\DUnitX.MemoryLeakMonitor.Default.pas', + DUnitX.MacOS.Console in '..\DUnitX.MacOS.Console.pas', + DUnitX.Loggers.XML.xUnit in '..\DUnitX.Loggers.XML.xUnit.pas', + DUnitX.Loggers.XML.NUnit in '..\DUnitX.Loggers.XML.NUnit.pas', + DUnitX.Loggers.XML.JUnit in '..\DUnitX.Loggers.XML.JUnit.pas', + DUnitX.Loggers.Text in '..\DUnitX.Loggers.Text.pas', + DUnitX.Loggers.Null in '..\DUnitX.Loggers.Null.pas', + DUnitX.Loggers.Console in '..\DUnitX.Loggers.Console.pas', + DUnitX.Linux.Console in '..\DUnitX.Linux.Console.pas', + DUnitX.InternalInterfaces in '..\DUnitX.InternalInterfaces.pas', + DUnitX.InternalDataProvider in '..\DUnitX.InternalDataProvider.pas', + DUnitX.Init in '..\DUnitX.Init.pas', + DUnitX.Helpers in '..\DUnitX.Helpers.pas', + DUnitX.Generics in '..\DUnitX.Generics.pas', + DUnitX.FixtureResult in '..\DUnitX.FixtureResult.pas', + DUnitX.FixtureProvider in '..\DUnitX.FixtureProvider.pas', + DUnitX.FixtureBuilder in '..\DUnitX.FixtureBuilder.pas', + DUnitX.Filters in '..\DUnitX.Filters.pas', + DUnitX.FilterBuilder in '..\DUnitX.FilterBuilder.pas', + DUnitX.Extensibility in '..\DUnitX.Extensibility.pas', + DUnitX.Exceptions in '..\DUnitX.Exceptions.pas', + DUnitX.DUnitCompatibility in '..\DUnitX.DUnitCompatibility.pas', + DUnitX.Constants in '..\DUnitX.Constants.pas', + DUnitX.ConsoleWriter.Base in '..\DUnitX.ConsoleWriter.Base.pas', + DUnitX.ComparableFormat.Xml in '..\DUnitX.ComparableFormat.Xml.pas', + DUnitX.ComparableFormat in '..\DUnitX.ComparableFormat.pas', + DUnitX.ComparableFormat.Csv in '..\DUnitX.ComparableFormat.Csv.pas', + DUnitX.CommandLine.Parser in '..\DUnitX.CommandLine.Parser.pas', + DUnitX.CommandLine.Options in '..\DUnitX.CommandLine.Options.pas', + DUnitX.CommandLine.OptionDef in '..\DUnitX.CommandLine.OptionDef.pas', + DUnitX.CategoryExpression in '..\DUnitX.CategoryExpression.pas', + DUnitX.Banner in '..\DUnitX.Banner.pas', + DUnitX.AutoDetect.Console in '..\DUnitX.AutoDetect.Console.pas', + DUnitX.Attributes in '..\DUnitX.Attributes.pas', + DUnitX.Assert in '..\DUnitX.Assert.pas', + DUnitX.Assert.Ex in '..\DUnitX.Assert.Ex.pas'; + +end. diff --git a/Source/Packages/DUnitX_VLC.dproj b/Source/Packages/DUnitX_VLC.dproj new file mode 100644 index 0000000..a5e7e89 --- /dev/null +++ b/Source/Packages/DUnitX_VLC.dproj @@ -0,0 +1,202 @@ + + + True + Package + Release + VCL + DUnitX_VLC.dpk + Win32 + {327D6780-81EC-4EC6-9EEC-A22C3C4C4835} + DUnitX_VLC + 20.3 + 3 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + DUnitX_VLC + All + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + true + true + true + + + Debug + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + Debug + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + true + true + DEBUG;$(DCC_Define) + true + true + false + true + true + + + false + + + 0 + RELEASE;$(DCC_Define) + false + 0 + + + DUnitX VCL/Windows + true + 1033 + + + + MainSource + + + + + +
GUIVCLTestRunner
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + +
+ + Delphi.Personality.12 + Package + + + + DUnitX_VLC.dpk + + + + + False + False + False + False + False + True + True + False + False + False + + + 12 + + + + +