Simple examples
Here, simple examples are given, to build different kind of extensions: standard C ones, ctypes-based ones, etc...
Minimal example
Let's say you have a foo packages, with a setup.py file in it. To build things with scons, your setup.py should look like
def configuration(parent_package='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('foo',parent_package,top_path)
config.add_sconscript('SConstruct')
return config
if __name__ == '__main__':
from numpy.distutils.core import setup
setup(configuration=configuration)
So basically, the only thing you need is to call add_sconscript, with the name of the SConscript file. The SConscript file must reside in the same directory than setup.py. A minimal SConscript file looks like:
from numscons import GetNumpyEnvironment env = GetNumpyEnvironment(ARGUMENTS)
So instead of using directly Environment, you get an env object from the GetNumpyEnvironment? function. You don't need to know the details on what this function does, but basically, it does all the work to initialize correctly the environment from distutils options (compilers, etc...), and set directories such as all built code is put in the build directory (to avoid cluttering the source directory). You *can* use Environment, but you are on your own then for cooperating with distutils.
Once you get an environment instance, you can do almost anything you can in scons. In particular, you can copy environments, set different compilation options to them, etc..
Ctypes-based extension
One notable thing not possible to build with distutils is ctypes extensions, that is a shared library loadable through the dlopen (and equivalent on non unix systems). The setup.py is the same than before, and the SConscript looks like:
from numpy.distutils.scons import GetNumpyEnvironment
env = GetNumpyEnvironment(ARGUMENTS)
env.NumpyCtypes('foo', source = ['foo.c'])
This will tell scons to build a library foo from the source file foo.c usable by ctypes. Note that this is totally cross-platform: you don't need to know the extension, prefix and suffix used on every platform, and it also takes care of building the 'right' dynamically loaded library (a .so on unix, a .pyd on windows, a .dylib module on Mac OS X). You still have to take care of calling convention in your code, though (on most unices, you don't need to do anything, but on windows, a function exported by a dll has to use the dllexport calling convention. You can find the whole example in source:branches/numpy.scons/numpy/scons_fake/ctypesext.
Build a C Python Extension
To build a standard C extension for python, you should use the NumpyPythonExtension? scons builder. Taking the spam example from python doc, if your module C source is hellomodule.c, which defines the spam module (that is the function PyMODINIT_FUNC initspam(void) is defined in your module code), all you have to do is:
from numpy.distutils.scons import GetNumpyEnvironment
env = GetNumpyEnvironment(ARGUMENTS)
env.NumpyPythonExtension('spam', source = ['hellomodule.c'])
Again, You can find the whole example in the tests directory of numscons.
More complicated things
Checking for a library
Let's say you want to build an extension which depends on an external library. For example, the scikits audiolab depends on sndfile to work. In autoconf, the standard way to check a library is to check whether some symbols can be found in the library (here sf_open in sndfile library). You can do it simply in a SConscript:
from numpy.distutils.scons import GetNumpyEnvironment, NumpyCheckLibAndHeader
env = GetNumpyEnvironment(ARGUMENTS)
config = env.NumpyConfigure(custom_tests = {'NumpyCheckLibAndHeader' : NumpyCheckLibAndHeader})
if not config.NumpyCheckLibAndHeader('sndfile', 'sf_open', 'sndfile.h', section = 'sndfile'):
print "sndfile not found, this package cannot work without sndfile !"
config.Finish()
First, you create a config context using the NumpyConfigure? function. Then, you check for the symbol sf_open in the library sndfile. Section is the name of section to use for customization in a site.cfg, that is in this example, if site.cfg contains [sndfile] section, the corresponding options will be used. This is useful for people who have custom libraries built somewhere in their home directory, etc...
Using Fortran
TODO: numpy.scons offers support so that f2py can 'dynamically' find fortran-related informations, through the following checkers:
- CheckF77Mangler : this function, when it succeeds, puts a python function in envF77_NAME_MANGLER? which can mangle a given function into the fortran name (upper vs lower case, underscore, etc...). It also defines F77_NO_APPEND_FORTRAN, F77_UPPERCASE_FORTRAN and F77_UNDERSCORE_G77, whose values are 0 or 1, and is compatible with the corresponding variables for f2py.
- CheckF77Clib: this function finds the link options necessary to link together fortran and C object code to build a shared library or a binary. The results are put in F77_LDFLAGS.
The above checks are also defined for F90, but those are not tested. The above F77 functions have been successfully tested with g77, gfortran, ifort, sunfort on Solaris, Linux and Mac Os X.
Compiling a python extension from fortran files
Unfortunatly, for now, scons is not as clever as numpy.distutils to build fortran extensions, so there is a bit more work to do.
==== Extensions based on .f files =====
To build extensions using directly fortran files: with numpy.distutils, you just put the fortran files a sources, and the wrappers are automatically created by f2py. With scons, at least for now, you first have to explicitely build the wrappers using the scons F2py builder. For example, let's say you want to build a fortran extension using the foo.f file. In numpy.ditutils, you do:
src = ['foo.f']
config.add_extension('foo', sources = src)
With scons, you do (in the SConstruct)
src = ['foo.f']
wrapper_src = env.F2py('foomodule.c', source = src)
env.NumpyPythonExtension('foo', src + wrapper_src)
More
Of course, you can do much more elaborate things with scons than the above. In perticular:
- Fortran support: TODO
- Customizing build environments
- build dir discussion
You can get a pretty good idea of what is possible by looking at the setupscons.py and SConscript files used to build numpy itself in the source:branches/numpy.scons/ branch.
