xref: /nrf52832-nimble/rt-thread/tools/building.py (revision 104654410c56c573564690304ae786df310c91fc)
1#
2# File      : building.py
3# This file is part of RT-Thread RTOS
4# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team
5#
6#  This program is free software; you can redistribute it and/or modify
7#  it under the terms of the GNU General Public License as published by
8#  the Free Software Foundation; either version 2 of the License, or
9#  (at your option) any later version.
10#
11#  This program is distributed in the hope that it will be useful,
12#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14#  GNU General Public License for more details.
15#
16#  You should have received a copy of the GNU General Public License along
17#  with this program; if not, write to the Free Software Foundation, Inc.,
18#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# Change Logs:
21# Date           Author       Notes
22# 2015-01-20     Bernard      Add copyright information
23# 2015-07-25     Bernard      Add LOCAL_CCFLAGS/LOCAL_CPPPATH/LOCAL_CPPDEFINES for
24#                             group definition.
25#
26
27import os
28import sys
29import string
30import utils
31
32from SCons.Script import *
33from utils import _make_path_relative
34from mkdist import do_copy_file
35
36BuildOptions = {}
37Projects = []
38Rtt_Root = ''
39Env = None
40
41# SCons PreProcessor patch
42def start_handling_includes(self, t=None):
43    """
44    Causes the PreProcessor object to start processing #import,
45    #include and #include_next lines.
46
47    This method will be called when a #if, #ifdef, #ifndef or #elif
48    evaluates True, or when we reach the #else in a #if, #ifdef,
49    #ifndef or #elif block where a condition already evaluated
50    False.
51
52    """
53    d = self.dispatch_table
54    p = self.stack[-1] if self.stack else self.default_table
55
56    for k in ('import', 'include', 'include_next', 'define'):
57        d[k] = p[k]
58
59def stop_handling_includes(self, t=None):
60    """
61    Causes the PreProcessor object to stop processing #import,
62    #include and #include_next lines.
63
64    This method will be called when a #if, #ifdef, #ifndef or #elif
65    evaluates False, or when we reach the #else in a #if, #ifdef,
66    #ifndef or #elif block where a condition already evaluated True.
67    """
68    d = self.dispatch_table
69    d['import'] = self.do_nothing
70    d['include'] =  self.do_nothing
71    d['include_next'] =  self.do_nothing
72    d['define'] =  self.do_nothing
73
74PatchedPreProcessor = SCons.cpp.PreProcessor
75PatchedPreProcessor.start_handling_includes = start_handling_includes
76PatchedPreProcessor.stop_handling_includes = stop_handling_includes
77
78class Win32Spawn:
79    def spawn(self, sh, escape, cmd, args, env):
80        # deal with the cmd build-in commands which cannot be used in
81        # subprocess.Popen
82        if cmd == 'del':
83            for f in args[1:]:
84                try:
85                    os.remove(f)
86                except Exception as e:
87                    print ('Error removing file: ' + e)
88                    return -1
89            return 0
90
91        import subprocess
92
93        newargs = ' '.join(args[1:])
94        cmdline = cmd + " " + newargs
95
96        # Make sure the env is constructed by strings
97        _e = dict([(k, str(v)) for k, v in env.items()])
98
99        # Windows(tm) CreateProcess does not use the env passed to it to find
100        # the executables. So we have to modify our own PATH to make Popen
101        # work.
102        old_path = os.environ['PATH']
103        os.environ['PATH'] = _e['PATH']
104
105        try:
106            proc = subprocess.Popen(cmdline, env=_e, shell=False)
107        except Exception as e:
108            print ('Error in calling command:' + cmdline.split(' ')[0])
109            print ('Exception: ' + os.strerror(e.errno))
110            if (os.strerror(e.errno) == "No such file or directory"):
111                print ("\nPlease check Toolchains PATH setting.\n")
112
113            return e.errno
114        finally:
115            os.environ['PATH'] = old_path
116
117        return proc.wait()
118
119# generate cconfig.h file
120def GenCconfigFile(env, BuildOptions):
121    import rtconfig
122
123    if rtconfig.PLATFORM == 'gcc':
124        contents = ''
125        if not os.path.isfile('cconfig.h'):
126            import gcc
127            gcc.GenerateGCCConfig(rtconfig)
128
129        # try again
130        if os.path.isfile('cconfig.h'):
131            f = open('cconfig.h', 'r')
132            if f:
133                contents = f.read()
134                f.close()
135
136                prep = PatchedPreProcessor()
137                prep.process_contents(contents)
138                options = prep.cpp_namespace
139
140                BuildOptions.update(options)
141
142                # add HAVE_CCONFIG_H definition
143                env.AppendUnique(CPPDEFINES = ['HAVE_CCONFIG_H'])
144
145def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = []):
146    import rtconfig
147
148    global BuildOptions
149    global Projects
150    global Env
151    global Rtt_Root
152
153    # ===== Add option to SCons =====
154    AddOption('--dist',
155                      dest = 'make-dist',
156                      action = 'store_true',
157                      default = False,
158                      help = 'make distribution')
159    AddOption('--dist-strip',
160                      dest = 'make-dist-strip',
161                      action = 'store_true',
162                      default = False,
163                      help = 'make distribution and strip useless files')
164    AddOption('--cscope',
165                      dest = 'cscope',
166                      action = 'store_true',
167                      default = False,
168                      help = 'Build Cscope cross reference database. Requires cscope installed.')
169    AddOption('--clang-analyzer',
170                      dest = 'clang-analyzer',
171                      action = 'store_true',
172                      default = False,
173                      help = 'Perform static analyze with Clang-analyzer. ' + \
174                           'Requires Clang installed.\n' + \
175                           'It is recommended to use with scan-build like this:\n' + \
176                           '`scan-build scons --clang-analyzer`\n' + \
177                           'If things goes well, scan-build will instruct you to invoke scan-view.')
178    AddOption('--buildlib',
179                      dest = 'buildlib',
180                      type = 'string',
181                      help = 'building library of a component')
182    AddOption('--cleanlib',
183                      dest = 'cleanlib',
184                      action = 'store_true',
185                      default = False,
186                      help = 'clean up the library by --buildlib')
187    AddOption('--target',
188                      dest = 'target',
189                      type = 'string',
190                      help = 'set target project: mdk/mdk4/mdk5/iar/vs/vsc/ua/cdk/ses')
191    AddOption('--genconfig',
192                dest = 'genconfig',
193                action = 'store_true',
194                default = False,
195                help = 'Generate .config from rtconfig.h')
196    AddOption('--useconfig',
197                dest = 'useconfig',
198                type = 'string',
199                help = 'make rtconfig.h from config file.')
200    AddOption('--verbose',
201                dest = 'verbose',
202                action = 'store_true',
203                default = False,
204                help = 'print verbose information during build')
205
206    Env = env
207    Rtt_Root = os.path.abspath(root_directory)
208
209    # make an absolute root directory
210    RTT_ROOT = Rtt_Root
211    Export('RTT_ROOT')
212
213    # set RTT_ROOT in ENV
214    Env['RTT_ROOT'] = Rtt_Root
215    # set BSP_ROOT in ENV
216    Env['BSP_ROOT'] = Dir('#').abspath
217
218    sys.path = sys.path + [os.path.join(Rtt_Root, 'tools')]
219
220    # {target_name:(CROSS_TOOL, PLATFORM)}
221    tgt_dict = {'mdk':('keil', 'armcc'),
222                'mdk4':('keil', 'armcc'),
223                'mdk5':('keil', 'armcc'),
224                'iar':('iar', 'iar'),
225                'vs':('msvc', 'cl'),
226                'vs2012':('msvc', 'cl'),
227                'vsc' : ('gcc', 'gcc'),
228                'cb':('keil', 'armcc'),
229                'ua':('gcc', 'gcc'),
230                'cdk':('gcc', 'gcc'),
231                'ses' : ('gcc', 'gcc')}
232    tgt_name = GetOption('target')
233
234    if tgt_name:
235        # --target will change the toolchain settings which clang-analyzer is
236        # depend on
237        if GetOption('clang-analyzer'):
238            print ('--clang-analyzer cannot be used with --target')
239            sys.exit(1)
240
241        SetOption('no_exec', 1)
242        try:
243            rtconfig.CROSS_TOOL, rtconfig.PLATFORM = tgt_dict[tgt_name]
244            # replace the 'RTT_CC' to 'CROSS_TOOL'
245            os.environ['RTT_CC'] = rtconfig.CROSS_TOOL
246            utils.ReloadModule(rtconfig)
247        except KeyError:
248            print ('Unknow target: '+ tgt_name+'. Avaible targets: ' +', '.join(tgt_dict.keys()))
249            sys.exit(1)
250    elif (GetDepend('RT_USING_NEWLIB') == False and GetDepend('RT_USING_NOLIBC') == False) \
251        and rtconfig.PLATFORM == 'gcc':
252        AddDepend('RT_USING_MINILIBC')
253
254    # auto change the 'RTT_EXEC_PATH' when 'rtconfig.EXEC_PATH' get failed
255    if not os.path.exists(rtconfig.EXEC_PATH):
256        if 'RTT_EXEC_PATH' in os.environ:
257            # del the 'RTT_EXEC_PATH' and using the 'EXEC_PATH' setting on rtconfig.py
258            del os.environ['RTT_EXEC_PATH']
259            utils.ReloadModule(rtconfig)
260
261    # add compability with Keil MDK 4.6 which changes the directory of armcc.exe
262    if rtconfig.PLATFORM == 'armcc':
263        if not os.path.isfile(os.path.join(rtconfig.EXEC_PATH, 'armcc.exe')):
264            if rtconfig.EXEC_PATH.find('bin40') > 0:
265                rtconfig.EXEC_PATH = rtconfig.EXEC_PATH.replace('bin40', 'armcc/bin')
266                Env['LINKFLAGS'] = Env['LINKFLAGS'].replace('RV31', 'armcc')
267
268        # reset AR command flags
269        env['ARCOM'] = '$AR --create $TARGET $SOURCES'
270        env['LIBPREFIX'] = ''
271        env['LIBSUFFIX'] = '.lib'
272        env['LIBLINKPREFIX'] = ''
273        env['LIBLINKSUFFIX'] = '.lib'
274        env['LIBDIRPREFIX'] = '--userlibpath '
275
276    elif rtconfig.PLATFORM == 'iar':
277        env['LIBPREFIX'] = ''
278        env['LIBSUFFIX'] = '.a'
279        env['LIBLINKPREFIX'] = ''
280        env['LIBLINKSUFFIX'] = '.a'
281        env['LIBDIRPREFIX'] = '--search '
282
283    # patch for win32 spawn
284    if env['PLATFORM'] == 'win32':
285        win32_spawn = Win32Spawn()
286        win32_spawn.env = env
287        env['SPAWN'] = win32_spawn.spawn
288
289    if env['PLATFORM'] == 'win32':
290        os.environ['PATH'] = rtconfig.EXEC_PATH + ";" + os.environ['PATH']
291    else:
292        os.environ['PATH'] = rtconfig.EXEC_PATH + ":" + os.environ['PATH']
293
294    # add program path
295    env.PrependENVPath('PATH', rtconfig.EXEC_PATH)
296    # add rtconfig.h/BSP path into Kernel group
297    DefineGroup("Kernel", [], [], CPPPATH=[str(Dir('#').abspath)])
298
299    # add library build action
300    act = SCons.Action.Action(BuildLibInstallAction, 'Install compiled library... $TARGET')
301    bld = Builder(action = act)
302    Env.Append(BUILDERS = {'BuildLib': bld})
303
304    # parse rtconfig.h to get used component
305    PreProcessor = PatchedPreProcessor()
306    f = open('rtconfig.h', 'r')
307    contents = f.read()
308    f.close()
309    PreProcessor.process_contents(contents)
310    BuildOptions = PreProcessor.cpp_namespace
311
312    if GetOption('clang-analyzer'):
313        # perform what scan-build does
314        env.Replace(
315                CC   = 'ccc-analyzer',
316                CXX  = 'c++-analyzer',
317                # skip as and link
318                LINK = 'true',
319                AS   = 'true',)
320        env["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_"))
321        # only check, don't compile. ccc-analyzer use CCC_CC as the CC.
322        # fsyntax-only will give us some additional warning messages
323        env['ENV']['CCC_CC']  = 'clang'
324        env.Append(CFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding'])
325        env['ENV']['CCC_CXX'] = 'clang++'
326        env.Append(CXXFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding'])
327        # remove the POST_ACTION as it will cause meaningless errors(file not
328        # found or something like that).
329        rtconfig.POST_ACTION = ''
330
331    # generate cconfig.h file
332    GenCconfigFile(env, BuildOptions)
333
334    # auto append '_REENT_SMALL' when using newlib 'nano.specs' option
335    if rtconfig.PLATFORM == 'gcc' and str(env['LINKFLAGS']).find('nano.specs') != -1:
336        env.AppendUnique(CPPDEFINES = ['_REENT_SMALL'])
337
338    if GetOption('genconfig'):
339        from genconf import genconfig
340        genconfig()
341        exit(0)
342
343    if env['PLATFORM'] != 'win32':
344        AddOption('--menuconfig',
345                    dest = 'menuconfig',
346                    action = 'store_true',
347                    default = False,
348                    help = 'make menuconfig for RT-Thread BSP')
349        if GetOption('menuconfig'):
350            from menuconfig import menuconfig
351            menuconfig(Rtt_Root)
352            exit(0)
353
354    AddOption('--pyconfig',
355                dest = 'pyconfig',
356                action = 'store_true',
357                default = False,
358                help = 'make menuconfig for RT-Thread BSP')
359    AddOption('--pyconfig-silent',
360                dest = 'pyconfig_silent',
361                action = 'store_true',
362                default = False,
363                help = 'Don`t show pyconfig window')
364
365    if GetOption('pyconfig_silent'):
366        from menuconfig import pyconfig_silent
367
368        pyconfig_silent(Rtt_Root)
369        exit(0)
370    elif GetOption('pyconfig'):
371        from menuconfig import pyconfig
372
373        pyconfig(Rtt_Root)
374        exit(0)
375
376    configfn = GetOption('useconfig')
377    if configfn:
378        from menuconfig import mk_rtconfig
379        mk_rtconfig(configfn)
380        exit(0)
381
382
383    if not GetOption('verbose'):
384        # override the default verbose command string
385        env.Replace(
386            ARCOMSTR = 'AR $TARGET',
387            ASCOMSTR = 'AS $TARGET',
388            ASPPCOMSTR = 'AS $TARGET',
389            CCCOMSTR = 'CC $TARGET',
390            CXXCOMSTR = 'CXX $TARGET',
391            LINKCOMSTR = 'LINK $TARGET'
392        )
393
394    # fix the linker for C++
395    if GetDepend('RT_USING_CPLUSPLUS'):
396        if env['LINK'].find('gcc') != -1:
397            env['LINK'] = env['LINK'].replace('gcc', 'g++')
398
399    # we need to seperate the variant_dir for BSPs and the kernels. BSPs could
400    # have their own components etc. If they point to the same folder, SCons
401    # would find the wrong source code to compile.
402    bsp_vdir = 'build'
403    kernel_vdir = 'build/kernel'
404    # board build script
405    objs = SConscript('SConscript', variant_dir=bsp_vdir, duplicate=0)
406    # include kernel
407    objs.extend(SConscript(Rtt_Root + '/src/SConscript', variant_dir=kernel_vdir + '/src', duplicate=0))
408    # include libcpu
409    if not has_libcpu:
410        objs.extend(SConscript(Rtt_Root + '/libcpu/SConscript',
411                    variant_dir=kernel_vdir + '/libcpu', duplicate=0))
412
413    # include components
414    objs.extend(SConscript(Rtt_Root + '/components/SConscript',
415                           variant_dir=kernel_vdir + '/components',
416                           duplicate=0,
417                           exports='remove_components'))
418
419    return objs
420
421def PrepareModuleBuilding(env, root_directory, bsp_directory):
422    import rtconfig
423
424    global BuildOptions
425    global Env
426    global Rtt_Root
427
428    # patch for win32 spawn
429    if env['PLATFORM'] == 'win32':
430        win32_spawn = Win32Spawn()
431        win32_spawn.env = env
432        env['SPAWN'] = win32_spawn.spawn
433
434    Env = env
435    Rtt_Root = root_directory
436
437    # parse bsp rtconfig.h to get used component
438    PreProcessor = PatchedPreProcessor()
439    f = open(bsp_directory + '/rtconfig.h', 'r')
440    contents = f.read()
441    f.close()
442    PreProcessor.process_contents(contents)
443    BuildOptions = PreProcessor.cpp_namespace
444
445    # add build/clean library option for library checking
446    AddOption('--buildlib',
447              dest='buildlib',
448              type='string',
449              help='building library of a component')
450    AddOption('--cleanlib',
451              dest='cleanlib',
452              action='store_true',
453              default=False,
454              help='clean up the library by --buildlib')
455
456    # add program path
457    env.PrependENVPath('PATH', rtconfig.EXEC_PATH)
458
459def GetConfigValue(name):
460    assert type(name) == str, 'GetConfigValue: only string parameter is valid'
461    try:
462        return BuildOptions[name]
463    except:
464        return ''
465
466def GetDepend(depend):
467    building = True
468    if type(depend) == type('str'):
469        if not depend in BuildOptions or BuildOptions[depend] == 0:
470            building = False
471        elif BuildOptions[depend] != '':
472            return BuildOptions[depend]
473
474        return building
475
476    # for list type depend
477    for item in depend:
478        if item != '':
479            if not item in BuildOptions or BuildOptions[item] == 0:
480                building = False
481
482    return building
483
484def LocalOptions(config_filename):
485    from SCons.Script import SCons
486
487    # parse wiced_config.h to get used component
488    PreProcessor = SCons.cpp.PreProcessor()
489
490    f = open(config_filename, 'r')
491    contents = f.read()
492    f.close()
493
494    PreProcessor.process_contents(contents)
495    local_options = PreProcessor.cpp_namespace
496
497    return local_options
498
499def GetLocalDepend(options, depend):
500    building = True
501    if type(depend) == type('str'):
502        if not depend in options or options[depend] == 0:
503            building = False
504        elif options[depend] != '':
505            return options[depend]
506
507        return building
508
509    # for list type depend
510    for item in depend:
511        if item != '':
512            if not item in options or options[item] == 0:
513                building = False
514
515    return building
516
517def AddDepend(option):
518    BuildOptions[option] = 1
519
520def MergeGroup(src_group, group):
521    src_group['src'] = src_group['src'] + group['src']
522    if 'CCFLAGS' in group:
523        if 'CCFLAGS' in src_group:
524            src_group['CCFLAGS'] = src_group['CCFLAGS'] + group['CCFLAGS']
525        else:
526            src_group['CCFLAGS'] = group['CCFLAGS']
527    if 'CPPPATH' in group:
528        if 'CPPPATH' in src_group:
529            src_group['CPPPATH'] = src_group['CPPPATH'] + group['CPPPATH']
530        else:
531            src_group['CPPPATH'] = group['CPPPATH']
532    if 'CPPDEFINES' in group:
533        if 'CPPDEFINES' in src_group:
534            src_group['CPPDEFINES'] = src_group['CPPDEFINES'] + group['CPPDEFINES']
535        else:
536            src_group['CPPDEFINES'] = group['CPPDEFINES']
537    if 'ASFLAGS' in group:
538        if 'ASFLAGS' in src_group:
539            src_group['ASFLAGS'] = src_group['ASFLAGS'] + group['ASFLAGS']
540        else:
541            src_group['ASFLAGS'] = group['ASFLAGS']
542
543    # for local CCFLAGS/CPPPATH/CPPDEFINES
544    if 'LOCAL_CCFLAGS' in group:
545        if 'LOCAL_CCFLAGS' in src_group:
546            src_group['LOCAL_CCFLAGS'] = src_group['LOCAL_CCFLAGS'] + group['LOCAL_CCFLAGS']
547        else:
548            src_group['LOCAL_CCFLAGS'] = group['LOCAL_CCFLAGS']
549    if 'LOCAL_CPPPATH' in group:
550        if 'LOCAL_CPPPATH' in src_group:
551            src_group['LOCAL_CPPPATH'] = src_group['LOCAL_CPPPATH'] + group['LOCAL_CPPPATH']
552        else:
553            src_group['LOCAL_CPPPATH'] = group['LOCAL_CPPPATH']
554    if 'LOCAL_CPPDEFINES' in group:
555        if 'LOCAL_CPPDEFINES' in src_group:
556            src_group['LOCAL_CPPDEFINES'] = src_group['LOCAL_CPPDEFINES'] + group['LOCAL_CPPDEFINES']
557        else:
558            src_group['LOCAL_CPPDEFINES'] = group['LOCAL_CPPDEFINES']
559
560    if 'LINKFLAGS' in group:
561        if 'LINKFLAGS' in src_group:
562            src_group['LINKFLAGS'] = src_group['LINKFLAGS'] + group['LINKFLAGS']
563        else:
564            src_group['LINKFLAGS'] = group['LINKFLAGS']
565    if 'LIBS' in group:
566        if 'LIBS' in src_group:
567            src_group['LIBS'] = src_group['LIBS'] + group['LIBS']
568        else:
569            src_group['LIBS'] = group['LIBS']
570    if 'LIBPATH' in group:
571        if 'LIBPATH' in src_group:
572            src_group['LIBPATH'] = src_group['LIBPATH'] + group['LIBPATH']
573        else:
574            src_group['LIBPATH'] = group['LIBPATH']
575    if 'LOCAL_ASFLAGS' in group:
576        if 'LOCAL_ASFLAGS' in src_group:
577            src_group['LOCAL_ASFLAGS'] = src_group['LOCAL_ASFLAGS'] + group['LOCAL_ASFLAGS']
578        else:
579            src_group['LOCAL_ASFLAGS'] = group['LOCAL_ASFLAGS']
580
581def DefineGroup(name, src, depend, **parameters):
582    global Env
583    if not GetDepend(depend):
584        return []
585
586    # find exist group and get path of group
587    group_path = ''
588    for g in Projects:
589        if g['name'] == name:
590            group_path = g['path']
591    if group_path == '':
592        group_path = GetCurrentDir()
593
594    group = parameters
595    group['name'] = name
596    group['path'] = group_path
597    if type(src) == type([]):
598        group['src'] = File(src)
599    else:
600        group['src'] = src
601
602    if 'CCFLAGS' in group:
603        Env.AppendUnique(CCFLAGS = group['CCFLAGS'])
604    if 'CPPPATH' in group:
605        paths = []
606        for item in group['CPPPATH']:
607            paths.append(os.path.abspath(item))
608        group['CPPPATH'] = paths
609        Env.AppendUnique(CPPPATH = group['CPPPATH'])
610    if 'CPPDEFINES' in group:
611        Env.AppendUnique(CPPDEFINES = group['CPPDEFINES'])
612    if 'LINKFLAGS' in group:
613        Env.AppendUnique(LINKFLAGS = group['LINKFLAGS'])
614    if 'ASFLAGS' in group:
615        Env.AppendUnique(ASFLAGS = group['ASFLAGS'])
616    if 'LOCAL_CPPPATH' in group:
617        paths = []
618        for item in group['LOCAL_CPPPATH']:
619            paths.append(os.path.abspath(item))
620        group['LOCAL_CPPPATH'] = paths
621
622    import rtconfig
623    if rtconfig.PLATFORM == 'gcc':
624        if 'CCFLAGS' in group:
625            group['CCFLAGS'] = utils.GCCC99Patch(group['CCFLAGS'])
626        if 'LOCAL_CCFLAGS' in group:
627            group['LOCAL_CCFLAGS'] = utils.GCCC99Patch(group['LOCAL_CCFLAGS'])
628
629    # check whether to clean up library
630    if GetOption('cleanlib') and os.path.exists(os.path.join(group['path'], GroupLibFullName(name, Env))):
631        if group['src'] != []:
632            print ('Remove library:'+ GroupLibFullName(name, Env))
633            fn = os.path.join(group['path'], GroupLibFullName(name, Env))
634            if os.path.exists(fn):
635                os.unlink(fn)
636
637    if 'LIBS' in group:
638        Env.AppendUnique(LIBS = group['LIBS'])
639    if 'LIBPATH' in group:
640        Env.AppendUnique(LIBPATH = group['LIBPATH'])
641
642    # check whether to build group library
643    if 'LIBRARY' in group:
644        objs = Env.Library(name, group['src'])
645    else:
646        # only add source
647        objs = group['src']
648
649    # merge group
650    for g in Projects:
651        if g['name'] == name:
652            # merge to this group
653            MergeGroup(g, group)
654            return objs
655
656    # add a new group
657    Projects.append(group)
658
659    return objs
660
661def GetCurrentDir():
662    conscript = File('SConscript')
663    fn = conscript.rfile()
664    name = fn.name
665    path = os.path.dirname(fn.abspath)
666    return path
667
668PREBUILDING = []
669def RegisterPreBuildingAction(act):
670    global PREBUILDING
671    assert callable(act), 'Could only register callable objects. %s received' % repr(act)
672    PREBUILDING.append(act)
673
674def PreBuilding():
675    global PREBUILDING
676    for a in PREBUILDING:
677        a()
678
679def GroupLibName(name, env):
680    import rtconfig
681    if rtconfig.PLATFORM == 'armcc':
682        return name + '_rvds'
683    elif rtconfig.PLATFORM == 'gcc':
684        return name + '_gcc'
685
686    return name
687
688def GroupLibFullName(name, env):
689    return env['LIBPREFIX'] + GroupLibName(name, env) + env['LIBSUFFIX']
690
691def BuildLibInstallAction(target, source, env):
692    lib_name = GetOption('buildlib')
693    for Group in Projects:
694        if Group['name'] == lib_name:
695            lib_name = GroupLibFullName(Group['name'], env)
696            dst_name = os.path.join(Group['path'], lib_name)
697            print ('Copy '+lib_name+' => ' +dst_name)
698            do_copy_file(lib_name, dst_name)
699            break
700
701def DoBuilding(target, objects):
702
703    # merge all objects into one list
704    def one_list(l):
705        lst = []
706        for item in l:
707            if type(item) == type([]):
708                lst += one_list(item)
709            else:
710                lst.append(item)
711        return lst
712
713    # handle local group
714    def local_group(group, objects):
715        if 'LOCAL_CCFLAGS' in group or 'LOCAL_CPPPATH' in group or 'LOCAL_CPPDEFINES' in group or 'LOCAL_ASFLAGS' in group:
716            CCFLAGS = Env.get('CCFLAGS', '') + group.get('LOCAL_CCFLAGS', '')
717            CPPPATH = Env.get('CPPPATH', ['']) + group.get('LOCAL_CPPPATH', [''])
718            CPPDEFINES = Env.get('CPPDEFINES', ['']) + group.get('LOCAL_CPPDEFINES', [''])
719            ASFLAGS = Env.get('ASFLAGS', '') + group.get('LOCAL_ASFLAGS', '')
720
721            for source in group['src']:
722                objects.append(Env.Object(source, CCFLAGS = CCFLAGS, ASFLAGS = ASFLAGS,
723                    CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES))
724
725            return True
726
727        return False
728
729    objects = one_list(objects)
730
731    program = None
732    # check whether special buildlib option
733    lib_name = GetOption('buildlib')
734    if lib_name:
735        objects = [] # remove all of objects
736        # build library with special component
737        for Group in Projects:
738            if Group['name'] == lib_name:
739                lib_name = GroupLibName(Group['name'], Env)
740                if not local_group(Group, objects):
741                    objects = Env.Object(Group['src'])
742
743                program = Env.Library(lib_name, objects)
744
745                # add library copy action
746                Env.BuildLib(lib_name, program)
747
748                break
749    else:
750        # remove source files with local flags setting
751        for group in Projects:
752            if 'LOCAL_CCFLAGS' in group or 'LOCAL_CPPPATH' in group or 'LOCAL_CPPDEFINES' in group:
753                for source in group['src']:
754                    for obj in objects:
755                        if source.abspath == obj.abspath or (len(obj.sources) > 0 and source.abspath == obj.sources[0].abspath):
756                            objects.remove(obj)
757
758        # re-add the source files to the objects
759        for group in Projects:
760            local_group(group, objects)
761
762        program = Env.Program(target, objects)
763
764    EndBuilding(target, program)
765
766def GenTargetProject(program = None):
767
768    if GetOption('target') == 'mdk':
769        from keil import MDKProject
770        from keil import MDK4Project
771        from keil import MDK5Project
772
773        template = os.path.isfile('template.Uv2')
774        if template:
775            MDKProject('project.Uv2', Projects)
776        else:
777            template = os.path.isfile('template.uvproj')
778            if template:
779                MDK4Project('project.uvproj', Projects)
780            else:
781                template = os.path.isfile('template.uvprojx')
782                if template:
783                    MDK5Project('project.uvprojx', Projects)
784                else:
785                    print ('No template project file found.')
786
787    if GetOption('target') == 'mdk4':
788        from keil import MDK4Project
789        MDK4Project('project.uvproj', Projects)
790
791    if GetOption('target') == 'mdk5':
792        from keil import MDK5Project
793        MDK5Project('project.uvprojx', Projects)
794
795    if GetOption('target') == 'iar':
796        from iar import IARProject
797        IARProject('project.ewp', Projects)
798
799    if GetOption('target') == 'vs':
800        from vs import VSProject
801        VSProject('project.vcproj', Projects, program)
802
803    if GetOption('target') == 'vs2012':
804        from vs2012 import VS2012Project
805        VS2012Project('project.vcxproj', Projects, program)
806
807    if GetOption('target') == 'cb':
808        from codeblocks import CBProject
809        CBProject('project.cbp', Projects, program)
810
811    if GetOption('target') == 'ua':
812        from ua import PrepareUA
813        PrepareUA(Projects, Rtt_Root, str(Dir('#')))
814
815    if GetOption('target') == 'vsc':
816        from vsc import GenerateVSCode
817        GenerateVSCode(Env)
818
819    if GetOption('target') == 'cdk':
820        from cdk import CDKProject
821        CDKProject('project.cdkproj', Projects)
822
823    if GetOption('target') == 'ses':
824        from ses import SESProject
825        SESProject(Env)
826
827def EndBuilding(target, program = None):
828    import rtconfig
829
830    need_exit = False
831
832    Env['target']  = program
833    Env['project'] = Projects
834
835    if hasattr(rtconfig, 'BSP_LIBRARY_TYPE'):
836        Env['bsp_lib_type'] = rtconfig.BSP_LIBRARY_TYPE
837
838    if hasattr(rtconfig, 'dist_handle'):
839        Env['dist_handle'] = rtconfig.dist_handle
840
841    Env.AddPostAction(target, rtconfig.POST_ACTION)
842    # Add addition clean files
843    Clean(target, 'cconfig.h')
844    Clean(target, 'rtua.py')
845    Clean(target, 'rtua.pyc')
846
847    if GetOption('target'):
848        GenTargetProject(program)
849
850    BSP_ROOT = Dir('#').abspath
851    if GetOption('make-dist') and program != None:
852        from mkdist import MkDist
853        MkDist(program, BSP_ROOT, Rtt_Root, Env)
854    if GetOption('make-dist-strip') and program != None:
855        from mkdist import MkDist_Strip
856        MkDist_Strip(program, BSP_ROOT, Rtt_Root, Env)
857        need_exit = True
858    if GetOption('cscope'):
859        from cscope import CscopeDatabase
860        CscopeDatabase(Projects)
861
862    if not GetOption('help') and not GetOption('target'):
863        if not os.path.exists(rtconfig.EXEC_PATH):
864            print ("Error: the toolchain path (" + rtconfig.EXEC_PATH + ") is not exist, please check 'EXEC_PATH' in path or rtconfig.py.")
865            need_exit = True
866
867    if need_exit:
868        exit(0)
869
870def SrcRemove(src, remove):
871    if not src:
872        return
873
874    src_bak = src[:]
875
876    if type(remove) == type('str'):
877        if os.path.isabs(remove):
878            remove = os.path.relpath(remove, GetCurrentDir())
879        remove = os.path.normpath(remove)
880
881        for item in src_bak:
882            if type(item) == type('str'):
883                item_str = item
884            else:
885                item_str = item.rstr()
886
887            if os.path.isabs(item_str):
888                item_str = os.path.relpath(item_str, GetCurrentDir())
889            item_str = os.path.normpath(item_str)
890
891            if item_str == remove:
892                src.remove(item)
893    else:
894        for remove_item in remove:
895            remove_str = str(remove_item)
896            if os.path.isabs(remove_str):
897                remove_str = os.path.relpath(remove_str, GetCurrentDir())
898            remove_str = os.path.normpath(remove_str)
899
900            for item in src_bak:
901                if type(item) == type('str'):
902                    item_str = item
903                else:
904                    item_str = item.rstr()
905
906                if os.path.isabs(item_str):
907                    item_str = os.path.relpath(item_str, GetCurrentDir())
908                item_str = os.path.normpath(item_str)
909
910                if item_str == remove_str:
911                    src.remove(item)
912
913def GetVersion():
914    import SCons.cpp
915    import string
916
917    rtdef = os.path.join(Rtt_Root, 'include', 'rtdef.h')
918
919    # parse rtdef.h to get RT-Thread version
920    prepcessor = PatchedPreProcessor()
921    f = open(rtdef, 'r')
922    contents = f.read()
923    f.close()
924    prepcessor.process_contents(contents)
925    def_ns = prepcessor.cpp_namespace
926
927    version = int(filter(lambda ch: ch in '0123456789.', def_ns['RT_VERSION']))
928    subversion = int(filter(lambda ch: ch in '0123456789.', def_ns['RT_SUBVERSION']))
929
930    if 'RT_REVISION' in def_ns:
931        revision = int(filter(lambda ch: ch in '0123456789.', def_ns['RT_REVISION']))
932        return '%d.%d.%d' % (version, subversion, revision)
933
934    return '0.%d.%d' % (version, subversion)
935
936def GlobSubDir(sub_dir, ext_name):
937    import os
938    import glob
939
940    def glob_source(sub_dir, ext_name):
941        list = os.listdir(sub_dir)
942        src = glob.glob(os.path.join(sub_dir, ext_name))
943
944        for item in list:
945            full_subdir = os.path.join(sub_dir, item)
946            if os.path.isdir(full_subdir):
947                src += glob_source(full_subdir, ext_name)
948        return src
949
950    dst = []
951    src = glob_source(sub_dir, ext_name)
952    for item in src:
953        dst.append(os.path.relpath(item, sub_dir))
954    return dst
955
956def PackageSConscript(package):
957    from package import BuildPackage
958
959    return BuildPackage(package)
960