godot-python
godot-python copied to clipboard
Add support for Apple M1
closes #297
@touilleMan I tried to follow the steps you outlined in #297 ... but please let me know if I've made a mistake 😅
I have been trying to build this on my m1 mac and I am running into some issues :(
The full error is below... but I think the issue is here building for macOS-x86_64 but attempting to link with file built for macOS-arm64
I don't understand why it's trying to build for macOS-x86_64
did I fail to set this somewhere?
(venv) bash-3.2$ scons platform=osx-aarch64 CC=clang release
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
clang -o build/osx-aarch64/pythonscript/libpythonscript.dylib -m64 -L/Users/username/godot-python/build/osx-aarch64/platforms/osx-aarch64/cpython_build/lib -Wl,-rpath,'@loader_path/lib' -install_name @rpath/libpythonscript.dylib -dynamiclib build/osx-aarch64/pythonscript/pythonscript.os -lpython3.9
ld: warning: ignoring file /Users/username/godot-python/build/osx-aarch64/platforms/osx-aarch64/cpython_build/lib/libpython3.9.dylib, building for macOS-x86_64 but attempting to link with file built for macOS-arm64
Undefined symbols for architecture x86_64:
"_PyCapsule_GetName", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyCapsule_GetPointer", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyCapsule_IsValid", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyDict_GetItemString", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyErr_Format", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyEval_InitThreads", referenced from:
_godot_gdnative_init in pythonscript.os
"_PyEval_RestoreThread", referenced from:
_godot_gdnative_terminate in pythonscript.os
"_PyEval_SaveThread", referenced from:
_godot_gdnative_init in pythonscript.os
"_PyExc_ImportError", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyExc_TypeError", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyImport_ImportModule", referenced from:
_godot_gdnative_init in pythonscript.os
"_PyModule_GetName", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_PyObject_GetAttrString", referenced from:
___Pyx_ImportFunction in pythonscript.os
"_Py_FinalizeEx", referenced from:
_godot_gdnative_terminate in pythonscript.os
"_Py_InitializeEx", referenced from:
_godot_gdnative_init in pythonscript.os
"_Py_SetProgramName", referenced from:
_godot_gdnative_init in pythonscript.os
"_Py_SetPythonHome", referenced from:
_godot_gdnative_init in pythonscript.os
"__Py_Dealloc", referenced from:
_godot_gdnative_init in pythonscript.os
___Pyx_ImportFunction in pythonscript.os
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
scons: *** [build/osx-aarch64/pythonscript/libpythonscript.dylib] Error 1
scons: building terminated because of errors.
I don't know anything about macos compilation, but I guess your issue is you have to pass the correct flag to clang to tell him the platform you want to build for (normally the compiler works for a single target, but I wouldn't be surprised if both aarch64 and x86_64 are available to support the generation of "fat binary" containing both code)
[...] I guess your issue is you have to pass the correct flag to clang to tell him the platform you want to build for [...] both aarch64 and x86_64 are available to support the generation of "fat binary" [...]
I found this answer on (stack overflow)
You need to compile with
clang -arch arm64 -arch x86_64
to get a fat binary out of clang. You need to do this for Apple clang as well.
How can I set these flags? I wasn't sure how to pass -arch
is it a CFLAG
? Should I just add it in the SConscript file?
I tried adding env.AppendUnique(CFLAGS=["-arch arm64"])
but this didn't seem to work... maybe I need to add arm64
and x86_64
, or perhaps I set the flag incorrectly... or both. :)
Edit:
My version is:
(venv) bash-3.2$ clang --version
Apple clang version 13.0.0 (clang-1300.0.29.30)
Target: arm64-apple-darwin21.2.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
So it looks like it is "Apple clang" ?
I tried adding env.AppendUnique(CFLAGS=["-arch arm64"]) but this didn't seem to work... maybe I need to add arm64 and x86_64, or perhaps I set the flag incorrectly... or both. :)
That's what I would do... maybe the space in your value is causing issue, can you try with -arch=arm64
?
I suggest you to have a look into [Godot build system] that should set the flag correctly ;-)
@touilleMan Thanks for the help.
I referenced the godot mac build config and added a few flags based on that setup. Godot makes a universal binary that supports either arm or x86 builds. However, I have no idea if that's possible with the python prebuilds...
For now, I just let the target be specified by setting an additional arch=
flag similar to the Godot compiling for mac docs
Running scons platform=osx arch=arm64 CC=clang release
on my m1 laptop now results in a successful build... with a few warnings 🙃
hey @kara-todd, have you been able to add Python scripts successfully with this build workflow?
i'm trying to test this locally but not seeing Python as an option when creating a new script. the build seems to complete successfully, but the godot output executable isn't runnable. i've tried using godot-binary
to point to an existing godot version with no luck there either. also, it seems like both scons test
and scons example
fail for me.
Hi @kara-todd, I'd also want to ask how is the developement of this support going, it would be nice to have it in the released version. I was trying to run this version on M1 laptop but ran into the same issue as @gabbydelforge here. Build runs without issues, but then Godot doesn't recognize Python scripts and scons test
fails. Even running ensurepip
, which I had to do on linux version, didn't help.
If you managed to build this properly, it would be nice to know how.
Godot4 introduce heavy changes, so I'm currently doing a big rewrite of Godot-Python to support it :smile:
I don't plan on releasing any new version of Godot-Python for Godot 3, so I can merge this PR as-is if you want, but won't do any support bug correction on my side (and the changes will need to be redo for the new Godot-Python for Godot4 in the future)
what do you think ?
@touilleMan Sorry. I was never able to fully test this. I got the build working using the command I posted, but I wasn't able to confirm that it worked correctly in Godot. I'm not able to circle back around and work through this right now. Based on what @jakzie2 @gabbydelforge are seeing... I'm not sure if the PR is good enough shape to merge.
@touilleMan I'm trying to pick this back up again. :)
I got a little farther this time... but there are a few things I'm not certain about.
- Godot uses a "universal binary" format for Mac OS in versions greater than 3.3.1 ... it seems like cpython only supports targeting a single platform either arm or x86. Will this still work at all only targeting arm as I have?
- I compiled... something :) However... I'm not sure it's working correctly. When I run
scons platform=osx-arm test
I'm getting the following error:
scons: Reading SConscript files ...
Building for macOS 10.15+, platform arm64.
scons: done reading SConscript files.
scons: Building targets ...
/godot-python/build/osx-arm/platforms/Godot_v3.3.1-stable_osx.universal --path tests/bindings
arguments
0: /godot-python/build/osx-arm/platforms/Godot_v3.3.1-stable_osx.universal
1: --path
2: tests/bindings
Current path: /godot-python
Godot Engine v3.3.1.stable.official - https://godotengine.org
OpenGL ES 3.0 Renderer: Apple M1 Max
OpenGL ES Batching: ON
UNSUPPORTED (log once): POSSIBLE ISSUE: unit 1 GLD_TEXTURE_INDEX_2D is unloadable and bound to sampler type (Float) - using zero texture because texture unloadable
ERROR: open_dynamic_library: Can't open dynamic library: /godot-python/build/osx-arm/platforms/../Frameworks/libpythonscript.dylib, error: dlopen(/godot-python/build/osx-arm/platforms/../Frameworks/libpythonscript.dylib, 0x0002): tried: '/godot-python/build/osx-arm/platforms/../Frameworks/libpythonscript.dylib' (no such file).
At: platform/osx/os_osx.mm:1941.
Registered camera FaceTime HD Camera with id 1 position 0 at index 0
ERROR: _load: No loader found for resource: res://main.py.
At: core/io/resource_loader.cpp:290.
ERROR: poll: res://main.tscn:3 - Parse Error: [ext_resource] referenced nonexistent resource at: res://main.py
At: scene/resources/resource_format_text.cpp:440.
ERROR: load: Failed to load resource 'res://main.tscn'.
At: core/io/resource_loader.cpp:212.
ERROR: _load: Failed loading resource: res://main.tscn. Make sure resources have been imported by opening the project in the editor at least once.
At: core/io/resource_loader.cpp:283.
ERROR: start: Failed loading scene: res://main.tscn
At: main/main.cpp:1961.
I know you are busy working on Godot 4 updates... but is there any guidance you could provide here? I think some of the community would still appreciate the Godot 3 support for osx arm
The line
ERROR: open_dynamic_library: [...]
Seems to indicate your dylib file is in the wrong path (I guess this is due to the framework folder being added in the path by something... I seem to recall this framework folder is somewhat a standard thing in macos so maybe the compiler add it to the rpath automatically....)
Regarding the universal binary thing, I have absolutely no idea how this black magic works ^^ As you say, python build standalone only provides per arch release (https://github.com/indygreg/python-build-standalone/releases/tag/20220802) But I guess we could compile pythonscript.c as universal binary, then have it load the correct libpython.so according to the current arch being executed (however I have no idea how to do that, I would guess you should look for the rpath and linking options )
But I guess we could compile pythonscript.c as universal binary, [...]
Godot and apple both recommend the approach of compiling for each architecture and then using the lipo
command to ... glue them together I guess. :) Black magic as you say 😂
For example, Godot says to do something like:
$ scons platform=osx arch=x86_64
$ scons platform=osx arch=arm64
$ lipo -create bin/godot.osx.tools.x86_64 bin/godot.osx.tools.arm64 -output bin/godot.osx.tools.universal
Can we do something similar here? I am not exactly sure what the lipo
command is doing... since a dylib
is being created will this still work? It seems like python "and commonly used packages" are being pulled together here... I wasn't sure if this was a simple "binary" that could use this style of execution or not...
edit
Actually... it looks like potentially multiple arch
flags can be used to create a "fat" binary... So something like CFLAGS="-arch arm64 -arch x86_64" LINKFLAGS="-arch arm64 -arch x86_64"
? However, I'm still not quite clear on how this relates to the python-build-standalone
downloads... Would I need to download each version and somehow specify them ?
Godot and apple both recommend the approach of compiling for each architecture and then using the lipo command to ... glue them together I guess. :) Black magic as you say 😂
That's interesting !
Can we do something similar here?
To work, we must ensure it is possible to glue together two dylib.
Then we should glue all the dylib in the project: pythonscript.so of course (the main entry point written in C), but also all the cython based libraries and all the libraries in the python distribution (so at least libpython.dylib)
And then we should ensure the whole python distribution is the same between the two architectures (i.e. only the libraries we should glued together differs, so we can safely keep just one of the two distribution with the patched libraries)
Actually... it looks like potentially multiple arch flags can be used to create a "fat" binary...
No mater what, python-build-standalone doesn't support universal binary for now (we may want to open an issue on their repo btw), so I think the first step is to try to create an universal python distribution by gluing two python-build-standalone distribs.
If we can do that, everything will come together easily enough I guess ;-)