building.py 38 KB

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