1# -*- coding: utf-8 -*- 2# 3# File : building.py 4# This file is part of RT-Thread RTOS 5# COPYRIGHT (C) 2006 - 2015, RT-Thread Development Team 6# 7# This program is free software; you can redistribute it and/or modify 8# it under the terms of the GNU General Public License as published by 9# the Free Software Foundation; either version 2 of the License, or 10# (at your option) any later version. 11# 12# This program is distributed in the hope that it will be useful, 13# but WITHOUT ANY WARRANTY; without even the implied warranty of 14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15# GNU General Public License for more details. 16# 17# You should have received a copy of the GNU General Public License along 18# with this program; if not, write to the Free Software Foundation, Inc., 19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20# 21# Change Logs: 22# Date Author Notes 23# 2015-01-20 Bernard Add copyright information 24# 2015-07-25 Bernard Add LOCAL_CCFLAGS/LOCAL_CPPPATH/LOCAL_CPPDEFINES for 25# group definition. 26# 2024-04-21 Bernard Add toolchain detection in sdk packages 27# 2025-01-05 Bernard Add logging as Env['log'] 28# 2025-03-02 ZhaoCake Add MkDist_Strip 29# 2025-01-05 Assistant Refactor SCons PreProcessor patch to independent class 30 31import os 32import sys 33import string 34import utils 35import operator 36import rtconfig 37import platform 38import logging 39from SCons.Script import * 40from utils import _make_path_relative 41from mkdist import do_copy_file 42from options import AddOptions 43from preprocessor import create_preprocessor_instance 44from win32spawn import Win32Spawn 45 46BuildOptions = {} 47Projects = [] 48Rtt_Root = '' 49Env = None 50 51def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = []): 52 53 global BuildOptions 54 global Projects 55 global Env 56 global Rtt_Root 57 58 AddOptions() 59 60 Env = env 61 # export the default environment 62 Export('env') 63 64 # prepare logging and set log 65 logging.basicConfig(level=logging.INFO, format="%(message)s") 66 logger = logging.getLogger('rt-scons') 67 if GetOption('verbose'): 68 logger.setLevel(logging.DEBUG) 69 Env['log'] = logger 70 71 Rtt_Root = os.path.abspath(root_directory) 72 73 # make an absolute root directory 74 RTT_ROOT = Rtt_Root 75 Export('RTT_ROOT') 76 77 # set RTT_ROOT in ENV 78 Env['RTT_ROOT'] = Rtt_Root 79 os.environ["RTT_DIR"] = Rtt_Root 80 # set BSP_ROOT in ENV 81 Env['BSP_ROOT'] = Dir('#').abspath 82 os.environ["BSP_DIR"] = Dir('#').abspath 83 84 sys.path += os.path.join(Rtt_Root, 'tools') 85 86 # {target_name:(CROSS_TOOL, PLATFORM)} 87 tgt_dict = {'mdk':('keil', 'armcc'), 88 'mdk4':('keil', 'armcc'), 89 'mdk5':('keil', 'armcc'), 90 'iar':('iar', 'iccarm'), 91 'vs':('msvc', 'cl'), 92 'vs2012':('msvc', 'cl'), 93 'vsc' : ('gcc', 'gcc'), 94 'vsc_workspace':('gcc', 'gcc'), 95 'cb':('keil', 'armcc'), 96 'ua':('gcc', 'gcc'), 97 'cdk':('gcc', 'gcc'), 98 'makefile':('gcc', 'gcc'), 99 'eclipse':('gcc', 'gcc'), 100 'ses' : ('gcc', 'gcc'), 101 'cmake':('gcc', 'gcc'), 102 'cmake-armclang':('keil', 'armclang'), 103 'xmake':('gcc', 'gcc'), 104 'codelite' : ('gcc', 'gcc'), 105 'esp-idf': ('gcc', 'gcc'), 106 'zig':('gcc', 'gcc')} 107 tgt_name = GetOption('target') 108 109 if tgt_name: 110 # --target will change the toolchain settings which clang-analyzer is 111 # depend on 112 if GetOption('clang-analyzer'): 113 print ('--clang-analyzer cannot be used with --target') 114 sys.exit(1) 115 116 SetOption('no_exec', 1) 117 try: 118 rtconfig.CROSS_TOOL, rtconfig.PLATFORM = tgt_dict[tgt_name] 119 # replace the 'RTT_CC' to 'CROSS_TOOL' 120 os.environ['RTT_CC'] = rtconfig.CROSS_TOOL 121 except KeyError: 122 print('Unknow target: '+ tgt_name+'. Avaible targets: ' +', '.join(tgt_dict.keys())) 123 sys.exit(1) 124 125 exec_prefix = GetOption('exec-prefix') 126 if exec_prefix: 127 os.environ['RTT_CC_PREFIX'] = exec_prefix 128 129 # auto change the 'RTT_EXEC_PATH' when 'rtconfig.EXEC_PATH' get failed 130 if not utils.CmdExists(os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)): 131 Env['log'].debug('To detect CC because CC path in rtconfig.py is invalid:') 132 Env['log'].debug(' rtconfig.py cc ->' + os.path.join(rtconfig.EXEC_PATH, rtconfig.CC)) 133 if 'RTT_EXEC_PATH' in os.environ: 134 # del the 'RTT_EXEC_PATH' and using the 'EXEC_PATH' setting on rtconfig.py 135 del os.environ['RTT_EXEC_PATH'] 136 137 try: 138 # try to detect toolchains in env 139 envm = utils.ImportModule('env_utility') 140 # from env import GetSDKPath 141 exec_path = envm.GetSDKPath(rtconfig.CC) 142 if exec_path != None: 143 if 'gcc' in rtconfig.CC: 144 exec_path = os.path.join(exec_path, 'bin') 145 146 if os.path.exists(exec_path): 147 Env['log'].debug('set CC to ' + exec_path) 148 rtconfig.EXEC_PATH = exec_path 149 os.environ['RTT_EXEC_PATH'] = exec_path 150 else: 151 Env['log'].debug('No Toolchain found in path(%s).' % exec_path) 152 except Exception as e: 153 # detect failed, ignore 154 Env['log'].debug(e) 155 pass 156 157 exec_path = GetOption('exec-path') 158 if exec_path: 159 os.environ['RTT_EXEC_PATH'] = exec_path 160 161 utils.ReloadModule(rtconfig) # update environment variables to rtconfig.py 162 163 # some env variables have loaded in Environment() of SConstruct before re-load rtconfig.py; 164 # after update rtconfig.py's variables, those env variables need to synchronize 165 if exec_prefix: 166 env['CC'] = rtconfig.CC 167 env['CXX'] = rtconfig.CXX 168 env['AS'] = rtconfig.AS 169 env['AR'] = rtconfig.AR 170 env['LINK'] = rtconfig.LINK 171 if exec_path: 172 env.PrependENVPath('PATH', rtconfig.EXEC_PATH) 173 env['ASCOM']= env['ASPPCOM'] 174 175 if GetOption('strict-compiling'): 176 STRICT_FLAGS = '' 177 if rtconfig.PLATFORM in ['gcc']: 178 STRICT_FLAGS += ' -Werror' #-Wextra 179 env.Append(CFLAGS=STRICT_FLAGS, CXXFLAGS=STRICT_FLAGS) 180 181 # add compability with Keil MDK 4.6 which changes the directory of armcc.exe 182 if rtconfig.PLATFORM in ['armcc', 'armclang']: 183 if rtconfig.PLATFORM == 'armcc' and not os.path.isfile(os.path.join(rtconfig.EXEC_PATH, 'armcc.exe')): 184 if rtconfig.EXEC_PATH.find('bin40') > 0: 185 rtconfig.EXEC_PATH = rtconfig.EXEC_PATH.replace('bin40', 'armcc/bin') 186 Env['LINKFLAGS'] = Env['LINKFLAGS'].replace('RV31', 'armcc') 187 188 # reset AR command flags 189 env['ARCOM'] = '$AR --create $TARGET $SOURCES' 190 env['LIBPREFIX'] = '' 191 env['LIBSUFFIX'] = '.lib' 192 env['LIBLINKPREFIX'] = '' 193 env['LIBLINKSUFFIX'] = '.lib' 194 env['LIBDIRPREFIX'] = '--userlibpath ' 195 196 elif rtconfig.PLATFORM == 'iccarm': 197 env['LIBPREFIX'] = '' 198 env['LIBSUFFIX'] = '.a' 199 env['LIBLINKPREFIX'] = '' 200 env['LIBLINKSUFFIX'] = '.a' 201 env['LIBDIRPREFIX'] = '--search ' 202 203 # patch for win32 spawn 204 if env['PLATFORM'] == 'win32': 205 win32_spawn = Win32Spawn() 206 win32_spawn.env = env 207 env['SPAWN'] = win32_spawn.spawn 208 209 if env['PLATFORM'] == 'win32': 210 os.environ['PATH'] = rtconfig.EXEC_PATH + ";" + os.environ['PATH'] 211 else: 212 os.environ['PATH'] = rtconfig.EXEC_PATH + ":" + os.environ['PATH'] 213 214 # add program path 215 env.PrependENVPath('PATH', os.environ['PATH']) 216 # add rtconfig.h/BSP path into Kernel group 217 DefineGroup("Kernel", [], [], CPPPATH=[str(Dir('#').abspath)]) 218 219 # add library build action 220 act = SCons.Action.Action(BuildLibInstallAction, 'Install compiled library... $TARGET') 221 bld = Builder(action = act) 222 Env.Append(BUILDERS = {'BuildLib': bld}) 223 224 # parse rtconfig.h to get used component 225 PreProcessor = create_preprocessor_instance() 226 f = open('rtconfig.h', 'r') 227 contents = f.read() 228 f.close() 229 PreProcessor.process_contents(contents) 230 BuildOptions = PreProcessor.cpp_namespace 231 232 if GetOption('clang-analyzer'): 233 # perform what scan-build does 234 env.Replace( 235 CC = 'ccc-analyzer', 236 CXX = 'c++-analyzer', 237 # skip as and link 238 LINK = 'true', 239 AS = 'true',) 240 env["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_")) 241 # only check, don't compile. ccc-analyzer use CCC_CC as the CC. 242 # fsyntax-only will give us some additional warning messages 243 env['ENV']['CCC_CC'] = 'clang' 244 env.Append(CFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding']) 245 env['ENV']['CCC_CXX'] = 'clang++' 246 env.Append(CXXFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding']) 247 # remove the POST_ACTION as it will cause meaningless errors(file not 248 # found or something like that). 249 rtconfig.POST_ACTION = '' 250 251 # auto append '_REENT_SMALL' when using newlib 'nano.specs' option 252 if rtconfig.PLATFORM in ['gcc'] and str(env['LINKFLAGS']).find('nano.specs') != -1: 253 env.AppendUnique(CPPDEFINES = ['_REENT_SMALL']) 254 255 attach_global_macros = GetOption('global-macros') 256 if attach_global_macros: 257 attach_global_macros = attach_global_macros.split(',') 258 if isinstance(attach_global_macros, list): 259 for config in attach_global_macros: 260 if isinstance(config, str): 261 AddDepend(attach_global_macros) 262 env.Append(CFLAGS=' -D' + config, CXXFLAGS=' -D' + config, AFLAGS=' -D' + config) 263 else: 264 print('--global-macros arguments are illegal!') 265 else: 266 print('--global-macros arguments are illegal!') 267 268 if GetOption('attach'): 269 from attachconfig import GenAttachConfigProject 270 GenAttachConfigProject() 271 exit(0) 272 273 if GetOption('genconfig'): 274 from env_utility import genconfig 275 genconfig() 276 exit(0) 277 278 if GetOption('stackanalysis'): 279 from WCS import ThreadStackStaticAnalysis 280 ThreadStackStaticAnalysis(Env) 281 exit(0) 282 283 if GetOption('menuconfig'): 284 from env_utility import menuconfig 285 menuconfig(Rtt_Root) 286 exit(0) 287 288 if GetOption('defconfig'): 289 from env_utility import defconfig 290 defconfig(Rtt_Root) 291 exit(0) 292 293 elif GetOption('guiconfig'): 294 from env_utility import guiconfig 295 guiconfig(Rtt_Root) 296 exit(0) 297 298 configfn = GetOption('useconfig') 299 if configfn: 300 from env_utility import mk_rtconfig 301 mk_rtconfig(configfn) 302 exit(0) 303 304 305 if not GetOption('verbose'): 306 # override the default verbose command string 307 env.Replace( 308 ARCOMSTR = 'AR $TARGET', 309 ASCOMSTR = 'AS $TARGET', 310 ASPPCOMSTR = 'AS $TARGET', 311 CCCOMSTR = 'CC $TARGET', 312 CXXCOMSTR = 'CXX $TARGET', 313 LINKCOMSTR = 'LINK $TARGET' 314 ) 315 316 # fix the linker for C++ 317 if GetDepend('RT_USING_CPLUSPLUS'): 318 if env['LINK'].find('gcc') != -1: 319 env['LINK'] = env['LINK'].replace('gcc', 'g++') 320 321 # we need to seperate the variant_dir for BSPs and the kernels. BSPs could 322 # have their own components etc. If they point to the same folder, SCons 323 # would find the wrong source code to compile. 324 bsp_vdir = 'build' 325 kernel_vdir = 'build/kernel' 326 # board build script 327 objs = SConscript('SConscript', variant_dir=bsp_vdir, duplicate=0) 328 # include kernel 329 objs.extend(SConscript(Rtt_Root + '/src/SConscript', variant_dir=kernel_vdir + '/src', duplicate=0)) 330 # include libcpu 331 if not has_libcpu: 332 objs.extend(SConscript(Rtt_Root + '/libcpu/SConscript', 333 variant_dir=kernel_vdir + '/libcpu', duplicate=0)) 334 335 # include components 336 objs.extend(SConscript(Rtt_Root + '/components/SConscript', 337 variant_dir=kernel_vdir + '/components', 338 duplicate=0, 339 exports='remove_components')) 340 # include testcases 341 if os.path.isfile(os.path.join(Rtt_Root, 'examples/utest/testcases/SConscript')): 342 objs.extend(SConscript(Rtt_Root + '/examples/utest/testcases/SConscript', 343 variant_dir=kernel_vdir + '/examples/utest/testcases', 344 duplicate=0)) 345 346 return objs 347 348def PrepareModuleBuilding(env, root_directory, bsp_directory): 349 350 global BuildOptions 351 global Env 352 global Rtt_Root 353 354 # patch for win32 spawn 355 if env['PLATFORM'] == 'win32': 356 win32_spawn = Win32Spawn() 357 win32_spawn.env = env 358 env['SPAWN'] = win32_spawn.spawn 359 360 Env = env 361 Rtt_Root = root_directory 362 363 # parse bsp rtconfig.h to get used component 364 PreProcessor = create_preprocessor_instance() 365 f = open(bsp_directory + '/rtconfig.h', 'r') 366 contents = f.read() 367 f.close() 368 PreProcessor.process_contents(contents) 369 BuildOptions = PreProcessor.cpp_namespace 370 371 AddOption('--buildlib', 372 dest = 'buildlib', 373 type = 'string', 374 help = 'building library of a component') 375 AddOption('--cleanlib', 376 dest = 'cleanlib', 377 action = 'store_true', 378 default = False, 379 help = 'clean up the library by --buildlib') 380 381 # add program path 382 env.PrependENVPath('PATH', rtconfig.EXEC_PATH) 383 384def GetConfigValue(name): 385 assert type(name) == str, 'GetConfigValue: only string parameter is valid' 386 try: 387 return BuildOptions[name] 388 except: 389 return '' 390 391def GetDepend(depend): 392 building = True 393 if type(depend) == type('str'): 394 if not depend in BuildOptions or BuildOptions[depend] == 0: 395 building = False 396 elif BuildOptions[depend] != '': 397 return BuildOptions[depend] 398 399 return building 400 401 # for list type depend 402 for item in depend: 403 if item != '': 404 if not item in BuildOptions or BuildOptions[item] == 0: 405 building = False 406 407 return building 408 409def LocalOptions(config_filename): 410 from SCons.Script import SCons 411 412 # parse wiced_config.h to get used component 413 PreProcessor = SCons.cpp.PreProcessor() 414 415 f = open(config_filename, 'r') 416 contents = f.read() 417 f.close() 418 419 PreProcessor.process_contents(contents) 420 local_options = PreProcessor.cpp_namespace 421 422 return local_options 423 424def GetLocalDepend(options, depend): 425 building = True 426 if type(depend) == type('str'): 427 if not depend in options or options[depend] == 0: 428 building = False 429 elif options[depend] != '': 430 return options[depend] 431 432 return building 433 434 # for list type depend 435 for item in depend: 436 if item != '': 437 if not depend in options or item == 0: 438 building = False 439 440 return building 441 442def AddDepend(option): 443 if isinstance(option, str): 444 BuildOptions[option] = 1 445 elif isinstance(option, list): 446 for obj in option: 447 if isinstance(obj, str): 448 BuildOptions[obj] = 1 449 else: 450 print('AddDepend arguements are illegal!') 451 else: 452 print('AddDepend arguements are illegal!') 453 454def Preprocessing(input, suffix, output = None, CPPPATH = None): 455 if hasattr(rtconfig, "CPP") and hasattr(rtconfig, "CPPFLAGS"): 456 if output == None: 457 import re 458 output = re.sub(r'[\.]+.*', suffix, input) 459 inc = ' ' 460 cpppath = CPPPATH 461 for cpppath_item in cpppath: 462 inc += ' -I' + cpppath_item 463 CPP = rtconfig.EXEC_PATH + '/' + rtconfig.CPP 464 if not os.path.exists(CPP): 465 CPP = rtconfig.CPP 466 CPP += rtconfig.CPPFLAGS 467 path = GetCurrentDir() + '/' 468 os.system(CPP + inc + ' ' + path + input + ' -o ' + path + output) 469 else: 470 print('CPP tool or CPPFLAGS is undefined in rtconfig!') 471 472def MergeGroup(src_group, group): 473 src_group['src'] = src_group['src'] + group['src'] 474 src_group['src'].sort() 475 if 'CFLAGS' in group: 476 if 'CFLAGS' in src_group: 477 src_group['CFLAGS'] = src_group['CFLAGS'] + group['CFLAGS'] 478 else: 479 src_group['CFLAGS'] = group['CFLAGS'] 480 if 'CCFLAGS' in group: 481 if 'CCFLAGS' in src_group: 482 src_group['CCFLAGS'] = src_group['CCFLAGS'] + group['CCFLAGS'] 483 else: 484 src_group['CCFLAGS'] = group['CCFLAGS'] 485 if 'CXXFLAGS' in group: 486 if 'CXXFLAGS' in src_group: 487 src_group['CXXFLAGS'] = src_group['CXXFLAGS'] + group['CXXFLAGS'] 488 else: 489 src_group['CXXFLAGS'] = group['CXXFLAGS'] 490 if 'CPPPATH' in group: 491 if 'CPPPATH' in src_group: 492 src_group['CPPPATH'] = src_group['CPPPATH'] + group['CPPPATH'] 493 else: 494 src_group['CPPPATH'] = group['CPPPATH'] 495 if 'CPPDEFINES' in group: 496 if 'CPPDEFINES' in src_group: 497 src_group['CPPDEFINES'] = src_group['CPPDEFINES'] + group['CPPDEFINES'] 498 else: 499 src_group['CPPDEFINES'] = group['CPPDEFINES'] 500 if 'ASFLAGS' in group: 501 if 'ASFLAGS' in src_group: 502 src_group['ASFLAGS'] = src_group['ASFLAGS'] + group['ASFLAGS'] 503 else: 504 src_group['ASFLAGS'] = group['ASFLAGS'] 505 506 # for local CCFLAGS/CPPPATH/CPPDEFINES 507 if 'LOCAL_CFLAGS' in group: 508 if 'LOCAL_CFLAGS' in src_group: 509 src_group['LOCAL_CFLAGS'] = src_group['LOCAL_CFLAGS'] + group['LOCAL_CFLAGS'] 510 else: 511 src_group['LOCAL_CFLAGS'] = group['LOCAL_CFLAGS'] 512 if 'LOCAL_CCFLAGS' in group: 513 if 'LOCAL_CCFLAGS' in src_group: 514 src_group['LOCAL_CCFLAGS'] = src_group['LOCAL_CCFLAGS'] + group['LOCAL_CCFLAGS'] 515 else: 516 src_group['LOCAL_CCFLAGS'] = group['LOCAL_CCFLAGS'] 517 if 'LOCAL_CXXFLAGS' in group: 518 if 'LOCAL_CXXFLAGS' in src_group: 519 src_group['LOCAL_CXXFLAGS'] = src_group['LOCAL_CXXFLAGS'] + group['LOCAL_CXXFLAGS'] 520 else: 521 src_group['LOCAL_CXXFLAGS'] = group['LOCAL_CXXFLAGS'] 522 if 'LOCAL_CPPPATH' in group: 523 if 'LOCAL_CPPPATH' in src_group: 524 src_group['LOCAL_CPPPATH'] = src_group['LOCAL_CPPPATH'] + group['LOCAL_CPPPATH'] 525 else: 526 src_group['LOCAL_CPPPATH'] = group['LOCAL_CPPPATH'] 527 if 'LOCAL_CPPDEFINES' in group: 528 if 'LOCAL_CPPDEFINES' in src_group: 529 src_group['LOCAL_CPPDEFINES'] = src_group['LOCAL_CPPDEFINES'] + group['LOCAL_CPPDEFINES'] 530 else: 531 src_group['LOCAL_CPPDEFINES'] = group['LOCAL_CPPDEFINES'] 532 533 if 'LINKFLAGS' in group: 534 if 'LINKFLAGS' in src_group: 535 src_group['LINKFLAGS'] = src_group['LINKFLAGS'] + group['LINKFLAGS'] 536 else: 537 src_group['LINKFLAGS'] = group['LINKFLAGS'] 538 if 'LIBS' in group: 539 if 'LIBS' in src_group: 540 src_group['LIBS'] = src_group['LIBS'] + group['LIBS'] 541 else: 542 src_group['LIBS'] = group['LIBS'] 543 if 'LIBPATH' in group: 544 if 'LIBPATH' in src_group: 545 src_group['LIBPATH'] = src_group['LIBPATH'] + group['LIBPATH'] 546 else: 547 src_group['LIBPATH'] = group['LIBPATH'] 548 if 'LOCAL_ASFLAGS' in group: 549 if 'LOCAL_ASFLAGS' in src_group: 550 src_group['LOCAL_ASFLAGS'] = src_group['LOCAL_ASFLAGS'] + group['LOCAL_ASFLAGS'] 551 else: 552 src_group['LOCAL_ASFLAGS'] = group['LOCAL_ASFLAGS'] 553 554def _PretreatListParameters(target_list): 555 while '' in target_list: # remove null strings 556 target_list.remove('') 557 while ' ' in target_list: # remove ' ' 558 target_list.remove(' ') 559 560 if(len(target_list) == 0): 561 return False # ignore this list, don't add this list to the parameter 562 563 return True # permit to add this list to the parameter 564 565def DefineGroup(name, src, depend, **parameters): 566 global Env 567 if not GetDepend(depend): 568 return [] 569 570 # find exist group and get path of group 571 group_path = '' 572 for g in Projects: 573 if g['name'] == name: 574 group_path = g['path'] 575 if group_path == '': 576 group_path = GetCurrentDir() 577 578 group = parameters 579 group['name'] = name 580 group['path'] = group_path 581 if type(src) == type([]): 582 # remove duplicate elements from list 583 src = list(set(src)) 584 group['src'] = File(src) 585 else: 586 group['src'] = src 587 588 if 'CFLAGS' in group: 589 target = group['CFLAGS'] 590 if len(target) > 0: 591 Env.AppendUnique(CFLAGS = target) 592 if 'CCFLAGS' in group: 593 target = group['CCFLAGS'] 594 if len(target) > 0: 595 Env.AppendUnique(CCFLAGS = target) 596 if 'CXXFLAGS' in group: 597 target = group['CXXFLAGS'] 598 if len(target) > 0: 599 Env.AppendUnique(CXXFLAGS = target) 600 if 'CPPPATH' in group: 601 target = group['CPPPATH'] 602 if _PretreatListParameters(target) == True: 603 paths = [] 604 for item in target: 605 paths.append(os.path.abspath(item)) 606 target = paths 607 Env.AppendUnique(CPPPATH = target) 608 if 'CPPDEFINES' in group: 609 target = group['CPPDEFINES'] 610 if _PretreatListParameters(target) == True: 611 Env.AppendUnique(CPPDEFINES = target) 612 if 'LINKFLAGS' in group: 613 target = group['LINKFLAGS'] 614 if len(target) > 0: 615 Env.AppendUnique(LINKFLAGS = target) 616 if 'ASFLAGS' in group: 617 target = group['ASFLAGS'] 618 if len(target) > 0: 619 Env.AppendUnique(ASFLAGS = target) 620 if 'LOCAL_CPPPATH' in group: 621 paths = [] 622 for item in group['LOCAL_CPPPATH']: 623 paths.append(os.path.abspath(item)) 624 group['LOCAL_CPPPATH'] = paths 625 626 627 if rtconfig.PLATFORM in ['gcc']: 628 if 'CFLAGS' in group: 629 group['CFLAGS'] = utils.GCCC99Patch(group['CFLAGS']) 630 if 'CCFLAGS' in group: 631 group['CCFLAGS'] = utils.GCCC99Patch(group['CCFLAGS']) 632 if 'CXXFLAGS' in group: 633 group['CXXFLAGS'] = utils.GCCC99Patch(group['CXXFLAGS']) 634 if 'LOCAL_CCFLAGS' in group: 635 group['LOCAL_CCFLAGS'] = utils.GCCC99Patch(group['LOCAL_CCFLAGS']) 636 if 'LOCAL_CXXFLAGS' in group: 637 group['LOCAL_CXXFLAGS'] = utils.GCCC99Patch(group['LOCAL_CXXFLAGS']) 638 if 'LOCAL_CFLAGS' in group: 639 group['LOCAL_CFLAGS'] = utils.GCCC99Patch(group['LOCAL_CFLAGS']) 640 # check whether to clean up library 641 if GetOption('cleanlib') and os.path.exists(os.path.join(group['path'], GroupLibFullName(name, Env))): 642 if group['src'] != []: 643 print('Remove library:'+ GroupLibFullName(name, Env)) 644 fn = os.path.join(group['path'], GroupLibFullName(name, Env)) 645 if os.path.exists(fn): 646 os.unlink(fn) 647 648 if 'LIBS' in group: 649 target = group['LIBS'] 650 if _PretreatListParameters(target) == True: 651 Env.AppendUnique(LIBS = target) 652 if 'LIBPATH' in group: 653 target = group['LIBPATH'] 654 if _PretreatListParameters(target) == True: 655 Env.AppendUnique(LIBPATH = target) 656 657 # check whether to build group library 658 if 'LIBRARY' in group: 659 objs = Env.Library(name, group['src']) 660 else: 661 # only add source 662 objs = group['src'] 663 664 # merge group 665 for g in Projects: 666 if g['name'] == name: 667 # merge to this group 668 MergeGroup(g, group) 669 return objs 670 671 def PriorityInsertGroup(groups, group): 672 length = len(groups) 673 for i in range(0, length): 674 if operator.gt(groups[i]['name'].lower(), group['name'].lower()): 675 groups.insert(i, group) 676 return 677 groups.append(group) 678 679 # add a new group 680 PriorityInsertGroup(Projects, group) 681 682 return objs 683 684def GetCurrentDir(): 685 conscript = File('SConscript') 686 fn = conscript.rfile() 687 name = fn.name 688 path = os.path.dirname(fn.abspath) 689 return path 690 691PREBUILDING = [] 692def RegisterPreBuildingAction(act): 693 global PREBUILDING 694 assert callable(act), 'Could only register callable objects. %s received' % repr(act) 695 PREBUILDING.append(act) 696 697def PreBuilding(): 698 global PREBUILDING 699 for a in PREBUILDING: 700 a() 701 702def GroupLibName(name, env): 703 704 if rtconfig.PLATFORM in ['armcc']: 705 return name + '_rvds' 706 elif rtconfig.PLATFORM in ['gcc']: 707 return name + '_gcc' 708 709 return name 710 711def GroupLibFullName(name, env): 712 return env['LIBPREFIX'] + GroupLibName(name, env) + env['LIBSUFFIX'] 713 714def BuildLibInstallAction(target, source, env): 715 lib_name = GetOption('buildlib') 716 for Group in Projects: 717 if Group['name'] == lib_name: 718 lib_name = GroupLibFullName(Group['name'], env) 719 dst_name = os.path.join(Group['path'], lib_name) 720 print('Copy '+lib_name+' => ' + dst_name) 721 do_copy_file(lib_name, dst_name) 722 break 723 724def DoBuilding(target, objects): 725 726 # merge all objects into one list 727 def one_list(l): 728 lst = [] 729 for item in l: 730 if type(item) == type([]): 731 lst += one_list(item) 732 else: 733 lst.append(item) 734 return lst 735 736 # handle local group 737 def local_group(group, objects): 738 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: 739 CFLAGS = Env.get('CFLAGS', '') + group.get('LOCAL_CFLAGS', '') 740 CCFLAGS = Env.get('CCFLAGS', '') + group.get('LOCAL_CCFLAGS', '') 741 CXXFLAGS = Env.get('CXXFLAGS', '') + group.get('LOCAL_CXXFLAGS', '') 742 CPPPATH = list(Env.get('CPPPATH', [''])) + group.get('LOCAL_CPPPATH', ['']) 743 CPPDEFINES = list(Env.get('CPPDEFINES', [''])) + group.get('LOCAL_CPPDEFINES', ['']) 744 ASFLAGS = Env.get('ASFLAGS', '') + group.get('LOCAL_ASFLAGS', '') 745 746 for source in group['src']: 747 objects.append(Env.Object(source, CFLAGS = CFLAGS, CCFLAGS = CCFLAGS, CXXFLAGS = CXXFLAGS, ASFLAGS = ASFLAGS, 748 CPPPATH = CPPPATH, CPPDEFINES = CPPDEFINES)) 749 750 return True 751 752 return False 753 754 PreBuilding() 755 objects = one_list(objects) 756 757 program = None 758 # check whether special buildlib option 759 lib_name = GetOption('buildlib') 760 if lib_name: 761 objects = [] # remove all of objects 762 # build library with special component 763 for Group in Projects: 764 if Group['name'] == lib_name: 765 lib_name = GroupLibName(Group['name'], Env) 766 if not local_group(Group, objects): 767 objects = Env.Object(Group['src']) 768 769 program = Env.Library(lib_name, objects) 770 771 # add library copy action 772 Env.BuildLib(lib_name, program) 773 774 break 775 else: 776 # generate build/compile_commands.json 777 if GetOption('cdb') and utils.VerTuple(SCons.__version__) >= (4, 0, 0): 778 Env.Tool("compilation_db") 779 Env.CompilationDatabase('build/compile_commands.json') 780 781 # remove source files with local flags setting 782 for group in Projects: 783 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: 784 for source in group['src']: 785 for obj in objects: 786 if source.abspath == obj.abspath or (len(obj.sources) > 0 and source.abspath == obj.sources[0].abspath): 787 objects.remove(obj) 788 789 # re-add the source files to the objects 790 791 objects_in_group = [] 792 for group in Projects: 793 local_group(group, objects_in_group) 794 795 # sort seperately, because the data type of 796 # the members of the two lists are different 797 objects_in_group = sorted(objects_in_group) 798 objects = sorted(objects) 799 objects.append(objects_in_group) 800 801 program = Env.Program(target, objects) 802 803 EndBuilding(target, program) 804 805def GenTargetProject(program = None): 806 807 if GetOption('target') in ['mdk', 'mdk4', 'mdk5']: 808 from targets.keil import MDK2Project, MDK4Project, MDK5Project, ARMCC_Version 809 810 if os.path.isfile('template.uvprojx') and GetOption('target') not in ['mdk4']: # Keil5 811 MDK5Project(Env, GetOption('project-name') + '.uvprojx', Projects) 812 print("Keil5 project is generating...") 813 elif os.path.isfile('template.uvproj') and GetOption('target') not in ['mdk5']: # Keil4 814 MDK4Project(Env, GetOption('project-name') + '.uvproj', Projects) 815 print("Keil4 project is generating...") 816 elif os.path.isfile('template.Uv2') and GetOption('target') not in ['mdk4', 'mdk5']: # Keil2 817 MDK2Project(Env, GetOption('project-name') + '.Uv2', Projects) 818 print("Keil2 project is generating...") 819 else: 820 print ('No template project file found.') 821 exit(1) 822 print("Keil Version: " + ARMCC_Version()) 823 print("Keil-MDK project has generated successfully!") 824 825 if GetOption('target') == 'iar': 826 from targets.iar import IARProject, IARVersion 827 print("IAR Version: " + IARVersion()) 828 IARProject(Env, GetOption('project-name') + '.ewp', Projects) 829 print("IAR project has generated successfully!") 830 831 if GetOption('target') == 'vs': 832 from targets.vs import VSProject 833 VSProject(GetOption('project-name') + '.vcproj', Projects, program) 834 835 if GetOption('target') == 'vs2012': 836 from targets.vs2012 import VS2012Project 837 VS2012Project(GetOption('project-name') + '.vcxproj', Projects, program) 838 839 if GetOption('target') == 'cb': 840 from targets.codeblocks import CBProject 841 CBProject(GetOption('project-name') + '.cbp', Projects, program) 842 843 if GetOption('target') == 'ua': 844 from targets.ua import PrepareUA 845 PrepareUA(Projects, Rtt_Root, str(Dir('#'))) 846 847 if GetOption('target') == 'vsc': 848 from targets.vsc import GenerateVSCode 849 GenerateVSCode(Env) 850 if GetOption('cmsispack'): 851 from vscpyocd import GenerateVSCodePyocdConfig 852 GenerateVSCodePyocdConfig(GetOption('cmsispack')) 853 854 if GetOption('target') == 'vsc_workspace': 855 from targets.vsc import GenerateVSCodeWorkspace 856 GenerateVSCodeWorkspace(Env) 857 858 if GetOption('target') == 'cdk': 859 from targets.cdk import CDKProject 860 CDKProject(GetOption('project-name') + '.cdkproj', Projects) 861 862 if GetOption('target') == 'ses': 863 from targets.ses import SESProject 864 SESProject(Env) 865 866 if GetOption('target') == 'makefile': 867 from targets.makefile import TargetMakefile 868 TargetMakefile(Env) 869 870 if GetOption('target') == 'eclipse': 871 from targets.eclipse import TargetEclipse 872 TargetEclipse(Env, GetOption('reset-project-config'), GetOption('project-name')) 873 874 if GetOption('target') == 'codelite': 875 from targets.codelite import TargetCodelite 876 TargetCodelite(Projects, program) 877 878 if GetOption('target') == 'cmake' or GetOption('target') == 'cmake-armclang': 879 from targets.cmake import CMakeProject 880 CMakeProject(Env, Projects, GetOption('project-name')) 881 882 if GetOption('target') == 'xmake': 883 from targets.xmake import XMakeProject 884 XMakeProject(Env, Projects) 885 886 if GetOption('target') == 'esp-idf': 887 from targets.esp_idf import ESPIDFProject 888 ESPIDFProject(Env, Projects) 889 890 if GetOption('target') == 'zig': 891 from targets.zigbuild import ZigBuildProject 892 ZigBuildProject(Env, Projects) 893 894def EndBuilding(target, program = None): 895 from mkdist import MkDist, MkDist_Strip 896 897 need_exit = False 898 899 Env['target'] = program 900 Env['project'] = Projects 901 902 if hasattr(rtconfig, 'BSP_LIBRARY_TYPE'): 903 Env['bsp_lib_type'] = rtconfig.BSP_LIBRARY_TYPE 904 905 if hasattr(rtconfig, 'dist_handle'): 906 Env['dist_handle'] = rtconfig.dist_handle 907 908 Env.AddPostAction(target, rtconfig.POST_ACTION) 909 # Add addition clean files 910 Clean(target, 'cconfig.h') 911 Clean(target, 'rtua.py') 912 Clean(target, 'rtua.pyc') 913 Clean(target, '.sconsign.dblite') 914 915 if GetOption('target'): 916 GenTargetProject(program) 917 need_exit = True 918 919 BSP_ROOT = Dir('#').abspath 920 921 project_name = GetOption('project-name') 922 project_path = GetOption('project-path') 923 924 # 合并处理打包相关选项 925 if program != None: 926 if GetOption('make-dist'): 927 MkDist(program, BSP_ROOT, Rtt_Root, Env, project_name, project_path) 928 need_exit = True 929 elif GetOption('dist_strip'): 930 MkDist_Strip(program, BSP_ROOT, Rtt_Root, Env, project_name, project_path) 931 need_exit = True 932 elif GetOption('make-dist-ide'): 933 import subprocess 934 if not isinstance(project_path, str) or len(project_path) == 0: 935 project_path = os.path.join(BSP_ROOT, 'rt-studio-project') 936 MkDist(program, BSP_ROOT, Rtt_Root, Env, project_name, project_path) 937 child = subprocess.Popen('scons --target=eclipse --project-name="{}"'.format(project_name), 938 cwd=project_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 939 stdout, stderr = child.communicate() 940 need_exit = True 941 942 if GetOption('cscope'): 943 from cscope import CscopeDatabase 944 CscopeDatabase(Projects) 945 946 if not GetOption('help') and not GetOption('target'): 947 if not os.path.exists(rtconfig.EXEC_PATH): 948 print ("Error: the toolchain path (" + rtconfig.EXEC_PATH + ") is not exist, please check 'EXEC_PATH' in path or rtconfig.py.") 949 need_exit = True 950 951 if need_exit: 952 exit(0) 953 954def SrcRemove(src, remove): 955 if not src: 956 return 957 958 src_bak = src[:] 959 960 if type(remove) == type('str'): 961 if os.path.isabs(remove): 962 remove = os.path.relpath(remove, GetCurrentDir()) 963 remove = os.path.normpath(remove) 964 965 for item in src_bak: 966 if type(item) == type('str'): 967 item_str = item 968 else: 969 item_str = item.rstr() 970 971 if os.path.isabs(item_str): 972 item_str = os.path.relpath(item_str, GetCurrentDir()) 973 item_str = os.path.normpath(item_str) 974 975 if item_str == remove: 976 src.remove(item) 977 else: 978 for remove_item in remove: 979 remove_str = str(remove_item) 980 if os.path.isabs(remove_str): 981 remove_str = os.path.relpath(remove_str, GetCurrentDir()) 982 remove_str = os.path.normpath(remove_str) 983 984 for item in src_bak: 985 if type(item) == type('str'): 986 item_str = item 987 else: 988 item_str = item.rstr() 989 990 if os.path.isabs(item_str): 991 item_str = os.path.relpath(item_str, GetCurrentDir()) 992 item_str = os.path.normpath(item_str) 993 994 if item_str == remove_str: 995 src.remove(item) 996 997def GetVersion(): 998 import SCons.cpp 999 import string 1000 1001 rtdef = os.path.join(Rtt_Root, 'include', 'rtdef.h') 1002 1003 # parse rtdef.h to get RT-Thread version 1004 prepcessor = create_preprocessor_instance() 1005 f = open(rtdef, 'r') 1006 contents = f.read() 1007 f.close() 1008 prepcessor.process_contents(contents) 1009 def_ns = prepcessor.cpp_namespace 1010 1011 version = int([ch for ch in def_ns['RT_VERSION_MAJOR'] if ch in '0123456789.']) 1012 subversion = int([ch for ch in def_ns['RT_VERSION_MINOR'] if ch in '0123456789.']) 1013 1014 if 'RT_VERSION_PATCH' in def_ns: 1015 revision = int([ch for ch in def_ns['RT_VERSION_PATCH'] if ch in '0123456789.']) 1016 return '%d.%d.%d' % (version, subversion, revision) 1017 1018 return '0.%d.%d' % (version, subversion) 1019 1020def GlobSubDir(sub_dir, ext_name): 1021 import os 1022 import glob 1023 1024 def glob_source(sub_dir, ext_name): 1025 list = os.listdir(sub_dir) 1026 src = glob.glob(os.path.join(sub_dir, ext_name)) 1027 1028 for item in list: 1029 full_subdir = os.path.join(sub_dir, item) 1030 if os.path.isdir(full_subdir): 1031 src += glob_source(full_subdir, ext_name) 1032 return src 1033 1034 dst = [] 1035 src = glob_source(sub_dir, ext_name) 1036 for item in src: 1037 dst.append(os.path.relpath(item, sub_dir)) 1038 return dst 1039 1040def PackageSConscript(package): 1041 from package import BuildPackage 1042 1043 return BuildPackage(package) 1044