"""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
