#*******************************************************************************
#
#     SCons main construct file
#
#     Processor: STM32F2xx (ST Microelecronics)
#
#     Toolkit:   EWARM
#
#     Copyright (c) 2006-2007, Harry E. Zhurov
#
#
#     $Date:: 2012-08-16 #$
#     $Rev: 270 $
#
#-------------------------------------------------------------------------------

import os
import sys
import glob
import subprocess
import re

sys.path.append(os.getcwd() + '/cfg/script')

import scmIC

#===============================================================================
#
#     User definable area
#
#-------------------------------------------------------------------------------
#
#     Project settings
#
ProjectName  = 'slon'

SourceDirs   = ['src',
                '../../../../../scmRTOS/core',
                '../../../../../scmRTOS/port/cortex/m3-iar',
                '../../../../../scmRTOS/ext/profiler',
                '../../../../../scmRTOS/ext/round-robin',
                'lib']
                
#--------------------------------------
#
#     Options
#
Processor    = 'stm32f205xe'

#Optimization = ' '
Optimization =' -Ohs '

ExtraAFlags  = ' '
ExtraCFlags  = ' --debug'
ExtraLFlags  = ' '


#--------------------------------------
#
#     User configuration data
#
user_config_data = { }

                                                                                

#===============================================================================
#
#     !!! Service Area !!!
#
#
#-------------------------------------------------------------------------------
#
#    Build number
#
#p = subprocess.Popen('svnversion.exe', stdin  = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE )
#out = p.communicate()[0]
#
#VersionFileContents  = '#ifndef VERSION_H' + os.linesep
#VersionFileContents += '#define VERSION_H' + os.linesep
#VersionFileContents += 'static const char * SOFTWARE_REVISION = "' + out.strip() + '";' + os.linesep
#VersionFileContents += '#endif' + os.linesep
#
#print 'Build: ' + out.strip()
#
#open('src/version.h', 'wb').write(VersionFileContents)

#-------------------------------------------------------------------------------
#
#    Project structure
#
ConfigDir   = 'cfg'
ScriptDir   = 'cfg/script'
ObjDir      = 'obj'
ListDir     = 'lst'
ExeDir      = 'bin'
ToolsSrcDir = 'src/tools'

ServiceDirs = [ ObjDir, ListDir, ExeDir ]
for i in ServiceDirs:
    curdir = os.getcwd() + os.sep + i
    if not os.path.exists(curdir):
        print 'Directory ' + curdir + ' does not exists. Creating the directory...',
        os.mkdir(curdir)
        print 'done'

#-------------------------------------------------------------------------------
#
#    Platform specific stuff
#
AsmExt  = 's'
CExt    = 'c'
CppExt  = 'cpp'
ObjExt  = 'o'
LstExt  = 'lst'
DbgExt  = 'out'
HexExt  = 'hex'

#-------------------------------------------------------------------------------
#
#      Toolkit and Paths
#
IAR = os.environ['IAR']
TOOLKIT_PATH = IAR + os.sep + 'ewarm630' + os.sep + 'arm'

TOOLKIT_PATH_BIN = TOOLKIT_PATH + os.sep + 'bin'
TOOLKIT_PATH_INC = TOOLKIT_PATH + os.sep + 'inc'
TOOLKIT_PATH_LIB = TOOLKIT_PATH + os.sep + 'lib'

ASM      = TOOLKIT_PATH_BIN + os.sep + 'iasmarm.exe'
CC       = TOOLKIT_PATH_BIN + os.sep + 'iccarm.exe'
Linker   = TOOLKIT_PATH_BIN + os.sep + 'ilinkarm.exe'

ToolkitIncPath = [TOOLKIT_PATH_INC + os.sep + 'ST', 
                  TOOLKIT_PATH_INC + os.sep + 'c', 
                  TOOLKIT_PATH_INC + os.sep + 'cpp',
                  '../st/inc']

ToolkitLibPath = [TOOLKIT_PATH_LIB]

#-------------------------------------------------------------------------------
#
#      Tools options
#
#----------------------------------------------------
#
#      Include, library, config files and paths
#
IncludePath = ''.join(' -I' + i for i in SourceDirs + ToolkitIncPath)
#IncludePathAsm = ''.join(' -flags-asm -I' + i for i in SourceDirs + ToolkitIncPath)
LibraryPath = ''.join(' --search ' + i for i in ToolkitLibPath)


LinkerConfig  = ConfigDir + os.sep + Processor + '.icf'

#----------------------------------------------------
#
#      Define macros for source files
#

compiler_macros = ' '
linker_macros = ' '
linker_macros1 = ' '
                                             
for key in user_config_data:
    compiler_macros += '-D' + key + '=' + user_config_data[key] + ' '                                              
    #linker_macros += '-MD' + key + '=' + user_config_data[key] + ' '                                              
    linker_macros += ' -flags-link -MD' + key + '=' + user_config_data[key] + ' '                                              

#-------------------------------------------------------------------------------
#
#    The options
#
COMMON_FLAGS = ' '

AFLAGS  = COMMON_FLAGS
AFLAGS += ' --cpu Cortex-M3'
AFLAGS += ' --fpu None' 
AFLAGS += ' -L' + ListDir + os.sep + ' -O' + ObjDir + os.sep + ' -M() -S -r -t4'
AFLAGS += ' -w+'
AFLAGS += IncludePath
AFLAGS += ExtraAFlags
#-------------------------------------------------------------------------------
CFLAGS  = COMMON_FLAGS
CFLAGS += ' --cpu=Cortex-M3'
CFLAGS += ' --cpu_mode=thumb '     
CFLAGS += ' --dlib_config ' + TOOLKIT_PATH_INC + os.sep + 'c' + os.sep + 'DLib_Config_Normal.h'               
CFLAGS += ' -e'
CFLAGS += ' '  #' --header_context'
CFLAGS += ' -lC ' + ListDir
CFLAGS += ' --macro_positions_in_diagnostics'
CFLAGS += ' --no_system_include'
CFLAGS += ' -o ' + ObjDir
CFLAGS += ' --preprocess=c ' + ListDir
CFLAGS += ' --relaxed_fp' + ' --silent'
CFLAGS += ' --use_unix_directory_separators'
CFLAGS += ' --no_wrap_diagnostics'
CFLAGS += ' '
CFLAGS += compiler_macros
CFLAGS += IncludePath + Optimization + ExtraCFlags
CPPFLAGS = CFLAGS + ' --c++' + ' --no_exceptions'
#-------------------------------------------------------------------------------
LFLAGS  = COMMON_FLAGS
LFLAGS += ' --search ' + ObjDir + LibraryPath
LFLAGS += ' --redirect _Printf=_PrintfLargeNoMb'
LFLAGS += ' --redirect _Scanf=_ScanfLargeNoMb'
LFLAGS += ' --redirect __write=__write_buffered'
LFLAGS += ' --semihosting'
LFLAGS += ' --entry __iar_program_start'                 
LFLAGS += ' --config ' + LinkerConfig
#LFLAGS += ' --call_graph ' + ListDir
#LFLAGS += ' --enable_stack_usage'
LFLAGS += ' --map ' + ListDir
#LFLAGS += ' -o ' + ExeDir
#LFLAGS += ' --stack_usage_control'
LFLAGS += ' '
LFLAGS += linker_macros       # dlpp7M_tl_ne.a  m7M_tl.a  rt7M_tl.a  shb_l.a
LFLAGS += ExtraLFlags
#-------------------------------------------------------------------------------

#-------------------------------------------------------------------------------
#
#    Action functions
#
#-------------------------------------------------------------------------------
#
#    Build object file from c source file
#
def compile_c(target, source, env):
    #------------------------------------------------
    #
    #   Check if the first source is compiling
    #
    if env['FIRST_ENTRY'] == False:
        env['FIRST_ENTRY'] = True
        print '*'*60

    #------------------------------------------------
    #
    #   Launch compiler
    #
    print 'Compile:  ' + str(source[0])
    cmd = env['CC'] + env['CFLAGS'] + ' ' + str(source[0])

    p = subprocess.Popen( cmd )
    rcode = p.wait()

    if rcode != 0:
        return rcode

#-------------------------------------------------------------------------------
#
#    Build object file from cpp source file
#
def compile_cpp(target, source, env):
    #------------------------------------------------
    #
    #   Check if the first source is compiling
    #
    if env['FIRST_ENTRY'] == False:
        env['FIRST_ENTRY'] = True
        print '*'*60

    #------------------------------------------------
    #
    #   Launch compiler
    #
    print 'Compile:  ' + str(source[0])
    cmd = env['CC'] + env['CPPFLAGS'] + ' ' + str(source[0])
    #print cmd
  #  p = subprocess.Popen(cmd, universal_newlines = True,
  #                       stdin  = subprocess.PIPE,
  #                       stdout = subprocess.PIPE,
  #                       stderr = subprocess.PIPE )

  #  out, err = p.communicate()
  #  out += err

  #  print out,

  #  rcode = p.returncode

    p = subprocess.Popen( cmd )
    rcode = p.wait()

    if rcode != 0:
        return rcode

#-------------------------------------------------------------------------------
#
#    Build object file from assembler source file
#
def assembly_src(target, source, env):
    #------------------------------------------------
    #
    #   Check if the first source is compiling
    #
    if env['FIRST_ENTRY'] == False:
        env['FIRST_ENTRY'] = True
        print '*'*60

    #------------------------------------------------
    #
    #   Launch assembler
    #
    src_name = str(source[0])
    print 'Assembly: ' + src_name
    #cmd = env['ASM'] + env['AFLAGS'] + ' -o ' + str(target[0]) + ' ' + src_name
    cmd = env['ASM'] + env['AFLAGS'] + src_name
    #print cmd
    p = subprocess.Popen( cmd )

    p.wait()
    return p.returncode

#-------------------------------------------------------------------------------
#
#    Build binary file from object files
#
def BuildTarget(target, source, env):
    if env['FIRST_ENTRY'] == False:
        print '*'*60
        print 'Sources are up to date'

    check_scmRTOS_cfg(target, source, env)
    print '*'*60
    print 'Link: ' + str(target[0])

    src_list = ''
    for i in xrange(len(source)):
        src_list += ' ' + str(source[i])

    cmd = env['LINKER'] + env['LFLAGS'] + ' -o ' + str(target[0]) + src_list 
    #cmd = env['LINKER'] + env['LFLAGS'] + src_list 
    #print cmd
    p = subprocess.Popen( cmd )
    rcode = p.wait()

    print '*'*60
    return rcode

#-------------------------------------------------------------------------------
#
#   Builder objects
#
c2obj      = Builder(action         = compile_c,
                     suffix         = ObjExt,
                     src_suffix     = CExt,
                     source_scanner = CScanner)  
#-------------------------------------------------------------------------------
cpp2obj    = Builder(action         = compile_cpp,
                     suffix         = ObjExt,
                     src_suffix     = CppExt,
                     source_scanner = CScanner)  
#-------------------------------------------------------------------------------
asm2obj    = Builder(action         = assembly_src,
                     suffix         = ObjExt,
                     src_suffix     = AsmExt,
                     source_scanner = CScanner)

#-------------------------------------------------------------------------------
build_exe = Builder(action       = BuildTarget,
                    suffix       = DbgExt,
                    src_suffix   = ObjExt)

#-------------------------------------------------------------------------------
#
#    State variables
#
FirstEntry = False

#-------------------------------------------------------------------------------
#
#    Environment
#

env = Environment(TOOLS = {})

env['BUILDERS'] = {
                     'asmObj' : asm2obj,
                     'cObj'   : c2obj,
                     'cppObj' : cpp2obj,
                     'Exe'    : build_exe
                  }

env['ASM'          ] = ASM
env['CC'           ] = CC
env['LINKER'       ] = Linker
env['AFLAGS'       ] = AFLAGS
env['CFLAGS'       ] = CFLAGS
env['CPPFLAGS'     ] = CPPFLAGS
env['LFLAGS'       ] = LFLAGS
env['SRC_PATH'     ] = SourceDirs[0:2] 
env['CPPPATH'      ] = SourceDirs + ToolkitIncPath
env['FIRST_ENTRY'  ] = FirstEntry

#-------------------------------------------------------------------------------
#
#    Service functions
#
#-------------------------------------------------------------------------------
#
#   Make dictionary in form { src : obj } from input source list
#
def make_target_dict(src_list):
    targets = {}

    for i in src_list:
        name_ext = os.path.split(i)[1]
        name     = os.path.splitext(name_ext)[0] + '.' + ObjExt
        targets[i] = ObjDir + os.sep + name
        Depends(targets[i],  'SConstruct')      
        Depends(targets[i],  LinkerConfig)      

    return targets

#-------------------------------------------------------------------------------
#
#   Make objects from sources
#
def make_objects(asm, cpp, c = {}):
    obj_list = []
    for i in c.items():
        obj_list.append( env.cObj(i[1], i[0]) )

    for i in cpp.items():
        obj_list.append( env.cppObj(i[1], i[0]) )

    for i in asm.items():
        obj_list.append( env.asmObj(i[1], i[0]) )

    return obj_list

#-------------------------------------------------------------------------------
#
#   Prepare source files list for object building
#
#-------------------------------------------------------------------------------
#
#    C source file list
#
c_files = []
for i in SourceDirs:
    c_files += glob.glob(i + '/*.' + CExt)

c = make_target_dict(c_files)

#-------------------------------------------------------------------------------
#
#    Cpp source file list
#
cpp_files = []
for i in SourceDirs:
    cpp_files += glob.glob(i + '/*.' + CppExt)

cpp = make_target_dict(cpp_files)
       
#-------------------------------------------------------------------------------
#
#    Asm source file list
#
asm_files = []
for i in SourceDirs:
    asm_files += glob.glob(i + '/*.' + AsmExt)

asm = make_target_dict(asm_files)

#-------------------------------------------------------------------------------
#
#    Objects builing
#
obj_list        = make_objects(asm, cpp, c)

#-------------------------------------------------------------------------------
#
#    Executable targets building
#
main_trg   = env.Exe(source = obj_list, target = ExeDir + os.sep + ProjectName)

#-------------------------------------------------------------------------------
#
#    Clean and Rebuid
#
def remove_files(dir_, mask):
    file_list = glob.glob(dir_ + '/*.' + mask)
    for i in file_list:
        os.remove(i)

#-------------------------------------------------------------------------------
def clean(target, source, env):
    print 'Cleaning targets...',
    if os.path.exists('.sconsign.dblite'): os.remove('.sconsign.dblite')

    remove_files(ObjDir,    ObjExt)
    remove_files(ObjDir,    's')
    remove_files(ObjDir,    'lst')
    remove_files(ExeDir,    DbgExt)
    remove_files(ExeDir,    HexExt)
    remove_files(ListDir,   'lst')
    remove_files(ScriptDir, 'pyc')
    print 'Done'

#-------------------------------------------------------------------------------
#
#
#
def check_scmRTOS_cfg(target, source, env):
    if env['FIRST_ENTRY'] == False:
        print '*'*60
        print 'Sources are up to date'

    print 'Run integrity checker...',
    rcode = scmIC.checker(env['SRC_PATH'])
    if rcode != 0:
        print '\n scmRTOS configuration is invalid.'

    print '*'*60

#-------------------------------------------------------------------------------
#
#       Targets
#
#-------------------------------------------------------
#
#   Intermediate targets
#
main     = env.Alias(ProjectName, ExeDir + os.sep + ProjectName + '.' + DbgExt)

clean_all = env.Alias('cln',      action = clean)
ic        = env.Alias('ic',       action = check_scmRTOS_cfg)

all = [main, ic]

#-------------------------------------------------------------------------------
#
#    Final targets
#
env.Alias('all', all)
env.Alias('rebuild', [clean_all, all])
env.Alias('load',    [main, ic])

env.AlwaysBuild('all', 'cln', 'ic')

#-------------------------------------------------------------------------------
              
#===============================================================================




