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