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
PyInstaller and Apple Silicon? #5315
Comments
I doubt it will make any difference to us. At worst, it may require a tweak or two to the bootloader but I wouldn't expect any of the Python components to change. |
Oh nice! That's good to hear, thanks :) |
I wanted to build arm64 target build on Catalina with Pyinstaller 3.6 and Python 2.7.18. XCode12 is installed on Machine. What changes would be needed to get this. |
@harshithdwivedi @bwoodsend can this be re-opened? Python 3.9.1 has experimental support for Apple's Universal2 and arm64 but it doesn't seem like it works with PyInstaller. The PyInstaller executable compiles but it runs as x86_64 emulated code. Simple test script:
Output with Python 3.9.1 calling test script:
Output with PyInstaller 4.1 and Python 3.9.1 compiling test script to an executable:
Reading through https://eclecticlight.co/2020/07/20/how-to-tell-intel-code-from-universal/ and I noticed that even with the Python 3.9.1 Universal2 install PyInstaller binaries still have the x86_64 |
Does Python have separate binaries for Apple arm64 then? In which case we probably need to follow and provide arm64 bootloaders. |
No, MacOS 11.0 introduces a new Universal2 executable file type which contains both x86_64 and arm64 native code. That way the vendor can ship one file, the user can install it on their Mac device without needing to know if it's Intel or Apple based and the binary gets native performance. The new M1 chips will run older x86_64 binaries but performance will suffer because it's using emulation. Apple also has some information on the Universal2 format: https://developer.apple.com/documentation/xcode/building_a_universal_macos_binary |
Thanks @jay0lee, that's useful stuff. I notice right at the bottom it says:
which I take to mean that it assumes x86_64 unless the executable is arm64 or this LSArchitecturePriority says use arm64. At a wild stretch, possibly we can set this flag and leave the bootloader be. The bootloader is just a shim and shouldn't have any noticeable performance implications itself if we can persuade Big Sur that the rest of the app is arm64-safe. More realistically though that probably won't work and we'll just have to build these universal bootloaders. This'll probably be more complicated than it should be because our build process is so ridiculous... |
Out of curiosity, how do PyPI and pip handle this in wheels that contain shared libraries (like PyQt5, for example)? I'd be surprised if all of those immediately switched to Universal2 format, so what happens at the moment? Do you always get x86_64 version of the package? Do you get x86_64 version on macOS running on Intel, and error on arm64 version of macOS? If there's a chance of ending up with x86_64-only shared libraries collected from python packages, then I guess we can never be sure that we're arm64-safe, and emulation is probably the only way to go... |
Looking at NumPy 1.19.5 which released yesterday, they're all still |
I've managed to build what I think is a hybrid boot loader. It's here. Github won't let be upload For the record, here's how to build from source. It was easier to circumvent our waf script than modify it. The build commands I used are (all ran from the
mkdir -p build
clang -arch x86_64 -mmacos-version-min=10.7 `ls src/*.c` -o build/run-x86_64 -lm -lz
clang -isysroot /Applications/Xcode2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk -arch arm64 -mmacos-version-min=11.0 -o build/run-arm64 `ls src/*.c` -lz -lc
lipo -create -o ../PyInstaller/bootloader/Darwin-64bit/run build/run-arm64 build/run-x86_64 You can sanity check the architectures using: $ lipo -archs ../PyInstaller/bootloader/Darwin-64bit/run
x86_64 arm64 |
When using the linked wheel, I get the following unexpected error when running a frozen program on the machine that frooze the program: [44067] Error loading Python lib '/Users/user/test_pyinstaller/dist-10.14.6-4.2.dev0/test_platform_machine/Python': dlopen: dlopen(/Users/user/test_pyinstaller/dist-10.14.6-4.2.dev0/test_platform_machine/Python, 10): no suitable image found. Did find:
/Users/user/test_pyinstaller/dist-10.14.6-4.2.dev0/test_platform_machine/Python: code signature invalid for '/Users/user/test_pyinstaller/dist-10.14.6-4.2.dev0/test_platform_machine/Python' I do not get such an error when using the current 4.2 release from pypi. I used this command to freeze the program: pyinstaller \
-y \
--clean \
--log-level ERROR \
--distpath "dist-$(sw_vers -productVersion)-$(pyinstaller -v)" \
--onedir \
--noupx \
test_platform_machine.py This is the program: #5494 (comment) |
That's because 4.2 automatically strips the invalid signature from python library. The linked wheel was probably created before #5451 was merged. So you'll need to strip the signature yourself:
|
Can confirm that removing the signature works. |
@papr Good to hear. Can you try freezing: import platform
print(platform.machine()) And check that it says |
That was my thought as well as confirming that same binary will run on an x86_64 Mac as well. |
I can confirm that freezing import platform
print(platform.machine()) on 🥳 🍾 🎉 |
Right then. I believe our build script will need some surgery [groan]. And the vagrant file will need to be updated to install macOS 11 SDKs (I've no idea how to do this either). |
Would it be possible to get a more recent build of the hybrid boot loader? I tried to build a
(even after removing the Python signature) Since this might be related to #5315 (comment), I thought I might give it try with a newer version before investigating further. |
Here you go. It's the 4.2 release but with the hybrid bootloader. Not entirely sure it'll fix your problem. |
@bwoodsend Then it does not contain #5451, correct? I will give it a try, nonetheless, thank you! |
4.2 contained fix #5451 as called out in the changelog:
https://pyinstaller.readthedocs.io/en/v4.2/CHANGES.html
…On Mon, Jan 25, 2021, 7:42 AM Pablo Prietz ***@***.***> wrote:
@bwoodsend <https://github.com/bwoodsend> Then it does not contain #5451
<#5451>, correct? I will
give it a try, nonetheless, thank you!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#5315 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABDIZMCHUG2KTB3E5PSYF3LS3VRLNANCNFSM4TRUQFEQ>
.
|
@jay0lee thank you for the note! import platform
print(platform.machine())
Freezing and signing command details
$ pyinstaller \ -y \ --clean \ --log-level ERROR \ --distpath "dist-$(sw_vers -productVersion)-$(pyinstaller -v)" \ --onedir \ --noupx \ test_platform_machine.py $ codesign \ --all-architectures \ --force \ --verify \ --verbose \ -s "$sign" \ --deep \ dist-10.14.6-4.2/test_platform_machine/test_platform_machine
Freezing and signing OS: macOS 10.14.6 $ lipo -archs dist-10.14.6-4.2/test_platform_machine/test_platform_machine x86_64 arm64 |
As for the wheels (provided we will try to get them out again with the next release), I suppose even if we fumble the tags for macOS and they end up undiscoverable on |
Not be default they won't - unless we add them to the |
Ohhhh, right, only Windows ones are in there at the moment... |
It'll take a bit of catching up for any packages you use to switch to universal2 too (assuming they ever do - I'd be surprised if something as huge as tensorflow are willing to double that size by using fat binaries). |
At any rate, maybe this should go into separate issue, as this one will be closed once I merge the PR. |
For reference:
|
@Safihre I'm very interested in this solution, but I don't quite follow the steps. Would you mind writing this up a little clearer? I have the following questions: |
Ooh very nice.
Why not just use:
GA is Github Actions - Github's continuous integration platform.
I am very, very surprised by this. |
What does this do compared to my code? It includes the recompile of the bootloader? Would make it a lot easier :)
You were right, the |
It does exactly the same thing - it's just a bit less code... |
But I still need the whole copy of the source files to compile the bootloader, right? So I still need all the extra code for that? |
Those extra source files are contained in the sdist (pyinstaller-4.7.tar.gz) which is what pip downloads if you specify |
But will it rebuild the bootloader? Considering the sdist contains the pre-built macOS (and Windows) bootloaders as well? |
Ohhh, you're right. I forgot that those bootloaders were still lurking about in there. @Safihre Ignore everything I said above. |
What if we added a switch (controlled by environment variable) that would force rebuild of the bootloader, even if they are already present? |
I was hoping for something like that! |
Sounds like a good idea. |
Any update? |
It's already part of PyInstaller, we happily use it!
|
Just a FYI: I've finally got actions/python-versions#114 merged so the Python installations provided by GitHub Actions will become |
You need to chmod the file to be ran as an executable
|
bro u da best, thank you so much |
Apple just unveiled their M1 chip yesterday and that got me thinking about the impact it will have on PyInstaller and the apps packaged with it.
Will this require a complete rewrite of the way PyInstaller functions or there's an alternative to it.
The text was updated successfully, but these errors were encountered: