Monday, May 13, 2013

Embedding python in Mac OS X

I have been trying to figure out how to embed Python on the Mac. Trying the simple example found at,

#include <Python.h>

main(int argc, char *argv[])
  Py_SetProgramName(argv[0]);  /* optional but recommended */
  PyRun_SimpleString("from time import time,ctime\n"
                     "print 'Today is',ctime(time())\n");
  return 0;

I got the error message that Python.h couldn't be found:

$ gcc test.c
test.c:1:20: error: Python.h: No such file or directory

Adding the header files to the search path then led to a linking problem:

$gcc -I /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Headers test.c 
Undefined symbols for architecture x86_64:
  "_PyRun_SimpleStringFlags", referenced from:
      _main in cch6pSfx.o
  "_Py_Finalize", referenced from:
      _main in cch6pSfx.o
  "_Py_Initialize", referenced from:
      _main in cch6pSfx.o
  "_Py_SetProgramName", referenced from:
      _main in cch6pSfx.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status

The all knowing Google was of limited help-- I could not find an explanation for how to embed python on the Mac platform. After divining the error message tea leaves, and pouring over man pages, I have finally found out how to EMBED PYTHON ON THE MAC!! (cue 2001: A Space Odyssey theme!)

The magic incantation is

gcc -I /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Headers -L /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib -lpython2.7 foo.c

I have macports python installed, but if you don't, you will need to change the "/opt/local/Library..." to "/System/Library..."

The -I option adds a directory for finding header files. The -L adds a directory for finding lib files. The -lx option loads the library file named libx.dylib.

I then added a bash alias:
alias pygcc='gcc -I /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Headers -L /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib -lpython2.7'

I can now invoke the compiler using
pygcc foo.c

Another thing I found out is that the current directory is not in sys.path. You therefore can't load modules that are in the current directory. You can add the current directory by adding PySys_SetArgv(argc, argv); after the call to Py_Initialize(); so that it looks like:

PySys_SetArgv(argc, argv);    //adds current directory to sys.path

Update: It seems that there is a slightly easier way to add the proper include and library directories using the python-config command (which doesn't have a man page on Mac OS BTW). Calling python-config with --includes --ldflags returns the appropriate options for gcc:

$ python-config --includes --ldflags
-I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7
-L/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/config -ldl -framework CoreFoundation -lpython2.7 -u _PyMac_Error /opt/local/Library/Frameworks/Python.framework/Versions/2.7/Python

So you can shorten the gcc incantation to:
gcc `python-config --includes --ldflags`
and shorten the alias to
alias pygcc='gcc `python-config --includes --ldflags`'

No comments:

Post a Comment