Embarcadero/PythonEnvironments

Mac M1 dylib not found (with workaround)

peardox opened this issue · 54 comments

From my research M1 Macs won't load a dylib unless it's in the DYLD_LIBRARY_PATH path

Without a $DYLD_LIBRARY_PATH setting this happens...

Python Home = /Users/ec2-user/.Lartis/python/3.9
Python exe = /Users/ec2-user/.Lartis/python/3.9/bin/python3.9
DLL Path = /Users/ec2-user/.Lartis/python/3.9/lib
DLL Name = libpython3.9.dylib
Error: Could not open Dll "libpython3.9.dylib"
Python could not be properly initialized. We must quit.

The path etc values are not exposed so I altered PythonEnvironments.pas to WriteLn(stderr, somemessage) to get the above.

Altering my .zshrc to set the $DYLD_LIBRARY_PATH to DllPath (/Users/ec2-user/.Lartis/python/3.9/lib in my test) resolved this issue and everything (nearly...) installed perfectly

This is an issue that could be resolved simply by altering activate.sh to accept an optional parameter to specify the library path for libpython. Actually, it seems a good idea to pass two params, EnvVar to set and value to set it to as this would enable setting it for e.g. Linux by passing LD_LIBRARY_PATH rather than DYLD_LIBRARY_PATH

So...

Invocation would be...
activate.sh DYLD_LIBRARY_PATH "/Users/ec2-user/.Lartis/python/3.9/lib"
In the above example

The 'nearly...' above refers to PSUtil not installing owing to not being able to find python.h in /usr/local/include (as it doesn't exist) - I'm playing with that now...

You said you had M1 working - I fail to see how unless you're not running Monteray and/or have libpython.dylib in the path some other way (easy to do on a dev machine - which is why everything needs clean machine testing...)

Can you clarify this? Are you refering to the activate script? Are you invoking the activate script? Are you refering to the Environment components?

https://github.com/lmbelo/python3-embeddable/blob/7a1f7a236b94ad724916d8e6f6dad6a5f3d81463/python3-macos/build.sh#L79

https://github.com/Embarcadero/PythonEnviroments/blob/151c0252daa9a63ae169659693c7a6a344fdfc09/src/Tools/PyTools.ExecCmd.Args.pas#L67

I don't understand what you're doing, sorry :/
It works pretty perfect on M1.

This is a test on an AWS Mac - first without pre-defining DYLD... and second with DYLD...

https://youtu.be/bm8lDxar224

Looking at that code it should make things work but it doesn't on this M1 unless I pre-define DYLD_LIBRARY_PATH (then it works perfectly)

Can you check that you've not got a pre-defined DYLD_LIBRARY_PATH on the M1 you're using pls (it'd work if it was already set)

Where should I set the DYLD_LIBRARY_PATH?
Describe your issue. You are facing something odd not related to what you're writing.

Narrowed it down with a simpler app

https://github.com/peardox/P4D-Tests runs perfectly on my x64 Mac but won't run on the AWS M1 ARM64

If it runs on x64 mac, how have you assumed this is an issue with DYLD_LIBRARY_PATH?

I observe that manually setting DYLD_LIBRARY_PATH makes Python load on ARM64 Mac as illustrated in the video.

The actual error that crashes everything is in PythonEngine.pas which triggers the Mac System Exception error in the video (which, YouTube says, you didn't look at)

There are more than one possibilities for the problem but as DYLD_LIBRARY_PATH fixed things it seems reasonable to use that as a 'something to trace what happens'

All I can say for certain is that it won't work on a fresh Mac. Have you tried running on a fresh Mac? This is where the problem is visible.

Yes, I tried it on a fresh M1. Have you wondered how Python find its dependencies? How they are set by a standalone executable? What is the setup differences between an embedabble interpreter and the Python executable? Have a look at the docs:
https://docs.python.org/3/c-api/init.html
If you're using the PythonEnvironments with valid paths it will work.

This is annoying, you insist it works and I can't get it to work on a M1

ATM PythonSink works on my x64 but not on the AWS (unless I manually set DYLD_LIBRARY_PATH). I'm currently wiping my x64 to check what happens on a clean x64 as a clean arm64 won't work (at least for me)

As I see it all this depends on DYLD_LIBRARY_PATH being set so the dylib can be loaded by TDynamicDLL.DoOpenDLL(AFile). When is activate.sh executed? I can't find it in the Pascal anywhere... Altering activate and adding a line with something like ...
date > $HOME/python_timestamp
should create a record of when activate.sh was called - on the M1 it's not doing that (so activate.sh isn't being called) - gonna try this on the x64 when it's finished the wipe too...

Have you got a little test repo that works both on x64 and arm64 Macs. Either I'm doing something stupid or something is different about your Mac so a known working version would help debug the problem.

Right - on a CLEAN Mac (i.e. no Python other than OS installed 2.7 and XCode installed 3.x - depends on XCode...) PythonEnvironment consitantly FAILS to work x86 and arm64.

PythonEnvironment worked on my Mac x64 so I wiped the OS and installed a clean system with the bare minimum - not sure if XCode is optional? - installed it to be safe (needed for iOS)

Now neither x64 or arm64 will work without hacking DYLD_LIBRARY_PATH

I note that this code will be executed https://github.com/Embarcadero/python4delphi/blob/master/Source/PythonEngine.pas#L3006 and returns an Invalid Handle to the caller

To properly see this you'll need to either wipe your Mac or (more reasonably) I can let you have an AWS M1 to use for a day (I've got plenty of free AWS credits).

If AWS is how you want to test I'll have to write up a 'how to connect to an AWS Mac GUI' - it's not nearly as simple as it should be. It's not hard but you need to issue some very specific commands and the documentation is all over the place. I'll also need to give you a SSH Key so you can connect to the box (PM on Discord seems easiest)

I modded some source to get some debug output...

Python Home = /Users/simon/Library/PythonSink/python/3.9
Python exe = /Users/simon/Library/PythonSink/python/3.9/bin/python3.9
DLL Path = /Users/simon/Library/PythonSink/python/3.9/lib
DLL Name = libpython3.9.dylib
DYLD_LIBRARY_PATH =
TDynamicDll.OpenDll(libpython3.9.dylib)
TPythonEngine.DoOpenDll - RegVersion = 3.9
TDynamicDll.DoOpenDll = /Users/simon/Library/PythonSink/python/3.9/lib/libpython3.9.dylib
Handle Invalid
Error: Could not open Dll "libpython3.9.dylib"
Python could not be properly initialized. We must quit.

[Then the VM got a false virus report and died a few mins later....]

Can you share a ready-to-go environment? I will try it.

I meant a M1 environment.

As in an AWS M1 - sure, gimme 30 mins to spin one up

All the stuff you need is in a Discord PM

Read the instructions - had problems with the key location - it's gotta be saved in somewhere like c:\users\lmbelo

it's gotta be saved in somewhere like c:\users\lmbelo

So how's Project1.app doing for you on the fresh Mac M1?

@lmbelo
Do you still need the AWS Mac M1? It's sitting there doing nothing ATM.

If you're not using it I'll switch it off later to save my credits (they're free but there are only so many)

I didn't have a chance to try it yesterday. I'm going after it right now.

I'm sorry to spend your credits.

Credits are for spending :) Credits are especially worth spending making sure there are no problems... I've got plenty of them but, fairly obviously, want to make them last as long as I can (as after the free ones run out AWS will want money for any further use).

As long as you're using it I'll leave it on (they have to stay up for 24 hours anyway owing to license, after than I can turn them off etc).

If you work out the solution and want to check against another clean Mac then I can set up another later as well - i.e. you can do what youo want with this one and I can just spin up anotherone after.

I'm unable to deploy and debug projects to that machine :/

Any way, the only reason I see that would prevent it to work is an invalid path or permission issue. Make sure you are sending and setting up the Python environment in a valid path.

I'm unable to deploy and debug projects to that machine :/

Why not?

I'll give it a quick go...

Try to place your Python environment in another folder, it will work.
Have you tried to active Python to your project?

Works for me - until Python starts

image

I put everything in [Home]/Library/[ProjectName]/python and set the path accordingly (this is a sensible location to store things on a Mac - for Windows it'd be c:\Users[User]\AppData\Roaming[ProjectName]\python)

Skia works properly

image

From the size of your Project1 all you've got is a form (no Python) whereas mine is much larger as it's got the Python ZIP embedded in the executable.

I set Python to my project.

You exe is 27M, the Python ZIP is 43M and nowhere to be seen

image

The deployer was failing to send my bundle.

I will give it one last try, ok?

It is still failing.

Brazilian Internet problems or something?

Maybe I can start up a Mac in Brazil somewhere? I'll have to check where AWS mave M1s

Nahh - only AWS locations for M1 are US (Ohio), Asia (Singapore) and Eu (Dublin)

Hmm - I wonder if code signing it with Apple Keys will help?

Gonna try that...

Well, this looks hopeful https://delphiworlds.com/2019/07/notarizing-your-macos-64-bit-apps-built-with-delphi/

Lazarus always just 'worked' - this also explains why my x64 no longer works (not bothered with keys yet)

Well, this looks hopeful https://delphiworlds.com/2019/07/notarizing-your-macos-64-bit-apps-built-with-delphi/

Lazarus always just 'worked' - this also explains why my x64 no longer works (not bothered with keys yet)

How is it helpful?

It allows unmanaged code to run (like libpython3.9.dylib)

If this works (looking likely now) then I'll be able to write up a proper installation instruction set for Windows + Mac at least - not sure about Android as Packages don't work ATM - maybe Linux as well

YouTube, here I come (maybe)

So, after all this it looks like it was me doing something stupid after all :)

You are facing a system integrity issue. You're not able to set DYLD path in your environment variables. You are only able to look for dylibs at "/usr/local/lib" or "/usr/lib". A workaround is to provide the Python libraries in the same manner we are providing for Android or disabling system integrity.

I'm closing the remote connection. Proceed with cleanup and save your credits.

I have an idea to simplify this process. It will work out.

Notarizing my build took me AGES - I've developed for quite a few people so my Apple account is linked to multiple companies. There is no warning that you need to cater for this and the error message Apple return is garbage...

Your Apple ID account is attached to other providers. You will need to specify which provider you intend to submit content to. Please contact us if you have questions or need help. (1627)

After a lot of Googling I needed to add an switch to the Notary command "--asc-provider SomeGarbage1234"

But it signed - I even got an Email telling me I could pass my app around AND...

It still don't work! :(

However, as I'm signing now sticking the dylib in the application directory seems possible (hack p4d time...)

Well, at least I've got Python loading...

image

You're making your own dylibs?

Did you try my suggestion?

Swapped out libpython3.9.dylib with the one from python.org and loaded it from ExeDir using SafeLoadLibrary -- not exactly sure which is the one that made it work - loading lib from ExeDir more likely than SafeLoadLibrary.

Basically, Skia works so crib from Skia until Python works.

Something else occurred to me a few mins ago - Mac has security levels, low, med + high - that may explain some different results on similar hardware previously (as it'a worked before without messing with libraries).

But I have pointed you your issue...

Swapped out libpython3.9.dylib with the one from python.org and loaded it from ExeDir using SafeLoadLibrary -- not exactly sure which is the one that made it work - loading lib from ExeDir more likely than SafeLoadLibrary.

Basically, Skia works so crib from Skia until Python works.

Something else occurred to me a few mins ago - Mac has security levels, low, med + high - that may explain some different results on similar hardware previously (as it'a worked before without messing with libraries).

How SafeLoadLibrary would solve this?

Probably didn't but it was 8AM (up all night, just got up) so not reverted that one yet

The big issue was that the lib wasn't loading on a fresh machine with factory settings. That's pretty essential if you want to release something using P4D to general public.

The Python environment officially provided by them are signed with their certificate, so it is recognized as a safe library.
Skia works because they are putting it within your app, then it will be signed with the same certificate of your app.
Have a look around the hardened runtime and all the stuff along libraries permissions and system integrity.
I think you have enough info to go on :)

Can I close this issue?

Yeah (it'll probably come back at some point though...)

Skia works because they are putting it within your app, then it will be signed with the same certificate of your app.

Tried using your library and Apple gave me a huge error report (long URL above)

Install the Python version officially distributed by them and use the TPyLocalEnvironment with the JSON file settings.

That's a solution - might try it.

It's annoying that write-once deploy-anywhere is such a mess :(

As Windows works with PythonEnvironment you'd need an alternate Python backend for Mac - not ideal but workable I guess.