Ongoing notes about my experience.
Bottom line: don’t use Pyinstaller on OSX with PySide yet (but you can use it with OSX and PyQt, or Windows and PySide?) As of this writing, Pyinstaller suffers bugs with PySide on OSX that it doesn’t suffer with PyQt. Specifically, because PySide installs itself in /usr/lib on OSX, Pyinstaller fails to find, or finds too many, dependencies.
You’ve written an app in Python language using PySide bindings which uses Qt framework. You develop on a Linux machine. You want to port to other desktop platforms, including Max OSX.
Pyinstaller is a tool that bundles your app. (There are alternative packaging tools like py2app, py2exe, macdeployqt, freeze, pyside-assistant etc. but AFAIK Pyinstaller has traction and covers more platforms.)
Bundles means puts everything needed into one file. Also called packaging. Note the difference in philosophy on different platforms: on Linux, a package does NOT include everything, but knows what other packages it depends on, and downloads and installs them as necessary. On OSX, and Windows, an app bundle (package) is totally self contained (except for standard platform, system libraries?), and won’t download and install other packages.
Pyinstaller for Linux is strange in that it uses the non-Linux philosophy: it bundles everything into one package, having no dependencies. It doesn’t make it any easier for users, since they don’t care which philosophy is used (either SHOULD work reliably.) But Pyinstaller makes it easier for the developer, because they don’t need to worry about the two philosophies on different platforms.
Pyinstaller is not a cross packager: you must be on the OS for which you are packaging, and the development environment must include all the libraries and Python packages that your app needs. (It can be a virtual machine. For example, you could be on a Linux box running Windows in a VirtualBox. But your Windows virtual machine must have all the libraries and Python packages installed that your app needs.) Pyinstaller will find those packages and bundle them up; the user will not need to recreate your steps in setting up the development environment.
Using Pyinstaller in the primary development environment
If you are developing on Linux (and your program is working) then presumably everything you need is installed. You should be able to install and use Pyinstaller.
Except! If you are using an IDE (integrated development environment) like PyDev plugin in Eclipse IDE. In that case, the IDE may play tricks with the PYTHON_PATH to help your app find dependent packages. For example, my app used other Python packages that I had written that were separate linked projects in Eclipse. Pyinstaller was unable to find them. So I needed to package up (Python packaging: > python setup.py sdist) those projects and install them (>python setup.py install) on my machine in the standard place. In other words, make your machine look like a user’s machine without an IDE who was figuring out the dependencies themselves, and downloading Python packages in order.
Then Pyinstaller worked fine.
Moving to OSX
On another platform, you first need to reconstruct your primary development environment. For example, download and install:
- (Python comes with OSX)
- your own dependent Python packages (that you wrote)
- any other Python packages not included with OSX
- Pyinstaller (the very latest, dev version since bugs for OSX are more likely fixed there. I encountered a Pyinstaller bug with “qt_menu.nib not found” that was fixed in the dev version of Pyinstaller)
Then Pyinstaller SHOULD work (and it seems to for me, the jury is still out.)
Pyinstaller works but your app may not. Especially for apps that use low-level details of the framework, like handling events in the event loop, your app may not work on other platforms.
Crashes on OSX
My app crashes with a seg fault.
A dialog appears titled “Problem Report for myApp”. It says it quit unexpectedly (user friendly terms for crashed.) Choose the “Details” button. It will show you a stack trace. Note the app is multithreaded.
My program also gets many warnings on the console: “..you may have two sets of Qt binaries..”
Enabling more meaningful stack trace from Python
Your stack trace likely includes many calls inside the Python interpreter. They won’t be very meaningful unless you are using a Python interpreter built for debugging. How to build a debugging version of Python is documented on the web. But I decided to go back to basics.
“Hello world” is a tiny program used for teaching and testing. It is a minimal test. When you report a bug, or are exploring a bug, you often reduce your program to the smallest program that exhibits the buggy behaviour. I hacked together this hello world:
import sys from PySide import QtCore, QtGui from PySide.QtGui import QApplication, QMessageBox def main(): a = QApplication(sys.argv) m = QMessageBox(QMessageBox.Information, "Hello", "World") m.exec_() if __name__=="__main__": main()
It opens a dialog box, using Qt and PySide.
This program does not crash or give warnings about “two copies”. However, I found that if you omit the second import, it does crash when you exit the program. This suggests that Pyinstaller is not quite figuring out the dependencies reliably.
At this point, I decided that the OSX platform is not receiving all the love it should:
- from Apple for not making it easier for open source developers
- from PySide for not installing itself so that Pyinstaller could determine dependencies as on other platforms
- from Pyinstaller for not hard-coding exceptions on the OSX platform for its dependency determination algorithm
I decided to move on to test Pyinstaller and PySide on a more popular platform (Windows.) In other words, pick low hanging fruit, least effort for the most reward.
To install gdb on OSX
If you can’t see your problem in the stack trace, you may need a debugger, like gdb, to examine the stack trace.
- get an Apple ID so you can use the App Store
- install Xcode (the official Apple IDE) free, in the store
- start Xcode and choose “Xcode>Preferences>Downloads”, and choose the “Install” button next to “Command Line Tools”
(gdb is apparently not part of OSX, and not part of Xcode until you perform this secondary install step from within Xcode.)
(I started to download gdb from the gnu site, and quickly ran into dependency hell: .configure told me gcc was not installed. I figure the Apple Xcode package is best.)
(You DON’T want to learn Xcode. The learning curve is steep. There is a reason you are writing in Python. You want to think about objects and not the tools. You DO want to learn Xcode. It is pretty and comprehensive. The Objective-C language derives from an object-oriented, high-level language SmallTalk but also derives from low-level C. It supports the mobile platform iOS.)
To be continued…
You should test your bundle on a clean machine. It may work on your development environment only because your app finds packages there that were not properly bundled. Another place where a virtual machine is handy.
Pyinstaller only creates a single executable (or a directory), but does not create an archive suitable for distribution (compressed, signed, with the proper suffix, etc.) through official, one-click-to-install, channels. (You could distribute the single executable to trusting and computer knowledgeable users.)