building.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. import os
  2. import sys
  3. import string
  4. from SCons.Script import *
  5. from utils import _make_path_relative
  6. BuildOptions = {}
  7. Projects = []
  8. Rtt_Root = ''
  9. Env = None
  10. class Win32Spawn:
  11. def spawn(self, sh, escape, cmd, args, env):
  12. import subprocess
  13. newargs = string.join(args[1:], ' ')
  14. cmdline = cmd + " " + newargs
  15. startupinfo = subprocess.STARTUPINFO()
  16. proc = subprocess.Popen(cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
  17. stderr=subprocess.PIPE, startupinfo=startupinfo, shell = False)
  18. data, err = proc.communicate()
  19. rv = proc.wait()
  20. if data:
  21. print data
  22. if err:
  23. print err
  24. if rv:
  25. return rv
  26. return 0
  27. def PrepareBuilding(env, root_directory, has_libcpu=False, remove_components = []):
  28. import SCons.cpp
  29. import rtconfig
  30. global BuildOptions
  31. global Projects
  32. global Env
  33. global Rtt_Root
  34. Env = env
  35. Rtt_Root = root_directory
  36. # add compability with Keil MDK 4.6 which changes the directory of armcc.exe
  37. if rtconfig.PLATFORM == 'armcc':
  38. if not os.path.isfile(os.path.join(rtconfig.EXEC_PATH, 'armcc.exe')):
  39. if rtconfig.EXEC_PATH.find('bin40') > 0:
  40. rtconfig.EXEC_PATH = rtconfig.EXEC_PATH.replace('bin40', 'armcc/bin')
  41. Env['LINKFLAGS']=Env['LINKFLAGS'].replace('RV31', 'armcc')
  42. # reset AR command flags
  43. env['ARCOM'] = '$AR --create $TARGET $SOURCES'
  44. env['LIBPREFIX'] = ''
  45. env['LIBSUFFIX'] = '_rvds.lib'
  46. env['LIBLINKPREFIX'] = ''
  47. env['LIBLINKSUFFIX'] = '_rvds.lib'
  48. env['LIBDIRPREFIX'] = '--userlibpath '
  49. elif rtconfig.PLATFORM == 'gcc':
  50. env['LIBSUFFIX'] = '_gcc.a'
  51. env['LIBLINKSUFFIX'] = '_gcc'
  52. # patch for win32 spawn
  53. if env['PLATFORM'] == 'win32' and rtconfig.PLATFORM == 'gcc':
  54. win32_spawn = Win32Spawn()
  55. win32_spawn.env = env
  56. env['SPAWN'] = win32_spawn.spawn
  57. if env['PLATFORM'] == 'win32':
  58. os.environ['PATH'] = rtconfig.EXEC_PATH + ";" + os.environ['PATH']
  59. else:
  60. os.environ['PATH'] = rtconfig.EXEC_PATH + ":" + os.environ['PATH']
  61. # add program path
  62. env.PrependENVPath('PATH', rtconfig.EXEC_PATH)
  63. # add library build action
  64. act = SCons.Action.Action(BuildLibInstallAction, 'Install compiled library... $TARGET')
  65. bld = Builder(action = act)
  66. Env.Append(BUILDERS = {'BuildLib': bld})
  67. # parse rtconfig.h to get used component
  68. PreProcessor = SCons.cpp.PreProcessor()
  69. f = file('rtconfig.h', 'r')
  70. contents = f.read()
  71. f.close()
  72. PreProcessor.process_contents(contents)
  73. BuildOptions = PreProcessor.cpp_namespace
  74. # add copy option
  75. AddOption('--copy',
  76. dest='copy',
  77. action='store_true',
  78. default=False,
  79. help='copy rt-thread directory to local.')
  80. AddOption('--copy-header',
  81. dest='copy-header',
  82. action='store_true',
  83. default=False,
  84. help='copy header of rt-thread directory to local.')
  85. AddOption('--cscope',
  86. dest='cscope',
  87. action='store_true',
  88. default=False,
  89. help='Build Cscope cross reference database. Requires cscope installed.')
  90. AddOption('--clang-analyzer',
  91. dest='clang-analyzer',
  92. action='store_true',
  93. default=False,
  94. help='Perform static analyze with Clang-analyzer. '+\
  95. 'Requires Clang installed.\n'+\
  96. 'It is recommended to use with scan-build like this:\n'+\
  97. '`scan-build scons --clang-analyzer`\n'+\
  98. 'If things goes well, scan-build will instruct you to invoke scan-view.')
  99. if GetOption('clang-analyzer'):
  100. # perform what scan-build does
  101. env.Replace(
  102. CC = 'ccc-analyzer',
  103. CXX = 'c++-analyzer',
  104. # skip as and link
  105. LINK = 'true',
  106. AS = 'true',)
  107. env["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_"))
  108. # only check, don't compile. ccc-analyzer use CCC_CC as the CC.
  109. # fsyntax-only will give us some additional warning messages
  110. env['ENV']['CCC_CC'] = 'clang'
  111. env.Append(CFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding'])
  112. env['ENV']['CCC_CXX'] = 'clang++'
  113. env.Append(CXXFLAGS=['-fsyntax-only', '-Wall', '-Wno-invalid-source-encoding'])
  114. # remove the POST_ACTION as it will cause meaningless errors(file not
  115. # found or something like that).
  116. rtconfig.POST_ACTION = ''
  117. # add build library option
  118. AddOption('--buildlib',
  119. dest='buildlib',
  120. type='string',
  121. help='building library of a component')
  122. AddOption('--cleanlib',
  123. dest='cleanlib',
  124. action='store_true',
  125. default=False,
  126. help='clean up the library by --buildlib')
  127. # add target option
  128. AddOption('--target',
  129. dest='target',
  130. type='string',
  131. help='set target project: mdk/iar/vs')
  132. #{target_name:(CROSS_TOOL, PLATFORM)}
  133. tgt_dict = {'mdk':('keil', 'armcc'),
  134. 'mdk4':('keil', 'armcc'),
  135. 'iar':('iar', 'iar'),
  136. 'vs':('msvc', 'cl'),
  137. 'vs2012':('msvc', 'cl'),
  138. 'cb':('keil', 'armcc')}
  139. tgt_name = GetOption('target')
  140. if tgt_name:
  141. # --target will change the toolchain settings which clang-analyzer is
  142. # depend on
  143. if GetOption('clang-analyzer'):
  144. print '--clang-analyzer cannot be used with --target'
  145. sys.exit(1)
  146. SetOption('no_exec', 1)
  147. try:
  148. rtconfig.CROSS_TOOL, rtconfig.PLATFORM = tgt_dict[tgt_name]
  149. except KeyError:
  150. print 'Unknow target: %s. Avaible targets: %s' % \
  151. (tgt_name, ', '.join(tgt_dict.keys()))
  152. sys.exit(1)
  153. elif (GetDepend('RT_USING_NEWLIB') == False and GetDepend('RT_USING_NOLIBC') == False) \
  154. and rtconfig.PLATFORM == 'gcc':
  155. AddDepend('RT_USING_MINILIBC')
  156. # add comstr option
  157. AddOption('--verbose',
  158. dest='verbose',
  159. action='store_true',
  160. default=False,
  161. help='print verbose information during build')
  162. if not GetOption('verbose'):
  163. # override the default verbose command string
  164. env.Replace(
  165. ARCOMSTR = 'AR $TARGET',
  166. ASCOMSTR = 'AS $TARGET',
  167. ASPPCOMSTR = 'AS $TARGET',
  168. CCCOMSTR = 'CC $TARGET',
  169. CXXCOMSTR = 'CXX $TARGET',
  170. LINKCOMSTR = 'LINK $TARGET'
  171. )
  172. # board build script
  173. objs = SConscript('SConscript', variant_dir='build', duplicate=0)
  174. Repository(Rtt_Root)
  175. # include kernel
  176. objs.extend(SConscript(Rtt_Root + '/src/SConscript', variant_dir='build/src', duplicate=0))
  177. # include libcpu
  178. if not has_libcpu:
  179. objs.extend(SConscript(Rtt_Root + '/libcpu/SConscript', variant_dir='build/libcpu', duplicate=0))
  180. # include components
  181. objs.extend(SConscript(Rtt_Root + '/components/SConscript',
  182. variant_dir='build/components',
  183. duplicate=0,
  184. exports='remove_components'))
  185. return objs
  186. def PrepareModuleBuilding(env, root_directory):
  187. import SCons.cpp
  188. import rtconfig
  189. global BuildOptions
  190. global Projects
  191. global Env
  192. global Rtt_Root
  193. Env = env
  194. Rtt_Root = root_directory
  195. # add program path
  196. env.PrependENVPath('PATH', rtconfig.EXEC_PATH)
  197. def GetConfigValue(name):
  198. assert type(name) == str, 'GetConfigValue: only string parameter is valid'
  199. try:
  200. return BuildOptions[name]
  201. except:
  202. return ''
  203. def GetDepend(depend):
  204. building = True
  205. if type(depend) == type('str'):
  206. if not BuildOptions.has_key(depend) or BuildOptions[depend] == 0:
  207. building = False
  208. elif BuildOptions[depend] != '':
  209. return BuildOptions[depend]
  210. return building
  211. # for list type depend
  212. for item in depend:
  213. if item != '':
  214. if not BuildOptions.has_key(item) or BuildOptions[item] == 0:
  215. building = False
  216. return building
  217. def AddDepend(option):
  218. BuildOptions[option] = 1
  219. def MergeGroup(src_group, group):
  220. src_group['src'] = src_group['src'] + group['src']
  221. if group.has_key('CCFLAGS'):
  222. if src_group.has_key('CCFLAGS'):
  223. src_group['CCFLAGS'] = src_group['CCFLAGS'] + group['CCFLAGS']
  224. else:
  225. src_group['CCFLAGS'] = group['CCFLAGS']
  226. if group.has_key('CPPPATH'):
  227. if src_group.has_key('CPPPATH'):
  228. src_group['CPPPATH'] = src_group['CPPPATH'] + group['CPPPATH']
  229. else:
  230. src_group['CPPPATH'] = group['CPPPATH']
  231. if group.has_key('CPPDEFINES'):
  232. if src_group.has_key('CPPDEFINES'):
  233. src_group['CPPDEFINES'] = src_group['CPPDEFINES'] + group['CPPDEFINES']
  234. else:
  235. src_group['CPPDEFINES'] = group['CPPDEFINES']
  236. if group.has_key('LINKFLAGS'):
  237. if src_group.has_key('LINKFLAGS'):
  238. src_group['LINKFLAGS'] = src_group['LINKFLAGS'] + group['LINKFLAGS']
  239. else:
  240. src_group['LINKFLAGS'] = group['LINKFLAGS']
  241. if group.has_key('LIBS'):
  242. if src_group.has_key('LIBS'):
  243. src_group['LIBS'] = src_group['LIBS'] + group['LIBS']
  244. else:
  245. src_group['LIBS'] = group['LIBS']
  246. if group.has_key('LIBPATH'):
  247. if src_group.has_key('LIBPATH'):
  248. src_group['LIBPATH'] = src_group['LIBPATH'] + group['LIBPATH']
  249. else:
  250. src_group['LIBPATH'] = group['LIBPATH']
  251. def DefineGroup(name, src, depend, **parameters):
  252. global Env
  253. if not GetDepend(depend):
  254. return []
  255. group = parameters
  256. group['name'] = name
  257. group['path'] = GetCurrentDir()
  258. if type(src) == type(['src1']):
  259. group['src'] = File(src)
  260. else:
  261. group['src'] = src
  262. if group.has_key('CCFLAGS'):
  263. Env.Append(CCFLAGS = group['CCFLAGS'])
  264. if group.has_key('CPPPATH'):
  265. Env.Append(CPPPATH = group['CPPPATH'])
  266. if group.has_key('CPPDEFINES'):
  267. Env.Append(CPPDEFINES = group['CPPDEFINES'])
  268. if group.has_key('LINKFLAGS'):
  269. Env.Append(LINKFLAGS = group['LINKFLAGS'])
  270. # check whether to clean up library
  271. if GetOption('cleanlib') and os.path.exists(os.path.join(group['path'], GroupLibName(name, Env))):
  272. if group['src'] != []:
  273. print 'Remove library:', GroupLibName(name, Env)
  274. do_rm_file(os.path.join(group['path'], GroupLibName(name, Env)))
  275. # check whether exist group library
  276. if not GetOption('buildlib') and os.path.exists(os.path.join(group['path'], GroupLibName(name, Env))):
  277. Env.Append(LIBS = [name])
  278. group['src'] = []
  279. Env.Append(LIBPATH = [GetCurrentDir()])
  280. if group.has_key('LIBS'):
  281. Env.Append(LIBS = group['LIBS'])
  282. if group.has_key('LIBPATH'):
  283. Env.Append(LIBPATH = group['LIBPATH'])
  284. objs = Env.Object(group['src'])
  285. if group.has_key('LIBRARY'):
  286. objs = Env.Library(name, objs)
  287. # merge group
  288. for g in Projects:
  289. if g['name'] == name:
  290. # merge to this group
  291. MergeGroup(g, group)
  292. return objs
  293. # add a new group
  294. Projects.append(group)
  295. return objs
  296. def GetCurrentDir():
  297. conscript = File('SConscript')
  298. fn = conscript.rfile()
  299. name = fn.name
  300. path = os.path.dirname(fn.abspath)
  301. return path
  302. PREBUILDING = []
  303. def RegisterPreBuildingAction(act):
  304. global PREBUILDING
  305. assert callable(act), 'Could only register callable objects. %s received' % repr(act)
  306. PREBUILDING.append(act)
  307. def PreBuilding():
  308. global PREBUILDING
  309. for a in PREBUILDING:
  310. a()
  311. def GroupLibName(name, env):
  312. return env['LIBPREFIX'] + name + env['LIBSUFFIX']
  313. def BuildLibInstallAction(target, source, env):
  314. lib_name = GetOption('buildlib')
  315. for Group in Projects:
  316. if Group['name'] == lib_name:
  317. lib_name = str(target[0])
  318. lib_name = GroupLibName(lib_name, env)
  319. dst_name = os.path.join(Group['path'], lib_name)
  320. print 'Copy %s => %s' % (lib_name, dst_name)
  321. do_copy_file(lib_name, dst_name)
  322. break
  323. def DoBuilding(target, objects):
  324. program = None
  325. # check whether special buildlib option
  326. lib_name = GetOption('buildlib')
  327. if lib_name:
  328. # build library with special component
  329. for Group in Projects:
  330. if Group['name'] == lib_name:
  331. objects = Env.Object(Group['src'])
  332. program = Env.Library(lib_name, objects)
  333. # add library copy action
  334. Env.BuildLib(lib_name, program)
  335. break
  336. else:
  337. program = Env.Program(target, objects)
  338. EndBuilding(target, program)
  339. def EndBuilding(target, program = None):
  340. import rtconfig
  341. from keil import MDKProject
  342. from keil import MDK4Project
  343. from iar import IARProject
  344. from vs import VSProject
  345. from vs2012 import VS2012Project
  346. from codeblocks import CBProject
  347. Env.AddPostAction(target, rtconfig.POST_ACTION)
  348. if GetOption('target') == 'mdk':
  349. template = os.path.isfile('template.Uv2')
  350. if template:
  351. MDKProject('project.Uv2', Projects)
  352. else:
  353. template = os.path.isfile('template.uvproj')
  354. if template:
  355. MDK4Project('project.uvproj', Projects)
  356. else:
  357. print 'No template project file found.'
  358. if GetOption('target') == 'mdk4':
  359. MDK4Project('project.uvproj', Projects)
  360. if GetOption('target') == 'iar':
  361. IARProject('project.ewp', Projects)
  362. if GetOption('target') == 'vs':
  363. VSProject('project.vcproj', Projects, program)
  364. if GetOption('target') == 'vs2012':
  365. VS2012Project('project.vcxproj', Projects, program)
  366. if GetOption('target') == 'cb':
  367. CBProject('project.cbp', Projects, program)
  368. if GetOption('copy') and program != None:
  369. MakeCopy(program)
  370. if GetOption('copy-header') and program != None:
  371. MakeCopyHeader(program)
  372. if GetOption('cscope'):
  373. from cscope import CscopeDatabase
  374. CscopeDatabase(Projects)
  375. def SrcRemove(src, remove):
  376. if type(src[0]) == type('str'):
  377. for item in src:
  378. if os.path.basename(item) in remove:
  379. src.remove(item)
  380. return
  381. for item in src:
  382. if os.path.basename(item.rstr()) in remove:
  383. src.remove(item)
  384. def GetVersion():
  385. import SCons.cpp
  386. import string
  387. rtdef = os.path.join(Rtt_Root, 'include', 'rtdef.h')
  388. # parse rtdef.h to get RT-Thread version
  389. prepcessor = SCons.cpp.PreProcessor()
  390. f = file(rtdef, 'r')
  391. contents = f.read()
  392. f.close()
  393. prepcessor.process_contents(contents)
  394. def_ns = prepcessor.cpp_namespace
  395. version = int(filter(lambda ch: ch in '0123456789.', def_ns['RT_VERSION']))
  396. subversion = int(filter(lambda ch: ch in '0123456789.', def_ns['RT_SUBVERSION']))
  397. if def_ns.has_key('RT_REVISION'):
  398. revision = int(filter(lambda ch: ch in '0123456789.', def_ns['RT_REVISION']))
  399. return '%d.%d.%d' % (version, subversion, revision)
  400. return '0.%d.%d' % (version, subversion)
  401. def GlobSubDir(sub_dir, ext_name):
  402. import os
  403. import glob
  404. def glob_source(sub_dir, ext_name):
  405. list = os.listdir(sub_dir)
  406. src = glob.glob(os.path.join(sub_dir, ext_name))
  407. for item in list:
  408. full_subdir = os.path.join(sub_dir, item)
  409. if os.path.isdir(full_subdir):
  410. src += glob_source(full_subdir, ext_name)
  411. return src
  412. dst = []
  413. src = glob_source(sub_dir, ext_name)
  414. for item in src:
  415. dst.append(os.path.relpath(item, sub_dir))
  416. return dst
  417. def do_rm_file(src):
  418. if os.path.exists(src):
  419. os.unlink(src)
  420. def do_copy_file(src, dst):
  421. import shutil
  422. # check source file
  423. if not os.path.exists(src):
  424. return
  425. path = os.path.dirname(dst)
  426. # mkdir if path not exist
  427. if not os.path.exists(path):
  428. os.makedirs(path)
  429. shutil.copy2(src, dst)
  430. def do_copy_folder(src_dir, dst_dir):
  431. import shutil
  432. # check source directory
  433. if not os.path.exists(src_dir):
  434. return
  435. if os.path.exists(dst_dir):
  436. shutil.rmtree(dst_dir)
  437. shutil.copytree(src_dir, dst_dir)
  438. source_ext = ["c", "h", "s", "S", "cpp", "xpm"]
  439. source_list = []
  440. def walk_children(child):
  441. global source_list
  442. global source_ext
  443. # print child
  444. full_path = child.rfile().abspath
  445. file_type = full_path.rsplit('.',1)[1]
  446. #print file_type
  447. if file_type in source_ext:
  448. if full_path not in source_list:
  449. source_list.append(full_path)
  450. children = child.all_children()
  451. if children != []:
  452. for item in children:
  453. walk_children(item)
  454. def MakeCopy(program):
  455. global source_list
  456. global Rtt_Root
  457. global Env
  458. target_path = os.path.join(Dir('#').abspath, 'rt-thread')
  459. if Env['PLATFORM'] == 'win32':
  460. RTT_ROOT = Rtt_Root.lower()
  461. else:
  462. RTT_ROOT = Rtt_Root
  463. if target_path.startswith(RTT_ROOT):
  464. return
  465. for item in program:
  466. walk_children(item)
  467. source_list.sort()
  468. # filte source file in RT-Thread
  469. target_list = []
  470. for src in source_list:
  471. if Env['PLATFORM'] == 'win32':
  472. src = src.lower()
  473. if src.startswith(RTT_ROOT):
  474. target_list.append(src)
  475. source_list = target_list
  476. # get source path
  477. src_dir = []
  478. for src in source_list:
  479. src = src.replace(RTT_ROOT, '')
  480. if src[0] == os.sep or src[0] == '/':
  481. src = src[1:]
  482. path = os.path.dirname(src)
  483. sub_path = path.split(os.sep)
  484. full_path = RTT_ROOT
  485. for item in sub_path:
  486. full_path = os.path.join(full_path, item)
  487. if full_path not in src_dir:
  488. src_dir.append(full_path)
  489. for item in src_dir:
  490. source_list.append(os.path.join(item, 'SConscript'))
  491. for src in source_list:
  492. dst = src.replace(RTT_ROOT, '')
  493. if dst[0] == os.sep or dst[0] == '/':
  494. dst = dst[1:]
  495. print '=> ', dst
  496. dst = os.path.join(target_path, dst)
  497. do_copy_file(src, dst)
  498. # copy tools directory
  499. print "=> tools"
  500. do_copy_folder(os.path.join(RTT_ROOT, "tools"), os.path.join(target_path, "tools"))
  501. do_copy_file(os.path.join(RTT_ROOT, 'AUTHORS'), os.path.join(target_path, 'AUTHORS'))
  502. do_copy_file(os.path.join(RTT_ROOT, 'COPYING'), os.path.join(target_path, 'COPYING'))
  503. def MakeCopyHeader(program):
  504. global source_ext
  505. source_ext = []
  506. source_ext = ["h", "xpm"]
  507. global source_list
  508. global Rtt_Root
  509. global Env
  510. target_path = os.path.join(Dir('#').abspath, 'rt-thread')
  511. if Env['PLATFORM'] == 'win32':
  512. RTT_ROOT = Rtt_Root.lower()
  513. else:
  514. RTT_ROOT = Rtt_Root
  515. if target_path.startswith(RTT_ROOT):
  516. return
  517. for item in program:
  518. walk_children(item)
  519. source_list.sort()
  520. # filte source file in RT-Thread
  521. target_list = []
  522. for src in source_list:
  523. if Env['PLATFORM'] == 'win32':
  524. src = src.lower()
  525. if src.startswith(RTT_ROOT):
  526. target_list.append(src)
  527. source_list = target_list
  528. for src in source_list:
  529. dst = src.replace(RTT_ROOT, '')
  530. if dst[0] == os.sep or dst[0] == '/':
  531. dst = dst[1:]
  532. print '=> ', dst
  533. dst = os.path.join(target_path, dst)
  534. do_copy_file(src, dst)
  535. # copy tools directory
  536. print "=> tools"
  537. do_copy_folder(os.path.join(RTT_ROOT, "tools"), os.path.join(target_path, "tools"))
  538. do_copy_file(os.path.join(RTT_ROOT, 'AUTHORS'), os.path.join(target_path, 'AUTHORS'))
  539. do_copy_file(os.path.join(RTT_ROOT, 'COPYING'), os.path.join(target_path, 'COPYING'))