电子说
rt-thread中一共有五个Github Action(rt-thread/.github/workflow),分别是:
RT-Thread BSP build check(.github/workflows/action.yml)
ToolsCI(.github/workflows/action_tools.yml)
AutoTestCI(.github/workflows/action_utest.yml)
Check File Format and License(.github/workflows/file_check.yml)
Static code analysis(.github/workflows/static_code_analysis.yml)
下面分别讲解这五个Github Action。
RT-Thread BSP build check
总的来说,这个Action会通过matrix尝试编译多个BSP,并记录编译成功和失败的信息。执行脚本中的每个BSP编译步骤都在日志中创建了一个分组,以便在编译成功或失败时可以更好地显示和记录相关信息。
matrix列表中每个元素有三个属性,分别是
RTT_BSP:这组BSP的名字,后续输出日志时会用到
RTT_TOOL_CHAIN:编译这组BSP使用的工具链
SUB_RTT_BSP:各个BSP的目录
RT-Thread BSP build check的第一步:
首先先安装gcc和menuconfig依赖的包
调用tools目录下的menuconfig的touch_env
这个函数主要是创建一系列后续会使用到的文件夹,并且拉取远程的packages
以及修改Kconfig
设置一些RT-Thread自己的环境遍历,供后续使用
name: Install Tools
shell: bash
run: |
sudo apt-get update
sudo apt-get -qq install gcc-multilib libncurses5 libncurses5-dev libncursesw5-dev scons
sudo python -m pip install --upgrade pip -qq
pip install requests -qq
git config --global http.postBuffer 524288000
python -c "import tools.menuconfig; tools.menuconfig.touch_env()"
echo "RTT_ROOT=**{{ github.workspace }}" >> **GITHUB_ENV
echo "RTT_CC=gcc" >> $GITHUB_ENV
RT-Thread BSP build check的第二步:
会根据matrix.legs.RTT_TOOL_CHAIN判断需要安装什么工具链
RT-Thread BSP build check的第三步:
第三步是这次BSP编译测试的核心
首先会遍历所有的SUB_RTT_BSP
根据scons命令执行的成功与否(||前一个命令执行失败、&&前一个命令执行成功)来判断执行成功还是失败的逻辑
输出的时候会在GitHub Actions日志中创建一个分组,用于显示BSP编译的信息
计算总共花费的时间,输出BSP编译成功或者失败的信息,输出至$GITHUB_STEP_SUMMARY
name: Bsp Scons Compile
if: ${{ success() }}
shell: bash
env:
RTT_BSP: ${{ matrix.legs.RTT_BSP }}
RTT_TOOL_CHAIN: ${{ matrix.legs.RTT_TOOL_CHAIN }}
SRTT_BSP: ${{ join(matrix.legs.SUB_RTT_BSP, ',') }}
run: |
source ~/.env/env.sh
failed=0
count=0
for bsp in **(echo **SRTT_BSP | tr ',' '\\n'); do
count=$((count+1))
echo "::group::Compiling BSP: ==**count=== **bsp ===="
echo bsp/$bsp
pushd bsp/$bsp && pkgs --update && popd
scons -C bsp/bsp -j(nproc) --debug=time | tee output.log ||
{ total_time=$(grep "Total command execution time" output.log | awk '{print $5}');
failed=**((failed+1)) ; echo "::endgroup::" ; echo "::error::build **bsp failed" ;
echo "- build **bsp failed in **total_time seconds " >> $GITHUB_STEP_SUMMARY ; } &&
{ total_time=$(grep "Total command execution time" output.log | awk '{print $5}');
echo "- build **bsp success in **total_time seconds " >> $GITHUB_STEP_SUMMARY ; echo "::endgroup::" ; }
done
exit $failed
ToolsCI
总的来说,ToolsCI这个Action比较简单,会去尝试Build一个BSP、生成其它工程文件等等,用来测试使用。
关于scons一些参数的使用可以参考:SCons (rt-thread.org)
name: ToolsCI
on:
schedule:
cron: '0 16 1 * *'
push:
branches:
master
paths-ignore:
documentation/**
'**/README.md'
'**/README_zh.md'
'**/*.c'
'**/*.h'
'**/*.cpp'
pull_request:
branches:
master
paths-ignore:
documentation/**
'**/README.md'
'**/README_zh.md'
'**/*.c'
'**/*.h'
'**/*.cpp'
permissions:
contents: read # to fetch code (actions/checkout)
jobs:
test:
runs-on: ubuntu-latest
name: Tools
strategy:
fail-fast: false
env:
TEST_BSP_ROOT: bsp/stm32/stm32f407-atk-explorer
steps:
uses: actions/checkout@v3
name: Install Tools
shell: bash
run: |
sudo apt-get update
sudo apt-get -yqq install scons
name: Install Arm ToolChains
if: ${{ success() }}
shell: bash
run: |
wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.3/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
sudo tar xjf gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2 -C /opt
/opt/gcc-arm-none-eabi-10-2020-q4-major/bin/arm-none-eabi-gcc --version
echo "RTT_EXEC_PATH=/opt/gcc-arm-none-eabi-10-2020-q4-major/bin" >> $GITHUB_ENV
name: Build Tools
run: |
scons --pyconfig-silent -C $TEST_BSP_ROOT
scons -j**(nproc) -C **TEST_BSP_ROOT
name: Project generate Tools
if: ${{ success() }}
run: |
echo "Test to generate eclipse project"
scons --target=eclipse -s -C $TEST_BSP_ROOT
echo "Test to generate cmake project"
scons --target=cmake -s -C $TEST_BSP_ROOT
echo "Test to generate makefile project"
scons --target=makefile -s -C $TEST_BSP_ROOT
name: Project dist Tools
if: ${{ success() }}
run: |
echo "Test to dist project"
scons --dist -C $TEST_BSP_ROOT
scons --dist-ide -C $TEST_BSP_ROOT
ls $TEST_BSP_ROOT
ls $TEST_BSP_ROOT/dist
scons -C $TEST_BSP_ROOT/dist/project
scons -C $TEST_BSP_ROOT/rt-studio-project
AutoTestCI
总的来说,这个Action的主要目标是根据matrix中的不同参数组合,安装必要的工具链、构建和测试代码。具体的步骤包括检出代码、安装所需工具、设置环境变量、构建代码,然后在qemu中运行测试并输出日志。根据参数的不同,这个工作流可以自动处理多个平台和架构的测试。
matrix列表中每个元素有五个属性,分别是
UTEST:这组BSP的名字,后续输出日志时会用到
RTT_BSP:测试使用的BSP
QEMU_ARCH:QEMU使用的平台架构
QEMU_MACHINE:选择QEMU的板级支持包
CONFIG_FILE:RT-Thread条件编译使用的CONFIG
SD_FILE:使用的sd.bin
RUN:是否启动
AutoTestCI的第一步:
安装必要的工具:scons、qemu、git
AutoTestCI的第二步:
根据matrix的属性选择安装相应的编译工具链
比如要测试arm架构和rtsmart/arm时,我们就选择安装arm-linux-musleabi_for_x86_64-pc-linux-g工具链
name: Install Arm Musl ToolChains
if: ${{ matrix.legs.QEMU_ARCH == 'arm' && matrix.legs.UTEST == 'rtsmart/arm' && success() }}
shell: bash
run: |
wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/arm-linux-musleabi_for_x86_64-pc-linux-g
sudo tar xjf arm-linux-musleabi_for_x86_64-pc-linux-gnu_stable.tar.bz2 -C /opt
/opt/arm-linux-musleabi_for_x86_64-pc-linux-gnu/bin/arm-linux-musleabi-gcc --version
echo "RTT_EXEC_PATH=/opt/arm-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV
echo "RTT_CC_PREFIX=arm-linux-musleabi-" >> $GITHUB_ENV
AutoTestCI的第三步:
第三步主要完成$TEST_BSP_ROOT下BSP的编译,以供后续在qemu上使用
name: Build BSP
run: |
echo CONFIG_RT_USING_UTESTCASES=y >> $TEST_BSP_ROOT/.config
cat examples/utest/configs/**TEST_CONFIG_FILE >> **TEST_BSP_ROOT/.config
scons --pyconfig-silent -C $TEST_BSP_ROOT
scons -j**(nproc) --strict -C **TEST_BSP_ROOT
AutoTestCI的第四步:
第四步也是最重要的一步,开始测试相关用例
拉取RT-Thread自动化测试机器人
制作sd.bin
使用需要测试的BSP启动qemu
最后输出相关日志
name: Start run Test
if: ${{matrix.legs.RUN == 'yes' && success() }}
run: |
git clone https://github.com/armink/UtestRunner.git
pushd $TEST_BSP_ROOT
dd if=/dev/zero of=sd.bin bs=1024 count=65536
popd
pushd UtestRunner
if [ $TEST_SD_FILE != "None" ]; then
python3 qemu_runner.py --system **TEST_QEMU_ARCH --machine **TEST_QEMU_MACHINE --elf ../$TEST_BSP_ROOT/rtthread
else
python3 qemu_runner.py --system **TEST_QEMU_ARCH --machine **TEST_QEMU_MACHINE --elf ../$TEST_BSP_ROOT/rtthread
fi
cat rtt_console.log
popd
Check File Format and License
这个Action主要是用来检查文件的格式化和版权信息的,主要工作都由tools/ci/file_check.py完成,在此之前主要先检出当前仓库的代码和安装Python脚本依赖的包。
name: Check File Format and License
on: [pull_request]
jobs:
scancode_job:
runs-on: ubuntu-latest
name: Scan code format and license
steps:
uses: actions/checkout@v3
name: Set up Python
uses: actions/setup-python@v3
with:
python-version: 3.8
name: Check Format and License
shell: bash
run: |
pip install click chardet PyYaml
python tools/ci/file_check.py check 'https://github.com/RT-Thread/rt-thread' 'master'
file_check.py
我们可以先忽略使用的click命令行库,或者也可以从命名和使用方式猜测出它们的功能。
因为这个文件比较简单,所以我们可以猜测函数的入口就是check()。主函数里的逻辑是十分简单的,可以看到通过checkout.get_new_file()获得了一个文件列表,然后传递给了FormatCheck和LicenseCheck,它们又分别调用了自身的check函数,最后根据它们返回值判断是否检查出错误。
def check(check_license, repo, branch):
"""
check files license and format.
"""
init_logger()
checkout = CheckOut(repo, branch)
file_list = checkout.get_new_file()
if file_list is None:
logging.error("checkout files fail")
sys.exit(1)
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)
checkout.get_new_file()获得的文件列表是需要检查的文件列表,而FormatCheck和LicenseCheck执行各自的检查逻辑。
首先是get_new_file,具体的逻辑也比较简单:
通过git命令获得新增和修改的文件列表
然后遍历这个文件列表
遍历这个文件列表中的文件路径的每一层目录,看是否存在.ignore_format.yml文件
然后根据.ignore_format.yml的属性来判断当前文件是否需要被检查
而FormatCheck主要完成的工作是:
搜索所有.c和.h文件
然后检查行首、行尾以及tab
而LicenseCheck的逻辑也比较简单,主要就是判断当前的Copyright的年份是否正确。
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.".fo
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
Static code analysis
这个Action和Check File Format and License是很类似的,主要流程都是相同的。
最重要的就是利用cppcheck完成静态代码检查的功能:
从文件列表中再一次过滤出C/C++相关文件
然后使用cppcheck逐个检查文件列表,并且捕获标准错误流
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:
result = subprocess.run(['cppcheck', '--enable=warning', 'performance', 'portability', '--inline-suppr', '--error-exitcode=1', '--force', file], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
logging.info(result.stdout.decode())
logging.info(result.stderr.decode())
if result.stderr:
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()
checkout = format_ignore.CheckOut()
file_list = checkout.get_new_file()
if file_list is None:
logging.error("checkout files fail")
sys.exit(1)
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()
全部0条评论
快来发表一下你的评论吧 !