1

I want to write a python extension in c. I work on Mac, I took a code from here:

#include <Python.h>

static PyObject* say_hello(PyObject* self, PyObject* args)
{
    const char* name;

    if (!PyArg_ParseTuple(args, "s", &name))
        return NULL;

    printf("Hello %s!\n", name);

    Py_RETURN_NONE;
}

static PyMethodDef HelloMethods[] =
{
     {"say_hello", say_hello, METH_VARARGS, "Greet somebody."},
     {NULL, NULL, 0, NULL}
};

PyMODINIT_FUNC

inithello(void)
{
     (void) Py_InitModule("hello", HelloMethods);
}

I compile it:

gcc -c -o py_module.o py_module.c -I/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/
gcc -o py_module py_module.o -I/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/ -lm

But I get this error:

Undefined symbols for architecture x86_64:
  "_PyArg_ParseTuple", referenced from:
      _say_hello in py_module.o
  "_Py_InitModule4_64", referenced from:
      _inithello in py_module.o
  "__Py_NoneStruct", referenced from:
      _say_hello in py_module.o
  "_main", referenced from:
      start in crt1.10.6.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
make: *** [py_module] Error 1

How comes python doesn't support X86_64 architecture?

0x90
  • 39,472
  • 36
  • 165
  • 245

3 Answers3

8

Two things:

  • You need to link your extension as a shared object (you're attempting to link an executable, which is why the linker is looking for main());
  • You need to link against the Python static library (-lpython).
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • 1
    where the static python lib can be found ? – 0x90 Mar 26 '13 at 07:55
  • Depends on your OS and Python install. On my box, it's in `/usr/lib` (there's a whole bunch of libraries actually, for different versions of Python). – NPE Mar 26 '13 at 07:57
  • I don't find a python static lib `find / -iname *libpython* 2>&1 | grep -v "^find"` only `.dylib` and `.a` files – 0x90 Mar 26 '13 at 08:21
  • 2
    @0x90: `.a` *is* a static lib. If you still can't find it, you might need to install it separately. – NPE Mar 26 '13 at 08:22
  • 5
    @0x90 There is a program called `python-config` which gives you some inprotant information: `[--prefix|--exec-prefix|--includes|--libs|--cflags|--ldflags|--help]`, e. g.: `python-config --ldflags`gives me `-lpthread -ldl -lutil -lm -lpython2.7 -Xlinker -export-dynamic` which eliminates the need for `find`. – glglgl Mar 26 '13 at 08:28
  • @glglgl you still need the `find` inorder to find the path to Python.h :) – 0x90 Mar 26 '13 at 09:21
  • 2
    @0x90 Look closer. There is `python-config --includes` and `python-config --cflags` which gives me `-I/usr/include/python2.7 -I/usr/include/python2.7` resp. `-I/usr/include/python2.7 -I/usr/include/python2.7 -fno-strict-aliasing -fomit-frame-pointer -fmessage-length=0 -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -g -DNDEBUG -fomit-frame-pointer -fmessage-length=0 -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables -g`. – glglgl Mar 26 '13 at 09:23
  • No need to manually specify Python library name as it is. E.g. this doesn't work for me on Apple M1 with Python installed with Homebrew. Here is the recipe: `g++ $(python3-config --ldflags --embed) `. I.e. `--embed` flag should save your day. – Denis The Menace Apr 29 '22 at 10:47
4
  1. Query the python env path with these commands:
$python-config --includes
-I/usr/include/python2.6 -I/usr/include/python2.6
$python-config --ldflags
-lpthread -ldl -lutil -lm -lpython2.6
  1. generate .o file:

$ g++ -fPIC -c -I/usr/include/python2.6 -I/usr/include/python2.6 xx.cpp

  1. generate .so file:

g++ -shared xx.o -o xx.so

hupantingxue
  • 2,134
  • 3
  • 19
  • 24
1

Thanks to @NPE @glglgl and anatoly here is my Makefile:

DIR=/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/
CC=gcc
CFLAGS=-I$(DIR)
ODIR=.

LIBS_DIR=/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config/
LIBS=-lpython2.7

_DEPS =
DEPS = $(patsubst %,$(IDIR)/%,$(_DEPS))

_OBJ = py_module.o
OBJ = $(patsubst %,$(ODIR)/%,$(_OBJ))


$(ODIR)/%.o: %.c $(DEPS)
        $(CC) -c -o $@ $< $(CFLAGS)

py_module: $(OBJ)
        gcc -shared $^ $(CFLAGS) -I$(LIBS_DIR) $(LIBS) -o $@

.PHONY: clean

clean:
        rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~

makefile template had been taken from here.

In order to find the paths, one may use python-config --ldflags

and python-config --includes

0x90
  • 39,472
  • 36
  • 165
  • 245
  • 1
    You don't need `-I$(LIBS_DIR)` if you do a `CFLAGS=$(shell python-config --cflags)`, and besides you can do `LDFLAGS=$(shell python-config --ldflags)` and use that with `gcc -shared $^ $(LDFLAGS) -o $@`. – glglgl Mar 26 '13 at 09:41
  • @glglgl almost went well enough except the output of `python-config`` doesn't give the full path. see http://stackoverflow.com/questions/15634863/possible-bug-in-python-config – 0x90 Mar 26 '13 at 10:35