From d328d707ef81caa16a78f6b6745fd0aa31d14a81 Mon Sep 17 00:00:00 2001 From: Dag Sverre Seljebotn Date: Wed, 21 Oct 2009 10:31:53 +0200 Subject: [PATCH] Preliminary scons support (in Tools dir) --- Tools/site_scons/site_tools/cython.py | 67 ++++++++ Tools/site_scons/site_tools/pyext.py | 233 ++++++++++++++++++++++++++ 2 files changed, 300 insertions(+) create mode 100644 Tools/site_scons/site_tools/cython.py create mode 100644 Tools/site_scons/site_tools/pyext.py diff --git a/Tools/site_scons/site_tools/cython.py b/Tools/site_scons/site_tools/cython.py new file mode 100644 index 00000000..6e5151be --- /dev/null +++ b/Tools/site_scons/site_tools/cython.py @@ -0,0 +1,67 @@ +""" +Tool to run Cython files (.pyx) into .c and .cpp. + +TODO: + - Add support for dynamically selecting in-process Cython + through CYTHONINPROCESS variable. + - Have a CYTHONCPP option which turns on C++ in flags and + changes output extension at the same time + +VARIABLES: + - CYTHON - The path to the "cython" command line tool. + - CYTHONFLAGS - Flags to pass to the "cython" command line tool. + +AUTHORS: + - David Cournapeau + - Dag Sverre Seljebotn + +""" +import SCons +from SCons.Builder import Builder +from SCons.Action import Action + +#def cython_action(target, source, env): +# print target, source, env +# from Cython.Compiler.Main import compile as cython_compile +# res = cython_compile(str(source[0])) + +cythonAction = Action("$CYTHONCOM") + +def create_builder(env): + try: + cython = env['BUILDERS']['Cython'] + except KeyError: + cython = SCons.Builder.Builder( + action = cythonAction, + emitter = {}, + suffix = cython_suffix_emitter, + single_source = 1) + env['BUILDERS']['Cython'] = cython + + return cython + +def cython_suffix_emitter(env, source): + print 'emitter called' + return "$CYTHONCFILESUFFIX" + +def generate(env): + env["CYTHON"] = "cython" + env["CYTHONCOM"] = "$CYTHON $CYTHONFLAGS -o $TARGET $SOURCE" + env["CYTHONCFILESUFFIX"] = ".c" + + c_file, cxx_file = SCons.Tool.createCFileBuilders(env) + + c_file.suffix['.pyx'] = cython_suffix_emitter + c_file.add_action('.pyx', cythonAction) + + c_file.suffix['.py'] = cython_suffix_emitter + c_file.add_action('.py', cythonAction) + + create_builder(env) + +def exists(env): + try: +# import Cython + return True + except ImportError: + return False diff --git a/Tools/site_scons/site_tools/pyext.py b/Tools/site_scons/site_tools/pyext.py new file mode 100644 index 00000000..60799757 --- /dev/null +++ b/Tools/site_scons/site_tools/pyext.py @@ -0,0 +1,233 @@ +"""SCons.Tool.pyext + +Tool-specific initialization for python extensions builder. + +AUTHORS: + - David Cournapeau + - Dag Sverre Seljebotn + +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import sys + +import SCons +from SCons.Tool import SourceFileScanner, ProgramScanner + +# Create common python builders + +def createPythonObjectBuilder(env): + """This is a utility function that creates the PythonObject Builder in an + Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + pyobj = env['BUILDERS']['PythonObject'] + except KeyError: + pyobj = SCons.Builder.Builder(action = {}, + emitter = {}, + prefix = '$PYEXTOBJPREFIX', + suffix = '$PYEXTOBJSUFFIX', + src_builder = ['CFile', 'CXXFile'], + source_scanner = SourceFileScanner, + single_source = 1) + env['BUILDERS']['PythonObject'] = pyobj + + return pyobj + +def createPythonExtensionBuilder(env): + """This is a utility function that creates the PythonExtension Builder in + an Environment if it is not there already. + + If it is already there, we return the existing one. + """ + + try: + pyext = env['BUILDERS']['PythonExtension'] + except KeyError: + import SCons.Action + import SCons.Defaults + action = SCons.Action.Action("$PYEXTLINKCOM", "$PYEXTLINKCOMSTR") + action_list = [ SCons.Defaults.SharedCheck, + action] + pyext = SCons.Builder.Builder(action = action_list, + emitter = "$SHLIBEMITTER", + prefix = '$PYEXTPREFIX', + suffix = '$PYEXTSUFFIX', + target_scanner = ProgramScanner, + src_suffix = '$PYEXTOBJSUFFIX', + src_builder = 'PythonObject') + env['BUILDERS']['PythonExtension'] = pyext + + return pyext + +def pyext_coms(platform): + """Return PYEXTCCCOM, PYEXTCXXCOM and PYEXTLINKCOM for the given + platform.""" + if platform == 'win32': + pyext_cccom = "$PYEXTCC /Fo$TARGET /c $PYEXTCCSHARED "\ + "$PYEXTCFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\ + "$_PYEXTCPPINCFLAGS $SOURCES" + pyext_cxxcom = "$PYEXTCXX /Fo$TARGET /c $PYEXTCSHARED "\ + "$PYEXTCXXFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\ + "$_PYEXTCPPINCFLAGS $SOURCES" + pyext_linkcom = '${TEMPFILE("$PYEXTLINK $PYEXTLINKFLAGS '\ + '/OUT:$TARGET.windows $( $_LIBDIRFLAGS $) '\ + '$_LIBFLAGS $_PYEXTRUNTIME $SOURCES.windows")}' + else: + pyext_cccom = "$PYEXTCC -o $TARGET -c $PYEXTCCSHARED "\ + "$PYEXTCFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\ + "$_PYEXTCPPINCFLAGS $SOURCES" + pyext_cxxcom = "$PYEXTCXX -o $TARGET -c $PYEXTCSHARED "\ + "$PYEXTCXXFLAGS $PYEXTCCFLAGS $_CCCOMCOM "\ + "$_PYEXTCPPINCFLAGS $SOURCES" + pyext_linkcom = "$PYEXTLINK -o $TARGET $PYEXTLINKFLAGS "\ + "$SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_PYEXTRUNTIME" + + if platform == 'darwin': + pyext_linkcom += ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + return pyext_cccom, pyext_cxxcom, pyext_linkcom + +def set_basic_vars(env): + # Set construction variables which are independant on whether we are using + # distutils or not. + env['PYEXTCPPPATH'] = SCons.Util.CLVar('$PYEXTINCPATH') + + env['_PYEXTCPPINCFLAGS'] = '$( ${_concat(INCPREFIX, PYEXTCPPPATH, '\ + 'INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' + env['PYEXTOBJSUFFIX'] = '$SHOBJSUFFIX' + env['PYEXTOBJPREFIX'] = '$SHOBJPREFIX' + + env['PYEXTRUNTIME'] = SCons.Util.CLVar("") + # XXX: this should be handled with different flags + env['_PYEXTRUNTIME'] = '$( ${_concat(LIBLINKPREFIX, PYEXTRUNTIME, '\ + 'LIBLINKSUFFIX, __env__)} $)' + # XXX: This won't work in all cases (using mingw, for example). To make + # this work, we need to know whether PYEXTCC accepts /c and /Fo or -c -o. + # This is difficult with the current way tools work in scons. + pycc, pycxx, pylink = pyext_coms(sys.platform) + + env['PYEXTLINKFLAGSEND'] = SCons.Util.CLVar('$LINKFLAGSEND') + + env['PYEXTCCCOM'] = pycc + env['PYEXTCXXCOM'] = pycxx + env['PYEXTLINKCOM'] = pylink + +def _set_configuration_nodistutils(env): + # Set env variables to sensible values when not using distutils + def_cfg = {'PYEXTCC' : '$SHCC', + 'PYEXTCFLAGS' : '$SHCFLAGS', + 'PYEXTCCFLAGS' : '$SHCCFLAGS', + 'PYEXTCXX' : '$SHCXX', + 'PYEXTCXXFLAGS' : '$SHCXXFLAGS', + 'PYEXTLINK' : '$LDMODULE', + 'PYEXTSUFFIX' : '$LDMODULESUFFIX', + 'PYEXTPREFIX' : ''} + + if sys.platform == 'darwin': + def_cfg['PYEXTSUFFIX'] = '.so' + + for k, v in def_cfg.items(): + ifnotset(env, k, v) + + ifnotset(env, 'PYEXT_ALLOW_UNDEFINED', + SCons.Util.CLVar('$ALLOW_UNDEFINED')) + ifnotset(env, 'PYEXTLINKFLAGS', SCons.Util.CLVar('$LDMODULEFLAGS')) + + env.AppendUnique(PYEXTLINKFLAGS = env['PYEXT_ALLOW_UNDEFINED']) + +def ifnotset(env, name, value): + if not env.has_key(name): + env[name] = value + +def set_configuration(env, use_distutils): + """Set construction variables which are platform dependants. + + If use_distutils == True, use distutils configuration. Otherwise, use + 'sensible' default. + + Any variable already defined is untouched.""" + + # We define commands as strings so that we can either execute them using + # eval (same python for scons and distutils) or by executing them through + # the shell. + dist_cfg = {'PYEXTCC': "sysconfig.get_config_var('CC')", + 'PYEXTCFLAGS': "sysconfig.get_config_var('CFLAGS')", + 'PYEXTCCSHARED': "sysconfig.get_config_var('CCSHARED')", + 'PYEXTLINKFLAGS': "sysconfig.get_config_var('LDFLAGS')", + 'PYEXTLINK': "sysconfig.get_config_var('LDSHARED')", + 'PYEXTINCPATH': "sysconfig.get_python_inc()", + 'PYEXTSUFFIX': "sysconfig.get_config_var('SO')"} + + from distutils import sysconfig + + # We set the python path even when not using distutils, because we rarely + # want to change this, even if not using distutils + ifnotset(env, 'PYEXTINCPATH', sysconfig.get_python_inc()) + + if use_distutils: + for k, v in dist_cfg.items(): + ifnotset(env, k, eval(v)) + else: + _set_configuration_nodistutils(env) + +def generate(env): + """Add Builders and construction variables for python extensions to an + Environment.""" + + if not env.has_key('PYEXT_USE_DISTUTILS'): + env['PYEXT_USE_DISTUTILS'] = False + + # This sets all constructions variables used for pyext builders. + set_basic_vars(env) + + set_configuration(env, env['PYEXT_USE_DISTUTILS']) + + # Create the PythonObject builder + pyobj = createPythonObjectBuilder(env) + action = SCons.Action.Action("$PYEXTCCCOM", "$PYEXTCCCOMSTR") + pyobj.add_emitter('.c', SCons.Defaults.SharedObjectEmitter) + pyobj.add_action('.c', action) + + action = SCons.Action.Action("$PYEXTCXXCOM", "$PYEXTCXXCOMSTR") + pyobj.add_emitter('$CXXFILESUFFIX', SCons.Defaults.SharedObjectEmitter) + pyobj.add_action('$CXXFILESUFFIX', action) + + # Create the PythonExtension builder + createPythonExtensionBuilder(env) + +def exists(env): + try: + # This is not quite right: if someone defines all variables by himself, + # it would work without distutils + from distutils import sysconfig + return True + except ImportError: + return False -- 2.26.2