1
0

eclipse.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. #
  2. # Copyright (c) 2006-2022, RT-Thread Development Team
  3. #
  4. # SPDX-License-Identifier: Apache-2.0
  5. #
  6. # Change Logs:
  7. # Date Author Notes
  8. # 2019-03-21 Bernard the first version
  9. # 2019-04-15 armink fix project update error
  10. #
  11. import glob
  12. import xml.etree.ElementTree as etree
  13. from xml.etree.ElementTree import SubElement
  14. from . import rt_studio
  15. import sys
  16. import os
  17. # Add parent directory to path to import building and utils
  18. sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
  19. from building import *
  20. from utils import *
  21. from utils import _make_path_relative
  22. from utils import xml_indent
  23. MODULE_VER_NUM = 6
  24. source_pattern = ['*.c', '*.cpp', '*.cxx', '*.cc', '*.s', '*.S', '*.asm','*.cmd']
  25. def OSPath(path):
  26. import platform
  27. if type(path) == type('str'):
  28. if platform.system() == 'Windows':
  29. return path.replace('/', '\\')
  30. else:
  31. return path.replace('\\', '/')
  32. else:
  33. if platform.system() == 'Windows':
  34. return [item.replace('/', '\\') for item in path]
  35. else:
  36. return [item.replace('\\', '/') for item in path]
  37. # collect the build source code path and parent path
  38. def CollectPaths(paths):
  39. all_paths = []
  40. def ParentPaths(path):
  41. ret = os.path.dirname(path)
  42. if ret == path or ret == '':
  43. return []
  44. return [ret] + ParentPaths(ret)
  45. for path in paths:
  46. # path = os.path.abspath(path)
  47. path = path.replace('\\', '/')
  48. all_paths = all_paths + [path] + ParentPaths(path)
  49. cwd = os.getcwd()
  50. for path in os.listdir(cwd):
  51. temp_path = cwd.replace('\\', '/') + '/' + path
  52. if os.path.isdir(temp_path):
  53. all_paths = all_paths + [temp_path]
  54. all_paths = list(set(all_paths))
  55. return sorted(all_paths)
  56. '''
  57. Collect all of files under paths
  58. '''
  59. def CollectFiles(paths, pattern):
  60. files = []
  61. for path in paths:
  62. if type(pattern) == type(''):
  63. files = files + glob.glob(path + '/' + pattern)
  64. else:
  65. for item in pattern:
  66. # print('--> %s' % (path + '/' + item))
  67. files = files + glob.glob(path + '/' + item)
  68. return sorted(files)
  69. def CollectAllFilesinPath(path, pattern):
  70. files = []
  71. for item in pattern:
  72. files += glob.glob(path + '/' + item)
  73. list = os.listdir(path)
  74. if len(list):
  75. for item in list:
  76. if item.startswith('.'):
  77. continue
  78. if item == 'bsp':
  79. continue
  80. if os.path.isdir(os.path.join(path, item)):
  81. files = files + CollectAllFilesinPath(os.path.join(path, item), pattern)
  82. return files
  83. '''
  84. Exclude files from infiles
  85. '''
  86. def ExcludeFiles(infiles, files):
  87. in_files = set([OSPath(file) for file in infiles])
  88. exl_files = set([OSPath(file) for file in files])
  89. exl_files = in_files - exl_files
  90. return exl_files
  91. # caluclate the exclude path for project
  92. def ExcludePaths(rootpath, paths):
  93. ret = []
  94. files = os.listdir(OSPath(rootpath))
  95. for file in files:
  96. if file.startswith('.'):
  97. continue
  98. fullname = os.path.join(OSPath(rootpath), file)
  99. if os.path.isdir(fullname):
  100. # print(fullname)
  101. if not fullname in paths:
  102. ret = ret + [fullname]
  103. else:
  104. ret = ret + ExcludePaths(fullname, paths)
  105. return ret
  106. rtt_path_prefix = '"${workspace_loc://${ProjName}//'
  107. def ConverToRttEclipsePathFormat(path):
  108. return rtt_path_prefix + path + '}"'
  109. def IsRttEclipsePathFormat(path):
  110. if path.startswith(rtt_path_prefix):
  111. return True
  112. else:
  113. return False
  114. # all libs added by scons should be ends with five whitespace as a flag
  115. rtt_lib_flag = 5 * " "
  116. def ConverToRttEclipseLibFormat(lib):
  117. return str(lib) + str(rtt_lib_flag)
  118. def IsRttEclipseLibFormat(path):
  119. if path.endswith(rtt_lib_flag):
  120. return True
  121. else:
  122. return False
  123. def IsCppProject():
  124. return GetDepend('RT_USING_CPLUSPLUS')
  125. def HandleToolOption(tools, env, project, reset):
  126. is_cpp_prj = IsCppProject()
  127. BSP_ROOT = os.path.abspath(env['BSP_ROOT'])
  128. CPPDEFINES = project['CPPDEFINES']
  129. paths = [ConverToRttEclipsePathFormat(RelativeProjectPath(env, os.path.normpath(i)).replace('\\', '/')) for i in project['CPPPATH']]
  130. compile_include_paths_options = []
  131. compile_include_files_options = []
  132. compile_defs_options = []
  133. linker_scriptfile_option = None
  134. linker_script_option = None
  135. linker_nostart_option = None
  136. linker_libs_option = None
  137. linker_paths_option = None
  138. linker_newlib_nano_option = None
  139. for tool in tools:
  140. if tool.get('id').find('compile') != 1:
  141. options = tool.findall('option')
  142. # find all compile options
  143. for option in options:
  144. option_id = option.get('id')
  145. if ('compiler.include.paths' in option_id) or ('compiler.option.includepaths' in option_id) or ('compiler.tasking.include' in option_id):
  146. compile_include_paths_options += [option]
  147. elif option.get('id').find('compiler.include.files') != -1 or option.get('id').find('compiler.option.includefiles') != -1 :
  148. compile_include_files_options += [option]
  149. elif option.get('id').find('compiler.defs') != -1 or option.get('id').find('compiler.option.definedsymbols') != -1:
  150. compile_defs_options += [option]
  151. if tool.get('id').find('linker') != -1:
  152. options = tool.findall('option')
  153. # find all linker options
  154. for option in options:
  155. # the project type and option type must equal
  156. if is_cpp_prj != (option.get('id').find('cpp.linker') != -1):
  157. continue
  158. if option.get('id').find('linker.scriptfile') != -1:
  159. linker_scriptfile_option = option
  160. elif option.get('id').find('linker.option.script') != -1:
  161. linker_script_option = option
  162. elif option.get('id').find('linker.nostart') != -1:
  163. linker_nostart_option = option
  164. elif option.get('id').find('linker.libs') != -1:
  165. linker_libs_option = option
  166. elif option.get('id').find('linker.paths') != -1 and 'LIBPATH' in env:
  167. linker_paths_option = option
  168. elif option.get('id').find('linker.usenewlibnano') != -1:
  169. linker_newlib_nano_option = option
  170. # change the inclue path
  171. for option in compile_include_paths_options:
  172. # find all of paths in this project
  173. include_paths = option.findall('listOptionValue')
  174. for item in include_paths:
  175. if reset is True or IsRttEclipsePathFormat(item.get('value')) :
  176. # clean old configuration
  177. option.remove(item)
  178. # print('c.compiler.include.paths')
  179. paths = sorted(paths)
  180. for item in paths:
  181. SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': item})
  182. # change the inclue files (default) or definitions
  183. for option in compile_include_files_options:
  184. # add '_REENT_SMALL' to CPPDEFINES when --specs=nano.specs has select
  185. if linker_newlib_nano_option is not None and linker_newlib_nano_option.get('value') == 'true' and '_REENT_SMALL' not in CPPDEFINES:
  186. CPPDEFINES += ['_REENT_SMALL']
  187. file_header = '''
  188. #ifndef RTCONFIG_PREINC_H__
  189. #define RTCONFIG_PREINC_H__
  190. /* Automatically generated file; DO NOT EDIT. */
  191. /* RT-Thread pre-include file */
  192. '''
  193. file_tail = '\n#endif /*RTCONFIG_PREINC_H__*/\n'
  194. rtt_pre_inc_item = '"${workspace_loc:/${ProjName}/rtconfig_preinc.h}"'
  195. # save the CPPDEFINES in to rtconfig_preinc.h
  196. with open('rtconfig_preinc.h', mode = 'w+') as f:
  197. f.write(file_header)
  198. for cppdef in CPPDEFINES:
  199. f.write("#define " + cppdef.replace('=', ' ') + '\n')
  200. f.write(file_tail)
  201. # change the c.compiler.include.files
  202. files = option.findall('listOptionValue')
  203. find_ok = False
  204. for item in files:
  205. if item.get('value') == rtt_pre_inc_item:
  206. find_ok = True
  207. break
  208. if find_ok is False:
  209. SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': rtt_pre_inc_item})
  210. if len(compile_include_files_options) == 0:
  211. for option in compile_defs_options:
  212. defs = option.findall('listOptionValue')
  213. project_defs = []
  214. for item in defs:
  215. if reset is True:
  216. # clean all old configuration
  217. option.remove(item)
  218. else:
  219. project_defs += [item.get('value')]
  220. if len(project_defs) > 0:
  221. cproject_defs = set(CPPDEFINES) - set(project_defs)
  222. else:
  223. cproject_defs = CPPDEFINES
  224. # print('c.compiler.defs')
  225. cproject_defs = sorted(cproject_defs)
  226. for item in cproject_defs:
  227. SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': item})
  228. # update linker script config
  229. if linker_scriptfile_option is not None :
  230. option = linker_scriptfile_option
  231. linker_script = 'link.lds'
  232. items = env['LINKFLAGS'].split(' ')
  233. if '-T' in items:
  234. linker_script = items[items.index('-T') + 1]
  235. linker_script = ConverToRttEclipsePathFormat(linker_script)
  236. listOptionValue = option.find('listOptionValue')
  237. if listOptionValue != None:
  238. if reset is True or IsRttEclipsePathFormat(listOptionValue.get('value')):
  239. listOptionValue.set('value', linker_script)
  240. else:
  241. SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': linker_script})
  242. # scriptfile in stm32cubeIDE
  243. if linker_script_option is not None :
  244. option = linker_script_option
  245. items = env['LINKFLAGS'].split(' ')
  246. if '-T' in items:
  247. linker_script = ConverToRttEclipsePathFormat(items[items.index('-T') + 1]).strip('"')
  248. option.set('value', linker_script)
  249. # update nostartfiles config
  250. if linker_nostart_option is not None :
  251. option = linker_nostart_option
  252. if env['LINKFLAGS'].find('-nostartfiles') != -1:
  253. option.set('value', 'true')
  254. else:
  255. option.set('value', 'false')
  256. # update libs
  257. if linker_libs_option is not None:
  258. option = linker_libs_option
  259. # remove old libs
  260. for item in option.findall('listOptionValue'):
  261. if IsRttEclipseLibFormat(item.get("value")):
  262. option.remove(item)
  263. # add new libs
  264. if 'LIBS' in env:
  265. for lib in env['LIBS']:
  266. lib_name = os.path.basename(str(lib))
  267. if lib_name.endswith('.a'):
  268. if lib_name.startswith('lib'):
  269. lib = lib_name[3:].split('.')[0]
  270. else:
  271. lib = ':' + lib_name
  272. formatedLib = ConverToRttEclipseLibFormat(lib)
  273. SubElement(option, 'listOptionValue', {
  274. 'builtIn': 'false', 'value': formatedLib})
  275. # update lib paths
  276. if linker_paths_option is not None:
  277. option = linker_paths_option
  278. # remove old lib paths
  279. for item in option.findall('listOptionValue'):
  280. if IsRttEclipsePathFormat(item.get('value')):
  281. # clean old configuration
  282. option.remove(item)
  283. # add new old lib paths
  284. for path in env['LIBPATH']:
  285. SubElement(option, 'listOptionValue', {'builtIn': 'false', 'value': ConverToRttEclipsePathFormat(RelativeProjectPath(env, path).replace('\\', '/'))})
  286. return
  287. def UpdateProjectStructure(env, prj_name):
  288. bsp_root = env['BSP_ROOT']
  289. rtt_root = env['RTT_ROOT']
  290. project = etree.parse('.project')
  291. root = project.getroot()
  292. if rtt_root.startswith(bsp_root):
  293. linkedResources = root.find('linkedResources')
  294. if linkedResources == None:
  295. linkedResources = SubElement(root, 'linkedResources')
  296. links = linkedResources.findall('link')
  297. # delete all RT-Thread folder links
  298. for link in links:
  299. if link.find('name').text.startswith('rt-thread'):
  300. linkedResources.remove(link)
  301. if prj_name:
  302. name = root.find('name')
  303. if name == None:
  304. name = SubElement(root, 'name')
  305. name.text = prj_name
  306. out = open('.project', 'w')
  307. out.write('<?xml version="1.0" encoding="UTF-8"?>\n')
  308. xml_indent(root)
  309. out.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
  310. out.close()
  311. return
  312. def GenExcluding(env, project):
  313. rtt_root = os.path.abspath(env['RTT_ROOT'])
  314. bsp_root = os.path.abspath(env['BSP_ROOT'])
  315. coll_dirs = CollectPaths(project['DIRS'])
  316. all_paths_temp = [OSPath(path) for path in coll_dirs]
  317. all_paths = []
  318. # add used path
  319. for path in all_paths_temp:
  320. if path.startswith(rtt_root) or path.startswith(bsp_root):
  321. all_paths.append(path)
  322. if bsp_root.startswith(rtt_root):
  323. # bsp folder is in the RT-Thread root folder, such as the RT-Thread source code on GitHub
  324. exclude_paths = ExcludePaths(rtt_root, all_paths)
  325. elif rtt_root.startswith(bsp_root):
  326. # RT-Thread root folder is in the bsp folder, such as project folder which generate by 'scons --dist' cmd
  327. check_path = []
  328. exclude_paths = []
  329. # analyze the primary folder which relative to BSP_ROOT and in all_paths
  330. for path in all_paths:
  331. if path.startswith(bsp_root):
  332. folders = RelativeProjectPath(env, path).split('\\')
  333. if folders[0] != '.' and '\\' + folders[0] not in check_path:
  334. check_path += ['\\' + folders[0]]
  335. # exclue the folder which has managed by scons
  336. for path in check_path:
  337. exclude_paths += ExcludePaths(bsp_root + path, all_paths)
  338. else:
  339. exclude_paths = ExcludePaths(rtt_root, all_paths)
  340. exclude_paths += ExcludePaths(bsp_root, all_paths)
  341. paths = exclude_paths
  342. exclude_paths = []
  343. # remove the folder which not has source code by source_pattern
  344. for path in paths:
  345. # add bsp and libcpu folder and not collect source files (too more files)
  346. if path.endswith('rt-thread\\bsp') or path.endswith('rt-thread\\libcpu'):
  347. exclude_paths += [path]
  348. continue
  349. set = CollectAllFilesinPath(path, source_pattern)
  350. if len(set):
  351. exclude_paths += [path]
  352. exclude_paths = [RelativeProjectPath(env, path).replace('\\', '/') for path in exclude_paths]
  353. all_files = CollectFiles(all_paths, source_pattern)
  354. src_files = project['FILES']
  355. exclude_files = ExcludeFiles(all_files, src_files)
  356. exclude_files = [RelativeProjectPath(env, file).replace('\\', '/') for file in exclude_files]
  357. env['ExPaths'] = exclude_paths
  358. env['ExFiles'] = exclude_files
  359. return exclude_paths + exclude_files
  360. def RelativeProjectPath(env, path):
  361. project_root = os.path.abspath(env['BSP_ROOT'])
  362. rtt_root = os.path.abspath(env['RTT_ROOT'])
  363. if path.startswith(project_root):
  364. return _make_path_relative(project_root, path)
  365. if path.startswith(rtt_root):
  366. return 'rt-thread/' + _make_path_relative(rtt_root, path)
  367. # TODO add others folder
  368. print('ERROR: the ' + path + ' not support')
  369. return path
  370. def HandleExcludingOption(entry, sourceEntries, excluding):
  371. old_excluding = []
  372. if entry != None:
  373. exclud = entry.get('excluding')
  374. if exclud != None:
  375. old_excluding = entry.get('excluding').split('|')
  376. sourceEntries.remove(entry)
  377. value = ''
  378. for item in old_excluding:
  379. if item.startswith('//'):
  380. old_excluding.remove(item)
  381. else:
  382. if value == '':
  383. value = item
  384. else:
  385. value += '|' + item
  386. for item in excluding:
  387. # add special excluding path prefix for RT-Thread
  388. item = '//' + item
  389. if value == '':
  390. value = item
  391. else:
  392. value += '|' + item
  393. SubElement(sourceEntries, 'entry', {'excluding': value, 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED', 'kind':'sourcePath', 'name':""})
  394. def UpdateCproject(env, project, excluding, reset, prj_name):
  395. excluding = sorted(excluding)
  396. cproject = etree.parse('.cproject')
  397. root = cproject.getroot()
  398. cconfigurations = root.findall('storageModule/cconfiguration')
  399. for cconfiguration in cconfigurations:
  400. tools = cconfiguration.findall('storageModule/configuration/folderInfo/toolChain/tool')
  401. HandleToolOption(tools, env, project, reset)
  402. sourceEntries = cconfiguration.find('storageModule/configuration/sourceEntries')
  403. if sourceEntries != None:
  404. entry = sourceEntries.find('entry')
  405. HandleExcludingOption(entry, sourceEntries, excluding)
  406. # update refreshScope
  407. if prj_name:
  408. prj_name = '/' + prj_name
  409. configurations = root.findall('storageModule/configuration')
  410. for configuration in configurations:
  411. resource = configuration.find('resource')
  412. configuration.remove(resource)
  413. SubElement(configuration, 'resource', {'resourceType': "PROJECT", 'workspacePath': prj_name})
  414. # write back to .cproject
  415. out = open('.cproject', 'w')
  416. out.write('<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n')
  417. out.write('<?fileVersion 4.0.0?>')
  418. xml_indent(root)
  419. out.write(etree.tostring(root, encoding='utf-8').decode('utf-8'))
  420. out.close()
  421. def TargetEclipse(env, reset=False, prj_name=None):
  422. global source_pattern
  423. print('Update eclipse setting...')
  424. # generate cproject file
  425. if not os.path.exists('.cproject'):
  426. if rt_studio.gen_cproject_file(os.path.abspath(".cproject")) is False:
  427. print('Fail!')
  428. return
  429. # generate project file
  430. if not os.path.exists('.project'):
  431. if rt_studio.gen_project_file(os.path.abspath(".project")) is False:
  432. print('Fail!')
  433. return
  434. # generate projcfg.ini file
  435. if not os.path.exists('.settings/projcfg.ini'):
  436. # if search files with uvprojx or uvproj suffix
  437. file = ""
  438. items = os.listdir(".")
  439. if len(items) > 0:
  440. for item in items:
  441. if item.endswith(".uvprojx") or item.endswith(".uvproj"):
  442. file = os.path.abspath(item)
  443. break
  444. chip_name = rt_studio.get_mcu_info(file)
  445. if rt_studio.gen_projcfg_ini_file(chip_name, prj_name, os.path.abspath(".settings/projcfg.ini")) is False:
  446. print('Fail!')
  447. return
  448. # enable lowwer .s file compiled in eclipse cdt
  449. if not os.path.exists('.settings/org.eclipse.core.runtime.prefs'):
  450. if rt_studio.gen_org_eclipse_core_runtime_prefs(
  451. os.path.abspath(".settings/org.eclipse.core.runtime.prefs")) is False:
  452. print('Fail!')
  453. return
  454. # add clean2 target to fix issues when files too many
  455. if not os.path.exists('makefile.targets'):
  456. if rt_studio.gen_makefile_targets(os.path.abspath("makefile.targets")) is False:
  457. print('Fail!')
  458. return
  459. project = ProjectInfo(env)
  460. # update the project file structure info on '.project' file
  461. UpdateProjectStructure(env, prj_name)
  462. # generate the exclude paths and files
  463. excluding = GenExcluding(env, project)
  464. # update the project configuration on '.cproject' file
  465. UpdateCproject(env, project, excluding, reset, prj_name)
  466. print('done!')
  467. return