@@ -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
201217function 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}
207251begin
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;
243274end ;
244275
245276function 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+
246294var
247295 I: integer;
248296 LLibName: string;
249297 LPath: string;
250- LSearch: string;
251298 LFiles: TArray<string>;
252299begin
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+
314365procedure TPyEmbeddableDistribution.LoadSettings ;
315366begin
316367 if FScanned then
317368 inherited ;
318369end ;
319370
371+ procedure TPyEmbeddableDistribution.Setup ;
372+ begin
373+ inherited ;
374+ if FDeleteEmbeddable and EmbeddableExists() then
375+ DoDeleteEmbeddable();
376+ end ;
377+
320378{ TPyEmbeddedEnvironment }
321379
322380constructor TPyEmbeddedEnvironment.Create(AOwner: TComponent);
323381begin
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;
326389end ;
327390
328391destructor TPyEmbeddedEnvironment.Destroy;
@@ -337,10 +400,17 @@ function TPyEmbeddedEnvironment.CreateCollection: TPyDistributionCollection;
337400end ;
338401
339402procedure TPyEmbeddedEnvironment.Prepare ;
340- var
403+ var
341404 LDistribution: TPyEmbeddableDistribution;
342405begin
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 ;
361435end ;
@@ -373,23 +447,40 @@ procedure TPyEmbeddedEnvironment.TScanner.Scan(
373447 I: Integer;
374448 LPath: string;
375449 LFiles: TArray<string>;
450+ LPythonVersion: string;
451+ LSearchPatter: string;
376452begin
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 ;
394485end ;
395486
0 commit comments