manual_bsp_build_all.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. #
  2. # Copyright (c) 2006-2024, RT-Thread Development Team
  3. #
  4. # SPDX-License-Identifier: Apache-2.0
  5. #
  6. # Change Logs:
  7. # Date Author Notes
  8. # 2024-07-25 supperthomas the first version
  9. #
  10. """
  11. 这个脚本用来编译所有的bsp
  12. 这里的脚本是用的arm-none-eabi-gcc, 默认根据本机已经安装的gcc来编译
  13. 其他的工具链暂时不支持,其实主要根据运行的环境中支持不支持,目前只打算支持主流的
  14. 失败的bsp会存到failed_bsp.log里面
  15. """
  16. import os
  17. import sys
  18. import shutil
  19. import multiprocessing
  20. from multiprocessing import Process
  21. import yaml
  22. #help说明
  23. def usage():
  24. print('%s all -- build all GCC bsp' % os.path.basename(sys.argv[0]))
  25. print('%s clean -- clean all bsp' % os.path.basename(sys.argv[0]))
  26. print('%s update -- update all prject files' % os.path.basename(sys.argv[0]))
  27. def add_summary(text):
  28. """
  29. add summary to github action.
  30. """
  31. os.system(f'echo "{text}" >> $GITHUB_STEP_SUMMARY ;')
  32. def run_cmd(cmd, output_info=True):
  33. """
  34. 这个函数用来执行命令
  35. run command and return output and result.
  36. """
  37. print('\033[1;32m' + cmd + '\033[0m ' + os.getcwd())
  38. output_str_list = []
  39. res = 0
  40. if output_info:
  41. res = os.system(cmd + " > output.txt 2>&1")
  42. else:
  43. res = os.system(cmd + " > /dev/null 2>output.txt")
  44. try:
  45. with open("output.txt", "r") as file:
  46. output_str_list = file.readlines()
  47. except FileNotFoundError:
  48. with open("output.txt", "w") as file:
  49. file.write("new file")
  50. for line in output_str_list:
  51. print(line, end='')
  52. os.remove("output.txt")
  53. return output_str_list, res
  54. def build_bsp(bsp, scons_args=''):
  55. """
  56. build bsp.
  57. cd {rtt_root}
  58. scons -C bsp/{bsp} --pyconfig-silent > /dev/null
  59. cd {rtt_root}/bsp/{bsp}
  60. pkgs --update > /dev/null
  61. pkgs --list
  62. cd {rtt_root}
  63. scons -C bsp/{bsp} -j{nproc} {scons_args}
  64. cd {rtt_root}/bsp/{bsp}
  65. scons -c > /dev/null
  66. rm -rf packages
  67. """
  68. success = True
  69. pwd = os.getcwd()
  70. print('======pwd==='+ os.getcwd()+'===bsp:=='+bsp)
  71. os.chdir(rtt_root)
  72. #有Kconfig 说明可以执行menuconfig
  73. if os.path.exists(f"{rtt_root}/bsp/{bsp}/Kconfig"):
  74. os.chdir(rtt_root)
  75. print('======pwd==='+ os.getcwd()+'===bsp:=='+bsp)
  76. run_cmd(f'scons -C bsp/{bsp} --pyconfig-silent', output_info=True)
  77. os.chdir(f'{rtt_root}/bsp/{bsp}')
  78. print('======pwd222==='+ os.getcwd()+'===bsp:=='+bsp)
  79. run_cmd('pkgs --update', output_info=True)
  80. run_cmd('pkgs --list')
  81. nproc = multiprocessing.cpu_count()
  82. os.chdir(rtt_root)
  83. cmd = f'scons -C bsp/{bsp} -j{nproc} {scons_args}'
  84. result_log, res = run_cmd(cmd, output_info=True)
  85. if res != 0:
  86. # 将失败的bsp写入特定的txt文件
  87. with open(os.path.join(rtt_root, 'failed_bsp_list.txt'), 'a') as file:
  88. file.write(bsp + '\n')
  89. # 打印失败的bsp的log,把它放到对应的文件名文bsp的txt文件中
  90. with open(os.path.join(rtt_root, 'failed_bsp.log'), 'a') as file:
  91. file.write(f'===================={bsp}====================\n')
  92. for line in result_log:
  93. file.write(line)
  94. print(f"::error::build {bsp} failed")
  95. add_summary(f"- ❌ build {bsp} failed.")
  96. success = False
  97. else:
  98. # 如果没有Kconfig直接执行scons
  99. os.chdir(f'{rtt_root}/bsp/{bsp}')
  100. run_cmd('scons', output_info=True)
  101. # 删除packages文件夹
  102. pkg_dir = os.path.join(rtt_root, 'bsp', bsp, 'packages')
  103. shutil.rmtree(pkg_dir, ignore_errors=True)
  104. #恢复到原目录
  105. os.chdir(pwd)
  106. return success
  107. #判断参数是否是2个
  108. if len(sys.argv) != 2:
  109. usage()
  110. sys.exit(0)
  111. #更新MDK等文件
  112. def update_project_file(project_dir):
  113. if os.path.isfile(os.path.join(project_dir, 'template.Uv2')):
  114. print('prepare MDK3 project file on ' + project_dir)
  115. command = ' --target=mdk -s'
  116. os.system('scons --directory=' + project_dir + command + ' > 1.txt')
  117. if os.path.isfile(os.path.join(project_dir, 'template.uvproj')):
  118. print('prepare MDK4 project file on ' + project_dir)
  119. command = ' --target=mdk4 -s'
  120. os.system('scons --directory=' + project_dir + command + ' > 1.txt')
  121. if os.path.isfile(os.path.join(project_dir, 'template.uvprojx')):
  122. print('prepare MDK5 project file on ' + project_dir)
  123. command = ' --target=mdk5 -s'
  124. os.system('scons --directory=' + project_dir + command + ' > 1.txt')
  125. if os.path.isfile(os.path.join(project_dir, 'template.ewp')):
  126. print('prepare IAR project file on ' + project_dir)
  127. command = ' --target=iar -s'
  128. os.system('scons --directory=' + project_dir + command + ' > 1.txt')
  129. #更新所有可以scons的文件夹文件,先执行menuconfig --silent 再执行scons --target=mdk5
  130. #处理带有sconstruct的文件夹
  131. def update_all_project_files(sconstruct_paths):
  132. for projects in sconstruct_paths:
  133. try:
  134. # update rtconfig.h and .config
  135. #执行menuconfig
  136. if os.path.isfile(os.path.join(projects, 'Kconfig')):
  137. if "win32" in sys.platform:
  138. retval = os.getcwd()
  139. os.chdir(projects)
  140. os.system("menuconfig --silent")
  141. os.chdir(retval)
  142. else:
  143. os.system('scons --pyconfig-silent -C {0}'.format(projects))
  144. print('==menuconfig=======projects='+ projects)
  145. else:
  146. print('==no kconfig=in==!!!!!=projects='+ projects)
  147. # update mdk, IAR etc file
  148. update_project_file(projects)
  149. except Exception as e:
  150. print("error message: {}".format(e))
  151. sys.exit(-1)
  152. #找到带有Sconstruct的文件夹
  153. def find_sconstruct_paths(project_dir, exclude_paths, include_paths):
  154. sconstruct_paths = []
  155. bsp_detail_path = os.path.join(rtt_root, 'tools', 'ci', 'bsp_detail.yml')
  156. if os.path.exists(bsp_detail_path):
  157. with open(bsp_detail_path, 'r') as file:
  158. bsp_detail = yaml.safe_load(file)
  159. for root, dirs, files in os.walk(project_dir):
  160. if include_paths:
  161. if any(include_path in root for include_path in include_paths) and all(exclude_path not in root for exclude_path in exclude_paths):
  162. if 'SConstruct' in files:
  163. bsp_name = os.path.relpath(root, bsp_root)
  164. if bsp_name in bsp_detail and bsp_detail[bsp_name].get('gcc') == 'arm-none-eabi-gcc':
  165. sconstruct_paths.append(root)
  166. else:
  167. if all(exclude_path not in root for exclude_path in exclude_paths):
  168. if 'SConstruct' in files:
  169. bsp_name = os.path.relpath(root, bsp_root)
  170. if bsp_name in bsp_detail and bsp_detail[bsp_name].get('gcc') == 'arm-none-eabi-gcc':
  171. sconstruct_paths.append(root)
  172. return sconstruct_paths
  173. #检查EXE命令是否存在,判断环境
  174. def check_command_availability(cmd):
  175. """
  176. Check if a command is available.
  177. """
  178. cmd_path = shutil.which(cmd)
  179. if cmd_path is not None:
  180. #print(f"{cmd} command is available at {cmd_path}")
  181. return True
  182. else:
  183. print(f"{cmd} command is not available")
  184. return False
  185. # Find the rt-thread root directory
  186. rtt_root = os.getcwd()
  187. while not os.path.exists(os.path.join(rtt_root, 'LICENSE')):
  188. rtt_root = os.path.dirname(rtt_root)
  189. bsp_root = os.path.join(rtt_root, 'bsp')
  190. #需要排除的文件夹名字
  191. exclude_paths = ['templates', 'doc', 'libraries', 'Libraries', 'template']
  192. include_paths = []#['nrf5x','qemu-vexpress-a9', 'ESP32_C3','simulator']
  193. sconstruct_paths = find_sconstruct_paths(bsp_root, exclude_paths,include_paths)
  194. # get command options
  195. command = ''
  196. command_clean_flag = False
  197. print(rtt_root)
  198. if sys.argv[1] == 'all':
  199. if os.path.exists(os.path.join(rtt_root, 'failed_bsp_list.txt')):
  200. os.remove(os.path.join(rtt_root, 'failed_bsp_list.txt'))
  201. if os.path.exists(os.path.join(rtt_root, 'failed_bsp.log')):
  202. os.remove(os.path.join(rtt_root, 'failed_bsp.log'))
  203. command = ' '
  204. #更新所有的工程
  205. print('begin to update all the bsp projects')
  206. update_all_project_files(sconstruct_paths)
  207. #iarbuild .\project.ewp -clean rt-thread
  208. elif sys.argv[1] == 'clean':
  209. command = ' -c'
  210. command_clean_flag = True
  211. print('begin to clean all the bsp projects')
  212. # 执行所有其他IDE的 update 但是不编译,这个一般不会出错
  213. elif sys.argv[1] == 'update':
  214. print('begin to update all the bsp projects')
  215. #更新所有的工程
  216. update_all_project_files(sconstruct_paths)
  217. print('finished!')
  218. sys.exit(0)
  219. else:
  220. usage()
  221. sys.exit(0)
  222. if sconstruct_paths:
  223. print("包含 'SConstruct' 文件的路径:")
  224. for path in sconstruct_paths:
  225. print(path)
  226. else:
  227. print("未找到包含 'SConstruct' 文件的路径")
  228. #遍历所有的sconstruct_paths 路径中的文件夹
  229. def bsp_scons_worker(project_dir):
  230. print('=========project_dir===='+ project_dir)
  231. #判断有没有SConstruct 文件,
  232. if os.path.isfile(os.path.join(project_dir, 'SConstruct')):
  233. print('==menuconfig=======rtt_root='+ rtt_root)
  234. print('==project_dir=======project_dir='+ project_dir)
  235. # 去掉 'bsp' 前面的三级目录
  236. parts = project_dir.split(os.sep)
  237. if 'bsp' in parts:
  238. bsp_index = parts.index('bsp')
  239. new_project_dir = os.sep.join(parts[bsp_index+1:])
  240. else:
  241. new_project_dir = project_dir
  242. print('==project_dir=======new_project_dir='+ new_project_dir)
  243. #开始编译bsp
  244. build_bsp(new_project_dir)
  245. # 发现有keil相关的,执行keil相关的命令,先检查一下UV4.exe命令有没有,然后执行UV4.exe
  246. if check_command_availability('UV4.exe') :
  247. """
  248. UV4.exe -b project.uvprojx -q -j0 -t rt-thread -o action_runner.log
  249. ls
  250. sleep 10
  251. cat action_runner.log
  252. """
  253. if os.path.isfile(os.path.join(project_dir, 'template.uvprojx')):
  254. if check_command_availability('UV4.exe'):
  255. print('Start to build keil project======')
  256. os.chdir(f'{project_dir}')
  257. print('clean keil project======')
  258. run_cmd('UV4.exe -c project.uvprojx -q')
  259. ___, res = run_cmd('UV4.exe -b project.uvprojx -q -j0 -t rt-thread -o keil.log')
  260. os.chdir(f'{rtt_root}')
  261. else:
  262. print('UV4.exe is not available, please check your keil installation')
  263. if check_command_availability('iarbuild.exe') :
  264. """
  265. iarbuild .\project.ewp rt-thread
  266. """
  267. if os.path.isfile(os.path.join(project_dir, 'template.ewp')):
  268. if check_command_availability('iarbuild.exe'):
  269. print('Start to build iar project======')
  270. os.chdir(f'{project_dir}')
  271. ___, res = run_cmd('iarbuild .\project.ewp -clean rt-thread')
  272. if res != 0:
  273. print('run clean failed!!')
  274. ___, res = run_cmd('iarbuild .\project.ewp rt-thread > iar.log')
  275. if res != 0:
  276. print('run_cmd1 failed!!')
  277. os.chdir(f'{rtt_root}')
  278. else:
  279. print('iarbuild is not available, please check your iar installation')
  280. processes = []
  281. for project_dir in sconstruct_paths:
  282. bsp_scons_worker(project_dir)
  283. #p = Process(target=bsp_scons_worker, args=(project_dir,))
  284. #p.start()
  285. #processes.append(p)
  286. #for p in processes:
  287. # p.join() # 等待所有进程完成
  288. print('finished!')
  289. # 将failed_bsp_list.txt的内容追加到failed_bsp.log文件中
  290. if os.path.exists(os.path.join(rtt_root, 'failed_bsp_list.txt')):
  291. with open(os.path.join(rtt_root, 'failed_bsp_list.txt'), 'r') as file:
  292. failed_bsp_list = file.read()
  293. if os.path.exists(os.path.join(rtt_root, 'failed_bsp.log')):
  294. with open(os.path.join(rtt_root, 'failed_bsp.log'), 'a') as file:
  295. file.write(failed_bsp_list)