building.py 37 KB

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