"""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 independent 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 name not in env:
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')", False),
'PYEXTCFLAGS': ("sysconfig.get_config_var('CFLAGS')", True),
'PYEXTCCSHARED': ("sysconfig.get_config_var('CCSHARED')", False),
'PYEXTLINKFLAGS': ("sysconfig.get_config_var('LDFLAGS')", True),
'PYEXTLINK': ("sysconfig.get_config_var('LDSHARED')", False),
'PYEXTINCPATH': ("sysconfig.get_python_inc()", False),
'PYEXTSUFFIX': ("sysconfig.get_config_var('SO')", False)}
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, should_split) in dist_cfg.items():
val = eval(v)
if should_split:
val = val.split()
ifnotset(env, k, val)
else:
_set_configuration_nodistutils(env)
def generate(env):
"""Add Builders and construction variables for python extensions to an
Environment."""
if 'PYEXT_USE_DISTUTILS' not in env:
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