Skip to content

Commit 0892340

Browse files
committed
Extending the Embedded environment to support the new Python project deployables
Former-commit-id: 4124bfc90c23a66ec8db4d763915f87d4ac9c196 [formerly f1253c23adff50cd25eae2a9d111dd2bebf9127a] [formerly 49a0f234cdd6ca08d0edbc218597bd44ab67905f [formerly f36a122e33f2c7c5d25ef66afe770fa7ffcb9f9d]] Former-commit-id: 80aabc52c05c28bc60ec9a2e5483a972bb5f043a [formerly db87bc566dc7579058fead92a7067d1d8bbeec29] Former-commit-id: 856ca437c12234c80ee421ad74c7dd7bf9b16ec6 Former-commit-id: 1e2f5ad
1 parent 9b515f1 commit 0892340

File tree

1 file changed

+137
-46
lines changed

1 file changed

+137
-46
lines changed

src/Embeddable/PyEnvironment.Embeddable.pas

Lines changed: 137 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,19 @@ TPyCustomEmbeddableDistribution = class(TPyDistribution)
8989
TPyEmbeddableDistribution = class(TPyCustomEmbeddableDistribution)
9090
private
9191
FScanned: boolean;
92+
FDeleteEmbeddable: boolean;
93+
procedure DoDeleteEmbeddable();
9294
protected
9395
procedure LoadSettings(); override;
9496
public
97+
procedure Setup(); override;
9598
property Scanned: boolean read FScanned write FScanned;
9699
published
97100
property EmbeddablePackage;
101+
/// <summary>
102+
/// Delete the embeddable zip file after install.
103+
/// </summary>
104+
property DeleteEmbeddable: boolean read FDeleteEmbeddable write FDeleteEmbeddable;
98105
end;
99106

100107
TPyEmbeddableCustomCollection = class(TPyDistributionCollection);
@@ -111,20 +118,28 @@ TPyCustomEmbeddedEnvironment = class(TPyEnvironment)
111118
[ComponentPlatforms(pidAllPlatforms)]
112119
TPyEmbeddedEnvironment = class(TPyCustomEmbeddedEnvironment)
113120
private type
121+
TScanRule = (srFolder, srFileName);
114122
TScanner = class(TPersistent)
115123
private
116124
FAutoScan: boolean;
125+
FScanRule: TScanRule;
117126
FEmbeddablesPath: string;
118127
FEnvironmentPath: string;
128+
FDeleteEmbeddable: boolean;
119129
public
120130
procedure Scan(ACallback: TProc<TPythonVersionProp, string>);
121131
published
122132
property AutoScan: boolean read FAutoScan write FAutoScan default false;
133+
property ScanRule: TScanRule read FScanRule write FScanRule;
123134
property EmbeddablesPath: string read FEmbeddablesPath write FEmbeddablesPath;
124135
/// <summary>
125136
/// Default environment path.
126137
/// </summary>
127138
property EnvironmentPath: string read FEnvironmentPath write FEnvironmentPath;
139+
/// <summary>
140+
/// Delete the embeddable zip file after install.
141+
/// </summary>
142+
property DeleteEmbeddable: boolean read FDeleteEmbeddable write FDeleteEmbeddable;
128143
end;
129144
private
130145
FScanner: TScanner;
@@ -148,7 +163,8 @@ implementation
148163
System.IOUtils, System.Character, System.StrUtils,
149164
PyEnvironment.Path,
150165
PyTools.ExecCmd,
151-
PyEnvironment.Notification
166+
PyEnvironment.Notification,
167+
PyEnvironment.Project
152168
{$IFDEF POSIX}
153169
, Posix.SysStat, Posix.Stdlib, Posix.String_, Posix.Errno
154170
{$ENDIF}
@@ -199,41 +215,56 @@ function TPyCustomEmbeddableDistribution.FileIsExecutable(
199215
{$ENDIF POSIX}
200216

201217
function TPyCustomEmbeddableDistribution.FindExecutable: string;
202-
var
203-
LFiles: TArray<string>;
218+
219+
function DoSearch(const APath: string): TArray<string>;
204220
{$IFDEF POSIX}
205-
LFile: string;
221+
var
222+
LFile: string;
206223
{$ENDIF POSIX}
224+
begin
225+
Result := TDirectory.GetFiles(APath, 'python*', TSearchOption.soTopDirectoryOnly,
226+
function(const Path: string; const SearchRec: TSearchRec): boolean
227+
begin
228+
Result := Char.IsDigit(SearchRec.Name, Length(SearchRec.Name) - 1);
229+
end);
230+
231+
{$IFDEF POSIX}
232+
for LFile in Result do begin
233+
if (TPath.GetFileName(LFile) = 'python' + PythonVersion) and (FileIsExecutable(LFile)) then
234+
Exit(TArray<string>.Create(LFile));
235+
end;
236+
237+
{$WARN SYMBOL_PLATFORM OFF}
238+
LFile := Result[High(Result)];
239+
if (TFileAttribute.faOwnerExecute in TFile.GetAttributes(LFile))
240+
or (TFileAttribute.faGroupExecute in TFile.GetAttributes(LFile))
241+
or (TFileAttribute.faOthersExecute in TFile.GetAttributes(LFile)) then //Avoiding symlinks
242+
Exit(TArray<string>.Create(LFile));
243+
{$WARN SYMBOL_PLATFORM ON}
244+
245+
{$ENDIF POSIX}
246+
end;
247+
{$IFNDEF MSWINDOWS}
248+
var
249+
LFiles: TArray<string>;
250+
{$ENDIF}
207251
begin
208-
LFiles := [];
209252
{$IFDEF MSWINDOWS}
210253
Result := TPath.Combine(GetEnvironmentPath(), 'python.exe');
211254
if not TFile.Exists(Result) then
212255
Result := String.Empty;
256+
{$ELSEIF DEFINED(ANDROID)}
257+
Result := TPath.GetLibraryPath();
258+
LFiles := DoSearch(Result);
259+
if LFiles <> nil then
260+
Exit(LFiles[Low(LFiles)]);
261+
Result := TPath.Combine(GetEnvironmentPath(), 'bin');
213262
{$ELSE}
214263
Result := TPath.Combine(GetEnvironmentPath(), 'bin');
215-
LFiles := TDirectory.GetFiles(Result, 'python*', TSearchOption.soTopDirectoryOnly,
216-
function(const Path: string; const SearchRec: TSearchRec): boolean
217-
begin
218-
Result := Char.IsDigit(SearchRec.Name, Length(SearchRec.Name) - 1);
219-
end);
220264

221-
{$IFDEF POSIX}
222-
for LFile in LFiles do begin
223-
if (TPath.GetFileName(LFile) = 'python' + PythonVersion) and (FileIsExecutable(LFile)) then
224-
Exit(LFile);
225-
end;
226-
{$ENDIF POSIX}
265+
LFiles := DoSearch(Result);
227266

228-
{$WARN SYMBOL_PLATFORM OFF}
229267
if Length(LFiles) > 0 then begin
230-
Result := LFiles[High(LFiles)];
231-
if (TFileAttribute.faOwnerExecute in TFile.GetAttributes(Result))
232-
or (TFileAttribute.faGroupExecute in TFile.GetAttributes(Result))
233-
or (TFileAttribute.faOthersExecute in TFile.GetAttributes(Result)) then //Avoiding symlinks
234-
Exit;
235-
{$WARN SYMBOL_PLATFORM ON}
236-
237268
Result := LFiles[Low(LFiles)];
238269
if not TFile.Exists(Result) then
239270
Result := String.Empty;
@@ -243,11 +274,27 @@ function TPyCustomEmbeddableDistribution.FindExecutable: string;
243274
end;
244275

245276
function TPyCustomEmbeddableDistribution.FindSharedLibrary: string;
277+
278+
function DoSearch(const ALibName: string; const APath: string): TArray<string>;
279+
var
280+
LFile: string;
281+
LSearch: string;
282+
begin
283+
LFile := TPath.Combine(APath, ALibName);
284+
if not TFile.Exists(LFile) then begin
285+
LSearch := ALibName.Replace(TPath.GetExtension(ALibName), '') + '*' + TPath.GetExtension(ALibName);
286+
Result := TDirectory.GetFiles(
287+
APath,
288+
LSearch, //Python <= 3.7 might contain a "m" as a sufix.
289+
TSearchOption.soTopDirectoryOnly);
290+
end else
291+
Result := [LFile];
292+
end;
293+
246294
var
247295
I: integer;
248296
LLibName: string;
249297
LPath: string;
250-
LSearch: string;
251298
LFiles: TArray<string>;
252299
begin
253300
for I := Low(PYTHON_KNOWN_VERSIONS) to High(PYTHON_KNOWN_VERSIONS) do
@@ -258,22 +305,21 @@ function TPyCustomEmbeddableDistribution.FindSharedLibrary: string;
258305

259306
{$IFDEF MSWINDOWS}
260307
LPath := GetEnvironmentPath();
308+
{$ELSEIF DEFINED(ANDROID)}
309+
LPath := TPath.GetLibraryPath();
310+
LFiles := DoSearch(LLibName, LPath);
311+
if LFiles <> nil then
312+
Exit(LFiles[Low(LFiles)]);
313+
LPath := GetEnvironmentPath();
261314
{$ELSE}
262315
LPath := TPath.Combine(GetEnvironmentPath(), 'lib');
263316
{$ENDIF}
264317

265-
Result := TPath.Combine(LPath, LLibName);
266-
if not TFile.Exists(Result) then begin
267-
LSearch := LLibName.Replace(TPath.GetExtension(LLibName), '') + '*' + TPath.GetExtension(LLibName);
268-
LFiles := TDirectory.GetFiles(
269-
LPath,
270-
LSearch, //Python <= 3.7 might contain a "m" as a sufix.
271-
TSearchOption.soTopDirectoryOnly);
272-
if Length(LFiles) > 0 then begin
273-
Result := LFiles[Low(LFiles)];
274-
end else
275-
Result := String.Empty;
276-
end;
318+
LFiles := DoSearch(LLibName, LPath);
319+
if LFiles <> nil then
320+
Result := LFiles[Low(LFiles)]
321+
else
322+
Result := String.Empty;
277323

278324
{$IFDEF LINUX}
279325
if TFile.Exists(Result + '.1.0') then //Targets directly to the so file instead of a symlink.
@@ -311,18 +357,35 @@ procedure TPyCustomEmbeddableDistribution.Setup;
311357

312358
{ TPyEmbeddableDistribution }
313359

360+
procedure TPyEmbeddableDistribution.DoDeleteEmbeddable;
361+
begin
362+
TFile.Delete(EmbeddablePackage);
363+
end;
364+
314365
procedure TPyEmbeddableDistribution.LoadSettings;
315366
begin
316367
if FScanned then
317368
inherited;
318369
end;
319370

371+
procedure TPyEmbeddableDistribution.Setup;
372+
begin
373+
inherited;
374+
if FDeleteEmbeddable and EmbeddableExists() then
375+
DoDeleteEmbeddable();
376+
end;
377+
320378
{ TPyEmbeddedEnvironment }
321379

322380
constructor TPyEmbeddedEnvironment.Create(AOwner: TComponent);
323381
begin
324382
inherited;
325383
FScanner := TScanner.Create();
384+
PythonVersion := PythonProject.PythonVersion;
385+
if PythonProject.Enabled then
386+
FScanner.ScanRule := TScanRule.srFileName
387+
else
388+
FScanner.ScanRule := TScanRule.srFolder;
326389
end;
327390

328391
destructor TPyEmbeddedEnvironment.Destroy;
@@ -337,10 +400,17 @@ function TPyEmbeddedEnvironment.CreateCollection: TPyDistributionCollection;
337400
end;
338401

339402
procedure TPyEmbeddedEnvironment.Prepare;
340-
var
403+
var
341404
LDistribution: TPyEmbeddableDistribution;
342405
begin
343406
if FScanner.AutoScan then begin
407+
if PythonProject.Enabled then
408+
if FScanner.EmbeddablesPath.IsEmpty() then begin
409+
FScanner.EmbeddablesPath := TPyEnvironmentPath.ResolvePath(TPyEnvironmentPath.DEPLOY_PATH);
410+
FScanner.ScanRule := TScanRule.srFileName;
411+
FScanner.DeleteEmbeddable := true;
412+
end;
413+
344414
FScanner.Scan(
345415
procedure(APyVersionInfo: TPythonVersionProp; AEmbeddablePackage: string) begin
346416
if Assigned(Distributions.LocateEnvironment(APyVersionInfo.RegVersion)) then
@@ -355,7 +425,11 @@ procedure TPyEmbeddedEnvironment.Prepare;
355425
APyVersionInfo.RegVersion);
356426
LDistribution.EmbeddablePackage := AEmbeddablePackage;
357427
LDistribution.OnZipProgress := FOnZipProgress;
428+
LDistribution.DeleteEmbeddable := FScanner.DeleteEmbeddable;
358429
end);
430+
431+
if PythonVersion.IsEmpty() and (Distributions.Count > 0) then
432+
PythonVersion := TPyEmbeddableDistribution(Distributions.Items[0]).PythonVersion;
359433
end;
360434
inherited;
361435
end;
@@ -373,23 +447,40 @@ procedure TPyEmbeddedEnvironment.TScanner.Scan(
373447
I: Integer;
374448
LPath: string;
375449
LFiles: TArray<string>;
450+
LPythonVersion: string;
451+
LSearchPatter: string;
376452
begin
377453
if not Assigned(ACallback) then
378454
Exit;
379455

380456
if not TDirectory.Exists(FEmbeddablesPath) then
381457
raise Exception.Create('Directory not found.');
382458

383-
for I := Low(PYTHON_KNOWN_VERSIONS) to High(PYTHON_KNOWN_VERSIONS) do begin
384-
LPath := TPath.Combine(FEmbeddablesPath, PYTHON_KNOWN_VERSIONS[I].RegVersion);
385-
if not TDirectory.Exists(LPath) then
386-
Continue;
459+
//Look for version named subfolders
460+
if (FScanRule = TScanRule.srFolder) then begin
461+
LSearchPatter := '*.zip';
462+
for I := Low(PYTHON_KNOWN_VERSIONS) to High(PYTHON_KNOWN_VERSIONS) do begin
463+
LPath := TPath.Combine(FEmbeddablesPath, PYTHON_KNOWN_VERSIONS[I].RegVersion);
464+
if not TDirectory.Exists(LPath) then
465+
Continue;
387466

388-
LFiles := TDirectory.GetFiles(LPath, '*.zip', TSearchOption.soTopDirectoryOnly);
389-
if (Length(LFiles) = 0) then
390-
Continue;
467+
LFiles := TDirectory.GetFiles(LPath, LSearchPatter, TSearchOption.soTopDirectoryOnly);
468+
if (Length(LFiles) = 0) then
469+
Continue;
391470

392-
ACallback(PYTHON_KNOWN_VERSIONS[I], LFiles[0]);
471+
ACallback(PYTHON_KNOWN_VERSIONS[I], LFiles[0]);
472+
end;
473+
end else if (FScanRule = TScanRule.srFileName) then begin
474+
//Look for pattern named files
475+
for I := Low(PYTHON_KNOWN_VERSIONS) to High(PYTHON_KNOWN_VERSIONS) do begin
476+
LPythonVersion := PYTHON_KNOWN_VERSIONS[I].RegVersion;
477+
LSearchPatter := Format('python3-*-%s*.zip', [LPythonVersion]);
478+
LFiles := TDirectory.GetFiles(FEmbeddablesPath, LSearchPatter, TSearchOption.soTopDirectoryOnly);
479+
if (Length(LFiles) = 0) then
480+
Continue;
481+
482+
ACallback(PYTHON_KNOWN_VERSIONS[I], LFiles[0]);
483+
end
393484
end;
394485
end;
395486

0 commit comments

Comments
 (0)