Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix dylib linking errors in an osx app generated with py2app? [duplicate]

I'm bundling a pygame app using py2app. The bundling works and the resulting bundle runs on my mac just fine. It also used to run on another person's mac just fine. However, recently I've started getting this error (from the console) when trying to run the bundle on his computer:

Traceback (most recent call last):
  File "/Users/.../tmp/withconsole.app/Contents/Resources/__boot__.py", line 316, in <module>
    _run()
  File "/Users/.../tmp/withconsole.app/Contents/Resources/__boot__.py", line 311, in _run
    exec(compile(source, path, 'exec'), globals(), globals())
  File "/Users/.../tmp/withconsole.app/Contents/Resources/withconsole.py", line 18, in <module>
pdb.set_trace()
  File "bdb.pyc", line 53, in trace_dispatch
  File "bdb.pyc", line 88, in dispatch_return
  File "pdb.pyc", line 190, in user_return
  File "pdb.pyc", line 210, in interaction
  File "cmd.pyc", line 142, in cmdloop
  File "pdb.pyc", line 279, in onecmd
  File "cmd.pyc", line 218, in onecmd
  File "pygame/macosx.pyc", line 10, in <module>
  File "pygame/sdlmain_osx.pyc", line 14, in <module>
  File "pygame/sdlmain_osx.pyc", line 10, in __load
ImportError: dlopen(/Users/.../tmp/withconsole.app/Contents/Resources/lib/python2.7/lib-dynload/pygame/sdlmain_osx.so, 2): Symbol not found: _OBJC_CLASS_$_NSObject
  Referenced from: /Users/.../tmp/withconsole.app/Contents/Resources/lib/python2.7/lib-dynload/pygame/sdlmain_osx.so
  Expected in: /usr/lib/libobjc.A.dylib
 in /Users/.../tmp/withconsole.app/Contents/Resources/lib/python2.7/lib-dynload/pygame/sdlmain_osx.so
2013-11-09 06:19:50.794 withconsole[2797:1c03] ogclient Error

I even tried running an older bundle that used to work, yet now it no longer works! I'm 95% certain of that, anyway.

In any case, what's the issue and how can I fix it?

like image 856
Claudiu Avatar asked Nov 20 '22 23:11

Claudiu


1 Answers

A standalone app from py2app creates its baked-in Python interpreter from whichever version of Python you use for py2app.

If you use, say, a Python built to run on any OS X 10.6 or later, the application (so long as it doesn't use any post-10.6 features) will also run on any OS X 10.6 or later. If you use a Python built to run on one particular 10.8 machine, the application will very likely not work on anything earlier than 10.8, and may not even work on other 10.8+ machines.

On top of that, if you're using any C extension modules that you built against C libraries that you built yourself, if those C libraries were built for your particular machine, they'll probably cause the same problem.

You built your Python interpreter with Homebrew. And maybe your SDL as well, which pygame depends on. And possibly other things. By default, Homebrew builds everything to run on your particular machine, not to be redistributable. Hence the problem.

To build an app that can run on 10.7 machines, you need to either build Python, SDL, etc. against the 10.7 SDK, or build it against a later API and specify -mmacosx-version-min=10.7. (There are a few other issues that can come up, but I'm pretty sure none of them affect CPython, so let's keep it simple.)

That will automatically set things up so any C extension modules you build use the same SDK and version settings, so you shouldn't have to worry about those. But you do have to worry about any C libraries you built that those extension modules depend on. For example, pygame links against libSDL, and if you built SDL for your local machine only, pygame isn't going to work on someone else's machine.

It's possible to get Homebrew to create SDK builds, but not always that easy. It's easier to just build Python manually and pass the right --configure flags. But it's even easier to take a binary that someone else has already built for portability and use that.

The official Python installers at python.org are built for 10.6+. The official SDL development libraries are built for 10.5+. Hopefully the same is true for other things you're depending on. If so, just brew uninstall (or, if you're wary and want to be able to undo this later, brew unlink) everything, run the binary installers, reinstall all the Python modules you need for the new Python, and py2app with it.


I wrote this all in general terms. From the output, it looks like the specific problem you're hitting on this particular run on your friend's particular 10.7 machine is that the SDL wrappers are depending on features of the ObjC runtime that didn't exist in 10.7. So, it's possible that just using a 10.7-friendly SDL, and your existing Python, would be sufficient. But I think you're better off using 10.7-friendly everything if at all possible.


If you're wondering what exactly the SDK does, the SDK Compatibility Guide in Apple's official docs explain the technical side of it pretty well, and there are a zillion blog posts like this one that explain the practical parts, but I'll try to summarize.

Each SDK is just a directory that has its own /usr/include, /usr/lib, /System/Library/Frameworks, and a few other things inside. When you build against an SDK, you end up compiling against its header files instead of your system's, and linking against its dylibs instead of your system's. This prevents you from using any new features added since 10.6 (with a nice compiler error, instead of a successful build that then won't start on half your users' machines). And it makes sure that, if you don't use any such new features, your program doesn't have load-time dependencies on anything that didn't exist in 10.6.

like image 109
abarnert Avatar answered Nov 24 '22 22:11

abarnert