原始版本
This commit is contained in:
256
RT_Thread/tools/ci/bsp_buildings.py
Normal file
256
RT_Thread/tools/ci/bsp_buildings.py
Normal 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)
|
||||
206
RT_Thread/tools/ci/bsp_detail.py
Normal file
206
RT_Thread/tools/ci/bsp_detail.py
Normal 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)
|
||||
2606
RT_Thread/tools/ci/bsp_detail.yml
Normal file
2606
RT_Thread/tools/ci/bsp_detail.yml
Normal file
File diff suppressed because it is too large
Load Diff
98
RT_Thread/tools/ci/compile_bsp_with_drivers.py
Normal file
98
RT_Thread/tools/ci/compile_bsp_with_drivers.py
Normal 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)
|
||||
118
RT_Thread/tools/ci/cpp_check.py
Normal file
118
RT_Thread/tools/ci/cpp_check.py
Normal 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()
|
||||
292
RT_Thread/tools/ci/file_check.py
Normal file
292
RT_Thread/tools/ci/file_check.py
Normal 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()
|
||||
84
RT_Thread/tools/ci/format_ignore.py
Normal file
84
RT_Thread/tools/ci/format_ignore.py
Normal 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
|
||||
219
RT_Thread/tools/ci/install.sh
Normal file
219
RT_Thread/tools/ci/install.sh
Normal 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
|
||||
338
RT_Thread/tools/ci/manual_bsp_build_all.py
Normal file
338
RT_Thread/tools/ci/manual_bsp_build_all.py
Normal 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)
|
||||
2
RT_Thread/tools/ci/requirements.txt
Normal file
2
RT_Thread/tools/ci/requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
pyyaml
|
||||
pandas
|
||||
87
RT_Thread/tools/ci/toolchain.sh
Normal file
87
RT_Thread/tools/ci/toolchain.sh
Normal 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
|
||||
428
RT_Thread/tools/ci/toolchain_bsp.yml
Normal file
428
RT_Thread/tools/ci/toolchain_bsp.yml
Normal 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
|
||||
Reference in New Issue
Block a user