Skip to content

Commit bed0168

Browse files
committed
Merge branch 'feature/19-auto-detect-new-compilers' into develop
Closes #19
2 parents 77473a1 + e169b47 commit bed0168

26 files changed

+1189
-70
lines changed

Src/CodeSnip.dpr

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,10 @@ uses
370370
UXMLDocConsts in 'UXMLDocConsts.pas',
371371
UXMLDocHelper in 'UXMLDocHelper.pas',
372372
UXMLDocumentEx in 'UXMLDocumentEx.pas',
373-
FmDeleteUserDBDlg in 'FmDeleteUserDBDlg.pas' {DeleteUserDBDlg};
373+
FmDeleteUserDBDlg in 'FmDeleteUserDBDlg.pas' {DeleteUserDBDlg},
374+
Compilers.UAutoDetect in 'Compilers.UAutoDetect.pas',
375+
Compilers.USettings in 'Compilers.USettings.pas',
376+
FmRegisterCompilersDlg in 'FmRegisterCompilersDlg.pas' {RegisterCompilersDlg};
374377

375378
// Include resources
376379
{$Resource ExternalObj.tlb} // Type library file

Src/CodeSnip.dproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,11 @@
575575
<DCCReference Include="FmDeleteUserDBDlg.pas">
576576
<Form>DeleteUserDBDlg</Form>
577577
</DCCReference>
578+
<DCCReference Include="Compilers.UAutoDetect.pas"/>
579+
<DCCReference Include="Compilers.USettings.pas"/>
580+
<DCCReference Include="FmRegisterCompilersDlg.pas">
581+
<Form>RegisterCompilersDlg</Form>
582+
</DCCReference>
578583
<None Include="CodeSnip.todo"/>
579584
<BuildConfiguration Include="Base">
580585
<Key>Base</Key>

Src/Compilers.UAutoDetect.pas

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
{
2+
* This Source Code Form is subject to the terms of the Mozilla Public License,
3+
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
4+
* obtain one at https://mozilla.org/MPL/2.0/
5+
*
6+
* Copyright (C) 2022, Peter Johnson (gravatar.com/delphidabbler).
7+
*
8+
* Implements a static class that can detect and register Delphi compilers
9+
* present on the user's system that are not yet registered with CodeSnip.
10+
}
11+
12+
13+
unit Compilers.UAutoDetect;
14+
15+
interface
16+
17+
uses
18+
Compilers.UGlobals,
19+
Compilers.UCompilers,
20+
UBaseObjects;
21+
22+
type
23+
TCompilerAutoDetect = class(TNoConstructObject)
24+
public
25+
type
26+
TCallback = reference to procedure (Compiler: ICompiler);
27+
strict private
28+
class procedure DoCallback(const Callback: TCallback;
29+
Compiler: ICompiler);
30+
public
31+
class procedure RegisterCompilers(Compilers: ICompilers;
32+
const Callback: TCallback = nil); overload;
33+
class procedure RegisterSpecificCompilers(AllCompilers: ICompilers;
34+
const RegList: TCompilerList; const Callback: TCallback = nil);
35+
class procedure ListRegisterableCompilers(Compilers: ICompilers;
36+
const Registerable: TCompilerList);
37+
end;
38+
39+
implementation
40+
41+
uses
42+
SysUtils;
43+
44+
{ TCompilerAutoDetect }
45+
46+
class procedure TCompilerAutoDetect.DoCallback(
47+
const Callback: TCallback; Compiler: ICompiler);
48+
begin
49+
if Assigned(Callback) then
50+
Callback(Compiler);
51+
end;
52+
53+
class procedure TCompilerAutoDetect.ListRegisterableCompilers(
54+
Compilers: ICompilers; const Registerable: TCompilerList);
55+
var
56+
Compiler: ICompiler;
57+
begin
58+
Registerable.Clear;
59+
for Compiler in Compilers do
60+
begin
61+
if not Supports(Compiler, ICompilerAutoDetect) then
62+
Continue; // compiler can't be auto-detected/registered
63+
if not (Compiler as ICompilerAutoDetect).IsInstalled then
64+
Continue; // compiler is not installed on the user's system
65+
if Compiler.IsAvailable then
66+
Continue; // compiler installed & already registered for use by CodeSnip
67+
if not (Compiler as ICompilerAutoDetect).GetCanAutoInstall then
68+
Continue; // user has excluded this compiler from being auto-registered
69+
// We get here then we have an installed, un-registered, auto-detectable
70+
// compiler with permission to register
71+
Registerable.Add(Compiler);
72+
end;
73+
end;
74+
75+
class procedure TCompilerAutoDetect.RegisterSpecificCompilers(
76+
AllCompilers: ICompilers; const RegList: TCompilerList;
77+
const Callback: TCallback);
78+
var
79+
Compiler: ICompiler;
80+
begin
81+
for Compiler in AllCompilers do
82+
begin
83+
if RegList.IndexOf(Compiler) >= 0 then
84+
begin
85+
Assert(Supports(Compiler, ICompilerAutoDetect), ClassName +
86+
'.RegisterCompilers: Compiler does not support ICompilerAutoDetect');
87+
if (Compiler as ICompilerAutoDetect).DetectExeFile then
88+
DoCallback(Callback, Compiler);
89+
end;
90+
end;
91+
end;
92+
93+
class procedure TCompilerAutoDetect.RegisterCompilers(Compilers: ICompilers;
94+
const Callback: TCallback);
95+
var
96+
Registerable: TCompilerList;
97+
Compiler: ICompiler;
98+
begin
99+
Registerable := TCompilerList.Create;
100+
try
101+
ListRegisterableCompilers(Compilers, Registerable);
102+
for Compiler in Registerable do
103+
begin
104+
Assert(Supports(Compiler, ICompilerAutoDetect), ClassName +
105+
'.RegisterCompilers: Compiler does not support ICompilerAutoDetect');
106+
if (Compiler as ICompilerAutoDetect).DetectExeFile then
107+
DoCallback(Callback, Compiler);
108+
end;
109+
finally
110+
Registerable.Free;
111+
end;
112+
end;
113+
114+
end.
115+

Src/Compilers.UBorland.pas

Lines changed: 80 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,22 @@ TBorlandCompiler = class(TCompilerBase)
3535
var
3636
fId: TCompilerID;
3737
{Identifies compiler}
38+
// Flags whether user permits compiler to be auto installed.
39+
fCanAutoInstall: Boolean;
3840
function InstallPathFromReg(const RootKey: HKEY): string;
3941
{Gets compiler install root path from given registry root key, if present.
4042
@param RootKey [in] Given registry root key.
4143
@return Required root path or '' if not compiler not installed.
4244
}
45+
/// <summary>Gets the path to the compiler exe file if the compiler is
46+
/// registered as installed on the user's computer.</summary>
47+
/// <param name="ExePath">string [out] Set to path to compiler executable
48+
/// file. Empty string if compiler not installed.</param>
49+
/// <returns>Boolean. True if compiler is registered as installed or False
50+
/// otherwise.</returns>
51+
/// <remarks>Does not check if compiler exe is actually present, just if
52+
/// it is registered.</remarks>
53+
function GetExePathIfInstalled(out ExePath: string): Boolean;
4354
strict protected
4455
function SearchDirParams: string; override;
4556
{One of more parameters that define any search directories to be passed
@@ -61,10 +72,35 @@ TBorlandCompiler = class(TCompilerBase)
6172
@param Obj Compiler object to copy.
6273
}
6374
{ ICompilerAutoDetect }
75+
/// <summary>Detects and records path to command line compiler exe file,
76+
/// if compiler is registered as installed.</summary>
77+
/// <returns>Boolean. True if compiler is registered as installed, False
78+
/// otherwise.</returns>
79+
/// <remarks>
80+
/// <para>Does not check if the compiler exe file actually exists.</para>
81+
/// <para>Does not set compiler exe file if compiler is not installed.
82+
/// </para>
83+
/// <para>Method of ICompilerAutoDetect.</para>
84+
/// </remarks>
6485
function DetectExeFile: Boolean;
65-
{Detects and records path to command line compiler if present.
66-
@return True if compiler path found, false otherwise.
67-
}
86+
/// <summary>Checks if the compiler is installed on the user's system.
87+
/// </summary>
88+
/// <returns>Boolean. True if compiler is physically installed, False
89+
/// otherwise.</returns>
90+
/// <remarks>
91+
/// <para>Checks if compiler exe is actually present.</para>
92+
/// <para>Method of ICompilerAutoDetect.</para>
93+
/// </remarks>
94+
function IsInstalled: Boolean;
95+
/// <summary>Checks if the compiler is permitted to be automatically
96+
/// installed.</summary>
97+
/// <remarks>Method of ICompilerAutoDetect.</remarks>
98+
function GetCanAutoInstall: Boolean;
99+
/// <summary>Determines whether the compiler can be automatically
100+
/// installed.</summary>
101+
/// <remarks>Method of ICompilerAutoDetect.</remarks>
102+
procedure SetCanAutoInstall(const Value: Boolean);
103+
68104
{ ICompiler }
69105
function GetDefaultSwitches: string; override;
70106
{Returns default command line switches for compiler.
@@ -88,7 +124,7 @@ implementation
88124

89125
uses
90126
// Delphi
91-
SysUtils, Registry,
127+
SysUtils, Registry, IOUtils,
92128
// Project
93129
UIStringList, UStrUtils, USystemInfo;
94130

@@ -109,6 +145,7 @@ constructor TBorlandCompiler.CreateCopy(const Obj: TBorlandCompiler);
109145
begin
110146
inherited CreateCopy(Obj);
111147
fId := Obj.GetID;
148+
fCanAutoInstall := Obj.GetCanAutoInstall;
112149
end;
113150

114151
procedure TBorlandCompiler.DeleteObjFiles(const Path, Project: string);
@@ -128,17 +165,16 @@ function TBorlandCompiler.DetectExeFile: Boolean;
128165
@return True if compiler path found, false otherwise.
129166
}
130167
var
131-
InstDir: string; // installation root directory
168+
ExePath: string;
132169
begin
133-
// try HKLM
134-
InstDir := InstallPathFromReg(HKEY_LOCAL_MACHINE);
135-
if InstDir = '' then
136-
// in case install was for user only, try HKCU
137-
InstDir := InstallPathFromReg(HKEY_CURRENT_USER);
138-
if InstDir = '' then
139-
Exit(False);
140-
SetExecFile(IncludeTrailingPathDelimiter(InstDir) + 'Bin\DCC32.exe');
141-
Result := True;
170+
Result := GetExePathIfInstalled(ExePath);
171+
if Result then
172+
SetExecFile(ExePath);
173+
end;
174+
175+
function TBorlandCompiler.GetCanAutoInstall: Boolean;
176+
begin
177+
Result := fCanAutoInstall;
142178
end;
143179

144180
function TBorlandCompiler.GetDefaultSwitches: string;
@@ -161,6 +197,22 @@ function TBorlandCompiler.GetDefaultSwitches: string;
161197
+ '-$P+'; // Open string params ON
162198
end;
163199

200+
function TBorlandCompiler.GetExePathIfInstalled(out ExePath: string): Boolean;
201+
var
202+
InstDir: string;
203+
begin
204+
ExePath := '';
205+
// try HKLM
206+
InstDir := InstallPathFromReg(HKEY_LOCAL_MACHINE);
207+
if InstDir = '' then
208+
// in case install was for user only, try HKCU
209+
InstDir := InstallPathFromReg(HKEY_CURRENT_USER);
210+
if InstDir = '' then
211+
Exit(False);
212+
ExePath := TPath.Combine(InstDir, 'Bin\DCC32.exe');
213+
Result := True;
214+
end;
215+
164216
function TBorlandCompiler.GetID: TCompilerID;
165217
{Provides the unique id of the compiler.
166218
@return Compiler id.
@@ -192,6 +244,15 @@ function TBorlandCompiler.InstallPathFromReg(const RootKey: HKEY): string;
192244
end;
193245
end;
194246

247+
function TBorlandCompiler.IsInstalled: Boolean;
248+
var
249+
ExePath: string;
250+
begin
251+
if not GetExePathIfInstalled(ExePath) then
252+
Exit(False);
253+
Result := TFile.Exists(ExePath, False);
254+
end;
255+
195256
function TBorlandCompiler.SearchDirParams: string;
196257
{One of more parameters that define any search directories to be passed to
197258
compiler on command line.
@@ -209,5 +270,10 @@ function TBorlandCompiler.SearchDirParams: string;
209270
+ ' ' + StrQuoteSpaced('-R' + Dirs.GetText(';', False));
210271
end;
211272

273+
procedure TBorlandCompiler.SetCanAutoInstall(const Value: Boolean);
274+
begin
275+
fCanAutoInstall := Value;
276+
end;
277+
212278
end.
213279

Src/Compilers.UCompilers.pas

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface
2020

2121

2222
uses
23+
// Delphi
24+
Generics.Collections,
2325
// Project
2426
Compilers.UGlobals, UBaseObjects;
2527

@@ -64,13 +66,18 @@ TCompilersFactory = class(TNoConstructObject)
6466
}
6567
end;
6668

69+
TCompilerList = class(TList<ICompiler>)
70+
public
71+
constructor Create;
72+
end;
73+
6774

6875
implementation
6976

7077

7178
uses
7279
// Delphi
73-
Generics.Collections, SysUtils,
80+
Generics.Defaults, SysUtils,
7481
// Project
7582
Compilers.UBDS, Compilers.UDelphi, Compilers.UFreePascal,
7683
Compilers.USearchDirs, IntfCommon, UConsts, UExceptions, UIStringList,
@@ -328,6 +335,12 @@ procedure TPersistCompilers.Load(const Compilers: ICompilers);
328335
// Load search directories
329336
SearchDirNames := Storage.GetStrings('SearchDirCount', 'SearchDir%d');
330337
Compiler.SetSearchDirs(TSearchDirs.Create(SearchDirNames.ToArray));
338+
339+
// Check if compiler can be auto-detected
340+
if Supports(Compiler, ICompilerAutoDetect) then
341+
(Compiler as ICompilerAutoDetect).SetCanAutoInstall(
342+
Storage.GetBoolean('CanAutoInstall', True)
343+
);
331344
end;
332345
end;
333346

@@ -363,10 +376,28 @@ procedure TPersistCompilers.Save(const Compilers: ICompilers);
363376
Storage.SetString('Namespaces', Compiler.GetRTLNamespaces);
364377
SearchDirNames := TIStringList.Create(Compiler.GetSearchDirs.ToStrings);
365378
Storage.SetStrings('SearchDirCount', 'SearchDir%d', SearchDirNames);
379+
if Supports(Compiler, ICompilerAutoDetect) then
380+
Storage.SetBoolean(
381+
'CanAutoInstall', (Compiler as ICompilerAutoDetect).GetCanAutoInstall
382+
);
366383
// save the data
367384
Storage.Save;
368385
end;
369386
end;
370387

388+
{ TCompilerList }
389+
390+
constructor TCompilerList.Create;
391+
begin
392+
inherited Create(
393+
TDelegatedComparer<ICompiler>.Create(
394+
function (const Left, Right: ICompiler): Integer
395+
begin
396+
Result := Ord(Left.GetID) - Ord(Right.GetID);
397+
end
398+
)
399+
)
400+
end;
401+
371402
end.
372403

Src/Compilers.UGlobals.pas

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ interface
299299
property Count: Integer read GetCount;
300300

301301
/// <summary>Number of compilers installed on this computer and made
302-
/// available CodeSnip.</summary>
302+
/// available to CodeSnip.</summary>
303303
property AvailableCount: Integer read GetAvailableCount;
304304

305305
/// <summary>Checks if any compilers in the list are displayable.</summary>
@@ -327,6 +327,15 @@ interface
327327
/// <summary>Detects and records the full path of the compiler's
328328
/// executable.</summary>
329329
function DetectExeFile: Boolean;
330+
/// <summary>Checks if the compiler is installed on the user's system.
331+
/// </summary>
332+
function IsInstalled: Boolean;
333+
/// <summary>Checks if the compiler is permitted to be automatically
334+
/// installed.</summary>
335+
function GetCanAutoInstall: Boolean;
336+
/// <summary>Determines whether the compiler can be automatically
337+
/// installed.</summary>
338+
procedure SetCanAutoInstall(const Value: Boolean);
330339
end;
331340

332341

0 commit comments

Comments
 (0)