building.py 36 KB

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