# # File : building.py # This file is part of RT-Thread RTOS # COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # # Change Logs: # Date Author Notes # 2015-01-20 Bernard Add copyright information # 2015-07-25 Bernard Add LOCAL_CCFLAGS/LOCAL_CPPPATH/LOCAL_CPPDEFINES for # group definition. # import os import sys import string import utils import operator import rtconfig import platform from SCons.Script import * from utils import _make_path_relative from mkdist import do_copy_file from options import AddOptions BuildOptions = {} Projects = [] Rtt_Root = '' Env = None # SCons PreProcessor patch def start_handling_includes(self, t=None): """ Causes the PreProcessor object to start processing #import, #include and #include_next lines. This method will be called when a #if, #ifdef, #ifndef or #elif evaluates True, or when we reach the #else in a #if, #ifdef, #ifndef or #elif block where a condition already evaluated False. """ d = self.dispatch_table p = self.stack[-1] if self.stack else self.default_table for k in ('import', 'include', 'include_next', 'define'): d[k] = p[k] def stop_handling_includes(self, t=None): """ Causes the PreProcessor object to stop processing #import, #include and #include_next lines. This method will be called when a #if, #ifdef, #ifndef or #elif evaluates False, or when we reach the #else in a #if, #ifdef, #ifndef or #elif block where a condition already evaluated True. """ d = self.dispatch_table d['import'] = self.do_nothing d['include'] = self.do_nothing d['include_next'] = self.do_nothing d['define'] = self.do_nothing PatchedPreProcessor = SCons.cpp.PreProcessor PatchedPreProcessor.start_handling_includes = start_handling_includes PatchedPreProcessor.stop_handling_includes = stop_handling_includes class Win32Spawn: def spawn(self, sh, escape, cmd, args, env): # deal with the cmd build-in commands which cannot be used in # subprocess.Popen if cmd == 'del': for f in args[1:]: try: os.remove(f) except Exception as e: print('Error removing file: ' + e) return -1 return 0 import subprocess newargs = ' '.join(args[1:]) cmdline = cmd + " " + newargs # Make sure the env is constructed by strings _e = dict([(k, str(v)) for k, v in env.items()]) # Windows(tm) CreateProcess does not use the env passed to it to find # the executables. So we have to modify our own PATH to make Popen # work. old_path = os.environ['PATH'] os.environ['PATH'] = _e['PATH'] try: proc = subprocess.Popen(cmdline, env=_e, shell=False) except Exception as e: print('Error in calling command:' + cmdline.split(' ')[0]) print('Exception: ' + os.strerror(e.errno)) if (os.strerror(e.errno) == "No such file or directory"): print ("\nPlease check Toolchains PATH setting.\n") return e.errno finally: os.environ['PATH'] = old_path return proc.wait() # generate cconfig.h file def GenCconfigFile(env, BuildOptions): # The cconfig.h will NOT generate in the lastest RT-Thread code. # When you want to use it, you can uncomment out the following code. # if rtconfig.PLATFORM in ['gcc']: # contents = '' # if not os.path.isfile('cconfig.h'): # import gcc # gcc.GenerateGCCConfig(rtconfig) # # try again # if os.path.isfile('cconfig.h'): # f = open('cconfig.h', 'r') # if f: # contents = f.read() # f.close() # prep = PatchedPreProcessor() # prep.process_contents(contents) # options = prep.cpp_namespace # BuildOptions.update(options) # # add HAVE_CCONFIG_H definition # env.AppendUnique(CPPDEFINES = ['HAVE_CCONFIG_H']) pass def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = []): # 定义全局变量 global BuildOptions global Projects global Env global Rtt_Root # 添加scons编译选项 AddOptions() # 从相对路径中读取绝对路径 Env = env Rtt_Root = os.path.abspath(root_directory) # 保存绝对路径,make an absolute root directory RTT_ROOT = Rtt_Root Export('RTT_ROOT') # set RTT_ROOT in ENV Env['RTT_ROOT'] = Rtt_Root # set BSP_ROOT in ENV Env['BSP_ROOT'] = Dir('#').abspath # 添加 RT-Thread 工具路径 sys.path = sys.path + [os.path.join(Rtt_Root, 'tools')] #———————————————————————————————————— 构建选项的实现 ——————————————————————————————————————# # 定义目标字典,包含目标名及其对应的编译器和平台 # {target_name:(CROSS_TOOL, PLATFORM)} tgt_dict = {'mdk':('keil', 'armcc'), 'mdk4':('keil', 'armcc'), 'mdk5':('keil', 'armcc'), 'iar':('iar', 'iccarm'), 'vs':('msvc', 'cl'), 'vs2012':('msvc', 'cl'), 'vsc' : ('gcc', 'gcc'), 'cb':('keil', 'armcc'), 'ua':('gcc', 'gcc'), 'cdk':('gcc', 'gcc'), 'makefile':('gcc', 'gcc'), 'eclipse':('gcc', 'gcc'), 'ses' : ('gcc', 'gcc'), 'cmake':('gcc', 'gcc'), 'cmake-armclang':('keil', 'armclang'), 'xmake':('gcc', 'gcc'), 'codelite' : ('gcc', 'gcc'), 'esp-idf': ('gcc', 'gcc')} # 获取目标工程名称 tgt_name = GetOption('target') # 根据目标名选择编译器和平台 if tgt_name: # --target will change the toolchain settings which clang-analyzer is # depend on if GetOption('clang-analyzer'): print ('--clang-analyzer cannot be used with --target') sys.exit(1) # 不生成执行代码 SetOption('no_exec', 1) try: # 替换rtconfig中配置的编译工具及平台 rtconfig.CROSS_TOOL, rtconfig.PLATFORM = tgt_dict[tgt_name] # replace the 'RTT_CC' to 'CROSS_TOOL' os.environ['RTT_CC'] = rtconfig.CROSS_TOOL except KeyError: print('Unknow target: '+ tgt_name+'. Avaible targets: ' +', '.join(tgt_dict.keys())) sys.exit(1) # 设置工具程序路径前缀 exec_prefix = GetOption('exec-prefix') if exec_prefix: os.environ['RTT_CC_PREFIX'] = exec_prefix # 当 rtconfig.EXEC_PATH 无效时,自动更改 'RTT_EXEC_PATH' # auto change the 'RTT_EXEC_PATH' when 'rtconfig.EXEC_PATH' get failed if not os.path.exists(rtconfig.EXEC_PATH): if 'RTT_EXEC_PATH' in os.environ: # del the 'RTT_EXEC_PATH' and using the 'EXEC_PATH' setting on rtconfig.py del os.environ['RTT_EXEC_PATH'] # 设置工具路径 exec_path = GetOption('exec-path') if exec_path: os.environ['RTT_EXEC_PATH'] = exec_path # 更新rtconfig中的环境变量 utils.ReloadModule(rtconfig) # update environment variables to rtconfig.py # 在 SConstruct 的 Environment() 中,已经加载了一些 env 变量,需要在重新加载 rtconfig.py 后同步这些变量 # some env variables have loaded in Environment() of SConstruct before re-load rtconfig.py; # after update rtconfig.py's variables, those env variables need to synchronize if exec_prefix: env['CC'] = rtconfig.CC env['CXX'] = rtconfig.CXX env['AS'] = rtconfig.AS env['AR'] = rtconfig.AR env['LINK'] = rtconfig.LINK if exec_path: env.PrependENVPath('PATH', rtconfig.EXEC_PATH) # 启用严格编译 if GetOption('strict-compiling'): STRICT_FLAGS = '' if rtconfig.PLATFORM in ['gcc']: STRICT_FLAGS += ' -Werror' #-Wextra #追加到编译选项cflag和cxxflag中 env.Append(CFLAGS=STRICT_FLAGS, CXXFLAGS=STRICT_FLAGS) # add compability with Keil MDK 4.6 which changes the directory of armcc.exe if rtconfig.PLATFORM in ['armcc', 'armclang']: if rtconfig.PLATFORM == 'armcc' and not os.path.isfile(os.path.join(rtconfig.EXEC_PATH, 'armcc.exe')): if rtconfig.EXEC_PATH.find('bin40') > 0: rtconfig.EXEC_PATH = rtconfig.EXEC_PATH.replace('bin40', 'armcc/bin') Env['LINKFLAGS'] = Env['LINKFLAGS'].replace('RV31', 'armcc') # reset AR command flags env['ARCOM'] = '$AR --create $TARGET $SOURCES' env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.lib' env['LIBLINKPREFIX'] = '' env['LIBLINKSUFFIX'] = '.lib' env['LIBDIRPREFIX'] = '--userlibpath ' elif rtconfig.PLATFORM == 'iccarm': env['LIBPREFIX'] = '' env['LIBSUFFIX'] = '.a' env['LIBLINKPREFIX'] = '' env['LIBLINKSUFFIX'] = '.a' env['LIBDIRPREFIX'] = '--search ' # patch for win32 spawn if env['PLATFORM'] == 'win32': win32_spawn = Win32Spawn() win32_spawn.env = env env['SPAWN'] = win32_spawn.spawn if env['PLATFORM'] == 'win32': os.environ['PATH'] = rtconfig.EXEC_PATH + ";" + os.environ['PATH'] else: os.environ['PATH'] = rtconfig.EXEC_PATH + ":" + os.environ['PATH'] # 添加系统中PATH环境变量中的所有路径,包括rtconfig中的 # add program path env.PrependENVPath('PATH', os.environ['PATH']) # 将 rtconfig.h/BSP 路径添加到内核组 # add rtconfig.h/BSP path into Kernel group DefineGroup("Kernel", [], [], CPPPATH=[str(Dir('#').abspath)]) # 创建一个新的 SCons Builder 对象,用于安装编译好的库 # add library build action act = SCons.Action.Action(BuildLibInstallAction, 'Install compiled library... $TARGET') bld = Builder(action = act) Env.Append(BUILDERS = {'BuildLib': bld}) # 从rtconfig.h中加载宏定义 # parse rtconfig.h to get used component PreProcessor = PatchedPreProcessor() f = open('rtconfig.h', 'r') contents = f.read() f.close() PreProcessor.process_contents(contents) BuildOptions = PreProcessor.cpp_namespace # 是否启用clang-analyzer用于代码静态分析 if GetOption('clang-analyzer'): # perform what scan-build does env.Replace( CC = 'ccc-analyzer', CXX = 'c++-analyzer', # skip as and link LINK = 'true', AS = 'true',) env["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_")) # only check, don't compile. ccc-analyzer use CCC_CC as the CC. # fsyntax-only will give us some additional warning messages env['ENV']['CCC_CC'] = 'clang' env.Append(CFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding']) env['ENV']['CCC_CXX'] = 'clang++' env.Append(CXXFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding']) # remove the POST_ACTION as it will cause meaningless errors(file not # found or something like that). rtconfig.POST_ACTION = '' # generate cconfig.h file GenCconfigFile(env, BuildOptions) # nano.specs 是gcc的一个体积更小的标准库newlib # auto append '_REENT_SMALL' when using newlib 'nano.specs' option if rtconfig.PLATFORM in ['gcc'] and str(env['LINKFLAGS']).find('nano.specs') != -1: env.AppendUnique(CPPDEFINES = ['_REENT_SMALL']) # 添加编译宏定义,同在rtconfig.h中添加是一样的作用 add_rtconfig = GetOption('add_rtconfig') if add_rtconfig: add_rtconfig = add_rtconfig.split(',') if isinstance(add_rtconfig, list): for config in add_rtconfig: if isinstance(config, str): AddDepend(add_rtconfig) env.Append(CFLAGS=' -D' + config, CXXFLAGS=' -D' + config, AFLAGS=' -D' + config) else: print('add_rtconfig arguements are illegal!') else: print('add_rtconfig arguements are illegal!') # Generate .config from rtconfig.h if GetOption('genconfig'): from genconf import genconfig genconfig() exit(0) # 栈空间分析 if GetOption('stackanalysis'): from WCS import ThreadStackStaticAnalysis ThreadStackStaticAnalysis(Env) exit(0) # 打开menuconfig配置 if platform.system() != 'Windows': if GetOption('menuconfig'): from menuconfig import menuconfig menuconfig(Rtt_Root) exit(0) # Don`t show pyconfig window if GetOption('pyconfig_silent'): from menuconfig import guiconfig_silent guiconfig_silent(Rtt_Root) exit(0) # Python GUI menuconfig for RT-Thread BSP elif GetOption('pyconfig'): from menuconfig import guiconfig guiconfig(Rtt_Root) exit(0) # make rtconfig.h from config file configfn = GetOption('useconfig') if configfn: from menuconfig import mk_rtconfig mk_rtconfig(configfn) exit(0) # 默认不使用详细输出,只输出编译结果 if not GetOption('verbose'): # override the default verbose command string env.Replace( ARCOMSTR = 'AR $TARGET', ASCOMSTR = 'AS $TARGET', ASPPCOMSTR = 'AS $TARGET', CCCOMSTR = 'CC $TARGET', CXXCOMSTR = 'CXX $TARGET', LINKCOMSTR = 'LINK $TARGET' ) # fix the linker for C++ if GetDepend('RT_USING_CPLUSPLUS'): if env['LINK'].find('gcc') != -1: env['LINK'] = env['LINK'].replace('gcc', 'g++') # 设置项目构建地址 # we need to seperate the variant_dir for BSPs and the kernels. BSPs could # have their own components etc. If they point to the same folder, SCons # would find the wrong source code to compile. bsp_vdir = 'build' kernel_vdir = 'build/kernel' # board build script objs = SConscript('SConscript', variant_dir=bsp_vdir, duplicate=0) # include kernel objs.extend(SConscript(Rtt_Root + '/src/SConscript', variant_dir=kernel_vdir + '/src', duplicate=0)) # 此处指的是,是否包含外部的libcpu移植代码。如果没有,则采用rt-thread中移植好的代码。 # include libcpu if not has_libcpu: objs.extend(SConscript(Rtt_Root + '/libcpu/SConscript', variant_dir=kernel_vdir + '/libcpu', duplicate=0)) # include components objs.extend(SConscript(Rtt_Root + '/components/SConscript', variant_dir=kernel_vdir + '/components', duplicate=0, exports='remove_components')) # include testcases if os.path.isfile(os.path.join(Rtt_Root, 'examples/utest/testcases/SConscript')): objs.extend(SConscript(Rtt_Root + '/examples/utest/testcases/SConscript', variant_dir=kernel_vdir + '/examples/utest/testcases', duplicate=0)) return objs def PrepareModuleBuilding(env, root_directory, bsp_directory): global BuildOptions global Env global Rtt_Root # patch for win32 spawn if env['PLATFORM'] == 'win32': win32_spawn = Win32Spawn() win32_spawn.env = env env['SPAWN'] = win32_spawn.spawn Env = env Rtt_Root = root_directory # parse bsp rtconfig.h to get used component PreProcessor = PatchedPreProcessor() f = open(bsp_directory + '/rtconfig.h', 'r') contents = f.read() f.close() PreProcessor.process_contents(contents) BuildOptions = PreProcessor.cpp_namespace AddOption('--buildlib', dest = 'buildlib', type = 'string', help = 'building library of a component') AddOption('--cleanlib', dest = 'cleanlib', action = 'store_true', default = False, help = 'clean up the library by --buildlib') # add program path env.PrependENVPath('PATH', rtconfig.EXEC_PATH) def GetConfigValue(name): assert type(name) == str, 'GetConfigValue: only string parameter is valid' try: return BuildOptions[name] except: return '' def GetDepend(depend): building = True if type(depend) == type('str'): if not depend in BuildOptions or BuildOptions[depend] == 0: building = False elif BuildOptions[depend] != '': return BuildOptions[depend] return building # for list type depend for item in depend: if item != '': if not item in BuildOptions or BuildOptions[item] == 0: building = False return building def LocalOptions(config_filename): from SCons.Script import SCons # parse wiced_config.h to get used component PreProcessor = SCons.cpp.PreProcessor() f = open(config_filename, 'r') contents = f.read() f.close() PreProcessor.process_contents(contents) local_options = PreProcessor.cpp_namespace return local_options def GetLocalDepend(options, depend): building = True if type(depend) == type('str'): if not depend in options or options[depend] == 0: building = False elif options[depend] != '': return options[depend] return building # for list type depend for item in depend: if item != '': if not item in options or options[item] == 0: building = False return building def AddDepend(option): if isinstance(option, str): BuildOptions[option] = 1 elif isinstance(option, list): for obj in option: if isinstance(obj, str): BuildOptions[obj] = 1 else: print('AddDepend arguements are illegal!') else: print('AddDepend arguements are illegal!') def MergeGroup(src_group, group): src_group['src'] = src_group['src'] + group['src'] src_group['src'].sort() if 'CFLAGS' in group: if 'CFLAGS' in src_group: src_group['CFLAGS'] = src_group['CFLAGS'] + group['CFLAGS'] else: src_group['CFLAGS'] = group['CFLAGS'] if 'CCFLAGS' in group: if 'CCFLAGS' in src_group: src_group['CCFLAGS'] = src_group['CCFLAGS'] + group['CCFLAGS'] else: src_group['CCFLAGS'] = group['CCFLAGS'] if 'CXXFLAGS' in group: if 'CXXFLAGS' in src_group: src_group['CXXFLAGS'] = src_group['CXXFLAGS'] + group['CXXFLAGS'] else: src_group['CXXFLAGS'] = group['CXXFLAGS'] if 'CPPPATH' in group: if 'CPPPATH' in src_group: src_group['CPPPATH'] = src_group['CPPPATH'] + group['CPPPATH'] else: src_group['CPPPATH'] = group['CPPPATH'] if 'CPPDEFINES' in group: if 'CPPDEFINES' in src_group: src_group['CPPDEFINES'] = src_group['CPPDEFINES'] + group['CPPDEFINES'] else: src_group['CPPDEFINES'] = group['CPPDEFINES'] if 'ASFLAGS' in group: if 'ASFLAGS' in src_group: src_group['ASFLAGS'] = src_group['ASFLAGS'] + group['ASFLAGS'] else: src_group['ASFLAGS'] = group['ASFLAGS'] # for local CCFLAGS/CPPPATH/CPPDEFINES if 'LOCAL_CFLAGS' in group: if 'LOCAL_CFLAGS' in src_group: src_group['LOCAL_CFLAGS'] = src_group['LOCAL_CFLAGS'] + group['LOCAL_CFLAGS'] else: src_group['LOCAL_CFLAGS'] = group['LOCAL_CFLAGS'] if 'LOCAL_CCFLAGS' in group: if 'LOCAL_CCFLAGS' in src_group: src_group['LOCAL_CCFLAGS'] = src_group['LOCAL_CCFLAGS'] + group['LOCAL_CCFLAGS'] else: src_group['LOCAL_CCFLAGS'] = group['LOCAL_CCFLAGS'] if 'LOCAL_CXXFLAGS' in group: if 'LOCAL_CXXFLAGS' in src_group: src_group['LOCAL_CXXFLAGS'] = src_group['LOCAL_CXXFLAGS'] + group['LOCAL_CXXFLAGS'] else: src_group['LOCAL_CXXFLAGS'] = group['LOCAL_CXXFLAGS'] if 'LOCAL_CPPPATH' in group: if 'LOCAL_CPPPATH' in src_group: src_group['LOCAL_CPPPATH'] = src_group['LOCAL_CPPPATH'] + group['LOCAL_CPPPATH'] else: src_group['LOCAL_CPPPATH'] = group['LOCAL_CPPPATH'] if 'LOCAL_CPPDEFINES' in group: if 'LOCAL_CPPDEFINES' in src_group: src_group['LOCAL_CPPDEFINES'] = src_group['LOCAL_CPPDEFINES'] + group['LOCAL_CPPDEFINES'] else: src_group['LOCAL_CPPDEFINES'] = group['LOCAL_CPPDEFINES'] if 'LINKFLAGS' in group: if 'LINKFLAGS' in src_group: src_group['LINKFLAGS'] = src_group['LINKFLAGS'] + group['LINKFLAGS'] else: src_group['LINKFLAGS'] = group['LINKFLAGS'] if 'LIBS' in group: if 'LIBS' in src_group: src_group['LIBS'] = src_group['LIBS'] + group['LIBS'] else: src_group['LIBS'] = group['LIBS'] if 'LIBPATH' in group: if 'LIBPATH' in src_group: src_group['LIBPATH'] = src_group['LIBPATH'] + group['LIBPATH'] else: src_group['LIBPATH'] = group['LIBPATH'] if 'LOCAL_ASFLAGS' in group: if 'LOCAL_ASFLAGS' in src_group: src_group['LOCAL_ASFLAGS'] = src_group['LOCAL_ASFLAGS'] + group['LOCAL_ASFLAGS'] else: src_group['LOCAL_ASFLAGS'] = group['LOCAL_ASFLAGS'] def _PretreatListParameters(target_list): while '' in target_list: # remove null strings target_list.remove('') while ' ' in target_list: # remove ' ' target_list.remove(' ') if(len(target_list) == 0): return False # ignore this list, don't add this list to the parameter return True # permit to add this list to the parameter def DefineGroup(name, src, depend, **parameters): global Env # 检查依赖项是否满足,如果不满足则返回空列表 if not GetDepend(depend): return [] # 查找已存在的组并获取组的路径 # find exist group and get path of group group_path = '' for g in Projects: if g['name'] == name: group_path = g['path'] if group_path == '': group_path = GetCurrentDir() # 创建组并设置属性 group = parameters group['name'] = name group['path'] = group_path # 如果源文件是列表类型,则去除重复元素并转换为 File 类型 if type(src) == type([]): # remove duplicate elements from list src = list(set(src)) group['src'] = File(src) else: group['src'] = src # 添加编译选项 if 'CFLAGS' in group: target = group['CFLAGS'] if len(target) > 0: Env.AppendUnique(CFLAGS = target) if 'CCFLAGS' in group: target = group['CCFLAGS'] if len(target) > 0: Env.AppendUnique(CCFLAGS = target) if 'CXXFLAGS' in group: target = group['CXXFLAGS'] if len(target) > 0: Env.AppendUnique(CXXFLAGS = target) # 添加头文件路径 if 'CPPPATH' in group: target = group['CPPPATH'] if _PretreatListParameters(target) == True: paths = [] for item in target: paths.append(os.path.abspath(item)) target = paths Env.AppendUnique(CPPPATH = target) # 添加预处理器定义 if 'CPPDEFINES' in group: target = group['CPPDEFINES'] if _PretreatListParameters(target) == True: Env.AppendUnique(CPPDEFINES = target) # 添加链接器选项 if 'LINKFLAGS' in group: target = group['LINKFLAGS'] if len(target) > 0: Env.AppendUnique(LINKFLAGS = target) # 添加汇编器选项 if 'ASFLAGS' in group: target = group['ASFLAGS'] if len(target) > 0: Env.AppendUnique(ASFLAGS = target) # 添加本地头文件路径 if 'LOCAL_CPPPATH' in group: paths = [] for item in group['LOCAL_CPPPATH']: paths.append(os.path.abspath(item)) group['LOCAL_CPPPATH'] = paths # 针对 gcc 平台的特殊处理 if rtconfig.PLATFORM in ['gcc']: if 'CFLAGS' in group: group['CFLAGS'] = utils.GCCC99Patch(group['CFLAGS']) if 'CCFLAGS' in group: group['CCFLAGS'] = utils.GCCC99Patch(group['CCFLAGS']) if 'CXXFLAGS' in group: group['CXXFLAGS'] = utils.GCCC99Patch(group['CXXFLAGS']) if 'LOCAL_CCFLAGS' in group: group['LOCAL_CCFLAGS'] = utils.GCCC99Patch(group['LOCAL_CCFLAGS']) if 'LOCAL_CXXFLAGS' in group: group['LOCAL_CXXFLAGS'] = utils.GCCC99Patch(group['LOCAL_CXXFLAGS']) if 'LOCAL_CFLAGS' in group: group['LOCAL_CFLAGS'] = utils.GCCC99Patch(group['LOCAL_CFLAGS']) # 检查是否需要清理库文件 if GetOption('cleanlib') and os.path.exists(os.path.join(group['path'], GroupLibFullName(name, Env))): if group['src'] != []: print('Remove library:'+ GroupLibFullName(name, Env)) fn = os.path.join(group['path'], GroupLibFullName(name, Env)) if os.path.exists(fn): os.unlink(fn) # 添加库文件 if 'LIBS' in group: target = group['LIBS'] if _PretreatListParameters(target) == True: Env.AppendUnique(LIBS = target) if 'LIBPATH' in group: target = group['LIBPATH'] if _PretreatListParameters(target) == True: Env.AppendUnique(LIBPATH = target) # 检查是否需要构建组库 # check whether to build group library if 'LIBRARY' in group: objs = Env.Library(name, group['src']) else: # only add source objs = group['src'] # 合并组 # merge group for g in Projects: if g['name'] == name: # merge to this group MergeGroup(g, group) return objs def PriorityInsertGroup(groups, group): length = len(groups) for i in range(0, length): if operator.gt(groups[i]['name'].lower(), group['name'].lower()): groups.insert(i, group) return groups.append(group) # add a new group PriorityInsertGroup(Projects, group) return objs def GetCurrentDir(): conscript = File('SConscript') fn = conscript.rfile() name = fn.name path = os.path.dirname(fn.abspath) return path PREBUILDING = [] def RegisterPreBuildingAction(act): global PREBUILDING assert callable(act), 'Could only register callable objects. %s received' % repr(act) PREBUILDING.append(act) def PreBuilding(): global PREBUILDING for a in PREBUILDING: a() def GroupLibName(name, env): if rtconfig.PLATFORM in ['armcc']: return name + '_rvds' elif rtconfig.PLATFORM in ['gcc']: return name + '_gcc' return name def GroupLibFullName(name, env): return env['LIBPREFIX'] + GroupLibName(name, env) + env['LIBSUFFIX'] def BuildLibInstallAction(target, source, env): lib_name = GetOption('buildlib') for Group in Projects: if Group['name'] == lib_name: lib_name = GroupLibFullName(Group['name'], env) dst_name = os.path.join(Group['path'], lib_name) print('Copy '+lib_name+' => ' + dst_name) do_copy_file(lib_name, dst_name) break def DoBuilding(target, objects): # 将所有对象合并为一个列表,拆除内部嵌套 # merge all objects into one list def one_list(l): lst = [] for item in l: if type(item) == type([]): lst += one_list(item) else: lst.append(item) return lst # 处理本地组,传入组和对象 # handle local group def local_group(group, objects): # 检查组内是否有特定的编译选项 if 'LOCAL_CFLAGS' in group or 'LOCAL_CXXFLAGS' in group or 'LOCAL_CCFLAGS' in group or 'LOCAL_CPPPATH' in group or 'LOCAL_CPPDEFINES' in group or 'LOCAL_ASFLAGS' in group: # 将本地组的编译选项添加到环境变量 CFLAGS = Env.get('CFLAGS', '') + group.get('LOCAL_CFLAGS', '') CCFLAGS = Env.get('CCFLAGS', '') + group.get('LOCAL_CCFLAGS', '') CXXFLAGS = Env.get('CXXFLAGS', '') + group.get('LOCAL_CXXFLAGS', '') CPPPATH = Env.get('CPPPATH', ['']) + group.get('LOCAL_CPPPATH', ['']) CPPDEFINES = Env.get('CPPDEFINES', ['']) + group.get('LOCAL_CPPDEFINES', ['']) ASFLAGS = Env.get('ASFLAGS', '') + group.get('LOCAL_ASFLAGS', '') # 将源文件添加到对象列表 # Env.Object 是 SCons 构建环境中的一个方法,用于将源文件编译成目标(对象)文件。 # 该方法的主要作用是将源代码文件(如 C、C++、汇编等)编译成对应的目标文件。 # 在构建过程中,这些目标文件将被链接成可执行程序或库。 for source in group['src']: objects.append(Env.Object(source, CFLAGS = CFLAGS, CCFLAGS = CCFLAGS, CXXFLAGS = CXXFLAGS, ASFLAGS = ASFLAGS, CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES)) return True return False # 合并所有对象为一个列表 objects = one_list(objects) program = None # 检查是否有特定的buildlib选项,是否有外部库的依赖 # 确保了具有本地标志设置的源文件能够正确处理 # check whether special buildlib option lib_name = GetOption('buildlib') if lib_name: objects = [] # remove all of objects # build library with special component for Group in Projects: if Group['name'] == lib_name: lib_name = GroupLibName(Group['name'], Env) if not local_group(Group, objects): objects = Env.Object(Group['src']) program = Env.Library(lib_name, objects) # add library copy action Env.BuildLib(lib_name, program) break else: # 移除具有本地标志设置的源文件 # remove source files with local flags setting for group in Projects: if 'LOCAL_CFLAGS' in group or 'LOCAL_CXXFLAGS' in group or 'LOCAL_CCFLAGS' in group or 'LOCAL_CPPPATH' in group or 'LOCAL_CPPDEFINES' in group: for source in group['src']: for obj in objects: if source.abspath == obj.abspath or (len(obj.sources) > 0 and source.abspath == obj.sources[0].abspath): objects.remove(obj) # re-add the source files to the objects # 将源文件重新添加到对象列表 objects_in_group = [] for group in Projects: local_group(group, objects_in_group) # 分别排序,因为这两个列表的成员数据类型不同 # sort seperately, because the data type of # the members of the two lists are different objects_in_group = sorted(objects_in_group) objects = sorted(objects) objects.append(objects_in_group) # 将处理后的对象列表传递给程序,进行链接输出可执行程序program,target为程序名称 program = Env.Program(target, objects) # 调用EndBuilding函数,传入目标名称和程序 EndBuilding(target, program) def GenTargetProject(program = None): if GetOption('target') in ['mdk', 'mdk4', 'mdk5']: from keil import MDK2Project, MDK4Project, MDK5Project, ARMCC_Version if os.path.isfile('template.uvprojx') and GetOption('target') not in ['mdk4']: # Keil5 MDK5Project(GetOption('project-name') + '.uvprojx', Projects) print("Keil5 project is generating...") elif os.path.isfile('template.uvproj') and GetOption('target') not in ['mdk5']: # Keil4 MDK4Project(GetOption('project-name') + '.uvproj', Projects) print("Keil4 project is generating...") elif os.path.isfile('template.Uv2') and GetOption('target') not in ['mdk4', 'mdk5']: # Keil2 MDK2Project(GetOption('project-name') + '.Uv2', Projects) print("Keil2 project is generating...") else: print ('No template project file found.') exit(1) print("Keil Version: " + ARMCC_Version()) print("Keil-MDK project has generated successfully!") if GetOption('target') == 'iar': from iar import IARProject, IARVersion print("IAR Version: " + IARVersion()) IARProject(GetOption('project-name') + '.ewp', Projects) print("IAR project has generated successfully!") if GetOption('target') == 'vs': from vs import VSProject VSProject(GetOption('project-name') + '.vcproj', Projects, program) if GetOption('target') == 'vs2012': from vs2012 import VS2012Project VS2012Project(GetOption('project-name') + '.vcxproj', Projects, program) if GetOption('target') == 'cb': from codeblocks import CBProject CBProject(GetOption('project-name') + '.cbp', Projects, program) if GetOption('target') == 'ua': from ua import PrepareUA PrepareUA(Projects, Rtt_Root, str(Dir('#'))) if GetOption('target') == 'vsc': from vsc import GenerateVSCode GenerateVSCode(Env) if GetOption('target') == 'cdk': from cdk import CDKProject CDKProject(GetOption('project-name') + '.cdkproj', Projects) if GetOption('target') == 'ses': from ses import SESProject SESProject(Env) if GetOption('target') == 'makefile': from makefile import TargetMakefile TargetMakefile(Env) if GetOption('target') == 'eclipse': from eclipse import TargetEclipse TargetEclipse(Env, GetOption('reset-project-config'), GetOption('project-name')) if GetOption('target') == 'codelite': from codelite import TargetCodelite TargetCodelite(Projects, program) if GetOption('target') == 'cmake' or GetOption('target') == 'cmake-armclang': from cmake import CMakeProject CMakeProject(Env,Projects) if GetOption('target') == 'xmake': from xmake import XMakeProject XMakeProject(Env, Projects) if GetOption('target') == 'esp-idf': from esp_idf import ESPIDFProject ESPIDFProject(Env, Projects) def EndBuilding(target, program = None): from mkdist import MkDist, MkDist_Strip need_exit = False # 设置环境变量 Env['target'] = program Env['project'] = Projects # 如果存在 BSP_LIBRARY_TYPE,将其设置为环境变量 if hasattr(rtconfig, 'BSP_LIBRARY_TYPE'): Env['bsp_lib_type'] = rtconfig.BSP_LIBRARY_TYPE # 如果存在 dist_handle,将其设置为环境变量 if hasattr(rtconfig, 'dist_handle'): Env['dist_handle'] = rtconfig.dist_handle # 添加后置动作 Env.AddPostAction(target, rtconfig.POST_ACTION) # 清除中间文件 Add addition clean files Clean(target, 'cconfig.h') Clean(target, 'rtua.py') Clean(target, 'rtua.pyc') Clean(target, '.sconsign.dblite') # 如果指定了 target,生成 target 工程并退出 if GetOption('target'): GenTargetProject(program) need_exit = True # 获取 BSP_ROOT、project_name、project_path 等变量 BSP_ROOT = Dir('#').abspath project_name = GetOption('project-name') project_path = GetOption('project-path') # 如果指定了 make-dist,生成软件分发包并退出 if GetOption('make-dist') and program != None: MkDist(program, BSP_ROOT, Rtt_Root, Env, project_name, project_path) need_exit = True # 如果指定了 make-dist-strip,生成剥离符号的软件分发包并退出 if GetOption('make-dist-strip') and program != None: MkDist_Strip(program, BSP_ROOT, Rtt_Root, Env) need_exit = True # 如果指定了 make-dist-ide,生成软件分发包和 Eclipse 工程并退出 if GetOption('make-dist-ide') and program != None: import subprocess if not isinstance(project_path, str) or len(project_path) == 0 : project_path = os.path.join(BSP_ROOT, 'rt-studio-project') MkDist(program, BSP_ROOT, Rtt_Root, Env, project_name, project_path) child = subprocess.Popen('scons --target=eclipse --project-name=' + project_name, cwd=project_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) stdout, stderr = child.communicate() need_exit = True # 如果指定了 cscope,生成 cscope 数据库 if GetOption('cscope'): from cscope import CscopeDatabase CscopeDatabase(Projects) # 如果不是获取帮助或目标,检查 toolchain 路径是否存在 if not GetOption('help') and not GetOption('target'): if not os.path.exists(rtconfig.EXEC_PATH): print ("Error: the toolchain path (" + rtconfig.EXEC_PATH + ") is not exist, please check 'EXEC_PATH' in path or rtconfig.py.") need_exit = True # 如果需要退出,则退出程序 if need_exit: exit(0) def SrcRemove(src, remove): if not src: return src_bak = src[:] if type(remove) == type('str'): if os.path.isabs(remove): remove = os.path.relpath(remove, GetCurrentDir()) remove = os.path.normpath(remove) for item in src_bak: if type(item) == type('str'): item_str = item else: item_str = item.rstr() if os.path.isabs(item_str): item_str = os.path.relpath(item_str, GetCurrentDir()) item_str = os.path.normpath(item_str) if item_str == remove: src.remove(item) else: for remove_item in remove: remove_str = str(remove_item) if os.path.isabs(remove_str): remove_str = os.path.relpath(remove_str, GetCurrentDir()) remove_str = os.path.normpath(remove_str) for item in src_bak: if type(item) == type('str'): item_str = item else: item_str = item.rstr() if os.path.isabs(item_str): item_str = os.path.relpath(item_str, GetCurrentDir()) item_str = os.path.normpath(item_str) if item_str == remove_str: src.remove(item) def GetVersion(): import SCons.cpp import string rtdef = os.path.join(Rtt_Root, 'include', 'rtdef.h') # parse rtdef.h to get RT-Thread version prepcessor = PatchedPreProcessor() f = open(rtdef, 'r') contents = f.read() f.close() prepcessor.process_contents(contents) def_ns = prepcessor.cpp_namespace version = int([ch for ch in def_ns['RT_VERSION_MAJOR'] if ch in '0123456789.']) subversion = int([ch for ch in def_ns['RT_VERSION_MINOR'] if ch in '0123456789.']) if 'RT_VERSION_PATCH' in def_ns: revision = int([ch for ch in def_ns['RT_VERSION_PATCH'] if ch in '0123456789.']) return '%d.%d.%d' % (version, subversion, revision) return '0.%d.%d' % (version, subversion) def GlobSubDir(sub_dir, ext_name): import os import glob def glob_source(sub_dir, ext_name): list = os.listdir(sub_dir) src = glob.glob(os.path.join(sub_dir, ext_name)) for item in list: full_subdir = os.path.join(sub_dir, item) if os.path.isdir(full_subdir): src += glob_source(full_subdir, ext_name) return src dst = [] src = glob_source(sub_dir, ext_name) for item in src: dst.append(os.path.relpath(item, sub_dir)) return dst def PackageSConscript(package): from package import BuildPackage return BuildPackage(package)