RT-Thread中的Github Actions介绍

电子说

1.2w人已加入

描述

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

Controls when the action will run. Triggers the workflow on push or pull request

events but only for the master branch

on:

Runs at 16:00 UTC (BeiJing 00:00) on the 1st of every month

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()

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)

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()

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()

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分