#*******************************************************************************
#
#     SCons main construct file
#
#     Processor: Blackfin (Analog Devices Inc.)
#
#     Toolkit:   bfin-elf
#
#     Copyright (c) 2006-2016, Harry E. Zhurov
#
#-------------------------------------------------------------------------------

import os
import sys
import glob
import subprocess
import re

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

import utils
import scmIC

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

RootDir      = '../../../../../'                          
SourceDirs   = ['src',
                '../lib',
                '../lib/io',
                RootDir + 'scmRTOS/core',
                RootDir + 'scmRTOS/ext/profiler',
                RootDir + 'scmRTOS/port/blackfin/gcc']
                
#--------------------------------------
#
#     Options
#
Processor    = 'bf533'
DeviceRev    = '0.6'

Optimization =' -O2 '
DebugLevel   =' -g3 '          # empty to disable debug info

ExtraAFlags  = ' '
ExtraCFlags  = ' '
ExtraLFlags  = ' '


#===============================================================================
#
#     !!! Service Area !!!
#
#
#-------------------------------------------------------------------------------
#
#    Project structure
#
ConfigDir   = 'cfg'
ScriptDir   = 'cfg/script'
ObjDir      = 'obj'
ListDir     = 'lst'
BinDir      = 'bin'

ServiceDirs = [ ObjDir, ListDir, BinDir ]
for i in ServiceDirs:
    curdir = os.path.join( os.getcwd(), 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'
MapExt  = 'map'
BinExt  = 'elf'
HexExt  = 'ldr'
LdExt   = 'ld'

#-------------------------------------------------------------------------------
#
#      Toolkit
#
TOOLKIT_PATH = os.environ['CAD'] + '/adi/bfin-elf'

ASM      = TOOLKIT_PATH + '/bin/bfin-elf-as'
CC       = TOOLKIT_PATH + '/bin/bfin-elf-gcc'
Linker   = TOOLKIT_PATH + '/bin/bfin-elf-ld'
OBJDUMP  = TOOLKIT_PATH + '/bin/bfin-elf-objdump'
LOADER   = TOOLKIT_PATH + '/bin/bfin-elf-ldr'
SIZE     = TOOLKIT_PATH + '/bin/bfin-elf-size'

ToolkitIncPath = [TOOLKIT_PATH] # + '/bfin-elf/include'] #,  TOOLKIT_PATH + '/Blackfin/lib/src/libio']

ToolkitLibPath = [TOOLKIT_PATH + '/bfin-elf/lib']

#-------------------------------------------------------------------------------
#
#      Tools options
#
#----------------------------------------------------
#
#      Include, library, config files and paths
#
IncludePath = ''.join(' -I' + i for i in SourceDirs + ToolkitIncPath)
LibraryPathOptions = ''#.join(' -L ' + i for i in ToolkitLibPath)


LinkerMainScript   = os.path.join(ConfigDir, Processor + 'cpp.' + LdExt)

#-------------------------------------------------------------------------------
#
#    The options
#
COMMON_FLAGS = ' -mcpu=' + Processor + '-' + DeviceRev

AFLAGS  = COMMON_FLAGS
AFLAGS += IncludePath
AFLAGS += DebugLevel  
AFLAGS += ' -x assembler-with-cpp'
AFLAGS += ' -c '
AFLAGS += ExtraAFlags
#-------------------------------------------------------------------------------
CFLAGS    = COMMON_FLAGS
#CFLAGS   += ' -fmessage-length=0'        # output messages in one line
CFLAGS   += ' -c'                         # Compiles and/or assembles only, but does not link
CFLAGS   += DebugLevel
CFLAGS   += ' -pipe'
CFLAGS   += ' -ffunction-sections -fdata-sections'
CFLAGS   += IncludePath + Optimization + ExtraCFlags
CFLAGS   += ' -DPRINTF_FLOAT'
CPPFLAGS  = CFLAGS 
CPPFLAGS += ' -fno-exceptions -fno-rtti'

#-------------------------------------------------------------------------------
LFLAGS  = COMMON_FLAGS
LFLAGS += ' -Wl,--gc-sections'
LFLAGS += ' -T' + LinkerMainScript
LFLAGS += ' -L '   + ObjDir + LibraryPathOptions
LFLAGS += ' -nostartfiles'
LFLAGS += ' -lm'
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 '*'*61

    #------------------------------------------------
    #
    #   Launch compiler
    #
    print 'Compile:  ' + os.path.basename( str(source[0]) )
    cmd = env['CC'] + env['CFLAGS'] + ' -o ' + str(target[0]) + ' ' + str(source[0])
    rcode, out, err = utils.pexec(cmd)
    out += err
    if out:        print out.replace('`', '\'')
    if rcode != 0: return rcode

    #------------------------------------------------
    #
    #   Create listing
    #
    cmd = env['OBJDUMP'] + ' -dCS ' + str(target[0])
    rcode, out, err = utils.pexec(cmd)
    open( os.path.join( ListDir, utils.namegen(str(source[0]), LstExt) ), 'wb' ).write(out)
#-------------------------------------------------------------------------------
#
#    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 '*'*61

    #------------------------------------------------
    #
    #   Launch compiler
    #
    print 'Compile:  ' + os.path.basename( str(source[0]) )
    cmd = env['CC'] + env['CPPFLAGS'] + ' -o ' + str(target[0]) + ' ' + str(source[0])
    rcode, out, err = utils.pexec(cmd)
    out += err
    if out:        print out.replace('`', '\'')
    if rcode != 0: return rcode

    #------------------------------------------------
    #
    #   Create listing
    #
    cmd = env['OBJDUMP'] + ' -dCS ' + str(target[0])
    rcode, out, err = utils.pexec(cmd)
    open( os.path.join( ListDir, utils.namegen(str(source[0]), LstExt) ), 'wb' ).write(out)
#-------------------------------------------------------------------------------
#
#    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 '*'*61

    #------------------------------------------------
    #
    #   Launch assembler
    #
    src_name = str(source[0])
    print 'Assembly: ' + os.path.basename( src_name ) 
    cmd = env['CC'] + env['AFLAGS'] + ' -o ' + str(target[0]) + ' ' + src_name
    rcode, out, err = utils.pexec(cmd)
    out += err
    if out:        print out.replace('`', '\'')
    if rcode != 0: return rcode

    #------------------------------------------------
    #
    #   Create listing
    #
    cmd = env['OBJDUMP'] + ' -dC ' + str(target[0])
    rcode, out, err = utils.pexec(cmd)
    open( os.path.join( ListDir, utils.namegen(str(source[0]), LstExt) ), 'wb' ).write(out)
#-------------------------------------------------------------------------------
#
#    Build executable file from object files
#
def build_bin(target, source, env):
    if env['FIRST_ENTRY'] == False:
        print '*'*61
        print 'Sources are up to date'

    print '*'*61
    print 'Link: ' + str(target[0])

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

    #-------------------------------------------------------------
    #
    #    Link executable
    #
    cmd  =  env['CC'] + ' -o ' + str(target[0]) + src_list + env['LFLAGS'] +\
           ' -Wl,-Map=' + os.path.join(ListDir, utils.namegen(str(target[0]), MapExt)) + ',--cref'
    rcode, out, err = utils.pexec(cmd)
    out += err
    if out: print out.replace('`', '\'')

    #print '*'*61
    if rcode != 0:
        return rcode

    #------------------------------------------------
    #
    #   Create listing
    #
    cmd = env['OBJDUMP'] + ' -dCS ' + str(target[0])
    rcode, out, er = utils.pexec(cmd)
    open( os.path.join( ListDir, utils.namegen(str(target[0]), LstExt) ), 'wb' ).write(out)

    #------------------------------------------------
    #
    #   Print sizes
    #
    cmd = env['SIZE'] + ' --format=sysv ' + str(target[0])
    rcode, out, er = utils.pexec(cmd)
    utils.print_size(out)

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


#-------------------------------------------------------------------------------
#
#   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)
#-------------------------------------------------------------------------------
binBuilder = Builder(action     = build_bin,
                     suffix     = BinExt,
                     src_suffix = ObjExt)

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

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

env = Environment(TOOLS = {})

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

env['ASM'          ] = ASM
env['CC'           ] = CC
env['LINKER'       ] = Linker
env['LOADER'       ] = LOADER
env['OBJDUMP'      ] = OBJDUMP
env['SIZE'         ] = SIZE
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 + '/' + name
        Depends(targets[i],  'SConstruct')      

    return targets

#-------------------------------------------------------------------------------
#
#   Make objects from sources
#
def make_objects(asm, cpp, c = {}):
    obj_list = []

    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]) )

    for i in c.items():
        obj_list.append( env.cObj(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.elf( source = obj_list, target = os.path.join(BinDir, ProjectName) )
Depends(main_trg, 'cfg/bf533cpp.ld')

#-------------------------------------------------------------------------------
#
#    Service targets
#
#---------  Runtime init  --------------
rt_asm_files = ['crt_init.S']
rt_cpp_files = ['main_init.cpp', 'hwinit.cpp']
          
rt_asm   = make_target_dict( [os.path.join('src/utils', i) for i in rt_asm_files] )
rt_cpp   = make_target_dict( [os.path.join('src/utils', i) for i in rt_cpp_files] )
rt_obj   = make_objects(rt_asm, rt_cpp)
init_trg = env.elf( source = rt_obj, target = os.path.join(BinDir, 'rtinit.elf') )
Depends(init_trg, 'cfg/bf533cpp.ld')

#-------------------------------------------------------------------------------
#
#    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(ObjDir,    'ii')
    remove_files(ObjDir,    'ti')
    remove_files(BinDir,    BinExt)
    remove_files(BinDir,    HexExt)
    remove_files(ListDir,   'lst')
    remove_files(ConfigDir, 'is')
    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, os.path.join(BinDir, ProjectName) + '.' + BinExt)
rtinit    = env.Alias('rtinit', os.path.join(BinDir, 'rtinit') + '.' + BinExt)
clean_all = env.Alias('cln',      action = clean)
ic        = env.Alias('ic',       action = check_scmRTOS_cfg)

all = [main, rtinit, ic]

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

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

Default(all)

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

