Bug 13119

Summary: pycairo links with all symbols marked private in _cairo.so on Mac OS X 10.5
Product: pycairo Reporter: Michael Dales <mwd>
Component: generalAssignee: Steve Chaplin <d74n5pohf9>
Status: RESOLVED NOTOURBUG QA Contact:
Severity: critical    
Priority: high CC: dmacks
Version: unspecified   
Hardware: x86 (IA32)   
OS: Mac OS X (All)   
Whiteboard:
i915 platform: i915 features:

Description Michael Dales 2007-11-07 05:11:36 UTC
I've just been trying to build "pycairo":http://cairographics.org/pycairo/, the Python wrappers for the Cairo graphics library on Mac OS X 10.5 Leopard. Unfortunately, the 1.4.0 tarball for pycairo doesn't work out the box. It'll compile, but when you try and use it you get the following error:

>>> import cairo
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Library/Python/2.5/site-packages/cairo/__init__.py", line 1, in <module>
    from _cairo import *
ImportError: dlopen(/Library/Python/2.5/site-packages/cairo/_cairo.so, 2): Symbol not found: _PycairoContext_FromContext
  Referenced from: /Library/Python/2.5/site-packages/cairo/_cairo.so
  Expected in: flat namespace

The problem is that as part of the build process all but one symbol in the _cairo.so file are made private using nmedit. To stop the nmedit stage from working I changed the cairo/Makefile.in file, making like 207 look like this:

<pre>_cairo_la_LDFLAGS = -module -avoid-version</pre>

Essentially removing the "-export-symbols-regex init_cairo" that's in the file originally. Then the symbols are still active and things should work. Now everything should be okay to use the cairo library under Python.

I assume my solution isn't correct, but at least gets me working again. I'm unfamiliar with nmedit and the symbol list generation, so I thought I'd file it for someone else to take from here. 

Please do get in touch if you need more information.
Comment 1 Steve Chaplin 2007-11-07 20:07:32 UTC
You can make the change you suggest by editing Makefile.am (instead of
Makefile.in) before configure is run.

You say "The problem is that as part of the build process all but one symbol
in the _cairo.so file are made private using nmedit."

I don't agree that this is the problem, in fact, from my understanding of
Python extensions, all symbols (except for 'init_cairo') are intended to be
private, and a python C extension should have only one public symbol - the
initialise module function - to help to avoid symbol name clashes with other
extension modules.

Some more information is described here:
  http://docs.python.org/ext/using-cobjects.html

I think the problem is "Why is your system trying to access private symbols".

I think, but don't know for sure, that other people have successfully ran
pycairo on Mac OS X.

I know that the pygtk 2.10.5 Python module uses 'export-symbols-regex' and a
similar approach as pycairo, and seems to work OK.

I don't use Mac OS X so can't investigate the problem, but I don't
think removing 'export-symbols-regex' is correct.

You could try installing using the setup.py - to see if this method works
and which linker options it chooses, and update this bug report if you discover anything new.
Comment 2 Michael Dales 2007-11-08 09:03:53 UTC
When I build with setup.py the installed library works, which is good. *But* if I look with nm I can see that the _cairo.so it's installed has all the symbols public, just like my solution produced.

That said, python setup.py build failed initially, due to this:

iver[pycairo-1.4.0]% python setup.py build                                16:51
cairo version >= 1.4.0 detected
creating pycairo.pc
running build
running build_py
creating build
creating build/lib.macosx-10.5-i386-2.5
creating build/lib.macosx-10.5-i386-2.5/cairo
copying cairo/__init__.py -> build/lib.macosx-10.5-i386-2.5/cairo
running build_ext
building 'cairo._cairo' extension
creating build/temp.macosx-10.5-i386-2.5
creating build/temp.macosx-10.5-i386-2.5/cairo
...[snip]...
gcc -Wl,-F. -bundle -undefined dynamic_lookup -arch i386 -arch ppc build/temp.macosx-10.5-i386-2.5/cairo/cairomodule.o build/temp.macosx-10.5-i386-2.5/cairo/pycairo-context.o build/temp.macosx-10.5-i386-2.5/cairo/pycairo-font.o build/temp.macosx-10.5-i386-2.5/cairo/pycairo-matrix.o build/temp.macosx-10.5-i386-2.5/cairo/pycairo-path.o build/temp.macosx-10.5-i386-2.5/cairo/pycairo-pattern.o build/temp.macosx-10.5-i386-2.5/cairo/pycairo-surface.o -L/opt/local/lib -L/usr/X11/lib -L/opt/local/lib -L/usr/X11/lib -lcairo -lSM -lICE -lfreetype -lz -lfontconfig -lexpat -lpng12 -lXrender -lX11 -o build/lib.macosx-10.5-i386-2.5/cairo/_cairo.so
ld: warning in /opt/local/lib/libcairo.dylib, file is not of required architecture
ld: warning in /opt/local/lib/libfreetype.dylib, file is not of required architecture
ld: warning in /opt/local/lib/libz.dylib, file is not of required architecture
ld: warning in /opt/local/lib/libfontconfig.dylib, file is not of required architecture
ld: warning in /opt/local/lib/libexpat.dylib, file is not of required architecture
ld: warning in /opt/local/lib/libpng12.dylib, file is not of required architecture
ld: warning in /opt/local/lib/libXrender.dylib, file is not of required architecture
iver[pycairo-1.4.0]% 

The problem here is that macports has not installed ppc compatible libraries for cairo etc. So I ran that last gcc call by hand removing the "-arch ppc" flag, then reran the build and it built okay. I'll happily admit that this may have caused the nmedit stage to be skipped, though at no point did I spot it geneting a symbol list.

I appreciate my solution was probably not the right one, but it was just enough to get me going :) I need pycairo for work, so it was just to let me get going. Let me know if there's any more I can do to assist. Alas my knowledge of the Apple linker setup is not very much currently.
Comment 3 Steve Chaplin 2007-11-08 16:41:50 UTC
On my system (Linux) the configure and setup.py build methods produce slightly different results too.

With the configure build, 'nm _cairo.so' shows the symbols are private:
000000000000a4d7 t PycairoContext_FromContext
000000000000addd t PycairoFontFace_FromFontFace
000000000000ac2c t PycairoFontOptions_FromFontOptions
000000000000b3aa t PycairoMatrix_FromMatrix
000000000000b9b3 t PycairoPath_FromPath
000000000000be4c t PycairoPattern_FromPattern
000000000000acc3 t PycairoScaledFont_FromScaledFont
000000000000c997 t PycairoSurface_FromSurface
000000000000dd7f t Pycairo_Check_Status
000000000000d1c0 T init_cairo

With the setup.py build the symbols are public:
000000000000babb T PycairoContext_FromContext
000000000000c37a T PycairoFontFace_FromFontFace
000000000000c19f T PycairoFontOptions_FromFontOptions
000000000000c410 T PycairoMatrix_FromMatrix
000000000000ced9 T PycairoPath_FromPath
000000000000d361 T PycairoPattern_FromPattern
000000000000c24b T PycairoScaledFont_FromScaledFont
000000000000de98 T PycairoSurface_FromSurface
0000000000009522 T Pycairo_Check_Status
0000000000008950 T init_cairo

I think this is because the functions listed should be declared 'static', as recommended by the manual reference I gave earlier. But since the module code is not a single file but spread over a number of files, and some of these functions are called from outside their own file, I needed to remove the 'static' declaration to get the module to compile, resulting in the functions inadvertently becoming public.
The configure install method uses the 'export-symbols-regex' option to force them back to being private. But the setup.py method leaves them as public.

Both methods (if they compile successfully) result in working pycairo modules, but the configure method is (theoretically) better since it only makes public the 'init_cairo' function.

The distutils 'Extension' class accepts 'export_symbols' argument which limits which symbols are exported a Python extension. But the documentation says its not used on all platforms. When I try using
    export_symbols = 'init_cairo'
in setup.py it does not prevent the other symbols from remaining public/global.
Comment 4 Richard Hult 2008-04-05 02:20:02 UTC
This looks like a similar problem I had with other modules on 10.5. In my case it was fixed by upgrading libtool to 1.5.26, which has a bunch of fixes for 10.5.

Comment 5 Steve Chaplin 2008-07-16 16:44:19 UTC
I see no evidence that this is a pycairo bug, just that pycairo exposes a bug on Mac OS X. The last comment suggests that the problem is a Mac OS X build environment problem, and is fixed using newer build tools.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.