"""SCons.Tool.nvcc

Tool-specific initialization for NVIDIA CUDA Compiler.

There normally shouldn't be any need to import this module directly.
It will usually be imported through the generic SCons.Tool.Tool()
selection method.

"""

import SCons.Tool
import SCons.Scanner.C
import SCons.Defaults
import os
import platform


def get_cuda_paths():
    """Determines CUDA {bin,lib,include} paths

    returns (bin_path,lib_path,inc_path)
    """

    # determine defaults
    if os.name == 'nt':
        bin_path = 'C:/CUDA/bin'
        lib_path = 'C:/CUDA/lib'
        inc_path = 'C:/CUDA/include'
    elif os.name == 'posix':
        bin_path = '/usr/local/cuda/bin'
        lib_path = '/usr/local/cuda/lib'
        inc_path = '/usr/local/cuda/include'
    else:
        raise ValueError('Error: unknown OS.  Where is nvcc installed?')

    lib_ext = ''
    if platform.machine()[-2:] == '64' and platform.platform()[:6] != 'Darwin':
        lib_ext = '64'

    # override with environement variables
    if 'CUDA_PATH' in os.environ:
	    bin_path = os.path.abspath(os.environ['CUDA_PATH']) + os.sep + 'bin'
	    lib_path = os.path.abspath(os.environ['CUDA_PATH']) + os.sep + 'lib'
	    inc_path = os.path.abspath(os.environ['CUDA_PATH']) + os.sep + 'include'
    if 'CUDA_BIN_PATH' in os.environ:
        bin_path = os.path.abspath(os.environ['CUDA_BIN_PATH'])
    if 'CUDA_LIB_PATH' in os.environ:
        lib_path = os.path.abspath(os.environ['CUDA_LIB_PATH'])
    if 'CUDA_INC_PATH' in os.environ:
        inc_path = os.path.abspath(os.environ['CUDA_INC_PATH'])

    return (bin_path, lib_path + lib_ext, inc_path)


CUDASuffixes = ['.cu']

# make a CUDAScanner for finding #includes
# cuda uses the c preprocessor, so we can use the CScanner
CUDAScanner = SCons.Scanner.C.CScanner()


def add_common_nvcc_variables(env):
    """
    Add underlying common "NVIDIA CUDA compiler" variables that
    are used by multiple builders.
    """

    # "NVCC common command line"
    if not env.has_key('_NVCCCOMCOM'):
        # nvcc needs '-I' prepended before each include path, regardless of
        # platform
        env['_NVCCWRAPCPPPATH'] = '${_concat("-I ", CPPPATH, "", __env__)}'
        # prepend -Xcompiler before each flag
        env['_NVCCWRAPCFLAGS'] = '${_concat("-Xcompiler ", CFLAGS,     "", __env__)}'
        env['_NVCCWRAPSHCFLAGS'] = '${_concat("-Xcompiler ", SHCFLAGS,   "", __env__)}'
        env['_NVCCWRAPCCFLAGS'] = '${_concat("-Xcompiler ", CCFLAGS,   "", __env__)}'
        env['_NVCCWRAPSHCCFLAGS'] = '${_concat("-Xcompiler ", SHCCFLAGS, "", __env__)}'
        # assemble the common command line
        env['_NVCCCOMCOM'] = '${_concat("-Xcompiler ", CPPFLAGS, "", __env__)} $_CPPDEFFLAGS $_NVCCWRAPCPPPATH'


def generate(env):
    """
    Add Builders and construction variables for CUDA compilers to an Environment.
    """

    # create a builder that makes PTX files from .cu files
    ptx_builder = SCons.Builder.Builder(
        action='$NVCC -ptx $NVCCFLAGS $_NVCCWRAPCFLAGS $NVCCWRAPCCFLAGS $_NVCCCOMCOM $SOURCES -o $TARGET',
        emitter={},
        suffix='.ptx',
        src_suffix=CUDASuffixes)
    env['BUILDERS']['PTXFile'] = ptx_builder

    # create builders that make static & shared objects from .cu files
    static_obj, shared_obj = SCons.Tool.createObjBuilders(env)

    for suffix in CUDASuffixes:
        # Add this suffix to the list of things buildable by Object
        static_obj.add_action('$CUDAFILESUFFIX', '$NVCCCOM')
        shared_obj.add_action('$CUDAFILESUFFIX', '$SHNVCCCOM')
        static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
        shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)

        # Add this suffix to the list of things scannable
        SCons.Tool.SourceFileScanner.add_scanner(suffix, CUDAScanner)

    add_common_nvcc_variables(env)

    # set the "CUDA Compiler Command" environment variable
    # windows is picky about getting the full filename of the executable
    if os.name == 'nt':
        env['NVCC'] = 'nvcc.exe'
        env['SHNVCC'] = 'nvcc.exe'
    else:
        env['NVCC'] = 'nvcc'
        env['SHNVCC'] = 'nvcc'

    # set the include path, and pass both c compiler flags and c++ compiler
    # flags
    env['NVCCFLAGS'] = SCons.Util.CLVar('')
    env['SHNVCCFLAGS'] = SCons.Util.CLVar('') + ' -shared'

    # 'NVCC Command'
    env['NVCCCOM'] = '$NVCC -o $TARGET -c $NVCCFLAGS $_NVCCWRAPCFLAGS $NVCCWRAPCCFLAGS $_NVCCCOMCOM $SOURCES'
    env['SHNVCCCOM'] = '$SHNVCC -o $TARGET -c $SHNVCCFLAGS $_NVCCWRAPSHCFLAGS $_NVCCWRAPSHCCFLAGS $_NVCCCOMCOM $SOURCES'

    # the suffix of CUDA source files is '.cu'
    env['CUDAFILESUFFIX'] = '.cu'

    # XXX add code to generate builders for other miscellaneous
    # CUDA files here, such as .gpu, etc.

    # XXX intelligently detect location of nvcc and cuda libraries here
    (bin_path, lib_path, inc_path) = get_cuda_paths()

    env.PrependENVPath('PATH', bin_path)


def exists(env):
    return env.Detect('nvcc')
