manual_bsp_build_all.py 11 KB

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