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