keil.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. #
  2. # File : keil.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. #
  24. import os
  25. import sys
  26. import string
  27. import shutil
  28. import xml.etree.ElementTree as etree
  29. from xml.etree.ElementTree import SubElement
  30. from utils import _make_path_relative
  31. from utils import xml_indent
  32. fs_encoding = sys.getfilesystemencoding()
  33. def _get_filetype(fn):
  34. if fn.rfind('.cpp') != -1 or fn.rfind('.cxx') != -1:
  35. return 8
  36. if fn.rfind('.c') != -1 or fn.rfind('.C') != -1:
  37. return 1
  38. # assemble file type
  39. if fn.rfind('.s') != -1 or fn.rfind('.S') != -1:
  40. return 2
  41. # header type
  42. if fn.rfind('.h') != -1:
  43. return 5
  44. if fn.rfind('.lib') != -1:
  45. return 4
  46. if fn.rfind('.o') != -1:
  47. return 3
  48. # other filetype
  49. return 5
  50. def MDK4AddGroupForFN(ProjectFiles, parent, name, filename, project_path):
  51. group = SubElement(parent, 'Group')
  52. group_name = SubElement(group, 'GroupName')
  53. group_name.text = name
  54. name = os.path.basename(filename)
  55. path = os.path.dirname (filename)
  56. basename = os.path.basename(path)
  57. path = _make_path_relative(project_path, path)
  58. path = os.path.join(path, name)
  59. files = SubElement(group, 'Files')
  60. file = SubElement(files, 'File')
  61. file_name = SubElement(file, 'FileName')
  62. name = os.path.basename(path)
  63. if name.find('.cpp') != -1:
  64. obj_name = name.replace('.cpp', '.o')
  65. elif name.find('.c') != -1:
  66. obj_name = name.replace('.c', '.o')
  67. elif name.find('.s') != -1:
  68. obj_name = name.replace('.s', '.o')
  69. elif name.find('.S') != -1:
  70. obj_name = name.replace('.s', '.o')
  71. else:
  72. obj_name = name
  73. if ProjectFiles.count(obj_name):
  74. name = basename + '_' + name
  75. ProjectFiles.append(obj_name)
  76. try: # python 2
  77. file_name.text = name.decode(fs_encoding)
  78. except: # python 3
  79. file_name.text = name
  80. file_type = SubElement(file, 'FileType')
  81. file_type.text = '%d' % _get_filetype(name)
  82. file_path = SubElement(file, 'FilePath')
  83. try: # python 2
  84. file_path.text = path.decode(fs_encoding)
  85. except: # python 3
  86. file_path.text = path
  87. return group
  88. def MDK4AddLibToGroup(ProjectFiles, group, name, filename, project_path):
  89. name = os.path.basename(filename)
  90. path = os.path.dirname (filename)
  91. basename = os.path.basename(path)
  92. path = _make_path_relative(project_path, path)
  93. path = os.path.join(path, name)
  94. files = SubElement(group, 'Files')
  95. file = SubElement(files, 'File')
  96. file_name = SubElement(file, 'FileName')
  97. name = os.path.basename(path)
  98. if name.find('.cpp') != -1:
  99. obj_name = name.replace('.cpp', '.o')
  100. elif name.find('.c') != -1:
  101. obj_name = name.replace('.c', '.o')
  102. elif name.find('.s') != -1:
  103. obj_name = name.replace('.s', '.o')
  104. elif name.find('.S') != -1:
  105. obj_name = name.replace('.s', '.o')
  106. else:
  107. obj_name = name
  108. if ProjectFiles.count(obj_name):
  109. name = basename + '_' + name
  110. ProjectFiles.append(obj_name)
  111. try:
  112. file_name.text = name.decode(fs_encoding)
  113. except:
  114. file_name.text = name
  115. file_type = SubElement(file, 'FileType')
  116. file_type.text = '%d' % _get_filetype(name)
  117. file_path = SubElement(file, 'FilePath')
  118. try:
  119. file_path.text = path.decode(fs_encoding)
  120. except:
  121. file_path.text = path
  122. return group
  123. def MDK4AddGroup(ProjectFiles, parent, name, files, project_path, group_scons):
  124. # don't add an empty group
  125. if len(files) == 0:
  126. return
  127. group = SubElement(parent, 'Group')
  128. group_name = SubElement(group, 'GroupName')
  129. group_name.text = name
  130. for f in files:
  131. fn = f.rfile()
  132. name = fn.name
  133. path = os.path.dirname(fn.abspath)
  134. basename = os.path.basename(path)
  135. path = _make_path_relative(project_path, path)
  136. path = os.path.join(path, name)
  137. files = SubElement(group, 'Files')
  138. file = SubElement(files, 'File')
  139. file_name = SubElement(file, 'FileName')
  140. name = os.path.basename(path)
  141. if name.find('.cpp') != -1:
  142. obj_name = name.replace('.cpp', '.o')
  143. elif name.find('.c') != -1:
  144. obj_name = name.replace('.c', '.o')
  145. elif name.find('.s') != -1:
  146. obj_name = name.replace('.s', '.o')
  147. elif name.find('.S') != -1:
  148. obj_name = name.replace('.s', '.o')
  149. if ProjectFiles.count(obj_name):
  150. name = basename + '_' + name
  151. ProjectFiles.append(obj_name)
  152. file_name.text = name # name.decode(fs_encoding)
  153. file_type = SubElement(file, 'FileType')
  154. file_type.text = '%d' % _get_filetype(name)
  155. file_path = SubElement(file, 'FilePath')
  156. file_path.text = path # path.decode(fs_encoding)
  157. # for local LOCAL_CFLAGS/LOCAL_CXXFLAGS/LOCAL_CCFLAGS/LOCAL_CPPPATH/LOCAL_CPPDEFINES
  158. MiscControls_text = ' '
  159. if file_type.text == '1' and 'LOCAL_CFLAGS' in group_scons:
  160. MiscControls_text = MiscControls_text + group_scons['LOCAL_CFLAGS']
  161. elif file_type.text == '8' and 'LOCAL_CXXFLAGS' in group_scons:
  162. MiscControls_text = MiscControls_text + group_scons['LOCAL_CXXFLAGS']
  163. if 'LOCAL_CCFLAGS' in group_scons:
  164. MiscControls_text = MiscControls_text + group_scons['LOCAL_CCFLAGS']
  165. if MiscControls_text != ' ' or ('LOCAL_CPPDEFINES' in group_scons):
  166. FileOption = SubElement(file, 'FileOption')
  167. FileArmAds = SubElement(FileOption, 'FileArmAds')
  168. Cads = SubElement(FileArmAds, 'Cads')
  169. VariousControls = SubElement(Cads, 'VariousControls')
  170. MiscControls = SubElement(VariousControls, 'MiscControls')
  171. MiscControls.text = MiscControls_text
  172. Define = SubElement(VariousControls, 'Define')
  173. if 'LOCAL_CPPDEFINES' in group_scons:
  174. Define.text = ', '.join(set(group_scons['LOCAL_CPPDEFINES']))
  175. else:
  176. Define.text = ' '
  177. Undefine = SubElement(VariousControls, 'Undefine')
  178. Undefine.text = ' '
  179. IncludePath = SubElement(VariousControls, 'IncludePath')
  180. if 'LOCAL_CPPPATH' in group_scons:
  181. IncludePath.text = ';'.join([_make_path_relative(project_path, os.path.normpath(i)) for i in group_scons['LOCAL_CPPPATH']])
  182. else:
  183. IncludePath.text = ' '
  184. return group
  185. # The common part of making MDK4/5 project
  186. def MDK45Project(tree, target, script):
  187. project_path = os.path.dirname(os.path.abspath(target))
  188. root = tree.getroot()
  189. out = open(target, 'w')
  190. out.write('<?xml version="1.0" encoding="UTF-8" standalone="no" ?>\n')
  191. CPPPATH = []
  192. CPPDEFINES = []
  193. LINKFLAGS = ''
  194. CXXFLAGS = ''
  195. CCFLAGS = ''
  196. CFLAGS = ''
  197. ProjectFiles = []
  198. # add group
  199. groups = tree.find('Targets/Target/Groups')
  200. if groups is None:
  201. groups = SubElement(tree.find('Targets/Target'), 'Groups')
  202. groups.clear() # clean old groups
  203. for group in script:
  204. group_tree = MDK4AddGroup(ProjectFiles, groups, group['name'], group['src'], project_path, group)
  205. # get each include path
  206. if 'CPPPATH' in group and group['CPPPATH']:
  207. if CPPPATH:
  208. CPPPATH += group['CPPPATH']
  209. else:
  210. CPPPATH += group['CPPPATH']
  211. # get each group's definitions
  212. if 'CPPDEFINES' in group and group['CPPDEFINES']:
  213. if CPPDEFINES:
  214. CPPDEFINES += group['CPPDEFINES']
  215. else:
  216. CPPDEFINES = group['CPPDEFINES']
  217. # get each group's link flags
  218. if 'LINKFLAGS' in group and group['LINKFLAGS']:
  219. if LINKFLAGS:
  220. LINKFLAGS += ' ' + group['LINKFLAGS']
  221. else:
  222. LINKFLAGS += group['LINKFLAGS']
  223. # get each group's CXXFLAGS flags
  224. if 'CXXFLAGS' in group and group['CXXFLAGS']:
  225. if CXXFLAGS:
  226. CXXFLAGS += ' ' + group['CXXFLAGS']
  227. else:
  228. CXXFLAGS += group['CXXFLAGS']
  229. # get each group's CCFLAGS flags
  230. if 'CCFLAGS' in group and group['CCFLAGS']:
  231. if CCFLAGS:
  232. CCFLAGS += ' ' + group['CCFLAGS']
  233. else:
  234. CCFLAGS += group['CCFLAGS']
  235. # get each group's CFLAGS flags
  236. if 'CFLAGS' in group and group['CFLAGS']:
  237. if CFLAGS:
  238. CFLAGS += ' ' + group['CFLAGS']
  239. else:
  240. CFLAGS += group['CFLAGS']
  241. # get each group's LIBS flags
  242. if 'LIBS' in group and group['LIBS']:
  243. for item in group['LIBPATH']:
  244. full_path = os.path.join(item, group['name'] + '.lib')
  245. if os.path.isfile(full_path): # has this library
  246. if group_tree != None:
  247. MDK4AddLibToGroup(ProjectFiles, group_tree, group['name'], full_path, project_path)
  248. else:
  249. group_tree = MDK4AddGroupForFN(ProjectFiles, groups, group['name'], full_path, project_path)
  250. # write include path, definitions and link flags
  251. IncludePath = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/VariousControls/IncludePath')
  252. IncludePath.text = ';'.join([_make_path_relative(project_path, os.path.normpath(i)) for i in set(CPPPATH)])
  253. Define = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/VariousControls/Define')
  254. Define.text = ', '.join(set(CPPDEFINES))
  255. if 'c99' in CXXFLAGS or 'c99' in CCFLAGS or 'c99' in CFLAGS:
  256. uC99 = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/uC99')
  257. uC99.text = '1'
  258. if 'gnu' in CXXFLAGS or 'gnu' in CCFLAGS or 'gnu' in CFLAGS:
  259. uGnu = tree.find('Targets/Target/TargetOption/TargetArmAds/Cads/uGnu')
  260. uGnu.text = '1'
  261. Misc = tree.find('Targets/Target/TargetOption/TargetArmAds/LDads/Misc')
  262. Misc.text = LINKFLAGS
  263. xml_indent(root)
  264. out.write(etree.tostring(root, encoding='utf-8').decode())
  265. out.close()
  266. def MDK4Project(target, script):
  267. if os.path.isfile('template.uvproj') is False:
  268. print ('Warning: The template project file [template.uvproj] not found!')
  269. return
  270. template_tree = etree.parse('template.uvproj')
  271. MDK45Project(template_tree, target, script)
  272. # remove project.uvopt file
  273. project_uvopt = os.path.abspath(target).replace('uvproj', 'uvopt')
  274. if os.path.isfile(project_uvopt):
  275. os.unlink(project_uvopt)
  276. # copy uvopt file
  277. if os.path.exists('template.uvopt'):
  278. import shutil
  279. shutil.copy2('template.uvopt', '{}.uvopt'.format(os.path.splitext(target)[0]))
  280. import threading
  281. import time
  282. def monitor_log_file(log_file_path):
  283. if not os.path.exists(log_file_path):
  284. open(log_file_path, 'w').close()
  285. empty_line_count = 0
  286. with open(log_file_path, 'r') as log_file:
  287. while True:
  288. line = log_file.readline()
  289. if line:
  290. print(line.strip())
  291. if 'Build Time Elapsed' in line:
  292. break
  293. empty_line_count = 0
  294. else:
  295. empty_line_count += 1
  296. time.sleep(1)
  297. if empty_line_count > 30:
  298. print("Timeout reached or too many empty lines, exiting log monitoring thread.")
  299. break
  300. def MDK5Project(target, script):
  301. if os.path.isfile('template.uvprojx') is False:
  302. print ('Warning: The template project file [template.uvprojx] not found!')
  303. return
  304. template_tree = etree.parse('template.uvprojx')
  305. MDK45Project(template_tree, target, script)
  306. # remove project.uvopt file
  307. project_uvopt = os.path.abspath(target).replace('uvprojx', 'uvoptx')
  308. if os.path.isfile(project_uvopt):
  309. os.unlink(project_uvopt)
  310. # copy uvopt file
  311. if os.path.exists('template.uvoptx'):
  312. import shutil
  313. shutil.copy2('template.uvoptx', '{}.uvoptx'.format(os.path.splitext(target)[0]))
  314. # build with UV4.exe
  315. if shutil.which('UV4.exe') is not None:
  316. target_name = template_tree.find('Targets/Target/TargetName')
  317. print('target_name:', target_name.text)
  318. log_file_path = 'keil.log'
  319. if os.path.exists(log_file_path):
  320. os.remove(log_file_path)
  321. log_thread = threading.Thread(target=monitor_log_file, args=(log_file_path,))
  322. log_thread.start()
  323. cmd = 'UV4.exe -b project.uvprojx -q -j0 -t '+ target_name.text +' -o '+log_file_path
  324. print('Start to build keil project')
  325. print(cmd)
  326. os.system(cmd)
  327. else:
  328. print('UV4.exe is not available, please check your keil installation')
  329. def MDK2Project(target, script):
  330. template = open(os.path.join(os.path.dirname(__file__), 'template.Uv2'), 'r')
  331. lines = template.readlines()
  332. project = open(target, "w")
  333. project_path = os.path.dirname(os.path.abspath(target))
  334. line_index = 5
  335. # write group
  336. for group in script:
  337. lines.insert(line_index, 'Group (%s)\r\n' % group['name'])
  338. line_index += 1
  339. lines.insert(line_index, '\r\n')
  340. line_index += 1
  341. # write file
  342. ProjectFiles = []
  343. CPPPATH = []
  344. CPPDEFINES = []
  345. LINKFLAGS = ''
  346. CFLAGS = ''
  347. # number of groups
  348. group_index = 1
  349. for group in script:
  350. # print group['name']
  351. # get each include path
  352. if 'CPPPATH' in group and group['CPPPATH']:
  353. if CPPPATH:
  354. CPPPATH += group['CPPPATH']
  355. else:
  356. CPPPATH += group['CPPPATH']
  357. # get each group's definitions
  358. if 'CPPDEFINES' in group and group['CPPDEFINES']:
  359. if CPPDEFINES:
  360. CPPDEFINES += group['CPPDEFINES']
  361. else:
  362. CPPDEFINES = group['CPPDEFINES']
  363. # get each group's link flags
  364. if 'LINKFLAGS' in group and group['LINKFLAGS']:
  365. if LINKFLAGS:
  366. LINKFLAGS += ' ' + group['LINKFLAGS']
  367. else:
  368. LINKFLAGS += group['LINKFLAGS']
  369. # generate file items
  370. for node in group['src']:
  371. fn = node.rfile()
  372. name = fn.name
  373. path = os.path.dirname(fn.abspath)
  374. basename = os.path.basename(path)
  375. path = _make_path_relative(project_path, path)
  376. path = os.path.join(path, name)
  377. if ProjectFiles.count(name):
  378. name = basename + '_' + name
  379. ProjectFiles.append(name)
  380. lines.insert(line_index, 'File %d,%d,<%s><%s>\r\n'
  381. % (group_index, _get_filetype(name), path, name))
  382. line_index += 1
  383. group_index = group_index + 1
  384. lines.insert(line_index, '\r\n')
  385. line_index += 1
  386. # remove repeat path
  387. paths = set()
  388. for path in CPPPATH:
  389. inc = _make_path_relative(project_path, os.path.normpath(path))
  390. paths.add(inc) #.replace('\\', '/')
  391. paths = [i for i in paths]
  392. CPPPATH = string.join(paths, ';')
  393. definitions = [i for i in set(CPPDEFINES)]
  394. CPPDEFINES = string.join(definitions, ', ')
  395. while line_index < len(lines):
  396. if lines[line_index].startswith(' ADSCINCD '):
  397. lines[line_index] = ' ADSCINCD (' + CPPPATH + ')\r\n'
  398. if lines[line_index].startswith(' ADSLDMC ('):
  399. lines[line_index] = ' ADSLDMC (' + LINKFLAGS + ')\r\n'
  400. if lines[line_index].startswith(' ADSCDEFN ('):
  401. lines[line_index] = ' ADSCDEFN (' + CPPDEFINES + ')\r\n'
  402. line_index += 1
  403. # write project
  404. for line in lines:
  405. project.write(line)
  406. project.close()
  407. def ARMCC_Version():
  408. import rtconfig
  409. import subprocess
  410. import re
  411. path = rtconfig.EXEC_PATH
  412. if(rtconfig.PLATFORM == 'armcc'):
  413. path = os.path.join(path, 'armcc.exe')
  414. elif(rtconfig.PLATFORM == 'armclang'):
  415. path = os.path.join(path, 'armlink.exe')
  416. if os.path.exists(path):
  417. cmd = path
  418. else:
  419. return "0.0"
  420. child = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
  421. stdout, stderr = child.communicate()
  422. '''
  423. example stdout:
  424. Product: MDK Plus 5.24
  425. Component: ARM Compiler 5.06 update 5 (build 528)
  426. Tool: armcc [4d3621]
  427. return version: MDK Plus 5.24/ARM Compiler 5.06 update 5 (build 528)/armcc [4d3621]
  428. '''
  429. if not isinstance(stdout, str):
  430. stdout = str(stdout, 'utf8') # Patch for Python 3
  431. version_Product = re.search(r'Product: (.+)', stdout).group(1)
  432. version_Product = version_Product[:-1]
  433. version_Component = re.search(r'Component: (.*)', stdout).group(1)
  434. version_Component = version_Component[:-1]
  435. version_Tool = re.search(r'Tool: (.*)', stdout).group(1)
  436. version_Tool = version_Tool[:-1]
  437. version_str_format = '%s/%s/%s'
  438. version_str = version_str_format % (version_Product, version_Component, version_Tool)
  439. return version_str