Python, ImportError: undefined symbol: g_utf8_skip

5k views Asked by At

There are like tens of similar questions on StackOverflow, but after several hours of lurking I finally gave up.

So I'm trying to write a C extension for Python. Let's call it mylib. Here is the header file:

mylib.h

#ifndef mylib_H
#define mylib_H

#include <Python.h>
< ... >
#include <glib.h>
< ... >

and setup.py:

from distutils.core import setup, Extension

include_list = [
    "/usr/include/glib-2.0", "-lglib-2.0",
    "/usr/lib/x86_64-linux-gnu/glib-2.0/include"
]

module = Extension('mylib', ['mylib.c'])

setup(name='mylib', version='1.0', 
      include_dirs=include_list,
      ext_modules=[module])

If I run python setup.py install, I get the following (which I take as successful installation):

running install
running build
running build_ext
building 'mylib' extension
creating build
creating build/temp.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/glib-2.0 -I-lglib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/python2.7 -c mylib.c -o build/temp.linux-x86_64-2.7/mylib.o
mylib.c: In function ‘c_sound_utf8’:
mylib.c:117:5: warning: ‘g_unicode_canonical_decomposition’ is deprecated (declared at /usr/include/glib-2.0/glib/gunicode.h:627) [-Wdeprecated-declarations]
     decomposition = g_unicode_canonical_decomposition(c_composed, &decomposition_len);
     ^
creating build/lib.linux-x86_64-2.7
x86_64-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -D_FORTIFY_SOURCE=2 -g -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security build/temp.linux-x86_64-2.7/mylib.o -o build/lib.linux-x86_64-2.7/mylib.so
running install_lib
copying build/lib.linux-x86_64-2.7/mylib.so -> /usr/local/lib/python2.7/dist-packages
running install_egg_info
Removing /usr/local/lib/python2.7/dist-packages/mylib-1.0.egg-info
Writing /usr/local/lib/python2.7/dist-packages/mylib-1.0.egg-info

But when I try to use mylib from inside Python, I get the following:

>>> import mylib
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: /usr/local/lib/python2.7/dist-packages/mylib.so: undefined symbol: g_utf8_skip

After rambling around StackOverflow for some time I got an idea that I should either 1. rebuild the needed library or 2. put all the links to needed library after all generated module names.

Rebuilding didn't work (or I did it the wrong way). As for placing links to the needed library after everything else - well, I didn't find out the way to make distutils change the order of links in its compile string. Is there a way?

I also tried providing extra_link_args/extra_compile_args to my extension (without any effect):

module = Extension('mylib', ['mylib.c'], 
                   extra_link_args=["-Xlinker", "-export-dynamic"])

I felt pretty miserable and kept googling on. Then I found out about SWIG. I decided to try it by making another library, (uppercase) MYLIB (I changed filenames and all text occurences of mylib to MYLIB). I wrote a shell script:

#!/bin/bash

GLIB_IMPORT_OPTS="-I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -lglib-2.0"
PY_IMPORT_OPTS="-I/usr/include/python2.7/ -lpython2.7"

swig -Wall -python MYLIB.i
gcc -fPIC -Wall -c MYLIB.c $GLIB_IMPORT_OPTS
gcc -fPIC -Wall -shared MYLIB.o MYLIB_wrap.c -o _MYLIB.so $GLIB_IMPORT_OPTS -L. $PY_IMPORT_OPTS $GLIB_IMPORT_OPTS

When I ran this thing, everything worked fine (I could import the library and do stuff with it). Here, as you can see, links are at the very end of the compile line. So now I'm trying to understand: what did I miss with the distutils way? How can I make it work?

2

There are 2 answers

0
oopcode On

Well, actually I found the solution. A had to add library links to extra_link_args:

extra_link_args=["-I", "/usr/include/glib-2.0", "-l", "glib-2.0", "-I", "/usr/lib/x86_64-linux-gnu/glib-2.0/include"]

which appends them to the end of compile string.

0
Kelly Stevens On

I found adding -fPIC to "extra_compile_args" in the Extension constructor also helped. Like so:

my_module = Extension('modulename',
                      ...
                      extra_compile_args=["-fPIC"]
                      sources = ['mycode.c'])