building.py 36 KB

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