Felix Programming Language

Authors : erickt : December 2009

Example python extension builder

posted on December 30, 2009 - 02:03 PM PST by Erick Tryzelaar
filed under: FBuild

Holger on the fbuild mailing list was trying to use fbuild to create python c extensions, so I mocked up a simple builder for him. There's a little work looking up the include directory, and I'm hardcoding the gcc option "-undefined dynamic_lookup", but once done it's pretty simple:

import fbuild.builders.c
import fbuild.db
import fbuild.path

@fbuild.db.caches
def python_c_builder(ctx, python='python'):
    """Creates and returns a python C extension builder."""

    # If we didn't explicitly chose which version of python to use, search the
    # environment for python.
    python = fbuild.builders.find_program(ctx, [python])

    # Create a helper program that calls out to python to get the include and
    # lib directories.
    stdout, stderr = ctx.execute([python, '-c',
        'import distutils.sysconfig; '
        'print(distutils.sysconfig.get_python_inc())'],
        quieter=1)
    includepath = stdout.decode().strip()

    # Return a fully specified python c extension builder.
    return fbuild.builders.c.guess_shared(ctx,
        includes=[includepath],
        flags=['-undefined', 'dynamic_lookup'],
        lib_prefix='',
        lib_suffix='.so')

def build(ctx):
    for python in ('python', 'python2.5', 'python2.6', 'python3.1'):
        # Configure the python.
        shared = python_c_builder(ctx, python)

        # Copy the src file so we don't have collisions.
        fbuild.path.Path('build/%s' % python).makedirs()
        fbuild.path.Path('spam.c').copy('build/%s' % python)

        # Build the extension module.
        shared.build_lib('%s/spam' % python, ['build/%s/spam.c' % python])

        # Test if we can actually import the module.
        ctx.execute([python, '-c', 'import spam; spam.system("echo hello world!")'],
            cwd='build/%s' % python)

Which results in:

looking for program python : ok /opt/local/bin/python
determining platform       : {'bsd', 'darwin', 'macosx', 'posix'}
looking for program gcc    : ok /usr/bin/gcc
checking gcc               : ok
checking gcc with -g       : ok
checking gcc with -O2      : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python/spam.c -> build/python/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python/spam.os -> build/python/spam.so
hello world!
looking for program python2.5           : ok /opt/local/bin/python2.5
checking gcc                            : ok
checking gcc with -g                    : ok
checking gcc with -O2                   : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python2.5/spam.c -> build/python2.5/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python2.5/spam.os -> build/python2.5/spam.so
hello world!
looking for program python2.6           : ok /opt/local/bin/python2.6
 * gcc -undefined dynamic_lookup -fPIC  : build/python2.6/spam.c -> build/python2.6/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python2.6/spam.os -> build/python2.6/spam.so
hello world!
looking for program python3.1           : ok /opt/local/bin/python3.1
checking gcc                            : ok
checking gcc with -g                    : ok
checking gcc with -O2                   : ok
checking gcc with -undefined dynamic_lookup -fPIC: ok
checking gcc with -undefined dynamic_lookup -dynamiclib: ok
checking gcc with -undefined dynamic_lookup: ok
checking if gcc -undefined dynamic_lookup -fPIC can make objects: ok
checking if gcc -undefined dynamic_lookup -fPIC can make libraries: ok
checking if gcc -undefined dynamic_lookup -fPIC can make exes: ok
checking if gcc -undefined dynamic_lookup -fPIC can link lib to exe: ok
 * gcc -undefined dynamic_lookup -fPIC  : build/python3.1/spam.c -> build/python3.1/spam.os
 * gcc -undefined dynamic_lookup -dynamiclib: build/python3.1/spam.os -> build/python3.1/spam.so
hello world!

I should be able to get a prototype builder out soon.

read comments