ci: tighten BSP build failure detection
ToolsCI / Tools (push) Has been cancelled
RT-Thread BSP Static Build Check / 🔍 Summary of Git Diff Changes (push) Has been cancelled
pkgs_test / change (push) Has been cancelled
utest_auto_run / A9 :components/dfs.cfg (push) Has been cancelled
utest_auto_run / A9 :components/lwip.cfg (push) Has been cancelled
utest_auto_run / A9 :components/netdev.cfg (push) Has been cancelled
utest_auto_run / A9 :components/sal.cfg (push) Has been cancelled
utest_auto_run / A9 :cpp11/cpp11.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :default.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :default.cfg (push) Has been cancelled
utest_auto_run / A9-smp :default.cfg (push) Has been cancelled
utest_auto_run / RISCV :default.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :default.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/atomic_c11.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9-smp :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/ipc.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / A9-smp :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/kernel_basic.cfg (push) Has been cancelled
utest_auto_run / AARCH64-rtsmart :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / A9-rtsmart :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / RISCV-rtsmart :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / XUANTIE-rtsmart :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / AARCH64 :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / AARCH64-smp :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / A9 :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / A9-smp :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / RISCV :kernel/mem.cfg (push) Has been cancelled
utest_auto_run / RISCV-smp :kernel/mem.cfg (push) Has been cancelled
RT-Thread BSP Static Build Check / ${{ matrix.legs.RTT_BSP }} (push) Has been cancelled
RT-Thread BSP Static Build Check / collect-artifacts (push) Has been cancelled

This commit is contained in:
CYFS
2026-06-05 14:41:51 +08:00
committed by Rbb666
parent 3932b77c64
commit 75246b56d5
2 changed files with 177 additions and 54 deletions
+152 -52
View File
@@ -18,12 +18,63 @@ import re
import multiprocessing
import yaml
SCONS_FATAL_PATTERNS = (
re.compile(r'No SConstruct file found', re.IGNORECASE),
re.compile(r'SConscript.*No such file or directory', re.IGNORECASE),
re.compile(r'No such file or directory.*SConscript', re.IGNORECASE),
re.compile(r"(can'?t|cannot|unable to)\s+(read|open|find).*SConscript", re.IGNORECASE),
re.compile(r'FileNotFoundError:.*SConscript', re.IGNORECASE),
re.compile(r'scons:\s+\*\*\*', re.IGNORECASE),
)
def add_summary(text):
"""
add summary to github action.
"""
os.system(f'echo "{text}" >> $GITHUB_STEP_SUMMARY ;')
summary_file = os.getenv('GITHUB_STEP_SUMMARY')
if summary_file:
with open(summary_file, 'a', encoding='utf-8') as file:
file.write(text + '\n')
def normalize_returncode(status):
"""
normalize os.system status to command exit code.
"""
if status == 0:
return 0
if os.name == 'nt':
return status
if hasattr(os, 'waitstatus_to_exitcode'):
try:
return os.waitstatus_to_exitcode(status)
except ValueError:
return status
return status >> 8
def contains_scons_fatal_error(output):
"""
check whether scons output contains a fatal build-script error.
"""
output_str = ''.join(output) if isinstance(output, list) else str(output)
return any(pattern.search(output_str) for pattern in SCONS_FATAL_PATTERNS)
def check_bsp_build_scripts(bsp_dir):
"""
check whether the BSP has the basic scons build entry files.
"""
missing = []
for filename in ('SConstruct', 'SConscript'):
if not os.path.isfile(os.path.join(bsp_dir, filename)):
missing.append(filename)
if missing:
print(f"::error::missing {', '.join(missing)} in {bsp_dir}")
return False
return True
def run_cmd(cmd, output_info=True):
"""
@@ -33,19 +84,27 @@ def run_cmd(cmd, output_info=True):
output_str_list = []
res = 0
output_file = f'output_{os.getpid()}_{threading.get_ident()}.txt'
devnull = os.devnull
if output_info:
res = os.system(cmd + " > output.txt 2>&1")
res = os.system(cmd + f" > {output_file} 2>&1")
else:
res = os.system(cmd + " > /dev/null 2>output.txt")
res = os.system(cmd + f" > {devnull} 2>{output_file}")
with open("output.txt", "r") as file:
with open(output_file, "r") as file:
output_str_list = file.readlines()
for line in output_str_list:
print(line, end='')
os.remove("output.txt")
os.remove(output_file)
res = normalize_returncode(res)
if contains_scons_fatal_error(output_str_list):
print(f"::error::scons fatal error detected while running: {cmd}")
if res == 0:
res = 1
return output_str_list, res
@@ -61,6 +120,10 @@ def run_dist_build_check(bsp, scons_args=''):
build BSP distribution and verify that the generated project can compile.
"""
os.chdir(rtt_root)
bsp_dir = os.path.join(rtt_root, 'bsp', bsp)
if not check_bsp_build_scripts(bsp_dir):
return False
dist_root = os.path.join(rtt_root, 'bsp', bsp, 'dist')
dist_project = os.path.join(dist_root, 'project')
if os.path.exists(dist_root):
@@ -77,6 +140,9 @@ def run_dist_build_check(bsp, scons_args=''):
print(f"::error::dist project not found: {dist_project}")
return False
if not check_bsp_build_scripts(dist_project):
return False
old_rtt_root = os.environ.pop('RTT_ROOT', None)
_, res = run_cmd(f'scons --pyconfig-silent -C {dist_project}', output_info=True)
if res != 0:
@@ -118,6 +184,15 @@ def build_bsp(bsp, scons_args='',name='default', pre_build_commands=None, post_b
"""
success = True
bsp_dir = os.path.join(rtt_root, 'bsp', bsp)
if not os.path.isdir(bsp_dir):
print(f"::error::BSP directory not found: {bsp_dir}")
return False
if not check_bsp_build_scripts(bsp_dir):
return False
# 设置环境变量
if bsp_build_env is not None:
print("Setting environment variables:")
@@ -128,50 +203,63 @@ def build_bsp(bsp, scons_args='',name='default', pre_build_commands=None, post_b
os.makedirs(f'{rtt_root}/output/bsp/{bsp}', exist_ok=True)
if os.path.exists(f"{rtt_root}/bsp/{bsp}/Kconfig"):
os.chdir(rtt_root)
run_cmd(f'scons -C bsp/{bsp} --pyconfig-silent', output_info=True)
_, res = run_cmd(f'scons -C bsp/{bsp} --pyconfig-silent', output_info=True)
if res != 0:
print(f"::error::pyconfig failed for {bsp}")
success = False
os.chdir(f'{rtt_root}/bsp/{bsp}')
run_cmd('pkgs --update-force', output_info=True)
run_cmd('pkgs --list')
nproc = multiprocessing.cpu_count()
if pre_build_commands is not None:
print("Pre-build commands:")
print(pre_build_commands)
for command in pre_build_commands:
print(command)
output, returncode = run_cmd(command, output_info=True)
print(output)
if returncode != 0:
print(f"Pre-build command failed: {command}")
print(output)
os.chdir(rtt_root)
# scons 编译命令
cmd = f'scons -C bsp/{bsp} -j{nproc} {scons_args}' # --debug=time for debug time
output, res = run_cmd(cmd, output_info=True)
if build_check_result is not None:
if res != 0 or not check_output(output, build_check_result):
print("Build failed or build check result not found")
print(output)
_, res = run_cmd('pkgs --update-force', output_info=True)
if res != 0:
print(f"::error::pkgs --update-force failed for {bsp}")
success = False
else:
#拷贝当前的文件夹下面的所有以elf结尾的文件拷贝到rt-thread/output文件夹下
import glob
# 拷贝编译生成的文件到output目录,文件拓展为 elf,bin,hex
for file_type in ['*.elf', '*.bin', '*.hex']:
files = glob.glob(f'{rtt_root}/bsp/{bsp}/{file_type}')
for file in files:
shutil.copy(file, f'{rtt_root}/output/bsp/{bsp}/{name.replace("/", "_")}.{file_type[2:]}')
if is_env_enabled('RTT_CI_BUILD_DIST'):
print(f"::group::\tChecking dist project: {bsp} {name}")
dist_res = run_dist_build_check(bsp, scons_args)
print("::endgroup::")
if not dist_res:
add_summary(f'\t- ❌ dist build {bsp} {name} failed.')
success = False
else:
add_summary(f'\t- ✅ dist build {bsp} {name} success.')
_, res = run_cmd('pkgs --list')
if res != 0:
print(f"::error::pkgs --list failed for {bsp}")
success = False
else:
print(f"Kconfig not found, skip pyconfig and package update: {os.path.join(bsp_dir, 'Kconfig')}")
nproc = multiprocessing.cpu_count()
if pre_build_commands is not None:
print("Pre-build commands:")
print(pre_build_commands)
for command in pre_build_commands:
print(command)
output, returncode = run_cmd(command, output_info=True)
print(output)
if returncode != 0:
print(f"Pre-build command failed: {command}")
print(output)
success = False
os.chdir(rtt_root)
# scons 编译命令
cmd = f'scons -C bsp/{bsp} -j{nproc} {scons_args}' # --debug=time for debug time
output, res = run_cmd(cmd, output_info=True)
if build_check_result is not None:
if res != 0 or not check_output(output, build_check_result):
print("Build failed or build check result not found")
print(output)
success = False
if res != 0:
success = False
if success:
#拷贝当前的文件夹下面的所有以elf结尾的文件拷贝到rt-thread/output文件夹下
import glob
# 拷贝编译生成的文件到output目录,文件拓展为 elf,bin,hex
for file_type in ['*.elf', '*.bin', '*.hex']:
files = glob.glob(f'{rtt_root}/bsp/{bsp}/{file_type}')
for file in files:
shutil.copy(file, f'{rtt_root}/output/bsp/{bsp}/{name.replace("/", "_")}.{file_type[2:]}')
if is_env_enabled('RTT_CI_BUILD_DIST'):
print(f"::group::\tChecking dist project: {bsp} {name}")
dist_res = run_dist_build_check(bsp, scons_args)
print("::endgroup::")
if not dist_res:
add_summary(f'\t- ❌ dist build {bsp} {name} failed.')
success = False
else:
add_summary(f'\t- ✅ dist build {bsp} {name} success.')
os.chdir(f'{rtt_root}/bsp/{bsp}')
if post_build_command is not None:
@@ -181,7 +269,11 @@ def build_bsp(bsp, scons_args='',name='default', pre_build_commands=None, post_b
if returncode != 0:
print(f"Post-build command failed: {command}")
print(output)
run_cmd('scons -c', output_info=False)
success = False
_, clean_res = run_cmd('scons -c', output_info=False)
if clean_res != 0:
print(f"::error::scons clean failed for {bsp}")
success = False
return success
@@ -240,19 +332,27 @@ def build_bsp_attachconfig(bsp, attach_file):
"""
config_file = os.path.join(rtt_root, 'bsp', bsp, '.config')
config_bacakup = config_file+'.origin'
shutil.copyfile(config_file, config_bacakup)
if not os.path.isfile(config_file):
print(f"::error::.config not found: {config_file}")
return False
attachconfig_dir = os.path.join(rtt_root, 'bsp', bsp, '.ci/attachconfig')
attach_path = os.path.join(attachconfig_dir, attach_file)
if not os.path.isfile(attach_path):
print(f"::error::attach config not found: {attach_path}")
return False
append_file(attach_path, config_file)
shutil.copyfile(config_file, config_bacakup)
scons_args = check_scons_args(attach_path)
try:
append_file(attach_path, config_file)
res = build_bsp(bsp, scons_args,name=attach_file)
scons_args = check_scons_args(attach_path)
shutil.copyfile(config_bacakup, config_file)
os.remove(config_bacakup)
res = build_bsp(bsp, scons_args,name=attach_file)
finally:
shutil.copyfile(config_bacakup, config_file)
os.remove(config_bacakup)
return res
+25 -2
View File
@@ -11,6 +11,7 @@
import subprocess
import logging
import os
import sys
CONFIG_BSP_USING_X = ["CONFIG_BSP_USING_UART", "CONFIG_BSP_USING_I2C", "CONFIG_BSP_USING_SPI", "CONFIG_BSP_USING_ADC", "CONFIG_BSP_USING_DAC"]
@@ -81,13 +82,32 @@ def modify_config(file_path, configs):
file.write("#define " + define1 + "\n")
file.write("#define " + define2 + "\n")
def check_scons_build_files(dir):
missing = []
for filename in ["SConstruct", "SConscript"]:
if not os.path.isfile(os.path.join(dir, filename)):
missing.append(filename)
if missing:
logging.error("missing %s in %s", ", ".join(missing), dir)
return False
return True
def recompile_bsp(dir):
logging.info("recomplie bsp: {}".format(dir))
os.system("scons -C " + dir)
if not check_scons_build_files(dir):
return 1
result = subprocess.run(["scons", "-C", dir])
if result.returncode != 0:
logging.error("scons failed for %s", dir)
return result.returncode
if __name__ == '__main__':
init_logger()
recompile_bsp_dirs = diff()
failed = 0
for dir in recompile_bsp_dirs:
dot_config_path = dir + "/" + ".config"
configs = check_config_in_file(dot_config_path)
@@ -95,4 +115,7 @@ if __name__ == '__main__':
logging.info(configs)
logging.info("Add configurations and recompile!")
modify_config(dir, configs)
recompile_bsp(dir)
if recompile_bsp(dir) != 0:
failed += 1
sys.exit(failed)