# This file is big and messy, BUT can be splitted in little files
# likes standard make files.
# As it's is a pre-alpha stage, it's easier for me to keep everything
# under control in that way. SUMMA SUMMARUM don't flame me ;-) - P.I.

# yep, this is a STANDARD python script!
import os, os.path, glob

# ----------------------------------------------- Envinronment and command line options

# XXX This is just an example of adding command line options. Currently not used TDB later!
opts = Options()
opts.AddOptions(
        PathOption('LLVMPATH', 'path to the root of LLVM', './llvm'),
)

# Build the environment
env = Environment(
    options = opts,                   # add options
)

Help(opts.GenerateHelpText(env)) # generate commandline help
env.SConsignFile()               # group all signatures in a unique file

# -- Common flags
env.Append(CPPDEFINES=[' __STDC_LIMIT_MACROS'])
env.Append(CPPPATH=['llvm/include','tablegen_includes'])

# Use STLPort STL. Update includes and libraries
env.Prepend(CPPPATH = ['STLport-4.6.2/stlport'])
env.Prepend(LIBPATH = ['STLport-4.6.2/lib'    ])

# ----------------------------------------------- Utilities functions
# XXX Must be removed from here. Group it in an external file
def remove_last_dir(path):
    def split_all(path):
        l = list( os.path.split(path) )
        if l[0]:
            l = split_all( l[0] )+l[1:]
        return l
    l = split_all(path)
    newpath = ""
    for part in l[2:]:
        newpath = os.path.join(newpath,part)
    return newpath
    
def srcs(pathname,minus=[]):
    minus = map(os.path.normpath, minus) # normalize paths
    return [os.path.join(env['BUILDDIR'],remove_last_dir(src)) for src in glob.glob(pathname) if os.path.normpath(src) not in minus]
    
def srcs2(pathname,minus=[]):
    minus = map(os.path.normpath, minus) # normalize paths
    return [src for src in glob.glob(pathname) if os.path.normpath(src) not in minus]
    
def ac_config_header(target, source, env):
    assert len(source) ==1, "Multiple sources not implemented in ac_config_header builder"
    assert len(target) ==1, "Multiple targets not implemented in ac_config_header builder"
    src = source[0]
    trg = target[0]
    content = src.get_contents()
    for definition, value in env['AC'].iteritems():
        if "#undef %s"%definition in content:
            if value == 0 or isinstance(value,str) and value.startswith('// scons -'):
                content = content.replace("#undef %s"%definition, "#undef %s // scons - undefined because conf value is %s"%(definition,value))
            elif value == 1:
                content = content.replace("#undef %s"%definition, "#define %s %s // scons - defined because conf value is %s"%(definition,value,value))
            else:
                raise "What to do with that (%s) value?"%value
    open(str(trg), 'w').write(content)
    return None

bld = Builder(action = ac_config_header, src_suffix = '.in')
env['BUILDERS']['ac_config_header']=bld

# ----------------------------------------------- Compiler flags
# XXX group based on various platforms

# Generals to  VC 7.1
env.Append(CPPFLAGS=[
    '/EHsc',       # turn on C++ exception
    '/GR',         # turn on C++ runtime information
])

# ----------------------------------------------- Configuration steps
env['AC']=ac={}
conf = Configure(env)

# -- dlopen() is required for plugin support.
# AC_SEARCH_LIBS(dlopen,dl,AC_DEFINE([HAVE_DLOPEN],[1], ...
# -- mallinfo is optional; the code can compile (minus features) without it
# AC_SEARCH_LIBS(mallinfo,malloc,AC_DEFINE([HAVE_MALLINFO],[1]
# -- pthread locking functions are optional - but llvm will not be thread-safe without locks.
# AC_SEARCH_LIBS(pthread_mutex_lock,pthread,HAVE_PTHREAD_MUTEX_LOCK=1,HAVE_PTHREAD_MUTEX_LOCK=0)
# AC_SUBST(HAVE_PTHREAD_MUTEX_LOCK)

# -- Checks for header files.
# AC_HEADER_STDC
# AC_HEADER_SYS_WAIT

# -- Checks for POSIX and other various system-specific header files
ac['HAVE_WINDOWS_H'] = conf.CheckCXXHeader('windows.h')

# AC_CHECK_HEADERS(fcntl.h limits.h sys/time.h unistd.h malloc.h sys/mman.h sys/resource.h dlfcn.h link.h execinfo.h windows.h)

# Check for things that need to be included in public headers, and so
# for which we may not have access to a HAVE_* preprocessor #define.
# (primarily used in DataTypes.h)
#AC_CHECK_HEADER([sys/types.h],[INCLUDE_SYS_TYPES_H='#include <sys/types.h>'],[INCLUDE_SYS_TYPES_H=''])
ac['INCLUDE_SYS_TYPES_H']="// scons - line removed"
#AC_CHECK_HEADER([inttypes.h],[INCLUDE_INTTYPES_H='#include <inttypes.h>'],[INCLUDE_INTTYPES_H=''])
ac['INCLUDE_SYS_TYPES_H']="// scons - line removed"
#AC_CHECK_HEADER([stdint.h],[INCLUDE_STDINT_H='#include <stdint.h>'],[INCLUDE_STDINT_H=''])
ac['INCLUDE_STDINT_H']="// scons - line removed"

# -- Check for types
#AC_TYPE_PID_T
#AC_TYPE_SIZE_T
# not necessary on VC - handled explicitly in include DataTypes.h.in -- AC_CHECK_TYPES([int64_t],,AC_MSG_ERROR([Type int64_t required but not found]))
# not necessary on VC - handled explicitly in include DataTypes.h.in -- AC_CHECK_TYPES([uint64_t],,AC_MSG_ERROR([Type uint64_t required but not found]))
#AC_HEADER_TIME
#AC_STRUCT_TM

# -- Check for various C features
#AC_C_PRINTF_A

# -- Check for the endianness of the target
#AC_C_BIGENDIAN(AC_SUBST([ENDIAN],[big]),AC_SUBST([ENDIAN],[little]))

# -- Check for C++ extensions
# filterout gnu stuff
ac['HAVE_GNU_EXT_HASH_MAP']= 0
ac['HAVE_GNU_EXT_HASH_SET']= 0
# whether the compiler has <ext/hash_map> defining template class std::hash_map
ac['HAVE_STD_EXT_HASH_MAP']     = conf.CheckType('std::hash_map<int, int>', '#include <ext/hash_map>', 'C++')
# whether the compiler has <hash_map> defining template class std::hash_map
# Well, in reality in the include/llvm/ADT/hash_map the std:: is appended...
ac['HAVE_GLOBAL_HASH_MAP']  = conf.CheckType('std::hash_map<int, int>','#include <hash_map>', 'C++')
# whether the compiler has <ext/hash_set> defining template class std::hash_set
ac['HAVE_STD_EXT_HASH_SET'] = conf.CheckType('std::hash_set<int>','#include <ext/hash_set>','C++')
# whether the compiler has <hash_set> defining template class std::hash_set
# Well, in reality in the include/llvm/ADT/hash_set the std:: is appended...
ac['HAVE_GLOBAL_HASH_SET']  = conf.CheckType('std::hash_set<int>','#include <hash_set>','C++')
# whether the compiler has the standard iterator
ac['HAVE_STD_ITERATOR']     = conf.CheckType('std::iterator<int,int,int>','#include <iterator>','C++')
# whether the compiler has the bidirectional iterator
ac['HAVE_BI_ITERATOR']      = conf.CheckType('std::bidirectional_iterator<int,int>','#include <iterator>','C++')
# whether the compiler has forward iterators
ac['HAVE_FWD_ITERATOR']     = conf.CheckType('std::forward_iterator<int,int>','#include <iterator>','C++')

#AC_FUNC_ISNAN
##ac['HAVE_ISNAN_IN_MATH_H'] = conf.CheckFunc('isnan', '#include <math.h>', 'C++')
ac['HAVE_ISNAN_IN_MATH_H'] = conf.CheckLibWithHeader(None, 'math.h', 'C++', 'isnan(0.0);');
##ac['HAVE_ISNAN_IN_CMATH'] = conf.CheckFunc('isnan', '#include <cmath>', 'C++')
ac['HAVE_ISNAN_IN_CMATH_H'] = conf.CheckLibWithHeader(None, 'cmath.h', 'C++', 'isnan(0.0);');
##ac['HAVE_STD_ISNAN_IN_CMATH'] = conf.CheckFunc('std::isnan','#include <cmath>','C++')
ac['HAVE_STD_ISNAN_IN_CMATH'] = conf.CheckLibWithHeader(None, 'cmath.h', 'C++', 'std::isnan(0.0);');
# This is a test added by me
ac['HAVE__ISNAN_IN_FLOAT_H'] = conf.CheckLibWithHeader(None, 'float.h', 'C++', '_isnan(0.0);');
#AC_FUNC_ISINF
##ac['HAVE_ISINF_IN_MATH_H'] = conf.CheckFunc('isinf', '#include <math.h>', 'C++')
ac['HAVE_ISINF_IN_MATH_H'] = conf.CheckLibWithHeader(None,'math.h','C++','isinf(0.0);')
##ac['HAVE_ISINF_IN_CMATH'] = conf.CheckFunc('isinf', '#include <cmath>', 'C++')
ac['HAVE_ISINF_IN_CMATH'] = conf.CheckLibWithHeader(None,'cmath.h','C++','isinf(0.0);')
##ac['HAVE_STD_ISINF_IN_CMATH'] = conf.CheckFunc('std::isinf','#include <cmath>','C++')
ac['HAVE_STD_ISINF_IN_CMATH'] = conf.CheckLibWithHeader(None,'cmath.h','C++','std::isinf(0.0);')
##ac['HAVE_FINITE_IN_IEEEFP_H'] = conf.CheckFunc('finite','#include <ieeefp.h>','C++')
ac['HAVE_FINITE_IN_IEEEFP_H'] = conf.CheckLibWithHeader(None,'ieeefp.h','C++','finite(0.0);')
# This is a test added by me
ac['HAVE__FINITE_IN_FLOAT_H'] = conf.CheckLibWithHeader(None,'float.h','C++','_finite(0.0);')

# -- Checks for library functions.
#AC_FUNC_ALLOCA
#AC_FUNC_MMAP
#if test "$ac_cv_func_mmap_fixed_mapped" = "no"
#then
#	AC_MSG_WARN([mmap() required but not found])
#fi
#AC_FUNC_MMAP_FILE
#if test "$ac_cv_func_mmap_file" = "no"
#then
#	AC_MSG_WARN([mmap() of files required but not found])
#fi
#AC_HEADER_MMAP_ANONYMOUS
#AC_TYPE_SIGNAL
#AC_CHECK_FUNCS(getcwd gettimeofday strdup strtoq strtoll backtrace isatty mkstemp getrusage)
#AC_CHECK_FUNC(mprotect,,AC_MSG_ERROR([Function mprotect() required but not found]))

#dnl Check whether they want to do an optimized build:
#AC_ARG_ENABLE(optimized,AC_HELP_STRING([--enable-optimized],[Compile with optimizations enabled (default is NO)]),,enableval=no)
#if test ${enableval} = "no"
#then
#	AC_SUBST(ENABLE_OPTIMIZED,[[]])
#else
#	AC_SUBST(ENABLE_OPTIMIZED,[[ENABLE_OPTIMIZED=1]])
#fi

#dnl JIT Option
#AC_ARG_ENABLE(jit,AC_HELP_STRING([--enable-jit],[Enable Just In Time Compiling (default is YES)]),,enableval=default)
#if test ${enableval} = "no"
#then
#	AC_SUBST(JIT,[[]])
#else
#	case $target in
#		*i*86*)
#			AC_SUBST(JIT,[[TARGET_HAS_JIT=1]])
#			;;
#		*sparc*)
#			AC_SUBST(JIT,[[TARGET_HAS_JIT=1]])
#			;;
#		*)
#			AC_SUBST(JIT,[[]])
#			;;
#	esac
#fi

env = conf.Finish()

# XXX Waiting for config.h.in patch
env['CPPDEFINES'].append( 'HAVE__FINITE_IN_FLOAT_H' ) 
env['CPPDEFINES'].append( 'HAVE__ISNAN_IN_FLOAT_H' )
#~ env['CPPDEFINES'].append( 'HAVE_WINDOWS_H' ) # ummmm this MUST already work!! it's checked above!

# ----------------------------------------------- Variant VC7.1 debug
env['VARIANT'] = 'vc71'
env['SRCDIR'] = 'llvm'
env.Append(CPPFLAGS=[
    '/Zi',         # Generates complete debugging information
    '/Yd',         # Places complete debugging information in all object files
])
env.Append(LINKFLAGS=[
    '/DEBUG'
])

# ----------------------------------------------- Build sconscript
# XXX That section must be moved to an external sconscript. Parameters are ALL set above.
env['BUILDDIR'] = 'build_'+env['VARIANT']
env.BuildDir(env['BUILDDIR'],'llvm',duplicate=0)

# -- configure files
# Emit processed include files from templates
env.ac_config_header('llvm/include/llvm/Config/config.h','llvm/include/llvm/Config/config.h.in')
env.ac_config_header('llvm/include/llvm/ADT/iterator','llvm/include/llvm/ADT/iterator.in')
env.ac_config_header('llvm/include/llvm/Support/DataTypes.h','llvm/include/llvm/Support/DataTypes.h.in')
### TBD include/llvm/Support/ThreadSupport.h
env.ac_config_header('llvm/include/llvm/ADT/hash_map','llvm/include/llvm/ADT/hash_map.in')
env.ac_config_header('llvm/include/llvm/ADT/hash_set','llvm/include/llvm/ADT/hash_set.in')

# ---------------------- Platform
# win32 has no symbolic links, so we copy the files in the platform directory
# XXX change the source dir according to platform peeping in the env
env.Install('llvm/lib/System/platform', srcs2('llvm/lib/System/Win32/*.*') )

# ---------------------- Libraries
analysis = env.StaticLibrary('analysis',srcs('llvm/lib/Analysis/*.cpp', [
    'llvm/lib/Analysis/IntervalPartition.cpp', # llvm\include\llvm\Analysis\IntervalIterator.h(224) : error C2660: 'remove' : function does not take 3 arguments
    'llvm/lib/Analysis/PostDominators.cpp', # (81) : error C2784: 'void llvm::set_intersect(S1Ty<ETy> &,const S2Ty &)' : could not deduce template argument for 'S1Ty<T2> &' from 'llvm::DominatorSetBase::DomSetType
    'llvm/lib/Analysis/ScalarEvolution.cpp', # llvm\lib\Analysis\ScalarEvolution.cpp(1850) : error C2668: 'sqrt' : ambiguous call to overloaded function
                                             # could be 'long double sqrt(long double)'
                                             # or       'float sqrt(float)'
                                             # or       'double sqrt(double)'
                                             # while trying to match the argument list '(uint64_t)'
]))
ipa = env.StaticLibrary('ipa',srcs('llvm/lib/Analysis/IPA/*.cpp'))
datastructure = env.StaticLibrary('datastructure',srcs('llvm/lib/Analysis/Datastructure/*.cpp'))
bcreader = env.StaticLibrary('bcreader',srcs('llvm/lib/Bytecode/Reader/*.cpp',[
    'llvm/lib/Bytecode/Reader/Reader.cpp', # llvm\lib\Bytecode\Reader\Reader.cpp(1484) : error C2057: expected constant expression
]))
bcwriter = env.StaticLibrary('bcwriter',srcs('llvm/lib/Bytecode/Writer/*.cpp'))
codegen = env.StaticLibrary('codegen',srcs('llvm/lib/CodeGen/*.cpp',[
    'llvm/lib/CodeGen/LiveInterval.cpp',  # Reason: cannot convert from 'const unsigned int' to 'const _STL::iterator_traits<_Iterator>::value_type' No constructor could take the source type, or constructor overload resolution was ambiguous
    'llvm/lib/CodeGen/LiveVariables.cpp', # MachineInstr *PhysRegInfoA[RegInfo->getNumRegs()]; C99
    'llvm/lib/CodeGen/LiveIntervalAnalysis.cpp' # (159) : error C2668: 'pow' : ambiguous call to overloaded function
                                                # C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\../include\math.h(572): could be 'float pow(float,int)'
                                                # C:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\../include\math.h(570): or       'float pow(float,float)'
                                                # while trying to match the argument list '(float, unsigned int)'
                                                # found no solution. Cast to float,int ?
]))
sched = env.StaticLibrary('sched',srcs('llvm/lib/CodeGen/InstrSched/*.cpp'))
modulosched = env.StaticLibrary('modulosched',srcs('llvm/lib/CodeGen/ModuloScheduling/*.cpp'))
selectiondag = env.StaticLibrary('selectiondag',srcs("llvm/lib/CodeGen/SelectionDAG/*.cpp"))
executionengine = env.StaticLibrary('executionengine',srcs('llvm/lib/ExecutionEngine/*.cpp'))
lli_interpreter = env.StaticLibrary('lli_interpreter',srcs('llvm/lib/ExecutionEngine/Interpreter/*.cpp',[
    'llvm/lib/ExecutionEngine/Interpreter/ExternalFunctions.cpp', # (220) : error C3861: 'drand48': identifier not found, even with argument-dependent lookup
]))
lli_jit = env.StaticLibrary('lli_jit',srcs('llvm/lib/ExecutionEngine/JIT/*.cpp'))
support = env.StaticLibrary('support',srcs('llvm/lib/Support/*.cpp', [
    'llvm/lib/Support/CommandLine.cpp', # 'PACKAGE_VERSION' : undeclared identifier - must define throw configure process.. TBD
    'llvm/lib/Support/DynamicLinker.cpp', #llvm\lib\Support\DynamicLinker.cpp(58) : error C3861: 'sprintf': identifier not found, even with argument-dependent lookup
    'llvm/lib/Support/FileUtilities.cpp',
    'llvm/lib/Support/SlowOperationInformer.cpp',
    'llvm/lib/Support/SystemUtils.cpp',
    'llvm/lib/Support/ToolRunner.cpp',
]))

llvmsystem = env.StaticLibrary('llvmsystem', srcs('llvm/lib/System/*.cpp',[
    'llvm/lib/System/Signals.cpp', # waiting for commit of patches
    'llvm/lib/System/TimeValue.cpp', # llvm\lib\System\TimeValue.cpp(19) : error C2065: 'INT64_MIN' : undeclared identifier
]))
env.Depends(llvmsystem,'llvm/lib/System/platform')

target = env.StaticLibrary('target',srcs('llvm/lib/Target/*.cpp'))
cwriter = env.StaticLibrary('cwriter',srcs('llvm/lib/Target/CBackend/*.cpp'))
x86 = env.StaticLibrary('x86',srcs('llvm/lib/Target/X86/*.cpp',[
    'llvm/lib/Target/X86/X86CodeEmitter.cpp', # llvm\lib\Target\X86\X86CodeEmitter.cpp(106) : error C3861: '__builtin_frame_address': identifier not found, even with argument-dependent lookup
]))
transforms = env.StaticLibrary('transforms',srcs('llvm/lib/Transforms/*.cpp'))
scalaropts = env.StaticLibrary('scalaropts',srcs('llvm/lib/Transforms/Scalar/*.cpp',[
    'llvm/lib/Transforms/Scalar/LoopSimplify.cpp', # LoopSimplify.cpp(666) : error C2784: 'void llvm::set_intersect(S1Ty<ETy> &,const S2Ty &)' : could not deduce template argument for 'S1Ty<T2> &' from 'llvm::DominatorSetBase::DomSetType'
    'llvm/lib/Transforms/Scalar/LowerGC.cpp', #llvm\lib\Transforms\Scalar\LowerGC.cpp(146) : error C2371: 'C' : redefinition; different basic types
]))
transformutils = env.StaticLibrary('transformutils',srcs('llvm/lib/Transforms/Utils/*.cpp'))
vmcore = env.StaticLibrary('vmcore',srcs('llvm/lib/VMCore/*.cpp'))

# ---------------------------------------------------------------------------

# -- TableGen
###env.CFile(target = 'llvm/utils/TableGen/FileLexer.cpp', source = 'llvm/utils/TableGen/FileLexer.l')
###env.CFile(target = 'llvm/utils/TableGen/FileParser.h', source = 'llvm/utils/TableGen/FileParser.y')
###prg_TableGen = env.Program('TableGen', srcs('llvm/utils/TableGen/*.cpp')+[
###    support, llvmsystem,
###    ],
###    LINKFLAGS = env['LINKFLAGS']+ "/NODEFAULTLIB:LIBCMT",
###    CPPPATH = env['CPPPATH']+['llvm/utils/TableGen']
### 
###)

# -- HowToUseJIT
prg_HowToUseJIT = env.Program('HowToUseJIT', srcs('llvm/examples/HowToUseJIT/*.cpp')+[
    lli_jit, lli_interpreter,codegen, executionengine, x86, cwriter, selectiondag, scalaropts, analysis, transformutils, bcreader, target, vmcore, support, llvmsystem
    ],
    LINKFLAGS = env['LINKFLAGS']+ "/NODEFAULTLIB:LIBCMT",
    CPPDEFINES=env['CPPDEFINES']+[('ENABLE_X86_JIT',1)]
)
env.MSVSProject(target = 'HowToUseJIT'+env['MSVSPROJECTSUFFIX'], buildtarget = prg_HowToUseJIT, variant = 'Debug',
    srcs = srcs2('llvm/examples/HowToUseJIT/*.cpp'),
    incs = srcs2('llvm/examples/HowToUseJIT/*.h')
)

# -- Fibonacci
env.Program('fibonacci', srcs('llvm/examples/Fibonacci/*.cpp')+['llvm/lib/System/Win32/Memory.cpp','llvm/lib/System/Win32/Process.cpp']+[
    lli_jit, lli_interpreter, codegen, executionengine, x86, selectiondag, scalaropts, analysis, transformutils, bcreader, target, vmcore, support
    ],
    LINKFLAGS = env['LINKFLAGS']+ "/NODEFAULTLIB:LIBCMT",
    CPPDEFINES=env['CPPDEFINES']+[('ENABLE_X86_JIT',1)]
)

