Skip to content

Commit 35a1baf

Browse files
committed
Option to install tools, like JDK and SDK
1 parent 73170fe commit 35a1baf

11 files changed

+841
-0
lines changed
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
unit Builder.Services.Tools.Install;
2+
3+
interface
4+
5+
uses
6+
System.Types,
7+
System.Classes,
8+
System.SysUtils,
9+
System.Generics.Collections,
10+
Builder.Types,
11+
Builder.Services;
12+
13+
type
14+
TToolInstallService = class(TInterfacedObject, IToolInstallServices)
15+
public type
16+
TWaitList = TArray<TPair<PToolInfo, IAsyncResult>>;
17+
TWaitListCallback = reference to procedure(const AWaitList: TWaitList;
18+
const ACurrent: integer);
19+
private
20+
FInstalling: TDictionary<PToolInfo, IAsyncResult>;
21+
22+
procedure CheckInstalling(const ATool: PToolInfo);
23+
24+
// Dependencies resolution strategy
25+
function BuildWaitList(const ATool: PToolInfo): TWaitList;
26+
procedure WaitForDependencies(const ATool: PToolInfo;
27+
const AWaitList: TWaitList; const AWaitListCallback: TWaitListCallback);
28+
procedure CheckDependencies(const ATool: PToolInfo);
29+
procedure ResolveDependencies(const ATool: PToolInfo;
30+
const AWaitList: TWaitList; const AProgress: TToolInstallationProgress;
31+
var AAbort: boolean);
32+
public
33+
constructor Create();
34+
destructor Destroy(); override;
35+
36+
function GetTools(): TArray<PToolInfo>;
37+
function GetMissingTools(): TArray<PToolInfo>;
38+
function GetInstallingTools(): TInstallingTools;
39+
40+
function IsInstalled(const ATool: PToolInfo): boolean;
41+
42+
function Install(const ATool: PToolInfo;
43+
const AProgress: TToolInstallationProgress = nil;
44+
const ACallback: TAsyncCallback = nil): IAsyncResult;
45+
end;
46+
47+
implementation
48+
49+
uses
50+
System.IOutils,
51+
System.Threading,
52+
Builder.Paths,
53+
Builder.Exception,
54+
Builder.Services.Tools.Installer.Factory;
55+
56+
type
57+
TToolsInfo = array[0..1] of TToolInfo;
58+
59+
TToolInstallAsyncResult = class(TBaseAsyncResult)
60+
private
61+
FTask: TProc;
62+
FCancellation: TProc;
63+
FCallback: TAsyncCallback;
64+
protected
65+
procedure AsyncDispatch; override;
66+
procedure Complete; override;
67+
procedure Schedule; override;
68+
69+
function DoCancel: Boolean; override;
70+
public
71+
constructor Create(const AContext: TObject;
72+
const ATask, ACancellationCallback: TProc;
73+
const ACallback: TAsyncCallback);
74+
end;
75+
76+
const
77+
INDEX_JDK_21_35 = 0;
78+
INDEX_SDK_32 = 1;
79+
80+
{$IFDEF WIN32}
81+
{$I required_tools_win_x86.inc}
82+
{$ENDIF WIN32}
83+
{$IFDEF WIN64}
84+
{$I required_tools_win_x64.inc}
85+
{$ENDIF WIN64}
86+
87+
{$IFDEF MACOS}
88+
{$IFDEF CPUARM64}
89+
{$I required_tools_mac_arm64.inc}
90+
{$ELSE}
91+
{$I required_tools_mac_x86_x64.inc}
92+
{$ENDIF CPUARM64}
93+
{$ENDIF MACOS}
94+
95+
{$IFDEF LINUX}
96+
{$I required_tools_linux_x86_x64.inc}
97+
{$ENDIF LINUX}
98+
99+
{ TToolInstallService }
100+
101+
constructor TToolInstallService.Create;
102+
begin
103+
inherited Create();
104+
FInstalling := TDictionary<PToolInfo, IAsyncResult>.Create();
105+
end;
106+
107+
destructor TToolInstallService.Destroy;
108+
begin
109+
FInstalling.Free();
110+
inherited;
111+
end;
112+
113+
procedure TToolInstallService.CheckInstalling(const ATool: PToolInfo);
114+
begin
115+
if FInstalling.ContainsKey(ATool) then
116+
if FInstalling.Items[ATool].IsCompleted or FInstalling.Items[ATool].IsCancelled then
117+
FInstalling.Remove(ATool)
118+
else
119+
raise EInstallInProgress.CreateFmt('"%s" is currently being installed.', [ATool^.Description]);
120+
end;
121+
122+
function TToolInstallService.BuildWaitList(
123+
const ATool: PToolInfo): TWaitList;
124+
begin
125+
Result := [];
126+
for var LRequired in ATool.Requires do
127+
if FInstalling.ContainsKey(LRequired) then
128+
Result := Result + [
129+
TPair<PToolInfo, IAsyncResult>.Create(
130+
LRequired, FInstalling.Items[LRequired])];
131+
end;
132+
133+
procedure TToolInstallService.WaitForDependencies(
134+
const ATool: PToolInfo; const AWaitList: TWaitList;
135+
const AWaitListCallback: TWaitListCallback);
136+
begin
137+
for var I := Low(AWaitList) to High(AWaitList) do begin
138+
// Notify we are waiting for this tool
139+
AWaitListCallback(AWaitList, I);
140+
// Wait for completition
141+
AWaitList[I].Value.AsyncWaitEvent.WaitFor();
142+
end;
143+
end;
144+
145+
procedure TToolInstallService.CheckDependencies(
146+
const ATool: PToolInfo);
147+
begin
148+
for var LMissingTool in GetMissingTools() do
149+
for var LDependency in ATool.Requires do
150+
if LDependency = LMissingTool then
151+
raise EMissingTool.CreateFmt('"%s" requires "%s" to be installed.', [ATool.Name, LDependency.Name]);
152+
end;
153+
154+
function TToolInstallService.IsInstalled(const ATool: PToolInfo): boolean;
155+
begin
156+
var LInstaller := TToolsInstallerFactory.Resolve(ATool);
157+
158+
if not Assigned(LInstaller) then
159+
Exit(false);
160+
161+
Result := LInstaller.IsInstalled(ATool);
162+
end;
163+
164+
procedure TToolInstallService.ResolveDependencies(const ATool: PToolInfo;
165+
const AWaitList: TWaitList; const AProgress: TToolInstallationProgress;
166+
var AAbort: boolean);
167+
begin
168+
var LAbort: PBoolean := @AAbort;
169+
// Wait for all dependencies to complete installation
170+
WaitForDependencies(ATool, AWaitList,
171+
procedure(const AWaitList: TWaitList; const ACurrent: integer) begin
172+
if Assigned(AProgress) then
173+
AProgress(ATool,
174+
Format('Waiting for [%s] to complete', [AWaitList[ACurrent].Key.Description]),
175+
Length(AWaitList), Succ(ACurrent));
176+
177+
if LAbort^ then
178+
raise EOperationCancelled.Create('The operation has been cancelled.');
179+
end);
180+
181+
// Check if all required tools are installed
182+
CheckDependencies(ATool);
183+
end;
184+
185+
function TToolInstallService.GetTools: TArray<PToolInfo>;
186+
begin
187+
Result := nil;
188+
for var I := Low(REQUIRED_TOOLS) to High(REQUIRED_TOOLS) do
189+
Result := Result + [@REQUIRED_TOOLS[I]];
190+
end;
191+
192+
function TToolInstallService.GetInstallingTools: TInstallingTools;
193+
begin
194+
Result := FInstalling.ToArray();
195+
end;
196+
197+
function TToolInstallService.GetMissingTools: TArray<PToolInfo>;
198+
begin
199+
Result := nil;
200+
for var I := Low(REQUIRED_TOOLS) to High(REQUIRED_TOOLS) do
201+
if not IsInstalled(@REQUIRED_TOOLS[I]) then
202+
Result := Result + [@REQUIRED_TOOLS[I]];
203+
end;
204+
205+
function TToolInstallService.Install(const ATool: PToolInfo;
206+
const AProgress: TToolInstallationProgress;
207+
const ACallback: TAsyncCallback): IAsyncResult;
208+
begin
209+
// Check for installation under progress
210+
CheckInstalling(ATool);
211+
212+
// Create the Android Tools directory
213+
var LToolsBaseFolder := TBuilderPaths.GetToolsInstallationBaseFolder();
214+
if not TDirectory.Exists(LToolsBaseFolder) then
215+
TDirectory.CreateDirectory(LToolsBaseFolder);
216+
217+
// Build the wait list for the installing tool
218+
var LWaitList := BuildWaitList(ATool);
219+
220+
// Check for undefined installation class
221+
if not Assigned(TToolsInstallerFactory.Resolve(ATool)) then
222+
raise EUnknownTool.CreateFmt('Unknown tool "%s" can''t be installed.', [
223+
ATool.Description]);
224+
225+
// Instantiate the installer
226+
var LAbort := false;
227+
Result := TToolInstallAsyncResult.Create(Self,
228+
procedure() begin
229+
var LInstaller := TToolsInstallerFactory.CreateInstance(ATool);
230+
try
231+
LInstaller.Install(
232+
procedure(const AToolInfo: PToolInfo) begin
233+
ResolveDependencies(AToolInfo, LWaitList, AProgress, LAbort)
234+
end, LAbort, AProgress);
235+
finally
236+
LInstaller.Free();
237+
end;
238+
end,
239+
// Cancellation callback
240+
procedure() begin
241+
LAbort := true;
242+
end,
243+
// Async callback
244+
procedure(const AResult: IAsyncResult) begin
245+
if Assigned(ACallback) then
246+
ACallback(AResult);
247+
248+
TThread.Queue(TThread.Current, procedure() begin
249+
FInstalling.Remove(ATool);
250+
end);
251+
end).Invoke();
252+
253+
FInstalling.Add(ATool, Result);
254+
end;
255+
256+
{ TToolInstallAsyncResult }
257+
258+
constructor TToolInstallAsyncResult.Create(const AContext: TObject;
259+
const ATask, ACancellationCallback: TProc; const ACallback: TAsyncCallback);
260+
begin
261+
inherited Create(AContext);
262+
FTask := ATask;
263+
FCancellation := ACancellationCallback;
264+
FCallback := ACallback;
265+
end;
266+
267+
procedure TToolInstallAsyncResult.AsyncDispatch;
268+
begin
269+
if Assigned(FTask) then
270+
FTask();
271+
end;
272+
273+
procedure TToolInstallAsyncResult.Complete;
274+
begin
275+
inherited;
276+
if Assigned(FCallback) then
277+
FCallback(Self);
278+
end;
279+
280+
function TToolInstallAsyncResult.DoCancel: Boolean;
281+
begin
282+
Result := inherited DoCancel();
283+
if Assigned(FCancellation) then
284+
FCancellation();
285+
end;
286+
287+
procedure TToolInstallAsyncResult.Schedule;
288+
begin
289+
TTask.Run(DoAsyncDispatch);
290+
end;
291+
292+
end.

0 commit comments

Comments
 (0)