Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Apple M1 #322

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

kara-todd
Copy link

closes #297

@kara-todd
Copy link
Author

@touilleMan I tried to follow the steps you outlined in #297 ... but please let me know if I've made a mistake 😅

@kara-todd
Copy link
Author

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.

@touilleMan
Copy link
Owner

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)

@kara-todd
Copy link
Author

kara-todd commented Jan 16, 2022

[...] 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" ?

@touilleMan
Copy link
Owner

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 ;-)

@kara-todd
Copy link
Author

@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 🙃

@gabbydelforge
Copy link

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.

@jakzie2
Copy link

jakzie2 commented May 24, 2022

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.

@touilleMan
Copy link
Owner

Godot4 introduce heavy changes, so I'm currently doing a big rewrite of Godot-Python to support it 😄

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 ?

@kara-todd
Copy link
Author

@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.

@kara-todd
Copy link
Author

@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.

  1. 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?
  2. 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

@touilleMan
Copy link
Owner

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 )

@kara-todd
Copy link
Author

kara-todd commented Sep 20, 2022

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 ?

@touilleMan
Copy link
Owner

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 ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Don't work on mac with chip m1
4 participants