Skip to content

Commit ce8ec52

Browse files
committed
Creating and unsing symlinks for Python executables on Android
Former-commit-id: 33d5eb4
1 parent f629694 commit ce8ec52

File tree

1 file changed

+63
-26
lines changed

1 file changed

+63
-26
lines changed

src/Embeddable/PyEnvironment.Embeddable.pas

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ TPyCustomEmbeddableDistribution = class(TPyDistribution)
6868
{$IFDEF POSIX}
6969
function FileIsExecutable(const AFilePath: string): boolean;
7070
{$ENDIF POSIX}
71+
procedure CreateSymlink(const ASymlink, ATarget: string);
7172
protected
7273
function EnvironmentExists(): boolean;
7374
/// <summary>
@@ -79,6 +80,7 @@ TPyCustomEmbeddableDistribution = class(TPyDistribution)
7980
/// An embeddable distribution will be used as an "image".
8081
/// </summary>
8182
procedure CreateEnvironment(const ACancelation: ICancelation); virtual;
83+
procedure CreateSymLinks(); virtual;
8284
procedure LoadSettings(const ACancelation: ICancelation); virtual;
8385
protected
8486
function GetEnvironmentPath(): string;
@@ -167,11 +169,12 @@ implementation
167169
System.IOUtils,
168170
System.Character,
169171
System.StrUtils,
172+
System.RegularExpressions,
170173
PyTools.ExecCmd,
171174
PyEnvironment.Exception,
172175
PyEnvironment.Path
173176
{$IFDEF POSIX}
174-
, Posix.SysStat, Posix.Stdlib, Posix.String_, Posix.Errno
177+
, Posix.SysStat, Posix.Stdlib, Posix.String_, Posix.Errno, Posix.Unistd
175178
{$ENDIF}
176179
;
177180

@@ -207,6 +210,52 @@ procedure TPyCustomEmbeddableDistribution.CreateEnvironment(const ACancelation:
207210
end;
208211
end;
209212

213+
procedure TPyCustomEmbeddableDistribution.CreateSymlink(const ASymlink,
214+
ATarget: string);
215+
var
216+
LExistingTarget: string;
217+
begin
218+
LExistingTarget := String.Empty;
219+
if TFile.Exists(ATarget) then begin
220+
//There is a bug with TFile.Exists and TFile.GetSymLinkTarget for Android
221+
//So we recreate it every time
222+
DeleteFile(ASymlink);
223+
224+
if not TFile.CreateSymLink(ASymlink, ATarget) then
225+
raise ESymlinkFailed.CreateFmt('Failed to create the symlink: %s -> %s', [ASymlink, ATarget]);
226+
end;
227+
end;
228+
229+
procedure TPyCustomEmbeddableDistribution.CreateSymLinks;
230+
var
231+
LTargetInterpreter: string;
232+
LTargetLauncher: string;
233+
LSymlinkInterpreter: string;
234+
LSynlinkLauncher: string;
235+
begin
236+
{$IFNDEF ANDROID}
237+
Exit;
238+
{$ENDIF ANDROID}
239+
//Real file names
240+
LTargetInterpreter := TPath.Combine(
241+
TPath.GetLibraryPath(),
242+
String.Format('libpython%s.so', [PythonVersion]));
243+
LTargetLauncher := TPath.Combine(
244+
TPath.GetLibraryPath(),
245+
String.Format('libpythonlauncher%s.so', [PythonVersion]));
246+
//Symlink names
247+
LSymlinkInterpreter := TPath.Combine(
248+
TPath.Combine(GetEnvironmentPath(), 'lib'),
249+
String.Format('libpython%s.so', [PythonVersion]));
250+
LSynlinkLauncher := TPath.Combine(
251+
TPath.Combine(GetEnvironmentPath(), 'bin'),
252+
String.Format('python%s', [PythonVersion]));
253+
//Creates the interpreter symlink
254+
CreateSymlink(LSymlinkInterpreter, LTargetInterpreter);
255+
//Creates the launcher symlink
256+
CreateSymlink(LSynlinkLauncher, LTargetLauncher);
257+
end;
258+
210259
procedure TPyCustomEmbeddableDistribution.DoZipProgressEvt(Sender: TObject; FileName: string;
211260
Header: TZipHeader; Position: Int64);
212261
begin
@@ -247,8 +296,10 @@ function TPyCustomEmbeddableDistribution.FindExecutable: string;
247296
begin
248297
Result := TDirectory.GetFiles(APath, 'python*', TSearchOption.soTopDirectoryOnly,
249298
function(const Path: string; const SearchRec: TSearchRec): boolean
299+
var
300+
LFileName: string;
250301
begin
251-
var LFileName: string := SearchRec.Name;
302+
LFileName := SearchRec.Name;
252303
if LFileName.EndsWith('m') then //3.7 and lower contain a "m" as sufix.
253304
LFileName := LFileName.Remove(Length(LFileName) - 1);
254305

@@ -274,14 +325,6 @@ function TPyCustomEmbeddableDistribution.FindExecutable: string;
274325
Exit(Result)
275326
else
276327
Exit(String.Empty);
277-
{$ELSEIF DEFINED(ANDROID)}
278-
//Let's try it in the library path first
279-
//we should place it in the library path in Android
280-
Result := TPath.GetLibraryPath();
281-
LFiles := DoSearch(Result);
282-
if LFiles <> nil then
283-
Exit(LFiles[Low(LFiles)]);
284-
Result := TPath.Combine(GetEnvironmentPath(), 'bin');
285328
{$ELSE}
286329
Result := TPath.Combine(GetEnvironmentPath(), 'bin');
287330
{$ENDIF}
@@ -303,14 +346,14 @@ function TPyCustomEmbeddableDistribution.FindSharedLibrary: string;
303346
LSearch: string;
304347
begin
305348
LFile := TPath.Combine(APath, ALibName);
306-
if not TFile.Exists(LFile) then begin
307-
LSearch := ALibName.Replace(TPath.GetExtension(ALibName), '') + '*' + TPath.GetExtension(ALibName);
308-
Result := TDirectory.GetFiles(
309-
APath,
310-
LSearch, //Python <= 3.7 might contain a "m" as sufix.
311-
TSearchOption.soTopDirectoryOnly);
312-
end else
313-
Result := [LFile];
349+
if TFile.Exists(LFile) then
350+
Exit(TArray<string>.Create(LFile));
351+
352+
LSearch := ALibName.Replace(TPath.GetExtension(ALibName), '') + '*' + TPath.GetExtension(ALibName);
353+
Result := TDirectory.GetFiles(
354+
APath,
355+
LSearch, //Python <= 3.7 might contain a "m" as sufix.
356+
TSearchOption.soTopDirectoryOnly);
314357
end;
315358

316359
var
@@ -327,14 +370,6 @@ function TPyCustomEmbeddableDistribution.FindSharedLibrary: string;
327370

328371
{$IFDEF MSWINDOWS}
329372
LPath := GetEnvironmentPath();
330-
{$ELSEIF DEFINED(ANDROID)}
331-
//Let's try it in the library path first - we should place it in the library path in Android
332-
LPath := TPath.GetLibraryPath();
333-
LFiles := DoSearch(LLibName, LPath);
334-
if LFiles <> nil then
335-
Exit(LFiles[Low(LFiles)]);
336-
//Try to find it in the environment folder
337-
LPath := TPath.Combine(GetEnvironmentPath(), 'lib');
338373
{$ELSEIF DEFINED(MACOS)}
339374
//Let's try it in the library path first
340375
LPath := TPyEnvironmentPath.ResolvePath(TPyEnvironmentPath.ENVIRONMENT_PATH);
@@ -385,6 +420,8 @@ function TPyCustomEmbeddableDistribution.Setup(const ACancelation: ICancelation)
385420
CreateEnvironment(ACancelation);
386421
end;
387422

423+
CreateSymLinks();
424+
388425
LoadSettings(ACancelation);
389426

390427
Result := true;

0 commit comments

Comments
 (0)