原始版本

This commit is contained in:
冯佳
2025-06-19 21:56:46 +08:00
parent fe98e5f010
commit a4841450cf
4152 changed files with 1910684 additions and 0 deletions

View File

@ -0,0 +1,256 @@
import os
import shutil
import re
import multiprocessing
import yaml
def add_summary(text):
"""
add summary to github action.
"""
os.system(f'echo "{text}" >> $GITHUB_STEP_SUMMARY ;')
def run_cmd(cmd, output_info=True):
"""
run command and return output and result.
"""
print('\033[1;32m' + cmd + '\033[0m')
output_str_list = []
res = 0
if output_info:
res = os.system(cmd + " > output.txt 2>&1")
else:
res = os.system(cmd + " > /dev/null 2>output.txt")
with open("output.txt", "r") as file:
output_str_list = file.readlines()
for line in output_str_list:
print(line, end='')
os.remove("output.txt")
return output_str_list, res
def build_bsp(bsp, scons_args=''):
"""
build bsp.
cd {rtt_root}
scons -C bsp/{bsp} --pyconfig-silent > /dev/null
cd {rtt_root}/bsp/{bsp}
pkgs --update > /dev/null
pkgs --list
cd {rtt_root}
scons -C bsp/{bsp} -j{nproc} {scons_args}
cd {rtt_root}/bsp/{bsp}
scons -c > /dev/null
rm -rf packages
"""
success = True
os.chdir(rtt_root)
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=False)
os.chdir(f'{rtt_root}/bsp/{bsp}')
run_cmd('pkgs --update-force', output_info=False)
run_cmd('pkgs --list')
nproc = multiprocessing.cpu_count()
os.chdir(rtt_root)
cmd = f'scons -C bsp/{bsp} -j{nproc} {scons_args}'
__, res = run_cmd(cmd, output_info=True)
if res != 0:
success = False
os.chdir(f'{rtt_root}/bsp/{bsp}')
run_cmd('scons -c', output_info=False)
#pkg_dir = os.path.join(rtt_root, 'bsp', bsp, 'packages')
#shutil.rmtree(pkg_dir, ignore_errors=True)
return success
def append_file(source_file, destination_file):
"""
append file to another file.
"""
with open(source_file, 'r') as source:
with open(destination_file, 'a') as destination:
for line in source:
destination.write(line)
def check_scons_args(file_path):
args = []
with open(file_path, 'r') as file:
for line in file:
match = re.search(r'#\s*scons:\s*(.*)', line)
if match:
args.append(match.group(1).strip())
return ' '.join(args)
def get_details_and_dependencies(details, projects, seen=None):
if seen is None:
seen = set()
detail_list = []
if details is not None:
for dep in details:
if dep not in seen:
dep_details=projects.get(dep)
seen.add(dep)
if dep_details is not None:
if dep_details.get('depends') is not None:
detail_temp=get_details_and_dependencies(dep_details.get('depends'), projects, seen)
for line in detail_temp:
detail_list.append(line)
if dep_details.get('kconfig') is not None:
for line in dep_details.get('kconfig'):
detail_list.append(line)
else:
print(f"::error::There are some problems with attachconfig depend: {dep}");
return detail_list
def build_bsp_attachconfig(bsp, attach_file):
"""
build bsp with attach config.
cp bsp/{bsp}/.config bsp/{bsp}/.config.origin
cat .ci/attachconfig/{attach_file} >> bsp/{bsp}/.config
build_bsp()
cp bsp/{bsp}/.config.origin bsp/{bsp}/.config
rm bsp/{bsp}/.config.origin
"""
config_file = os.path.join(rtt_root, 'bsp', bsp, '.config')
config_bacakup = config_file+'.origin'
shutil.copyfile(config_file, config_bacakup)
attachconfig_dir = os.path.join(rtt_root, 'bsp', bsp, '.ci/attachconfig')
attach_path = os.path.join(attachconfig_dir, attach_file)
append_file(attach_path, config_file)
scons_args = check_scons_args(attach_path)
res = build_bsp(bsp, scons_args)
shutil.copyfile(config_bacakup, config_file)
os.remove(config_bacakup)
return res
if __name__ == "__main__":
"""
build all bsp and attach config.
1. build all bsp.
2. build all bsp with attach config.
"""
failed = 0
count = 0
rtt_root = os.getcwd()
srtt_bsp = os.getenv('SRTT_BSP').split(',')
for bsp in srtt_bsp:
count += 1
print(f"::group::Compiling BSP: =={count}=== {bsp} ====")
res = build_bsp(bsp)
if not res:
print(f"::error::build {bsp} failed")
add_summary(f"- ❌ build {bsp} failed.")
failed += 1
else:
add_summary(f'- ✅ build {bsp} success.')
print("::endgroup::")
yml_files_content = []
directory = os.path.join(rtt_root, 'bsp', bsp, '.ci/attachconfig')
if os.path.exists(directory):
for root, dirs, files in os.walk(directory):
for filename in files:
if filename.endswith('attachconfig.yml'):
file_path = os.path.join(root, filename)
if os.path.exists(file_path):
try:
with open(file_path, 'r') as file:
content = yaml.safe_load(file)
if content is None:
continue
yml_files_content.append(content)
except yaml.YAMLError as e:
print(f"::error::Error parsing YAML file: {e}")
continue
except Exception as e:
print(f"::error::Error reading file: {e}")
continue
config_file = os.path.join(rtt_root, 'bsp', bsp, '.config')
for projects in yml_files_content:
for name, details in projects.items():
count += 1
config_bacakup = config_file+'.origin'
shutil.copyfile(config_file, config_bacakup)
with open(config_file, 'a') as destination:
if details.get("kconfig") is None:
continue
if(projects.get(name) is not None):
detail_list=get_details_and_dependencies([name],projects)
for line in detail_list:
destination.write(line + '\n')
scons_arg=[]
if details.get('scons_arg') is not None:
for line in details.get('scons_arg'):
scons_arg.append(line)
scons_arg_str=' '.join(scons_arg) if scons_arg else ' '
print(f"::group::\tCompiling yml project: =={count}==={name}=scons_arg={scons_arg_str}==")
res = build_bsp(bsp, scons_arg_str)
if not res:
print(f"::error::build {bsp} {name} failed.")
add_summary(f'\t- ❌ build {bsp} {name} failed.')
failed += 1
else:
add_summary(f'\t- ✅ build {bsp} {name} success.')
print("::endgroup::")
shutil.copyfile(config_bacakup, config_file)
os.remove(config_bacakup)
attach_dir = os.path.join(rtt_root, 'bsp', bsp, '.ci/attachconfig')
attach_list = []
for root, dirs, files in os.walk(attach_dir):
for file in files:
if file.endswith('attach'):
file_path = os.path.join(root, file)
relative_path = os.path.relpath(file_path, attach_dir)
attach_list.append(relative_path)
for attach_file in attach_list:
count += 1
print(f"::group::\tCompiling BSP: =={count}=== {bsp} {attach_file}===")
res = build_bsp_attachconfig(bsp, attach_file)
if not res:
print(f"::error::build {bsp} {attach_file} failed.")
add_summary(f'\t- ❌ build {attach_file} failed.')
failed += 1
else:
add_summary(f'\t- ✅ build {attach_file} success.')
print("::endgroup::")
exit(failed)

View File

@ -0,0 +1,206 @@
#
# Copyright (c) 2024, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2024-08-24 supperthomas the first version
#
# 这个文件会根据bsp中的信息生成对应的bsp_detail.yml文件这个文件会包含bsp中的一些信息比如是否有Kconfig文件是否有template.uvprojx文件等等
# 根据生成的bsp_detail.yml文件会生成一个toolchain_bsp.yml文件这个文件会包含所有的gcc编译器的信息以及对应的bsp文件夹
import os
import pandas as pd
import yaml
from datetime import datetime
import subprocess
#pip install pandas
#pip install tabulate
#pip install pyyaml
# 添加每个工具链的下载地址
download_urls = {
'arm-none-eabi-gcc': 'https://github.com/RT-Thread/toolchains-ci/releases/download/v1.3/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2',
'mips-sde-elf-gcc': 'https://github.com/RT-Thread/toolchains-ci/releases/download/v1.6/gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf.tar.xz',
'riscv64-unknown-elf-gcc': 'https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz',
'riscv32-unknown-elf-gcc': 'https://github.com/hpmicro/riscv-gnu-toolchain/releases/download/2022.05.15/riscv32-unknown-elf-newlib-multilib_2022.05.15_linux.tar.gz',
'llvm-arm': 'https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-16.0.0/LLVMEmbeddedToolchainForArm-16.0.0-Linux-x86_64.tar.gz',
'riscv-none-embed-gcc': 'https://github.com/RT-Thread/toolchains-ci/releases/download/v1.5/xpack-riscv-none-embed-gcc-8.3.0-2.3-linux-x64.tar.gz',
'riscv32-esp-elf-gcc': 'https://github.com/espressif/crosstool-NG/releases/download/esp-2022r1-RC1/riscv32-esp-elf-gcc11_2_0-esp-2022r1-RC1-linux-amd64.tar.xz',
'clang': 'https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-16.0.0/LLVMEmbeddedToolchainForArm-16.0.0-Linux-x86_64.tar.gz',
# 添加其他工具链的下载地址
}
# 产生toolchain.yml文件
def generate_toolchain_yaml(input_file, output_file, header_comment):
with open(input_file, 'r', encoding='utf-8') as file:
data = yaml.safe_load(file)
toolchain_data = {}
for folder, details in data.items():
gcc = details.get('gcc')
if gcc:
if gcc not in toolchain_data:
toolchain_data[gcc] = {'bsp': []}
toolchain_data[gcc]['bsp'].append(folder)
# 添加每个工具链的个数
for gcc, details in toolchain_data.items():
details['count'] = len(details['bsp'])
download_url = download_urls.get(gcc)
if download_url:
details['download_url'] = download_url
with open(output_file, 'w', encoding='utf-8') as file:
file.write(f"# {header_comment}\n")
yaml.dump(toolchain_data, file, default_flow_style=False, allow_unicode=True)
# 这个函数通过检查文件是否存在来检查bsp的支持情况
def check_files(root_dir, file_list):
data = []
folders_checked = set()
for projects in sconstruct_paths:
found_arch = False # Flag to track if ARCH has been found
found_cpu = False # Flag to track if ARCH has been found
if projects not in folders_checked:
#file_dict = {file: True if os.path.isfile(os.path.join(projects, file)) else '' for file in file_list}
file_dict = {}
for file in file_list:
file_exists = os.path.isfile(os.path.join(projects, file))
if file == 'template.uvprojx':
file_dict['mdk5'] = True if file_exists else False
elif file == 'template.ewp':
file_dict['iar'] = True if file_exists else False
elif file == 'template.uvproj':
file_dict['mdk4'] = True if file_exists else False
elif file == 'template.Uv2':
file_dict['mdk3'] = True if file_exists else False
elif file == 'Kconfig':
file_dict['menuconfig'] = True if file_exists else False
else:
file_dict[file] = True if file_exists else False
# 提取 rtconfig.py 中的 PREFIX 信息
rtconfig_path = os.path.join(projects, 'rtconfig.py')
if os.path.isfile(rtconfig_path):
print(f"Reading {rtconfig_path}")
with open(rtconfig_path, 'r') as f:
for line in f:
if not found_arch and line.strip().startswith('ARCH'):
arch_value = line.split('=')[1].strip().strip("'\"")
file_dict['arch'] = f"{arch_value}"
print(f"Found ARCH: {arch_value} in {rtconfig_path}")
found_arch = True # Set the flag to True
# 解析CPU属性
if not found_cpu and line.strip().startswith('CPU'):
cpu_value = line.split('=')[1].strip().strip("'\"")
file_dict['cpu'] = f"{cpu_value}"
print(f"Found CPU: {cpu_value} in {rtconfig_path}")
found_cpu = True
if line.strip().startswith('PREFIX'):
prefix_value = line.split('=')[1].strip().strip("'\"")
# 只提取实际的编译器前缀
if 'os.getenv' in prefix_value:
prefix_value = prefix_value.split('or')[-1].strip().strip("'\"")
file_dict['gcc'] = f"{prefix_value}gcc"
print(f"Found PREFIX: {prefix_value} in {rtconfig_path}")
break
else:
print(f"No PREFIX found in {rtconfig_path}")
# 去掉路径中的 '/workspaces/rt-thread/bsp/' 部分
projects2 = projects.replace(root_dir + '/', '')
file_dict['Folder'] = projects2
data.append(file_dict)
#data.append({'Folder': projects2, **file_dict})
folders_checked.add(projects)
df = pd.DataFrame(data)
return df
def find_sconstruct_paths(project_dir, exclude_paths):
sconstruct_paths = []
for root, dirs, files in os.walk(project_dir):
if all(exclude_path not in root for exclude_path in exclude_paths):
if 'SConstruct' in files:
sconstruct_paths.append(root)
return sconstruct_paths
def output_to_markdown(df, output_file):
with open(output_file, 'w', encoding='utf-8') as file:
file.write(df.to_markdown(index=False))
def output_to_yaml(dataframe, output_file, header_comment):
data = dataframe.to_dict(orient='records')
yaml_data = {}
for item in data:
folder = item.pop('Folder')
filtered_item = {k: v for k, v in item.items() if v is True or isinstance(v, str)}
yaml_data[folder] = filtered_item
with open(output_file, 'w', encoding='utf-8') as file:
file.write(f"# {header_comment}\n")
yaml.dump(yaml_data, file, default_flow_style=False, allow_unicode=True)
# Find the rt-thread root directory
rtt_root = os.getcwd()
while not os.path.exists(os.path.join(rtt_root, 'LICENSE')):
rtt_root = os.path.dirname(rtt_root)
bsp_root = os.path.join(rtt_root, 'bsp')
exclude_paths = ['templates', 'doc']
files_to_check = ['README.md','rtconfig.h', '.config','Kconfig', 'template.uvprojx','template.ewp', 'README.md', 'README_ZH.md', 'template.Uv2','template.uvproj']
sconstruct_paths = find_sconstruct_paths(bsp_root, exclude_paths)
result_table = check_files(bsp_root, files_to_check)
print(result_table)
output_file = 'output.md'
output_to_markdown(result_table, output_file)
# 将 output.yml 和 toolchain.yml 文件保存到 bsp 目录下
# 获取今天的日期
today_date = datetime.today().strftime('%Y-%m-%d')
# 获取当前年份
current_year = datetime.today().year
def get_git_user_name():
try:
result = subprocess.run(['git', 'config', 'user.name'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode == 0:
return result.stdout.strip()
else:
return "Unknown Author"
except Exception as e:
return "Unknown Author"
# 获取 Git 用户名
author_name = get_git_user_name()
# 头部注释
header_comment = f"""
# Copyright (c) {current_year}, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# {today_date} {author_name} the first version
#
"""
# 将 output.yml 和 toolchain.yml 文件保存到 tools/ci 目录下
ci_dir = os.path.join(rtt_root, 'tools', 'ci')
os.makedirs(ci_dir, exist_ok=True)
bsp_detail_file = os.path.join(ci_dir, 'bsp_detail.yml')
output_to_yaml(result_table, bsp_detail_file, header_comment)
toolchain_output_file = os.path.join(ci_dir, 'toolchain_bsp.yml')
generate_toolchain_yaml(bsp_detail_file, toolchain_output_file, header_comment)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
#
# Copyright (c) 2006-2023, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2023-06-27 dejavudwh the first version
#
import subprocess
import logging
import os
CONFIG_BSP_USING_X = ["CONFIG_BSP_USING_UART", "CONFIG_BSP_USING_I2C", "CONFIG_BSP_USING_SPI", "CONFIG_BSP_USING_ADC", "CONFIG_BSP_USING_DAC"]
def init_logger():
log_format = "[%(filename)s %(lineno)d %(levelname)s] %(message)s "
date_format = '%Y-%m-%d %H:%M:%S %a '
logging.basicConfig(level=logging.INFO,
format=log_format,
datefmt=date_format,
)
def diff():
result = subprocess.run(['git', 'diff', '--name-only', 'HEAD', 'origin/master', '--diff-filter=ACMR', '--no-renames', '--full-index'], stdout = subprocess.PIPE)
file_list = result.stdout.decode().strip().split('\n')
logging.info(file_list)
bsp_paths = set()
for file in file_list:
if "bsp/" in file:
logging.info("Modifed file: {}".format(file))
bsp_paths.add(file)
dirs = set()
for dir in bsp_paths:
dir = os.path.dirname(dir)
while "bsp/" in dir:
files = os.listdir(dir)
if ".config" in files and "rt-thread.elf" not in files and not dir.endswith("bsp"):
logging.info("Found bsp path: {}".format(dir))
dirs.add(dir)
break
new_dir = os.path.dirname(dir)
dir = new_dir
return dirs
def check_config_in_line(line):
for config in CONFIG_BSP_USING_X:
if config in line and '#' in line:
logging.info("Found in {}".format(line))
return config
return ""
def check_config_in_file(file_path):
configs = set()
found = False
try:
with open(file_path, 'r') as file:
for line in file:
line.strip()
if found:
res = check_config_in_line(line)
if res:
configs.add(res)
elif "On-chip Peripheral Drivers" in line:
logging.info("Found On-chip Peripheral Drivers")
found = True
except FileNotFoundError:
logging.error("The .config file does not exist for this BSP, please recheck the file directory!")
return configs
def modify_config(file_path, configs):
with open(file_path + "/rtconfig.h", 'a') as file:
for item in configs:
define1 = item.replace("CONFIG_BSP", "BSP")
define2 = item.replace("CONFIG_BSP", "RT")
file.write("#define " + define1 + "\n")
file.write("#define " + define2 + "\n")
def recompile_bsp(dir):
logging.info("recomplie bsp: {}".format(dir))
os.system("scons -C " + dir)
if __name__ == '__main__':
init_logger()
recompile_bsp_dirs = diff()
for dir in recompile_bsp_dirs:
dot_config_path = dir + "/" + ".config"
configs = check_config_in_file(dot_config_path)
logging.info("add config:")
logging.info(configs)
logging.info("Add configurations and recompile!")
modify_config(dir, configs)
recompile_bsp(dir)

View File

@ -0,0 +1,118 @@
#
# Copyright (c) 2006-2023, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2023-05-16 dejavudwh the first version
# 2024-09-12 supperthomas add cppcheck summary for detail
#
import click
import logging
import subprocess
import sys
import format_ignore
import os
'''
--suppress=syntaxError
该选项用于抑制特定的错误类型。在这里syntaxError 是被忽略的错误类型。这意味着 Cppcheck 不会报告语法错误syntaxError。这是因为在某些情况下分析工具可能会误报语法错误但 CI持续集成系统会在编译时捕获这些错误因此可以忽略。
--enable=warning
该选项用于启用特定类型的检查。Cppcheck 可以检查不同的级别和种类的问题,如错误、警告、性能问题等。--enable=warning 启用的是警告级别的检查,帮助检测代码中可能存在的问题,但不是严重的错误。
performance
Cppcheck 可以启用多个检查种类,在这里指定了 performance意味着会检查与代码性能相关的潜在问题例如不必要的拷贝操作、无效的条件判断等。这有助于提高代码的运行效率。
portability
Cppcheck 会检查代码的可移植性问题。可移植性检查帮助发现代码在不同平台或编译器中可能遇到的问题,如特定平台上不支持的类型、函数或特性。这对跨平台开发非常重要。
--inline-suppr
该选项允许在代码中使用注释来抑制特定的警告或错误信息。通过这种方式,开发者可以直接在代码中添加注释,告诉 Cppcheck 忽略某些检查或警告。例如,开发者可以在代码中添加 // cppcheck-suppress <message> 来抑制特定的警告信息。
--error-exitcode=1
该选项告诉 Cppcheck 如果遇到错误,就返回指定的退出码。在这里指定的是 1。这对自动化工具非常有用尤其是在 CI 环境中,因为它可以通过检查返回码来判断 Cppcheck 是否发现了错误。如果有错误CI 系统会将整个任务标记为失败。
--force
这个选项强制 Cppcheck 对所有文件进行检查即使它检测到编译条件缺失或某些配置问题。通常如果某些宏定义或依赖项缺失Cppcheck 可能会跳过某些文件的检查。但 --force 会强制工具继续执行分析,即使有可能缺少某些信息。这在某些大型项目中很有用,可以确保所有文件都经过检查。
'''
def add_summary(text):
"""
add summary to github action.
"""
os.system(f'echo "{text}" >> $GITHUB_STEP_SUMMARY ;')
class CPPCheck:
def __init__(self, file_list):
self.file_list = file_list
def check(self):
file_list_filtered = [file for file in self.file_list if file.endswith(('.c', '.cpp', '.cc', '.cxx'))]
logging.info("Start to static code analysis.")
check_result = True
for file in file_list_filtered:
macros = []
if os.path.basename(file) == 'lwp.c':
macros.append('-DRT_USING_DFS')
result = subprocess.run(
[
'cppcheck',
'-DRT_ASSERT(x)=',
'-DRTM_EXPORT(x)=',
'-Drt_list_for_each_entry(a,b,c)=a=(void*)b;',
'-I include',
'-I thread/components/finsh',
# it's okay because CI will do the real compilation to check this
'--suppress=syntaxError',
'--check-level=exhaustive',
'--enable=warning',
'performance',
'portability',
'--inline-suppr',
'--error-exitcode=1',
'--force',
file
] + macros,
stdout = subprocess.PIPE, stderr = subprocess.PIPE)
logging.info(result.stdout.decode())
logging.info(result.stderr.decode())
if result.stderr:
add_summary("The following errors are for reference only. If they are not actual issues, please ignore them and do not make unnecessary modifications.")
add_summary("以下错误仅供参考,如果发现没有问题,请直接忽略,不需要强行修改")
add_summary(f"- :rotating_light: {result.stderr.decode()}")
check_result = False
return check_result
@click.group()
@click.pass_context
def cli(ctx):
pass
@cli.command()
def check():
"""
static code analysis(cppcheck).
"""
format_ignore.init_logger()
# get modified files list
checkout = format_ignore.CheckOut()
file_list = checkout.get_new_file()
if file_list is None:
logging.error("checkout files fail")
sys.exit(1)
# use cppcheck
cpp_check = CPPCheck(file_list)
cpp_check_result = cpp_check.check()
if not cpp_check_result:
logging.error("static code analysis(cppcheck) fail.")
sys.exit(1)
logging.info("check success.")
sys.exit(0)
if __name__ == '__main__':
cli()

View File

@ -0,0 +1,292 @@
#
# Copyright (c) 2006-2022, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2021-04-01 LiuKang the first version
#
import os
import re
import sys
import click
import yaml
import chardet
import logging
import datetime
def init_logger():
log_format = "[%(filename)s %(lineno)d %(levelname)s] %(message)s "
date_format = '%Y-%m-%d %H:%M:%S %a '
logging.basicConfig(level=logging.INFO,
format=log_format,
datefmt=date_format,
)
class CheckOut:
def __init__(self, rtt_repo, rtt_branch):
self.root = os.getcwd()
self.rtt_repo = rtt_repo
self.rtt_branch = rtt_branch
def __exclude_file(self, file_path):
dir_number = file_path.split('/')
ignore_path = file_path
# gets the file path depth.
for i in dir_number:
# current directory.
dir_name = os.path.dirname(ignore_path)
ignore_path = dir_name
# judge the ignore file exists in the current directory.
ignore_file_path = os.path.join(dir_name, ".ignore_format.yml")
if not os.path.exists(ignore_file_path):
continue
try:
with open(ignore_file_path) as f:
ignore_config = yaml.safe_load(f.read())
file_ignore = ignore_config.get("file_path", [])
dir_ignore = ignore_config.get("dir_path", [])
except Exception as e:
logging.error(e)
continue
logging.debug("ignore file path: {}".format(ignore_file_path))
logging.debug("file_ignore: {}".format(file_ignore))
logging.debug("dir_ignore: {}".format(dir_ignore))
try:
# judge file_path in the ignore file.
for file in file_ignore:
if file is not None:
file_real_path = os.path.join(dir_name, file)
if file_real_path == file_path:
logging.info("ignore file path: {}".format(file_real_path))
return 0
file_dir_path = os.path.dirname(file_path)
for _dir in dir_ignore:
if _dir is not None:
dir_real_path = os.path.join(dir_name, _dir)
if file_dir_path.startswith(dir_real_path):
logging.info("ignore dir path: {}".format(dir_real_path))
return 0
except Exception as e:
logging.error(e)
continue
return 1
def get_new_file(self):
file_list = list()
try:
os.system('git remote add rtt_repo {}'.format(self.rtt_repo))
os.system('git fetch rtt_repo')
os.system('git merge rtt_repo/{}'.format(self.rtt_branch))
os.system('git reset rtt_repo/{} --soft'.format(self.rtt_branch))
os.system('git status > git.txt')
except Exception as e:
logging.error(e)
return None
try:
with open('git.txt', 'r') as f:
file_lines = f.readlines()
except Exception as e:
logging.error(e)
return None
file_path = ''
for line in file_lines:
if 'new file' in line:
file_path = line.split('new file:')[1].strip()
logging.info('new file -> {}'.format(file_path))
elif 'deleted' in line:
logging.info('deleted file -> {}'.format(line.split('deleted:')[1].strip()))
elif 'modified' in line:
file_path = line.split('modified:')[1].strip()
logging.info('modified file -> {}'.format(file_path))
else:
continue
result = self.__exclude_file(file_path)
if result != 0:
file_list.append(file_path)
return file_list
class FormatCheck:
def __init__(self, file_list):
self.file_list = file_list
def __check_rt_errorcode(self, line):
pattern = re.compile(r'return\s+(RT_ERROR|RT_ETIMEOUT|RT_EFULL|RT_EEMPTY|RT_ENOMEM|RT_ENOSYS|RT_EBUSY|RT_EIO|RT_EINTR|RT_EINVAL|RT_ENOENT|RT_ENOSPC|RT_EPERM|RT_ETRAP|RT_EFAULT)')
match = pattern.search(line)
if match:
return False
else:
return True
def __check_file(self, file_lines, file_path):
line_num = 0
check_result = True
for line in file_lines:
line_num += 1
# check line start
line_start = line.replace(' ', '')
# find tab
if line_start.startswith('\t'):
logging.error("{} line[{}]: please use space replace tab at the start of this line.".format(file_path, line_num))
check_result = False
# check line end
line_end = line.split('\n')[0]
if line_end.endswith(' ') or line_end.endswith('\t'):
logging.error("{} line[{}]: please delete extra space at the end of this line.".format(file_path, line_num))
check_result = False
if self.__check_rt_errorcode(line) == False:
logging.error("{} line[{}]: the RT-Thread error code should return negative value. e.g. return -RT_ERROR".format(file_path, line_num))
check_result = False
return check_result
def check(self):
logging.info("Start to check files format.")
if len(self.file_list) == 0:
logging.warning("There are no files to check format.")
return True
encoding_check_result = True
format_check_fail_files = 0
for file_path in self.file_list:
code = ''
if file_path.endswith(".c") or file_path.endswith(".h"):
try:
with open(file_path, 'rb') as f:
file = f.read()
# get file encoding
chardet_report = chardet.detect(file)
code = chardet_report['encoding']
confidence = chardet_report['confidence']
except Exception as e:
logging.error(e)
else:
continue
if code != 'utf-8' and code != 'ascii' and confidence > 0.8:
logging.error("[{0}]: encoding {1} not utf-8, please format it.".format(file_path, code))
encoding_check_result = False
else:
logging.info('[{0}]: encoding check success.'.format(file_path))
with open(file_path, 'r', encoding = "utf-8") as f:
file_lines = f.readlines()
if not self.__check_file(file_lines, file_path):
format_check_fail_files += 1
if (not encoding_check_result) or (format_check_fail_files != 0):
logging.error("files format check fail.")
return False
logging.info("files format check success.")
return True
class LicenseCheck:
def __init__(self, file_list):
self.file_list = file_list
def check(self):
current_year = datetime.date.today().year
logging.info("current year: {}".format(current_year))
if len(self.file_list) == 0:
logging.warning("There are no files to check license.")
return 0
logging.info("Start to check files license.")
check_result = True
for file_path in self.file_list:
if file_path.endswith(".c") or file_path.endswith(".h"):
try:
with open(file_path, 'r') as f:
file = f.readlines()
except Exception as e:
logging.error(e)
else:
continue
if 'Copyright' in file[1] and 'SPDX-License-Identifier: Apache-2.0' in file[3]:
try:
license_year = re.search(r'2006-\d{4}', file[1]).group()
true_year = '2006-{}'.format(current_year)
if license_year != true_year:
logging.warning("[{0}]: license year: {} is not true: {}, please update.".format(file_path,
license_year,
true_year))
else:
logging.info("[{0}]: license check success.".format(file_path))
except Exception as e:
logging.error(e)
else:
logging.error("[{0}]: license check fail.".format(file_path))
check_result = False
return check_result
@click.group()
@click.pass_context
def cli(ctx):
pass
@cli.command()
@click.option(
'--license',
"check_license",
required=False,
type=click.BOOL,
flag_value=True,
help="Enable File license check.",
)
@click.argument(
'repo',
nargs=1,
type=click.STRING,
default='https://github.com/RT-Thread/rt-thread',
)
@click.argument(
'branch',
nargs=1,
type=click.STRING,
default='master',
)
def check(check_license, repo, branch):
"""
check files license and format.
"""
init_logger()
# get modified files list
checkout = CheckOut(repo, branch)
file_list = checkout.get_new_file()
if file_list is None:
logging.error("checkout files fail")
sys.exit(1)
# check modified files format
format_check = FormatCheck(file_list)
format_check_result = format_check.check()
license_check_result = True
if check_license:
license_check = LicenseCheck(file_list)
license_check_result = license_check.check()
if not format_check_result or not license_check_result:
logging.error("file format check or license check fail.")
sys.exit(1)
logging.info("check success.")
sys.exit(0)
if __name__ == '__main__':
cli()

View File

@ -0,0 +1,84 @@
#
# Copyright (c) 2006-2023, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2023-05-16 dejavudwh the first version
#
import yaml
import logging
import os
import subprocess
def init_logger():
log_format = "[%(filename)s %(lineno)d %(levelname)s] %(message)s "
date_format = '%Y-%m-%d %H:%M:%S %a '
logging.basicConfig(level=logging.INFO,
format=log_format,
datefmt=date_format,
)
class CheckOut:
def __init__(self):
pass
def __exclude_file(self, file_path):
dir_number = file_path.split('/')
ignore_path = file_path
# gets the file path depth.
for i in dir_number:
# current directory.
dir_name = os.path.dirname(ignore_path)
ignore_path = dir_name
# judge the ignore file exists in the current directory.
ignore_file_path = os.path.join(dir_name, ".ignore_format.yml")
if not os.path.exists(ignore_file_path):
continue
try:
with open(ignore_file_path) as f:
ignore_config = yaml.safe_load(f.read())
file_ignore = ignore_config.get("file_path", [])
dir_ignore = ignore_config.get("dir_path", [])
except Exception as e:
logging.error(e)
continue
logging.debug("ignore file path: {}".format(ignore_file_path))
logging.debug("file_ignore: {}".format(file_ignore))
logging.debug("dir_ignore: {}".format(dir_ignore))
try:
# judge file_path in the ignore file.
for file in file_ignore:
if file is not None:
file_real_path = os.path.join(dir_name, file)
if file_real_path == file_path:
logging.info("ignore file path: {}".format(file_real_path))
return 0
file_dir_path = os.path.dirname(file_path)
for _dir in dir_ignore:
if _dir is not None:
dir_real_path = os.path.join(dir_name, _dir)
if file_dir_path.startswith(dir_real_path):
logging.info("ignore dir path: {}".format(dir_real_path))
return 0
except Exception as e:
logging.error(e)
continue
return 1
def get_new_file(self):
result = subprocess.run(['git', 'diff', '--name-only', 'HEAD', 'origin/master', '--diff-filter=ACMR', '--no-renames', '--full-index'], stdout = subprocess.PIPE)
file_list = result.stdout.decode().strip().split('\n')
new_files = []
for line in file_list:
logging.info("modified file -> {}".format(line))
result = self.__exclude_file(line)
if result != 0:
new_files.append(line)
return new_files

View File

@ -0,0 +1,219 @@
#!/bin/bash
#
# Copyright (c) 2024, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2024-08-27 Supperthomas the first version
#
#这个脚本用于安装RT-Thread开发环境 请确保网络畅通
# 设置环境变量 如果希望生效请在当前shell中执行source install.sh
export RTT_ROOT=$(pwd)
export RTT_CC=gcc
echo "RTT_ROOT is set to: $RTT_ROOT"
check_if_china_ip() {
# 默认情况下不使用gitee
use_gitee=false
# 尝试通过IP地址判断
ip=$(curl -s https://ifconfig.me/ip)
if [ -n "$ip" ]; then
location=$(curl -s http://www.ip-api.com/json/$ip | grep -o '"country":"China"')
if [ "$location" == '"country":"China"' ]; then
use_gitee=true
echo "Detected China IP. Using gitee."
else
echo "IP location is not in China."
fi
else
echo "Failed to retrieve IP address. Falling back to timezone check."
# 通过时区判断
if [ $(($(date +%z)/100)) -eq 8 ]; then
use_gitee=true
echo "Detected timezone UTC+8. Using gitee."
else
echo "Timezone is not UTC+8."
fi
fi
echo $use_gitee
}
# 检测操作系统类型和发行版
detect_os() {
if command -v uname >/dev/null 2>&1; then
OS=$(uname -s)
else
if [ -f "/etc/os-release" ]; then
OS="Linux"
elif [ -f "/System/Library/CoreServices/SystemVersion.plist" ]; then
OS="macOS"
elif [[ -d "/mnt/c/Windows" || -d "/c/Windows" ]]; then
OS="WSL"
else
OS="UNKNOWN"
fi
fi
if [ "$OS" == "Linux" ]; then
if [ -f /etc/os-release ]; then
. /etc/os-release
DISTRO=$ID
VERSION=$VERSION_ID
elif [ -f /etc/lsb-release ]; then
. /etc/lsb-release
DISTRO=$DISTRIB_ID
VERSION=$DISTRIB_RELEASE
else
DISTRO="UNKNOWN"
VERSION="UNKNOWN"
fi
fi
echo "Detected Operating System: $OS, Distribution: $DISTRO, Version: $VERSION"
}
# 修改的安装函数
install_on_ubuntu() {
echo "Installing on Debian/Ubuntu..."
use_gitee=$(check_if_china_ip)
# 根据检测结果决定是否使用--gitee参数
if [ "$use_gitee" = true ]; then
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_ubuntu.sh
chmod 777 install_ubuntu.sh
echo "Installing on China gitee..."
./install_ubuntu.sh --gitee
else
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_ubuntu.sh
chmod 777 install_ubuntu.sh
echo "Installing on no China..."
./install_ubuntu.sh
fi
rm install_ubuntu.sh
}
install_on_fedora() {
echo "Installing on Fedora..."
}
install_on_centos() {
echo "Installing on CentOS/RHEL..."
}
install_on_arch() {
echo "Installing on Arch Linux..."
}
install_on_macos() {
echo "Installing on macOS..."
use_gitee=$(check_if_china_ip)
# 根据检测结果决定是否使用--gitee参数
if [ "$use_gitee" = true ]; then
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_macos.sh
chmod 777 install_macos.sh
echo "Installing on China gitee..."
./install_macos.sh --gitee
else
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_macos.sh
chmod 777 install_macos.sh
echo "Installing on no China..."
./install_macos.sh
fi
rm ./install_macos.sh
}
install_on_wsl() {
echo "Installing on Windows Subsystem for Linux (WSL)..."
}
install_on_windows() {
echo "Installing on Windows using PowerShell..."
use_gitee=$(check_if_china_ip)
# 根据检测结果决定是否使用--gitee参数
if [ "$use_gitee" = true ]; then
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_windows.ps1
echo "Installing on China gitee..."
./install_windows.ps1 --gitee
else
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_windows.ps1
echo "Installing on no China..."
./install_windows.ps1
fi
rm ./install_windows.ps1
}
install_on_opensuse() {
echo "Installing on openSUSE..."
use_gitee=$(check_if_china_ip)
if [ "$use_gitee" = true ]; then
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_suse.sh
chmod 777 install_suse.sh
echo "Installing on China gitee..."
./install_suse.sh --gitee
else
wget https://raw.githubusercontent.com/RT-Thread/env/master/install_suse.sh
chmod 777 install_suse.sh
echo "Installing on no China..."
./install_suse.sh
fi
rm ./install_suse.sh
}
# 主函数
main() {
detect_os
case "$OS" in
Linux)
case "$DISTRO" in
ubuntu|debian)
install_on_ubuntu
;;
fedora)
install_on_fedora
;;
centos|rhel)
install_on_centos
;;
arch)
install_on_arch
;;
*)
echo "Unsupported Linux distribution: $DISTRO"
exit 1
;;
esac
;;
macOS)
install_on_macos
;;
WSL)
install_on_wsl
;;
Windows)
install_on_windows
;;
*)
echo "Unsupported Operating System: $OS"
exit 1
;;
esac
echo "Installation completed!"
}
# 执行主函数
main

View File

@ -0,0 +1,338 @@
#
# Copyright (c) 2006-2024, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2024-07-25 supperthomas the first version
#
"""
这个脚本用来编译所有的bsp
这里的脚本是用的arm-none-eabi-gcc, 默认根据本机已经安装的gcc来编译
其他的工具链暂时不支持,其实主要根据运行的环境中支持不支持,目前只打算支持主流的
失败的bsp会存到failed_bsp.log里面
"""
import os
import sys
import shutil
import multiprocessing
from multiprocessing import Process
import yaml
#help说明
def usage():
print('%s all -- build all GCC bsp' % os.path.basename(sys.argv[0]))
print('%s clean -- clean all bsp' % os.path.basename(sys.argv[0]))
print('%s update -- update all prject files' % os.path.basename(sys.argv[0]))
def add_summary(text):
"""
add summary to github action.
"""
os.system(f'echo "{text}" >> $GITHUB_STEP_SUMMARY ;')
def run_cmd(cmd, output_info=True):
"""
这个函数用来执行命令
run command and return output and result.
"""
print('\033[1;32m' + cmd + '\033[0m ' + os.getcwd())
output_str_list = []
res = 0
if output_info:
res = os.system(cmd + " > output.txt 2>&1")
else:
res = os.system(cmd + " > /dev/null 2>output.txt")
try:
with open("output.txt", "r") as file:
output_str_list = file.readlines()
except FileNotFoundError:
with open("output.txt", "w") as file:
file.write("new file")
for line in output_str_list:
print(line, end='')
os.remove("output.txt")
return output_str_list, res
def build_bsp(bsp, scons_args=''):
"""
build bsp.
cd {rtt_root}
scons -C bsp/{bsp} --pyconfig-silent > /dev/null
cd {rtt_root}/bsp/{bsp}
pkgs --update > /dev/null
pkgs --list
cd {rtt_root}
scons -C bsp/{bsp} -j{nproc} {scons_args}
cd {rtt_root}/bsp/{bsp}
scons -c > /dev/null
rm -rf packages
"""
success = True
pwd = os.getcwd()
print('======pwd==='+ os.getcwd()+'===bsp:=='+bsp)
os.chdir(rtt_root)
#有Kconfig 说明可以执行menuconfig
if os.path.exists(f"{rtt_root}/bsp/{bsp}/Kconfig"):
os.chdir(rtt_root)
print('======pwd==='+ os.getcwd()+'===bsp:=='+bsp)
run_cmd(f'scons -C bsp/{bsp} --pyconfig-silent', output_info=True)
os.chdir(f'{rtt_root}/bsp/{bsp}')
print('======pwd222==='+ os.getcwd()+'===bsp:=='+bsp)
run_cmd('pkgs --update', output_info=True)
run_cmd('pkgs --list')
nproc = multiprocessing.cpu_count()
os.chdir(rtt_root)
cmd = f'scons -C bsp/{bsp} -j{nproc} {scons_args}'
result_log, res = run_cmd(cmd, output_info=True)
if res != 0:
# 将失败的bsp写入特定的txt文件
with open(os.path.join(rtt_root, 'failed_bsp_list.txt'), 'a') as file:
file.write(bsp + '\n')
# 打印失败的bsp的log把它放到对应的文件名文bsp的txt文件中
with open(os.path.join(rtt_root, 'failed_bsp.log'), 'a') as file:
file.write(f'===================={bsp}====================\n')
for line in result_log:
file.write(line)
print(f"::error::build {bsp} failed")
add_summary(f"- ❌ build {bsp} failed.")
success = False
else:
# 如果没有Kconfig直接执行scons
os.chdir(f'{rtt_root}/bsp/{bsp}')
run_cmd('scons', output_info=True)
# 删除packages文件夹
pkg_dir = os.path.join(rtt_root, 'bsp', bsp, 'packages')
shutil.rmtree(pkg_dir, ignore_errors=True)
#恢复到原目录
os.chdir(pwd)
return success
#判断参数是否是2个
if len(sys.argv) != 2:
usage()
sys.exit(0)
#更新MDK等文件
def update_project_file(project_dir):
if os.path.isfile(os.path.join(project_dir, 'template.Uv2')):
print('prepare MDK3 project file on ' + project_dir)
command = ' --target=mdk -s'
os.system('scons --directory=' + project_dir + command + ' > 1.txt')
if os.path.isfile(os.path.join(project_dir, 'template.uvproj')):
print('prepare MDK4 project file on ' + project_dir)
command = ' --target=mdk4 -s'
os.system('scons --directory=' + project_dir + command + ' > 1.txt')
if os.path.isfile(os.path.join(project_dir, 'template.uvprojx')):
print('prepare MDK5 project file on ' + project_dir)
command = ' --target=mdk5 -s'
os.system('scons --directory=' + project_dir + command + ' > 1.txt')
if os.path.isfile(os.path.join(project_dir, 'template.ewp')):
print('prepare IAR project file on ' + project_dir)
command = ' --target=iar -s'
os.system('scons --directory=' + project_dir + command + ' > 1.txt')
#更新所有可以scons的文件夹文件先执行menuconfig --silent 再执行scons --target=mdk5
#处理带有sconstruct的文件夹
def update_all_project_files(sconstruct_paths):
for projects in sconstruct_paths:
try:
# update rtconfig.h and .config
#执行menuconfig
if os.path.isfile(os.path.join(projects, 'Kconfig')):
if "win32" in sys.platform:
retval = os.getcwd()
os.chdir(projects)
os.system("menuconfig --silent")
os.chdir(retval)
else:
os.system('scons --pyconfig-silent -C {0}'.format(projects))
print('==menuconfig=======projects='+ projects)
else:
print('==no kconfig=in==!!!!!=projects='+ projects)
# update mdk, IAR etc file
update_project_file(projects)
except Exception as e:
print("error message: {}".format(e))
sys.exit(-1)
#找到带有Sconstruct的文件夹
def find_sconstruct_paths(project_dir, exclude_paths, include_paths):
sconstruct_paths = []
bsp_detail_path = os.path.join(rtt_root, 'tools', 'ci', 'bsp_detail.yml')
if os.path.exists(bsp_detail_path):
with open(bsp_detail_path, 'r') as file:
bsp_detail = yaml.safe_load(file)
for root, dirs, files in os.walk(project_dir):
if include_paths:
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):
if 'SConstruct' in files:
bsp_name = os.path.relpath(root, bsp_root)
if bsp_name in bsp_detail and bsp_detail[bsp_name].get('gcc') == 'arm-none-eabi-gcc':
sconstruct_paths.append(root)
else:
if all(exclude_path not in root for exclude_path in exclude_paths):
if 'SConstruct' in files:
bsp_name = os.path.relpath(root, bsp_root)
if bsp_name in bsp_detail and bsp_detail[bsp_name].get('gcc') == 'arm-none-eabi-gcc':
sconstruct_paths.append(root)
return sconstruct_paths
#检查EXE命令是否存在判断环境
def check_command_availability(cmd):
"""
Check if a command is available.
"""
cmd_path = shutil.which(cmd)
if cmd_path is not None:
#print(f"{cmd} command is available at {cmd_path}")
return True
else:
print(f"{cmd} command is not available")
return False
# Find the rt-thread root directory
rtt_root = os.getcwd()
while not os.path.exists(os.path.join(rtt_root, 'LICENSE')):
rtt_root = os.path.dirname(rtt_root)
bsp_root = os.path.join(rtt_root, 'bsp')
#需要排除的文件夹名字
exclude_paths = ['templates', 'doc', 'libraries', 'Libraries', 'template']
include_paths = []#['nrf5x','qemu-vexpress-a9', 'ESP32_C3','simulator']
sconstruct_paths = find_sconstruct_paths(bsp_root, exclude_paths,include_paths)
# get command options
command = ''
command_clean_flag = False
print(rtt_root)
if sys.argv[1] == 'all':
if os.path.exists(os.path.join(rtt_root, 'failed_bsp_list.txt')):
os.remove(os.path.join(rtt_root, 'failed_bsp_list.txt'))
if os.path.exists(os.path.join(rtt_root, 'failed_bsp.log')):
os.remove(os.path.join(rtt_root, 'failed_bsp.log'))
command = ' '
#更新所有的工程
print('begin to update all the bsp projects')
update_all_project_files(sconstruct_paths)
#iarbuild .\project.ewp -clean rt-thread
elif sys.argv[1] == 'clean':
command = ' -c'
command_clean_flag = True
print('begin to clean all the bsp projects')
# 执行所有其他IDE的 update 但是不编译,这个一般不会出错
elif sys.argv[1] == 'update':
print('begin to update all the bsp projects')
#更新所有的工程
update_all_project_files(sconstruct_paths)
print('finished!')
sys.exit(0)
else:
usage()
sys.exit(0)
if sconstruct_paths:
print("包含 'SConstruct' 文件的路径:")
for path in sconstruct_paths:
print(path)
else:
print("未找到包含 'SConstruct' 文件的路径")
#遍历所有的sconstruct_paths 路径中的文件夹
def bsp_scons_worker(project_dir):
print('=========project_dir===='+ project_dir)
#判断有没有SConstruct 文件,
if os.path.isfile(os.path.join(project_dir, 'SConstruct')):
print('==menuconfig=======rtt_root='+ rtt_root)
print('==project_dir=======project_dir='+ project_dir)
# 去掉 'bsp' 前面的三级目录
parts = project_dir.split(os.sep)
if 'bsp' in parts:
bsp_index = parts.index('bsp')
new_project_dir = os.sep.join(parts[bsp_index+1:])
else:
new_project_dir = project_dir
print('==project_dir=======new_project_dir='+ new_project_dir)
#开始编译bsp
build_bsp(new_project_dir)
# 发现有keil相关的执行keil相关的命令先检查一下UV4.exe命令有没有然后执行UV4.exe
if check_command_availability('UV4.exe') :
"""
UV4.exe -b project.uvprojx -q -j0 -t rt-thread -o action_runner.log
ls
sleep 10
cat action_runner.log
"""
if os.path.isfile(os.path.join(project_dir, 'template.uvprojx')):
if check_command_availability('UV4.exe'):
print('Start to build keil project======')
os.chdir(f'{project_dir}')
print('clean keil project======')
run_cmd('UV4.exe -c project.uvprojx -q')
___, res = run_cmd('UV4.exe -b project.uvprojx -q -j0 -t rt-thread -o keil.log')
os.chdir(f'{rtt_root}')
else:
print('UV4.exe is not available, please check your keil installation')
if check_command_availability('iarbuild.exe') :
"""
iarbuild .\project.ewp rt-thread
"""
if os.path.isfile(os.path.join(project_dir, 'template.ewp')):
if check_command_availability('iarbuild.exe'):
print('Start to build iar project======')
os.chdir(f'{project_dir}')
___, res = run_cmd('iarbuild .\project.ewp -clean rt-thread')
if res != 0:
print('run clean failed!!')
___, res = run_cmd('iarbuild .\project.ewp rt-thread > iar.log')
if res != 0:
print('run_cmd1 failed!!')
os.chdir(f'{rtt_root}')
else:
print('iarbuild is not available, please check your iar installation')
processes = []
for project_dir in sconstruct_paths:
bsp_scons_worker(project_dir)
#p = Process(target=bsp_scons_worker, args=(project_dir,))
#p.start()
#processes.append(p)
#for p in processes:
# p.join() # 等待所有进程完成
print('finished!')
# 将failed_bsp_list.txt的内容追加到failed_bsp.log文件中
if os.path.exists(os.path.join(rtt_root, 'failed_bsp_list.txt')):
with open(os.path.join(rtt_root, 'failed_bsp_list.txt'), 'r') as file:
failed_bsp_list = file.read()
if os.path.exists(os.path.join(rtt_root, 'failed_bsp.log')):
with open(os.path.join(rtt_root, 'failed_bsp.log'), 'a') as file:
file.write(failed_bsp_list)

View File

@ -0,0 +1,2 @@
pyyaml
pandas

View File

@ -0,0 +1,87 @@
#!/bin/bash
declare -A download_urls=(
["arm-none-eabi-gcc"]="https://github.com/RT-Thread/toolchains-ci/releases/download/v1.3/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2"
["mips-sde-elf-gcc"]="https://github.com/RT-Thread/toolchains-ci/releases/download/v1.1/mips-2016.05-7-mips-sde-elf-i686-pc-linux-gnu.tar.bz2"
["aarch64-none-elf-gcc"]="https://github.com/RT-Thread/toolchains-ci/releases/download/v1.6/gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf.tar.xz"
["riscv64-unknown-elf-gcc"]="https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz"
["riscv32-unknown-elf-gcc"]="https://github.com/hpmicro/riscv-gnu-toolchain/releases/download/2022.05.15/riscv32-unknown-elf-newlib-multilib_2022.05.15_linux.tar.gz"
["riscv-none-embed-gcc"]="https://github.com/RT-Thread/toolchains-ci/releases/download/v1.5/xpack-riscv-none-embed-gcc-8.3.0-2.3-linux-x64.tar.gz"
["riscv32-esp-elf-gcc"]="https://github.com/espressif/crosstool-NG/releases/download/esp-2022r1-RC1/riscv32-esp-elf-gcc11_2_0-esp-2022r1-RC1-linux-amd64.tar.xz"
["clang"]="https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-16.0.0/LLVMEmbeddedToolchainForArm-16.0.0-Linux-x86_64.tar.gz"
)
show_help() {
echo "Available toolchains:"
for key in "${!download_urls[@]}"; do
echo " - $key"
done
}
extract_file() {
local file_name=$1
local destination=$2
echo "Extracting $file_name to $destination..."
case "$file_name" in
*.tar.bz2) tar -xjf "$file_name" -C "$destination" --strip-components=1;;
*.tar.gz) tar -xzf "$file_name" -C "$destination" --strip-components=1;;
*.tar.xz) tar -xJf "$file_name" -C "$destination" --strip-components=1;;
*) echo "Unsupported file format: $file_name"; exit 1;;
esac
echo "Extracted to $destination"
}
install_toolchain() {
local toolchain_name=$1
local url="${download_urls[$toolchain_name]}"
if [ -z "$url" ]; then
echo "Toolchain not found."
exit 1
fi
local file_name=$(basename "$url")
echo "Downloading $file_name..."
wget -q "$url"
echo "Extracting $file_name..."
local target_dir="/opt/$toolchain_name"
echo "target_dir $target_dir..."
mkdir -p "$target_dir"
extract_file "$file_name" "$target_dir"
rm $file_name
echo "Installed: $target_dir"
local toolchain_bin="$target_dir/bin"
if [[ ":$PATH:" != *":$toolchain_bin:"* ]]; then
echo "Adding $toolchain_bin to PATH..."
export PATH="$PATH:$toolchain_bin"
echo "export PATH=\$PATH:$toolchain_bin" >> ~/.bashrc
fi
#need source toolchain.sh or source ~/.bashrc to work
#local toolchain_exec=$(find "$toolchain_bin" -type f -executable | head -n 1)
local toolchain_exec=$(which "$toolchain_name")
if [ -x "$toolchain_exec" ]; then
echo "Testing executable: $toolchain_exec"
$toolchain_exec --version
else
echo "No executable found in $toolchain_bin"
fi
}
if [[ $# -eq 1 ]]; then
if [[ "$1" == "help" ]]; then
show_help
elif [ "$1" == "all" ]; then
for toolchain in "${!download_urls[@]}"; do
install_toolchain "$toolchain"
done
else
install_toolchain "$1"
fi
else
show_help
fi

View File

@ -0,0 +1,428 @@
#
# Copyright (c) 2024, RT-Thread Development Team
#
# SPDX-License-Identifier: Apache-2.0
#
# Change Logs:
# Date Author Notes
# 2024-08-25 supperthomas the first version
#
aarch64-none-elf-gcc:
bsp:
- cvitek/cv18xx_aarch64
- phytium/aarch64
- qemu-virt64-aarch64
- raspberry-pi/raspi3-64
- raspberry-pi/raspi4-64
- rockchip/rk3568
- zynqmp-a53-dfzu2eg
count: 7
arc-elf32-gcc:
bsp:
- synopsys/boards
count: 1
arm-none-eabi-gcc:
bsp:
- CME_M7
- Infineon/psoc6-cy8ckit-062-BLE
- Infineon/psoc6-cy8ckit-062-WIFI-BT
- Infineon/psoc6-cy8ckit-062S2-43012
- Infineon/psoc6-cy8ckit-062s4
- Infineon/psoc6-cy8cproto-062S3-4343W
- Infineon/psoc6-evaluationkit-062S2
- Infineon/xmc7200-kit_xmc7200_evk
- Vango/v85xx
- Vango/v85xxp
- acm32/acm32f0x0-nucleo
- acm32/acm32f4xx-nucleo
- airm2m/air105
- airm2m/air32f103
- allwinner_tina
- amebaz
- apm32/apm32e103ze-evalboard
- apm32/apm32e103ze-tinyboard
- apm32/apm32f030r8-miniboard
- apm32/apm32f051r8-evalboard
- apm32/apm32f072vb-miniboard
- apm32/apm32f091vc-miniboard
- apm32/apm32f103vb-miniboard
- apm32/apm32f103xe-minibroard
- apm32/apm32f107vc-evalboard
- apm32/apm32f407ig-minibroard
- apm32/apm32f407zg-evalboard
- apm32/apm32s103vb-miniboard
- apollo2
- asm9260t
- at32/at32a403a-start
- at32/at32a423-start
- at32/at32f402-start
- at32/at32f403a-start
- at32/at32f405-start
- at32/at32f407-start
- at32/at32f413-start
- at32/at32f415-start
- at32/at32f421-start
- at32/at32f423-start
- at32/at32f425-start
- at32/at32f435-start
- at32/at32f437-start
- at91/at91sam9260
- at91/at91sam9g45
- beaglebone
- dm365
- efm32
- essemi/es32f0654
- essemi/es32f365x
- essemi/es32f369x
- fm33lc026
- frdm-k64f
- ft2004
- ft32/ft32f072xb-starter
- fujitsu/mb9x/mb9bf500r
- fujitsu/mb9x/mb9bf506r
- fujitsu/mb9x/mb9bf568r
- fujitsu/mb9x/mb9bf618s
- gd32/arm/gd32103c-eval
- gd32/arm/gd32105c-eval
- gd32/arm/gd32105r-start
- gd32/arm/gd32107c-eval
- gd32/arm/gd32205r-start
- gd32/arm/gd32207i-eval
- gd32/arm/gd32303c-start
- gd32/arm/gd32303e-eval
- gd32/arm/gd32305r-start
- gd32/arm/gd32307e-start
- gd32/arm/gd32407v-lckfb
- gd32/arm/gd32407v-start
- gd32/arm/gd32450z-eval
- gd32/arm/gd32470z-lckfb
- gd32/arm/gd32h759i-start
- hc32/ev_hc32f448_lqfp80
- hc32/ev_hc32f460_lqfp100_v2
- hc32/ev_hc32f472_lqfp100
- hc32/ev_hc32f4a0_lqfp176
- hc32/lckfb-hc32f4a0-lqfp100
- hc32l136
- hc32l196
- hk32/hk32f030c8-mini
- ht32/ht32f12366
- ht32/ht32f52352
- lm3s8962
- lm3s9b9x
- lm4f232
- maxim/max32660-evsys
- microchip/samc21
- microchip/samd51-adafruit-metro-m4
- microchip/samd51-seeed-wio-terminal
- microchip/same54
- microchip/same70
- microchip/saml10
- mini2440
- mini4020
- mm32/mm32f3270-100ask-pitaya
- mm32f103x
- mm32f327x
- mm32l07x
- mm32l3xx
- msp432e401y-LaunchPad
- n32/n32g43xcl-stb
- n32/n32g457qel-stb
- n32/n32g45xcl-stb
- n32/n32g45xml-stb
- n32/n32g45xrl-stb
- n32/n32g45xvl-stb
- n32/n32g4frml-stb
- n32/n32l40xcl-stb
- n32/n32l436-evb
- n32/n32l43xml-stb
- n32/n32l43xrl-stb
- n32/n32wb45xl-evb
- n32g452xx/n32g452xx-mini-system
- nrf5x/nrf51822
- nrf5x/nrf52832
- nrf5x/nrf52833
- nrf5x/nrf52840
- nrf5x/nrf5340
- nuvoton/ma35-rtp
- nuvoton/nk-980iot
- nuvoton/nk-n9h30
- nuvoton/nk-rtu980
- nuvoton/numaker-hmi-ma35d1
- nuvoton/numaker-iot-m467
- nuvoton/numaker-iot-m487
- nuvoton/numaker-iot-ma35d1
- nuvoton/numaker-m032ki
- nuvoton/numaker-m2354
- nuvoton/numaker-m467hj
- nuvoton/numaker-pfm-m487
- nv32f100x
- nxp/imx/imx6sx/cortex-a9
- nxp/imx/imx6ul
- nxp/imx/imx6ull-smart
- nxp/imx/imxrt/imxrt1021-nxp-evk
- nxp/imx/imxrt/imxrt1052-atk-commander
- nxp/imx/imxrt/imxrt1052-fire-pro
- nxp/imx/imxrt/imxrt1052-nxp-evk
- nxp/imx/imxrt/imxrt1052-seeed-ArchMix
- nxp/imx/imxrt/imxrt1060-nxp-evk
- nxp/imx/imxrt/imxrt1061-forlinx-OK1061-S
- nxp/imx/imxrt/imxrt1064-nxp-evk
- nxp/imx/imxrt/imxrt1170-nxp-evk/m7
- nxp/lpc/lpc1114
- nxp/lpc/lpc176x
- nxp/lpc/lpc178x
- nxp/lpc/lpc2148
- nxp/lpc/lpc2478
- nxp/lpc/lpc408x
- nxp/lpc/lpc43xx/M0
- nxp/lpc/lpc43xx/M4
- nxp/lpc/lpc5410x
- nxp/lpc/lpc54114-lite
- nxp/lpc/lpc54608-LPCXpresso
- nxp/lpc/lpc55sxx/Libraries/template/lpc55s6xxxx
- nxp/lpc/lpc55sxx/lpc55s06_nxp_evk
- nxp/lpc/lpc55sxx/lpc55s16_nxp_evk
- nxp/lpc/lpc55sxx/lpc55s28_nxp_evk
- nxp/lpc/lpc55sxx/lpc55s36_nxp_evk
- nxp/lpc/lpc55sxx/lpc55s69_nxp_evk
- nxp/lpc/lpc824
- nxp/mcx/mcxa/frdm-mcxa153
- nxp/mcx/mcxn/frdm-mcxn236
- nxp/mcx/mcxn/frdm-mcxn947
- phytium/aarch32
- qemu-vexpress-a9
- raspberry-pi/raspi2
- raspberry-pi/raspi3-32
- raspberry-pi/raspi4-32
- raspberry-pico
- renesas/ebf_qi_min_6m5
- renesas/libraries/bsp-template
- renesas/ra2l1-cpk
- renesas/ra4m2-eco
- renesas/ra6m3-ek
- renesas/ra6m3-hmi-board
- renesas/ra6m4-cpk
- renesas/ra6m4-iot
- renesas/ra8d1-ek
- renesas/ra8d1-vision-board
- renesas/ra8m1-ek
- renesas/rzt2m_rsk
- rm48x50
- rockchip/rk2108
- sam7x
- samd21
- smartfusion2
- stm32/stm32f072-st-nucleo
- stm32/stm32f091-st-nucleo
- stm32/stm32f103-100ask-mini
- stm32/stm32f103-100ask-pro
- stm32/stm32f103-atk-nano
- stm32/stm32f103-atk-warshipv3
- stm32/stm32f103-blue-pill
- stm32/stm32f103-dofly-M3S
- stm32/stm32f103-dofly-lyc8
- stm32/stm32f103-fire-arbitrary
- stm32/stm32f103-gizwits-gokitv21
- stm32/stm32f103-hw100k-ibox
- stm32/stm32f103-onenet-nbiot
- stm32/stm32f103-yf-ufun
- stm32/stm32f103-ys-f1pro
- stm32/stm32f107-uc-eval
- stm32/stm32f207-st-nucleo
- stm32/stm32f302-st-nucleo
- stm32/stm32f334-st-nucleo
- stm32/stm32f401-st-nucleo
- stm32/stm32f401-weact-blackpill
- stm32/stm32f405-smdz-breadfruit
- stm32/stm32f405zg-mini-template
- stm32/stm32f407-armfly-v5
- stm32/stm32f407-atk-explorer
- stm32/stm32f407-fk407m2-zgt6
- stm32/stm32f407-lckfb-skystar
- stm32/stm32f407-robomaster-c
- stm32/stm32f407-rt-spark
- stm32/stm32f407-st-discovery
- stm32/stm32f410-st-nucleo
- stm32/stm32f411-atk-nano
- stm32/stm32f411-st-nucleo
- stm32/stm32f411-weact-blackpill
- stm32/stm32f412-st-nucleo
- stm32/stm32f413-st-nucleo
- stm32/stm32f427-robomaster-a
- stm32/stm32f429-armfly-v6
- stm32/stm32f429-atk-apollo
- stm32/stm32f429-fire-challenger
- stm32/stm32f429-st-disco
- stm32/stm32f446-st-nucleo
- stm32/stm32f469-st-disco
- stm32/stm32f723-st-disco
- stm32/stm32f746-st-disco
- stm32/stm32f746-st-nucleo
- stm32/stm32f767-atk-apollo
- stm32/stm32f767-fire-challenger-v1
- stm32/stm32f767-st-nucleo
- stm32/stm32f769-st-disco
- stm32/stm32g070-st-nucleo
- stm32/stm32g071-st-nucleo
- stm32/stm32g431-st-nucleo
- stm32/stm32g474-st-nucleo
- stm32/stm32g491-st-nucleo
- stm32/stm32h503-st-nucleo
- stm32/stm32h563-st-nucleo
- stm32/stm32h743-armfly-v7
- stm32/stm32h743-atk-apollo
- stm32/stm32h743-openmv-h7plus
- stm32/stm32h743-st-nucleo
- stm32/stm32h747-st-discovery
- stm32/stm32h750-armfly-h7-tool
- stm32/stm32h750-artpi
- stm32/stm32h750-fk750m1-vbt6
- stm32/stm32h750-weact-ministm32h7xx
- stm32/stm32h7s7-st-disco
- stm32/stm32l010-st-nucleo
- stm32/stm32l053-st-nucleo
- stm32/stm32l412-st-nucleo
- stm32/stm32l431-BearPi
- stm32/stm32l431-tencentos-tiny-EVB_MX+
- stm32/stm32l432-st-nucleo
- stm32/stm32l433-ali-startkit
- stm32/stm32l433-st-nucleo
- stm32/stm32l452-st-nucleo
- stm32/stm32l475-atk-pandora
- stm32/stm32l475-st-discovery
- stm32/stm32l476-st-nucleo
- stm32/stm32l496-ali-developer
- stm32/stm32l496-st-discovery
- stm32/stm32l496-st-nucleo
- stm32/stm32l4r5-st-nucleo
- stm32/stm32l4r9-st-eval
- stm32/stm32l4r9-st-sensortile-box
- stm32/stm32l552-st-nucleo
- stm32/stm32mp157a-st-discovery
- stm32/stm32mp157a-st-ev1
- stm32/stm32u575-st-nucleo
- stm32/stm32u585-iot02a
- stm32/stm32wb55-st-nucleo
- stm32/stm32wl55-st-nucleo
- stm32/stm32wle5-yizhilian-lm401
- stm32/stm32wle5-yizhilian-lm402
- synwit/swm320-mini
- synwit/swm341-mini
- ti/c28x/tms320f28379d
- tkm32F499
- tm4c123bsp
- tm4c129x
- w60x
- wch/arm/ch32f103c8-core
- wch/arm/ch32f203r-evt
- wch/arm/ch579m
- xplorer4330/M0
- xplorer4330/M4
- yichip/yc3121-pos
- yichip/yc3122-pos
- zynqmp-r5-axu4ev
count: 298
download_url: https://github.com/RT-Thread/toolchains-ci/releases/download/v1.3/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
avr32-gcc:
bsp:
- avr32/at32uc3a0256
- avr32/at32uc3b0256
count: 2
csky-abiv2-elf-gcc:
bsp:
- ck802
- essemi/es32vf2264
count: 2
gcc:
bsp:
- simulator
count: 1
i386-unknown-elf-gcc:
bsp:
- x86
count: 1
m32c-elf-gcc:
bsp:
- m16c62p
- upd70f3454
count: 2
mips-sde-elf-gcc:
bsp:
- loongson/ls1bdev
- loongson/ls1cdev
- loongson/ls2kdev
- mipssim
count: 4
download_url: https://github.com/RT-Thread/toolchains-ci/releases/download/v1.6/gcc-arm-10.2-2020.11-x86_64-aarch64-none-elf.tar.xz
powerpc-eabi-gcc:
bsp:
- taihu
count: 1
riscv-none-embed-gcc:
bsp:
- gd32/risc-v/gd32vf103r-start
- gd32/risc-v/gd32vf103v-eval
- hifive1
- juicevm
- k210
- rv32m1_vega/ri5cy
- wch/risc-v/ch32v103r-evt
- wch/risc-v/ch32v208w-r0
- wch/risc-v/ch32v307v-r1
- wch/risc-v/ch569w-evt
- wch/risc-v/yd-ch32v307vct6
count: 11
download_url: https://github.com/RT-Thread/toolchains-ci/releases/download/v1.5/xpack-riscv-none-embed-gcc-8.3.0-2.3-linux-x64.tar.gz
riscv-nuclei-elf-gcc:
bsp:
- nuclei/gd32vf103_rvstar
- nuclei/nuclei_fpga_eval
count: 2
riscv32-esp-elf-gcc:
bsp:
- ESP32_C3
count: 1
download_url: https://github.com/espressif/crosstool-NG/releases/download/esp-2022r1-RC1/riscv32-esp-elf-gcc11_2_0-esp-2022r1-RC1-linux-amd64.tar.xz
riscv32-unknown-elf-gcc:
bsp:
- core-v-mcu/core-v-cv32e40p
- hpmicro/hpm5300evk
- hpmicro/hpm5301evklite
- hpmicro/hpm6200evk
- hpmicro/hpm6300evk
- hpmicro/hpm6750evk
- hpmicro/hpm6750evk2
- hpmicro/hpm6750evkmini
- hpmicro/hpm6800evk
count: 9
download_url: https://github.com/hpmicro/riscv-gnu-toolchain/releases/download/2022.05.15/riscv32-unknown-elf-newlib-multilib_2022.05.15_linux.tar.gz
riscv64-unknown-elf-gcc:
bsp:
- bluetrum/ab32vg1-ab-prougen
- bouffalo_lab/bl60x
- bouffalo_lab/bl61x
- bouffalo_lab/bl70x
- bouffalo_lab/bl808/lp
- bouffalo_lab/bl808/m0
- cvitek/c906_little
- cvitek/cv18xx_risc-v
- qemu-virt64-riscv
- sparkfun-redv
- thead-smart
count: 11
download_url: https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz
riscv64-unknown-linux-musl-gcc:
bsp:
- allwinner/d1
- allwinner/d1s
- bouffalo_lab/bl808/d0
count: 3
sparc-gaisler-elf-gcc:
bsp:
- bm3803
count: 1
unicore32-linux-gcc:
bsp:
- sep6200
count: 1