当前位置: 首页 > news >正文

Rockchip RK3588 - Rockchip Linux SDK脚本分析

----------------------------------------------------------------------------------------------------------------------------

开发板 :ArmSoM-Sige7开发板eMMC64GBLPDDR48GB
显示屏 :15.6英寸HDMI接口显示屏u-boot2017.09linux5.10
----------------------------------------------------------------------------------------------------------------------------

在《Rockchip RK3588 - Rockchip Linux SDK编译》我们SDK的编译流程以及固件升级相关的内容,本节将会对编译脚本进行深入的分析。

一、build.sh分析

Rockchip Linux SDK编译命令是由build.sh脚本实现的,其入口函数为main函数。

1.1 main函数

由于main函数代码内容较长,我们只对重点内容进行分析:

点击查看代码
main()
{[ -z "$DEBUG" ] || set -xtrap 'err_handler' ERRset -eE# Save intial envionmentsunset INITIAL_SESSIONINITIAL_ENV=$(mktemp -u)if [ -z "$RK_SESSION" ]; thenINITIAL_SESSION=1env > "$INITIAL_ENV"fiexport LC_ALL=Cexport SCRIPTS_DIR="$(dirname "$(realpath "$BASH_SOURCE")")"export COMMON_DIR="$(realpath "$SCRIPTS_DIR/..")"export SDK_DIR="$(realpath "$COMMON_DIR/../../..")"export DEVICE_DIR="$SDK_DIR/device/rockchip"export CHIPS_DIR="$DEVICE_DIR/.chips"export CHIP_DIR="$DEVICE_DIR/.chip"export RK_DATA_DIR="$COMMON_DIR/data"export RK_TOOL_DIR="$COMMON_DIR/tools"export RK_IMAGE_DIR="$COMMON_DIR/images"export RK_KBUILD_DIR="$COMMON_DIR/linux-kbuild"export RK_CONFIG_IN="$COMMON_DIR/configs/Config.in"export RK_BUILD_HOOK_DIR="$COMMON_DIR/build-hooks"export BUILD_HELPER="$RK_BUILD_HOOK_DIR/build-helper"export RK_POST_HOOK_DIR="$COMMON_DIR/post-hooks"export POST_HELPER="$RK_POST_HOOK_DIR/post-helper"export PARTITION_HELPER="$SCRIPTS_DIR/partition-helper"export RK_SESSION="${RK_SESSION:-$(date +%F_%H-%M-%S)}"export RK_OUTDIR="$SDK_DIR/output"export RK_SESSION_DIR="$RK_OUTDIR/sessions"export RK_LOG_BASE_DIR="$RK_OUTDIR/log"export RK_LOG_DIR="$RK_SESSION_DIR/$RK_SESSION"export RK_INITIAL_ENV="$RK_LOG_DIR/initial.env"export RK_CUSTOM_ENV="$RK_LOG_DIR/custom.env"export RK_FINAL_ENV="$RK_LOG_DIR/final.env"export RK_ROCKDEV_DIR="$SDK_DIR/rockdev"export RK_FIRMWARE_DIR="$RK_OUTDIR/firmware"export RK_SECURITY_FIRMWARE_DIR="$RK_OUTDIR/security-firmware"export RK_CONFIG="$RK_OUTDIR/.config"export RK_DEFCONFIG_LINK="$RK_OUTDIR/defconfig"# For Makefilecase "$@" inmake-targets)# Chip targetsls "$CHIPS_DIR";&make-usage)run_build_hooks "$@"rm -f "$INITIAL_ENV"exit 0 ;;esac# Log SDK informationMANIFEST="$SDK_DIR/.repo/manifest.xml"if [ -e "$MANIFEST" ]; thenif [ ! -L "$MANIFEST" ]; thenMANIFEST="$SDK_DIR/.repo/manifests/$(grep -o "[^\"]*\.xml" "$MANIFEST")"fiTAG="$(grep -o "linux-.*-gen-rkr[^.\"]*" "$MANIFEST" | \head -n 1 || true)"MANIFEST="$(basename "$(realpath "$MANIFEST")")"echoecho -e "\e[35m############### Rockchip Linux SDK ###############\e[0m"echoecho -e "\e[35mManifest: $MANIFEST\e[0m"if [ "$TAG" ]; thenecho -e "\e[35mVersion: $TAG\e[0m"fiechofi# Prepare firmware dirsmkdir -p "$RK_FIRMWARE_DIR" "$RK_SECURITY_FIRMWARE_DIR"cd "$SDK_DIR"[ -f README.md ] || ln -rsf "$COMMON_DIR/README.md" .[ -d common ] || ln -rsf "$COMMON_DIR" .# TODO: Remove it in the repo manifest.xmlrm -f envsetup.shOPTIONS=${@:-allsave}# Special handle for chip and defconfig# e.g. ./build.sh rk3588:rockchip_defconfigfor opt in $OPTIONS; doif [ -d "$CHIPS_DIR/${opt%%:*}" ]; thenOPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \sed "s/^$opt$/chip:$opt/" | xargs)elif echo "$opt" | grep -q "^[0-9a-z_]*_defconfig$"; thenOPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \sed "s/^$opt$/defconfig:$opt/" | xargs)fidone# Options checkingCMDS="$(run_build_hooks support-cmds all | xargs)"for opt in $OPTIONS; docase "$opt" inhelp | h | -h | --help | usage | \?) usage ;;clean:*)# Check cleanup modulesfor m in $(echo ${opt#clean:} | tr ':' ' '); dogrep -wq clean_hook \"$SCRIPTS_DIR/mk-$m.sh" \2>/dev/null || usagedone;&shell | cleanall)# Check single optionsif [ "$opt" = "$OPTIONS" ]; thenbreakfiecho "ERROR: $opt cannot combine with other options!";;post-rootfs)if [ "$opt" = "$1" -a -d "$2" ]; then# Hide other args from build stagesOPTIONS=$optbreakfiecho "ERROR: $opt should be the first option followed by rootfs dir!";;*)# Make sure that all options are handledif option_check "$CMDS" $opt; thencontinuefiecho "ERROR: Unhandled option: $opt";;esacusagedone# Prepare log dirsif [ ! -d "$RK_LOG_DIR" ]; thenrm -rf "$RK_LOG_BASE_DIR" "$RK_LOG_DIR" "$RK_SESSION_DIR/latest"mkdir -p "$RK_LOG_DIR"ln -rsf "$RK_SESSION_DIR" "$RK_LOG_BASE_DIR"ln -rsf "$RK_LOG_DIR" "$RK_SESSION_DIR/latest"echo -e "\e[33mLog saved at $RK_LOG_DIR\e[0m"echofi# Drop old logscd "$RK_LOG_BASE_DIR"rm -rf $(ls -t | sed '1,10d')cd "$SDK_DIR"# Save initial envionmentsif [ "$INITIAL_SESSION" ]; thenrm -f "$RK_INITIAL_ENV"mv "$INITIAL_ENV" "$RK_INITIAL_ENV"ln -rsf "$RK_INITIAL_ENV" "$RK_OUTDIR/"fi# Init stage (preparing SDK configs, etc.)run_build_hooks init $OPTIONSrm -f "$RK_OUTDIR/.tmpconfig*"# No need to go furtherCMDS="$(run_build_hooks support-cmds pre-build build \post-build | xargs) cleanall clean post-rootfs"option_check "$CMDS" $OPTIONS || return 0# Force exporting config environmentsset -a# Load config environmentssource "$RK_CONFIG"cp "$RK_CONFIG" "$RK_LOG_DIR"if [ -z "$INITIAL_SESSION" ]; then# Inherit session environmentssed -n 's/^\(RK_.*=\)\(.*\)/\1"\2"/p' "$RK_FINAL_ENV" > \"$INITIAL_ENV"source "$INITIAL_ENV"rm -f "$INITIAL_ENV"else# Detect and save custom environments# Find custom environmentsrm -f "$RK_CUSTOM_ENV"for cfg in $(grep "^RK_" "$RK_INITIAL_ENV" || true); doenv | grep -q "^${cfg//\"/}$" || \echo "$cfg" >> "$RK_CUSTOM_ENV"done# Allow custom environments overridingif [ -e "$RK_CUSTOM_ENV" ]; thenln -rsf "$RK_CUSTOM_ENV" "$RK_OUTDIR/"echo -e "\e[31mWARN: Found custom environments: \e[0m"cat "$RK_CUSTOM_ENV"echo -e "\e[31mAssuming that is expected, please clear them if otherwise.\e[0m"read -t 10 -p "Press enter to continue."source "$RK_CUSTOM_ENV"if grep -q "^RK_KERNEL_VERSION=" "$RK_CUSTOM_ENV"; thenecho -e "\e[31mCustom RK_KERNEL_VERSION ignored!\e[0m"load_config RK_KERNEL_VERSIONfiif grep -q "^RK_ROOTFS_SYSTEM=" "$RK_CUSTOM_ENV"; thenecho -e "\e[31mCustom RK_ROOTFS_SYSTEM ignored!\e[0m"load_config RK_ROOTFS_SYSTEMfififisource "$PARTITION_HELPER"rk_partition_initset +aexport PYTHON3=/usr/bin/python3export RK_KERNEL_VERSION_REAL=$(kernel_version_real)# Handle special commandscase "$OPTIONS" incleanall)run_build_hooks cleanrm -rf "$RK_OUTDIR" "$SDK_DIR/rockdev"finish_build cleanallexit 0 ;;clean:*)MODULES="$(echo ${OPTIONS#clean:} | tr ':' ' ')"for m in $MODULES; do"$SCRIPTS_DIR/mk-$m.sh" cleandonefinish_build clean - $MODULESexit 0 ;;post-rootfs)shiftrun_post_hooks $@finish_build post-rootfsexit 0 ;;esac# Save final environmentsrm -f "$RK_FINAL_ENV"env > "$RK_FINAL_ENV"ln -rsf "$RK_FINAL_ENV" "$RK_OUTDIR/"# Log configsechoecho "=========================================="echo "          Final configs"echo "=========================================="env | grep -E "^RK_.*=.+" | grep -vE "PARTITION_[0-9]" | \grep -vE "=\"\"$|_DEFAULT=y" | \grep -vE "^RK_CONFIG|_BASE_CFG=|_LINK=|DIR=|_ENV=|_NAME=" | sortecho# Pre-build stage (submodule configuring, etc.)run_build_hooks pre-build $OPTIONS# No need to go furtherCMDS="$(run_build_hooks support-cmds build post-build | xargs)"option_check "$CMDS" $OPTIONS || return 0# Build stage (building, etc.)run_build_hooks build $OPTIONS# No need to go furtherCMDS="$(run_build_hooks support-cmds post-build | xargs)"option_check "$CMDS" $OPTIONS || return 0# Post-build stage (firmware packing, etc.)run_build_hooks post-build $OPTIONS
}
1.1.1 提示模式和错误处理

调试模式设置:首先通过判断是否设置了环境变量DEBUG,决定是否启用调试模式。如果 DEBUG 被设置为非空值,则运行 set -x 开启调试模式,这样脚本执行时会显示每一行命令的执行情况。

错误处理:使用 trapERR 捕获脚本中的错误。一旦发生错误,将调用 err_handler 函数;同时,set -eE 确保如果任何命令失败,脚本会立即退出,并且支持 ERR 跟踪。

保存初始环境变量:接着尝试保存当前的环境变量到一个临时文件中。首先,它解除定义了 INITIAL_SESSION 变量(如果存在),然后使用 mktemp -u 生成一个唯一的临时文件路径。如果 RK_SESSION 变量未设置,则认为这是一个新的会话,并将所有当前环境变量保存到生成的临时文件中。

1.1.2 设置环境变量

接着是设置一系列环境变,这些变量定义了一些重要的目录路径,通常在脚本执行过程中会用到这些路径。

export LC_ALL=Cexport SCRIPTS_DIR="$(dirname "$(realpath "$BASH_SOURCE")")"
export COMMON_DIR="$(realpath "$SCRIPTS_DIR/..")"
......

realpath "$BASH_SOURCE" 获取当前脚本的绝对路径,由于build.sh指向了device/rockchip/common/scripts/build.sh;所以:

SCRIPTS_DIR=<SDK>/device/rockchip/common/scripts
COMMON_DIR=<SDK>/device/rockchip/common
SDK_DIR=<SDK>
DEVICE_DIR=<SDK>/device/rockchip
CHIPS_DIR=<SDK>/device/rockchip/.chips
CHIP_DIR=<SDK>/device/rockchip/.chip
RK_DATA_DIR=<SDK>/device/rockchip/common/data
RK_TOOL_DIR=<SDK>/device/rockchip/common/tools
RK_IMAGE_DIR=<SDK>/device/rockchip/common/images
RK_KBUILD_DIR=<SDK>/device/rockchip/common/linux-kbuild
RK_CONFIG_IN=<SDK>/device/rockchip/common/configs/Config.in
RK_BUILD_HOOK_DIR=<SDK>/device/rockchip/common/build-hooks
BUILD_HELPER=<SDK>/device/rockchip/common/build-hooks/build-helper
RK_POST_HOOK_DIR=<SDK>/device/rockchip/common/post-hooks
POST_HELPER=<SDK>/device/rockchip/common/post-hooks/post-helper
PARTITION_HELPER=<SDK>/device/rockchip/common/scripts/partition-helper
RK_SESSION=2024-07-08_21-52-58
RK_OUTDIR=<SDK>/output
RK_SESSION_DIR=<SDK>output/sessions
RK_LOG_BASE_DIR=<SDK>/output/log
RK_LOG_DIR=<SDK>/output/sessions/2024-07-08_21-52-58
RK_INITIAL_ENV=<SDK>/output/sessions/2024-07-08_21-52-58/initial.env
RK_CUSTOM_ENV=<SDK>/output/sessions/2024-07-08_21-52-58/custom.env
RK_FINAL_ENV=<SDK>/output/sessions/2024-07-08_21-52-58/final.env
RK_ROCKDEV_DIR=<SDK>/rockdev
RK_FIRMWARE_DIR=<SDK>/output/firmware
RK_SECURITY_FIRMWARE_DIR=<SDK>/output/security-firmware
RK_CONFIG=<SDK>/output/.config
RK_DEFCONFIG_LINK=<SDK>/output/defconfig
1.1.3 选项make-targets/make-usage

接着就是对选项make-targetsmake-usage的支持,实际上也是输出帮助信息。

1.1.4 选项chip/defconfig

接着就是对如下选项的支持:

  • chip[:<chip>[:<config>]] <chip>可选,表示SoC,比如rk3588<config>可选,表示板级配置,比如rockchip_defconfig
  • defconfig[:<config>]:<config>可选,表示板级配置,比如rockchip_defconfig

(1) 如果有参数传递给脚本,则OPTIONS被设置为这些参数的组合;如果没有参数传递,则OPTIONS被设置为 allsave

(2) 接着对$OPTIONS变量进行特殊处理,用于处理 chipdefconfig的情况。它会检查每个参数,并根据特定的条件对其进行转换。

首先:

if [ -d "$CHIPS_DIR/${opt%%:*}" ]; thenOPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \sed "s/^$opt$/chip:$opt/" | xargs)

检查是否存在以$CHIPS_DIR开头、后面跟着$opt变量:分隔之前的字符串作为目录名, 如果满足;修改 $OPTIONS 变量,替换内容$optchip:$opt

  • 比如./builsh rk3588:rockchip_defconfigopt=rk3588:rockchip_defconfig,处理后OPTIONS=chip:rk3588:rockchip_defconfig

接着:

elif echo "$opt" | grep -q "^[0-9a-z_]*_defconfig$"; thenOPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \sed "s/^$opt$/defconfig:$opt/" | xargs)
fi

如果是以_defconfig结尾命名,修改 $OPTIONS 变量,替换内容$optdefconfig:$opt

  • 比如./builsh rockchip_defconfigopt=rockchip_defconfig,处理后OPTIONS=defconfig:rockchip_defconfig
1.1.5 选项检查

首先是获取支持的所有命令:

CMDS="$(run_build_hooks support-cmds all | xargs)"

xargs 将会把 run_build_hooks support-cmds all 的输出(echo函数的输出)捕获,并通过管道传递给 xargs

由于run_build_hooks函数会依次执行<SDK>/device/rockchip/common/build-hooks下的*.sh脚本,因此CMDS存储的就是这些脚本执行的输出,具体分析参考后文《run_build_hooks》。

执行完毕CMDS中存储build.sh支持的所有命令;

CMDS='chip defconfig lunch .*_defconfig olddefconfig savedefconfig menuconfig config shell print-parts mod-parts edit-parts new-parts insert-part del-part move-part rename-part resize-part kernel-config kernel-make kmake kernel modules linux-headers wifibt rtos buildroot debian yocto buildroot-config buildroot-make bmake rootfs buildroot debian yocto recovery pcba security_check createkeys security_ramboot security_uboot security_boot security_recovery security_rootfs loader uboot uefi firmware edit-package-file edit-ota-package-file edit-package-file edit-ota-package-file updateimg otapackage all allsave save'

接着是一段选项检查的代码;

for opt in $OPTIONS; docase "$opt" inhelp | h | -h | --help | usage | \?) usage ;;clean:*)# Check cleanup modulesfor m in $(echo ${opt#clean:} | tr ':' ' '); dogrep -wq clean_hook \"$SCRIPTS_DIR/mk-$m.sh" \2>/dev/null || usagedone;&shell | cleanall)# Check single optionsif [ "$opt" = "$OPTIONS" ]; thenbreakfiecho "ERROR: $opt cannot combine with other options!";;post-rootfs)if [ "$opt" = "$1" -a -d "$2" ]; then# Hide other args from build stagesOPTIONS=$optbreakfiecho "ERROR: $opt should be the first option followed by rootfs dir!";;*)# Make sure that all options are handledif option_check "$CMDS" $opt; thencontinuefiecho "ERROR: Unhandled option: $opt";;esacusage
done

根据传入的选项($OPTIONS)进行判断和处理,这里遍历选项依次执行:

  • 当选项满足help | h | -h | --help | usage | \?,执行usage函数。比如我们前文执行 ./build.sh help,就是则调用usage函数来显示帮助信息;
  • 如果选项以 clean: 开头,则验证验证相应的清理模块是否存在,比如./build.sh clean:kernel,则判断"device/rockchip/common/scripts/mk-kernel.sh/mk-kernel.sh中是否包含clean_hook方法;后续会调用mk-kernel.sh clean执行清理工作;
  • 如果选项是shellcleanall,执行函数run_build_hooks clean,该函数则会遍历device/rockchip/common/scriptsshell脚本文件,并传入clean参数函数依次执行;
  • 如果选项是post-rootfs,并且满足上面的条件,则设置OPTIONS=$opt
  • 如果不满足以上条件,则调用option_check函数检查给定的命令选项是否在指定的命令列表中,如果匹配失败,则输出错误信息,并调用usage函数。
1.1.6 准备日志目录

接着是创建编译日志目录:

# Prepare log dirs
if [ ! -d "$RK_LOG_DIR" ]; thenrm -rf "$RK_LOG_BASE_DIR" "$RK_LOG_DIR" "$RK_SESSION_DIR/latest"mkdir -p "$RK_LOG_DIR"ln -rsf "$RK_SESSION_DIR" "$RK_LOG_BASE_DIR"ln -rsf "$RK_LOG_DIR" "$RK_SESSION_DIR/latest"echo -e "\e[33mLog saved at $RK_LOG_DIR\e[0m"echo
fi

首先判断日志目录$RK_LOG_DIR是否存在,如果该目录不存在,则执行一系列操作来创建目录结构并设置符号链接,将 $RK_LOG_DIR符号链接到$RK_SESSION_DIR/latest,确保最新日志目录符号链接始终指向最新的日志目录。

1.1.7 清理旧日志

接着是清理日志目录,只保留最新的10次编译的日志记录:

# Drop old logs
cd "$RK_LOG_BASE_DIR"
rm -rf $(ls -t | sed '1,10d')
cd "$SDK_DIR"

跳转到<SDK>/output/log目录,把除了最新的10个文件或目录外的所有内容传递给rm -rf命令进行删除操作。

1.1.8 保存初始化环境变量

接着是保存初始化环境变量;

# Save initial envionments
if [ "$INITIAL_SESSION" ]; thenrm -f "$RK_INITIAL_ENV"mv "$INITIAL_ENV" "$RK_INITIAL_ENV"ln -rsf "$RK_INITIAL_ENV" "$RK_OUTDIR/"
fi

由于前文设置INITIAL_ENV=$(mktemp-u),即使用mktemp -u生成一个唯一的临时文件路径,比如/tmp/tmp.ag1ffjvNMG

因此该段脚本:

  • 它会先删除旧的<SDK>/output/sessions/initial.env文件;
  • 然后将临时文件移动并重命名为<SDK>/output/sessions/$RK_SESSION/initial.env
  • 最后在指定的输出目录<SDK>/output下创建一个指向<SDK>/output/sessions/$RK_SESSION/initial.env的符号链接。
1.1.9 初始化阶段

脚本进入初始化阶段,用于准备SDK配置等工作;

# Init stage (preparing SDK configs, etc.)
run_build_hooks init $OPTIONS
rm -f "$RK_OUTDIR/.tmpconfig*"

这里再次调用run_build_hooks函数,入参为init $OPTIONS,具体分析参考后文《run_build_hooks》。

1.1.10 选项检查

接下来就是判断我们传入的选项是否是build.sh pre-build build post-build阶段中定义的命令。

# No need to go further
CMDS="$(run_build_hooks support-cmds pre-build build \post-build | xargs) cleanall clean post-rootfs"
option_check "$CMDS" $OPTIONS || return 0

xargs 将会把 run_build_hooks support-cmds pre-build build post-build 的输出(echo函数的输出)捕获,并通过管道传递给 xargs

由于run_build_hooks函数会依次执行<SDK>/device/rockchip/common/build-hooks下的sh脚本,因此CMDS存储的就是这些脚本执行的输出。

执行完毕CMDS中存储build.shpre-build build post-build阶段中支持的命令;

CMDS='shell print-parts mod-parts edit-parts new-parts insert-part del-part move-part rename-part resize-part kernel-config kernel-make kmake kernel modules linux-headers wifibt rtos buildroot-config buildroot-make bmake rootfs buildroot debian yocto recovery pcba security_check createkeys security_ramboot security_uboot security_boot security_recovery security_rootfs loader uboot uefi firmware edit-package-file edit-ota-package-file updateimg otapackage all allsave save cleanall clean post-rootfs'

接着调用option_check检查变量CMDS中的命令是否包含在$OPTIONS指定的选项中。

  • 如果找到任何一个在任何一个选项中有匹配,则函数返回0,表示命令匹配成功;
  • 如果所有的命令都没有在任何一个选项中找到匹配,则函数最终返回1,表示命令匹配失败。

如果option_check函数返回非0值(即失败),则执行return 0,表示退出当前函数。

1.1.11 导出config environments
# Force exporting config environments
set -a# Load config environments
source "$RK_CONFIG"
cp "$RK_CONFIG" "$RK_LOG_DIR"if [ -z "$INITIAL_SESSION" ]; then# Inherit session environmentssed -n 's/^\(RK_.*=\)\(.*\)/\1"\2"/p' "$RK_FINAL_ENV" > \"$INITIAL_ENV"source "$INITIAL_ENV"rm -f "$INITIAL_ENV"
else# Detect and save custom environments# Find custom environmentsrm -f "$RK_CUSTOM_ENV"for cfg in $(grep "^RK_" "$RK_INITIAL_ENV" || true); doenv | grep -q "^${cfg//\"/}$" || \echo "$cfg" >> "$RK_CUSTOM_ENV"done# Allow custom environments overridingif [ -e "$RK_CUSTOM_ENV" ]; thenln -rsf "$RK_CUSTOM_ENV" "$RK_OUTDIR/"echo -e "\e[31mWARN: Found custom environments: \e[0m"cat "$RK_CUSTOM_ENV"echo -e "\e[31mAssuming that is expected, please clear them if otherwise.\e[0m"read -t 10 -p "Press enter to continue."source "$RK_CUSTOM_ENV"if grep -q "^RK_KERNEL_VERSION=" "$RK_CUSTOM_ENV"; thenecho -e "\e[31mCustom RK_KERNEL_VERSION ignored!\e[0m"load_config RK_KERNEL_VERSIONfiif grep -q "^RK_ROOTFS_SYSTEM=" "$RK_CUSTOM_ENV"; thenecho -e "\e[31mCustom RK_ROOTFS_SYSTEM ignored!\e[0m"load_config RK_ROOTFS_SYSTEMfifi
fisource "$PARTITION_HELPER"
rk_partition_initset +a
1.1.12 选项claenall/clean:*/post-rootfs
export PYTHON3=/usr/bin/python3
export RK_KERNEL_VERSION_REAL=$(kernel_version_real)# Handle special commands
case "$OPTIONS" incleanall)run_build_hooks cleanrm -rf "$RK_OUTDIR" "$SDK_DIR/rockdev"finish_build cleanallexit 0 ;;clean:*)MODULES="$(echo ${OPTIONS#clean:} | tr ':' ' ')"for m in $MODULES; do"$SCRIPTS_DIR/mk-$m.sh" cleandonefinish_build clean - $MODULESexit 0 ;;post-rootfs)shiftrun_post_hooks $@finish_build post-rootfsexit 0 ;;
esac
1.1.13 保存final环境变量
# Save final environments
rm -f "$RK_FINAL_ENV"
env > "$RK_FINAL_ENV"
ln -rsf "$RK_FINAL_ENV" "$RK_OUTDIR/"
1.1.14 预编译/编译/编译后
# Log configs
echo
echo "=========================================="
echo "          Final configs"
echo "=========================================="
env | grep -E "^RK_.*=.+" | grep -vE "PARTITION_[0-9]" | \grep -vE "=\"\"$|_DEFAULT=y" | \grep -vE "^RK_CONFIG|_BASE_CFG=|_LINK=|DIR=|_ENV=|_NAME=" | sort
echo# Pre-build stage (submodule configuring, etc.)
run_build_hooks pre-build $OPTIONS# No need to go further
CMDS="$(run_build_hooks support-cmds build post-build | xargs)"
option_check "$CMDS" $OPTIONS || return 0# Build stage (building, etc.)
run_build_hooks build $OPTIONS# No need to go further
CMDS="$(run_build_hooks support-cmds post-build | xargs)"
option_check "$CMDS" $OPTIONS || return 0# Post-build stage (firmware packing, etc.)
run_build_hooks post-build $OPTIONS
1.2 option_check

option_check函数检查给定的命令选项是否在指定的命令列表中。

option_check()
{# 将第一个参数$1赋值给变量CMDS,这个参数是一组命令列表,用空格分隔CMDS="$1"# 将参数列表向左移动一位,去掉了第一个参数(即CMDS),剩下的参数存储在$@中shift# 嵌套循环检查选项和命令for opt in $@; do     # 遍历传递给函数的所有剩余参数(除了第一个参数CMDS)for cmd in $CMDS; do    # 再嵌套一个循环,遍历存储在CMDS变量中的命令列表# NOTE: There might be patterns in commands 检查命令是否匹配选项,如果grep 命令没有匹配到,则继续下一次循>环echo "${opt%%:*}" | grep -q "^$cmd$" || continue# 如果匹配成功,则返回状态码 0,表示找到了匹配的命令选项return 0donedonereturn 1
}

比如我们给函数传入:

commands="run jump swim"
option_check "$commands" run swim sleep

在这个示例中:

  • $commands被传递给CMDS变量,即CMDS="run jump swim;
  • run swim sleep是要检查的选项,即option1=runoption2=swimoption3=sleep
    函数会逐个检查每个选项是否在命令列表CMDS中,如果找到任意匹配的命令,则返回状态码0;否则返回状态码1。
1.3 run_hooks

run_hooks在指定目录中查找并执行所有以 .sh 结尾的脚本文件。如果执行任何一个脚本失败,它将调用错误处理函数 err_handler 处理错误,并以失败的返回码退出整个脚本。

run_hooks()
{# 将第一个参数$1赋值给变量DIRDIR="$1"# 将参数列表向左移动一位,去掉了第一个参数(即DIR),剩下的参数存储在$@中shift# 遍历目录for dir in "$CHIP_DIR/$(basename "$DIR")/" "$DIR"; do# 当前的$dir是否是一个存在的目录。如果不是,则继续到下一个循环迭代[ -d "$dir" ] || continue# 在$dir目录下(不深入子目录,-maxdepth 1)查找所有以.sh结尾的文件for hook in $(find "$dir" -maxdepth 1 -name "*.sh" | sort); do"$hook" $@ && continue   # *.sh退出状态码为0则执行continueHOOK_RET=$?err_handler $HOOK_RET "${FUNCNAME[0]} $*" "$hook $*"exit $HOOK_RETdonedone
}

比如我们给函数传入:

RK_BUILD_HOOK_DIR=<SDK>/device/rockchip/common/build-hooks
run_hooks "$RK_BUILD_HOOK_DIR" support-cmds all

在这个示例中:

  • 将遍历目录<SDK>/device/rockchip/.chip/build-hooks<SDK>/device/rockchip/common/build-hooks

  • 由于<SDK>/device/rockchip/.chip/build-hooks不是目录将跳过;

  • 接着在<SDK>/device/rockchip/common/build-hooks 目录下(不深入子目录,-maxdepth 1)查找所有以.sh 结尾的文件;依次执行

    • 执行找到的每一个脚本文件,传递参数support-cmds all
    • 如果执行成功(exit 0),则继续下一个钩子脚本的执行;
    • 如果执行失败(exit 非0),调用err_handler 函数处理错误,传递错误码、当前函数名以及传递给当前函数的所有参数,以HOOK_RET的值退出脚本,终止整个构建过程。

我们查看<SDK>/device/rockchip/common/build-hooks目录下的sh脚本;

root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ ll device/rockchip/common/build-hooks/
lrwxrwxrwx  1 root root   23  6月  9 12:58 00-config.sh -> ../scripts/mk-config.sh*
lrwxrwxrwx  1 root root   22  6月  9 12:58 00-shell.sh -> ../scripts/mk-shell.sh*
lrwxrwxrwx  1 root root   27  6月  9 12:58 05-partitions.sh -> ../scripts/mk-partitions.sh*
lrwxrwxrwx  1 root root   23  6月  9 12:58 10-kernel.sh -> ../scripts/mk-kernel.sh*
lrwxrwxrwx  1 root root   23  6月  9 12:58 20-wifibt.sh -> ../scripts/mk-wifibt.sh*
lrwxrwxrwx  1 root root   21  6月  9 12:58 25-rtos.sh -> ../scripts/mk-rtos.sh*
lrwxrwxrwx  1 root root   23  6月  9 12:58 30-rootfs.sh -> ../scripts/mk-rootfs.sh*
lrwxrwxrwx  1 root root   25  6月  9 12:58 40-recovery.sh -> ../scripts/mk-recovery.sh*
lrwxrwxrwx  1 root root   21  6月  9 12:58 50-pcba.sh -> ../scripts/mk-pcba.sh*
lrwxrwxrwx  1 root root   25  6月  9 12:58 60-security.sh -> ../scripts/mk-security.sh*
lrwxrwxrwx  1 root root   23  6月  9 12:58 70-loader.sh -> ../scripts/mk-loader.sh*
lrwxrwxrwx  1 root root   25  6月  9 12:58 80-firmware.sh -> ../scripts/mk-firmware.sh*
lrwxrwxrwx  1 root root   26  6月  9 12:58 90-updateimg.sh -> ../scripts/mk-updateimg.sh*
lrwxrwxrwx  1 root root   20  6月  9 12:58 99-all.sh -> ../scripts/mk-all.sh*
lrwxrwxrwx  1 root root   23  6月  9 12:58 build-helper -> ../scripts/build-helper
-rwxr-xr-x  1 root root 4210  6月  9 12:58 example.sh.in*

其中,比较重要的有:

  • 00-config.sh:板载配置相关,支持的编译选项有chipdefconfigmenuconfig等;
  • 05-partitions.sh:分区配置相关,支持的编译选项有:print-partsmod-parts edit-parts等;
  • 10-kernel.sh:内核编译相关,支持的编译选项有:kernelmoduleslinux-headers等;
  • 20-wifibt.shwifibt编译相关;
  • 30-rootfs.sh:根文件系统编译相关,支持的编译选项有:buildrootyoctodebian等;
  • 40-recovery.shrecovery编译相关;
  • 70-loader.shuboot编译相关,支持的编译选项有:loader[:cmds]uboot[:cmds] uefi[:cmds] 等;
  • 80-firmware.sh:分区镜像打包相关;
  • 90-updateimg.sh:统一固件打包相关;
  • 99-all.sh :所有,支持的编译选项有:allsave等;

在该示例中将会按顺序依次执行*.sh脚本,并传递参数support-cmds all,有关这些*.sh脚本的执行我们在后面介绍。

1.4 run_build_hooks

run_build_hooks函数作用是运行一些构建过程中的钩子(hooks),并且记录执行日志。

run_build_hooks()
{# Don't log these hookscase "$1" ininit | pre-build | make-* | usage | support-cmds)run_hooks "$RK_BUILD_HOOK_DIR" $@ || truereturn 0;;esac# 设置日志文件LOG_FILE="$(start_log "$1")"# 记录运行日志echo -e "# run hook: $@\n" >> "$LOG_FILE"# 运行钩子并记录输出run_hooks "$RK_BUILD_HOOK_DIR" $@ 2>&1 | tee -a "$LOG_FILE"# 处理钩子执行结果HOOK_RET=${PIPESTATUS[0]}if [ $HOOK_RET -ne 0 ]; thenerr_handler $HOOK_RET "${FUNCNAME[0]} $*" "$@"exit $HOOK_RETfi
}

函数执行流程如下:

  • 根据传递给函数的第一个参数$1进行匹配,如果$1匹配到initpre-build、以make-开头的任意字符串、usage或者 support-cmds中的一个;

    • 调用run_hooks函数,并传递参数<SDK>/device/rockchip/common/build-hooks$@(所有传递给当前函数的参数);
    • || true 确保即使run_hooks函数失败,也不会中断执行;
    • 返回状态码 0,表示成功执行;
  • 设置日志文件:LOG_FILE被设置为一个日志文件的路径,使用start_log函数生成基于传入参数$1的日志文件名;

  • 记录运行日志:将# run hook: $@打印到$LOG_FILE中;

  • 运行钩子并记录输出:

    • 再次调用run_hooks函数,传递<SDK>/device/rockchip/common/build-hooks$@,标准错误(2)输出到(>)到标准输出(&1);
    • 将标准错误输出重定向到标准输出,这样可以确保错误信息也被tee命令捕获到;
    • tee -a将管道中的输出同时输出到标准输出和追加到$LOG_FILE文件中;
  • 处理钩子执行结果:将管道中上一个命令的退出状态保存在HOOK_RET变量中,PIPESTATUS[0]Bash 内置的数组,包含了最近一个管道中命令的退出状态;

    • 如果HOOK_RET不等于 0,则表示有错误发生;
    • 调用err_handler 数处理错误,传递错误码、当前函数名以及传递给当前函数的所有参数;
    • HOOK_RET的值退出脚本,终止整个构建过程。

在编译过程中,main函数多次调用run_build_hooks 函数的,其执行顺序依次为;

  • run_build_hooks support-cmds all
  • run_build_hooks init recovery
  • run_build_hooks support-cmds pre-build build post-build
  • run_build_hooks pre-build recovery
  • run_build_hooks support-cmds build post-build
  • run_build_hooks build recovery
  • run_build_hooks support-cmds post-build
  • run_build_hooks post-buildrecovery

二、build-helper

build-helper从文件名字不难猜测出来这是一个编译辅助脚本,其位于<SDK>/device/rockchip/common/build-hooks目录下,提供了若干个辅助函数。

2. 1 脚本入口

build-helper脚本会被<SDK>/device/rockchip/common/build-hooks下的所有*.sh脚本调用,并传入不同的参数,比如:

  • support-cmds all
  • init recovery
  • support-cmds pre-build build post-build
  • pre-build recovery
  • support-cmds build post-build
  • build recovery
  • support-cmds post-build;
  • post-build recovery

其中:recovery可以替换成./build.sh支持的任意选项,比如allbuildrootubootkernel等。

脚本入口代码如下:

......# 跳转到<SDK>目录
cd "$SDK_DIR"# 比如 LOG_FILE_NAME=$(*.sh脚本名称,比如00-config)-(参数1,比如`support-cmds`)_2024-07-08_21-12-25
LOG_FILE_NAME="$(basename "${0%.sh}")-$1_$(date +"%F_%H-%M-%S")"case "$1" inhelp | h | -h | --help | \?) usage ;;make-targets) make_targets; exit 0 ;;make-usage) make_usage; exit 0 ;;usage) usage_hook; exit 0 ;;support-cmds)   # 将参数列表向左移动一位,去掉了第一个参数(support-cmds),剩下的参数存储在$@中(即all)shift{ALL_CMDS="$INIT_CMDS $PRE_BUILD_CMDS $BUILD_CMDS \$POST_BUILD_CMDS"for stage in ${@:-all}; docase $stage ininit) echo "$INIT_CMDS" ;;pre-build) echo "$PRE_BUILD_CMDS" ;;build) echo "$BUILD_CMDS" ;;post-build) echo "$POST_BUILD_CMDS" ;;# 走这里,输出支持的命令 chip defconfig lunch .*_defconfig olddefconfig savedefconfig menuconfig configall) echo "$ALL_CMDS" ;;   esacdone} | xargs -n 1 | grep -v "^default$" | xargs || true   # 过滤掉单独包含 "default" 的行,然后将剩余的行作为参数依次传递给下一个命令exit 0;;clean)try_func clean_hookexit 0;;init)shifttry_hook init_hook "$INIT_CMDS" $@exit 0;;pre-build)shifttry_hook pre_build_hook "$PRE_BUILD_CMDS" $@exit 0;;build)shifttry_hook build_hook "$BUILD_CMDS" $@exit 0;;post-build)shifttry_hook post_build_hook "$POST_BUILD_CMDS" $@exit 0;;
esacif [ "$DRY_RUN" ]; thenecho "Environment 'DRY_RUN' ignored!"unset DRY_RUN
fiif [ "$2" = cmds ]; thenexport DRY_RUN=1
fi
2.1.1 入参为support-cmds all

如果入参为support-cmds all,代码会进入support-cmds分支;

support-cmds)    # 走这里# 将参数列表向左移动一位,去掉了第一个参数(support-cmds),剩下的参数存储在$@中(即all)shift{ALL_CMDS="$INIT_CMDS $PRE_BUILD_CMDS $BUILD_CMDS \$POST_BUILD_CMDS"for stage in ${@:-all}; docase $stage ininit) echo "$INIT_CMDS" ;;pre-build) echo "$PRE_BUILD_CMDS" ;;build) echo "$BUILD_CMDS" ;;post-build) echo "$POST_BUILD_CMDS" ;;# 走这里,输出支持的命令 chip defconfig lunch .*_defconfig olddefconfig savedefconfig menuconfig configall) echo "$ALL_CMDS" ;;   esacdone} | xargs -n 1 | grep -v "^default$" | xargs || true   # 过滤掉单独包含 "default" 的行,然后将剩余的行作为参数依次传递给下一个命令exit 0;;

首先输出ALL_CMDS,最后执行exit 0退出*.sh脚本(build-heleper是在*.sh脚本中以source方式执行的)文件的执行;

通过代码分析,我们可以得到入参第一个参数为support-cmds的命令均会进入该分支:

  • support-cmds all

  • support-cmds pre-build build post-build

  • support-cmds build post-build

  • support-cmds post-build;

脚本根据support-cmds之后的参数来执行不同的处理逻辑,比如:

  • all:输出所有支持的命令,包括INIT_CMDSPRE_BUILD_CMDSBUILD_CMDSPOST_BUILD_CMDS
  • pre-build:输出PRE_BUILD_CMDS
  • build:输出BUILD_CMDS
  • post-build:输出POST_BUILD_CMDS

由于<SDK>/device/rockchip/common/build-hooks目录下各个*.sh脚本中支持的INIT_CMDSPRE_BUILD_CMDSBUILD_CMDSPOST_BUILD_CMDS是不同的,因此每个*.sh脚本在使用source命令执行build-helper脚本时输出也是不同的。

我们以如下命令为例:

CMDS="$(run_build_hooks support-cmds all | xargs)"

xargs 将会把 run_build_hooks support-cmds all 的输出(echo函数的输出)捕获,并通过管道传递给 xargs。由于run_build_hooks函数会依次执行*.sh脚本,因此CMDS存储的就是这些脚本执行的输出结果。

2.1.2 入参为init recovery

如果入参为init recovery,代码会进入init分支;

init)# 将参数列表向左移动一位,去掉了第一个参数(support-cmds),剩下的参数存储在$@中(即recovery)shifttry_hook init_hook "$INIT_CMDS" $@exit 0;;

接着会执行try_hook函数,具体参考《try_hook分析》。

2.1.3 入参为pre-build recovery

如果入参为pre-build recovery,代码会进入pre-build分支;

pre-build)shifttry_hook pre_build_hook "$PRE_BUILD_CMDS" $@exit 0;;

接着会执行try_hook函数,具体参考《try_hook分析》。

2.1.4 入参为build recovery

如果入参为build recovery,代码会进入build分支;

build)shifttry_hook build_hook "$BUILD_CMDS" $@exit 0;;

接着会执行try_hook函数,具体参考《try_hook分析》。

2.1.5 入参为post-build recovery

如果入参为post-build recovery,代码会进入post-build分支;

post-build)shifttry_hook post_build_hook "$POST_BUILD_CMDS" $@exit 0;;

接着会执行try_hook函数,具体参考《try_hook分析》。

2.2 err_handler

error处理函数,脚本中的任何命令失败(即退出状态为非零),那么就会调用err_handler函数;

err_handler()
{# 如果参数1为空,则获取最近一次执行命令的退出状态ret=${1:-$?}# 退出状态为0,直接返回[ "$ret" -eq 0 ] && return# 入参参数2为空,输出调用栈中的第二个函数名echo "ERROR: Running $0 - ${2:-${FUNCNAME[1]}} failed!"# 输出错误状态码,以及发生错误的行号echo "ERROR: exit code $ret from line ${BASH_LINENO[0]}:"# 如果参数3为空,输出当前正在执行的命令echo "    ${3:-$BASH_COMMAND}"echo "ERROR: call stack:"for i in $(seq 1 $((${#FUNCNAME[@]} - 1))); doSOURCE="${BASH_SOURCE[$i]}"LINE=${BASH_LINENO[$(( $i - 1 ))]}echo "    $(basename "$SOURCE"): ${FUNCNAME[$i]}($LINE)"doneexit $ret
}
trap 'err_handler' ERR
set -eE
2.3 try_hook

try_hook函数接收3个参数:

  • 参数1是被调用的hook函数;
  • 参数2是hook函数支持的命令数组CMDS
  • 参数3是当前需要执行的任一选项命令;

函数定义如下:

try_hook()
{# init_hook、pre_build_hook等FUNC=$1# 将参数列表向左移动一位,去掉了第一个参数(init_hook)shift# 支持的命令数组CMDS="$1"# 将参数列表向左移动一位,去掉了第二个参数(CMDS)shift# 配以default开头并且后面跟着一个空格或者直接是行尾的行if echo "$CMDS" | grep -qE "^default( |$)"; thenOPTS="default $@"else# 一般走这里OPTS="$@ default"fi# 检查名为$FUNC的函数是否存在并且可执行,不存在或不可执行则return 0type $FUNC >/dev/null 2>/dev/null || return 0# 遍历选项数组for opt in $OPTS; do#匹配包含:cmds这个字符串,并且后面紧跟着一个冒号:,或者是行尾的地方  比如opt="recovery:cmds",IS_DRY_RUN=$(echo $opt | grep -E ":cmds(:|$)" || true)# 遍历命令数组for cmd in $CMDS; do# NOTE: There might be patterns in commands .... default 姑且认为查找opt、cmd相等的项ARGS="$(echo $opt | grep -E "^$cmd(:|$)" | \tr ':' ' ' || true)"[ "$ARGS" ] || continue# D优先使用DRY_RUN变量的值,如果DRY_RUN不存在或为空,则根据IS_DRY_RUN的值来决定是否展开为1。DRY_RUN=${DRY_RUN:-${IS_DRY_RUN:+1}} \try_func $FUNC $ARGSdonedone
}

这里我们姑且认为双层循环就是查找在CMDS数组中支持的选项,然后调用try_func函数,并传递参数$FUNC以及匹配到的选项。

2.3.1 入参为init_hook

build-helper脚本中入参为init_hook的函数调用如下:

try_hook init_hook "$INIT_CMDS" $@

由于<SDK>/device/rockchip/common/build-hooks目录下各个*.sh脚本中定义的init_hook函数、INIT_CMDS数组不相同,尽管$@参数一样但是执行的结果均是不同的。

注意:实际上只有部分*.sh脚本定义有init_hook,如果该函数未定义的try_hook会直接return 0

2.3.2 入参为pre_build_hook

build-helper脚本中入参为pre_build_hook的函数调用如下:

try_hook pre_build_hook "$PRE_BUILD_CMDS" $@

由于<SDK>/device/rockchip/common/build-hooks目录下各个*.sh脚本中定义的pre_build_hook函数、PRE_BUILD_CMDS数组不相同,尽管$@参数一样但是执行的结果均是不同的。

注意:实际上只有部分*.sh脚本定义有pre_build_hook,如果该函数未定义的try_hook会直接return 0

2.3.3 入参为build_hook

build-helper脚本中入参为build_hook的函数调用如下:

try_hook build_hook "$BUILD_CMDS" $@

由于<SDK>/device/rockchip/common/build-hooks目录下各个*.sh脚本中定义的build_hook函数、BUILD_CMDS数组不相同,尽管$@参数一样但是执行的结果均是不同的。

注意:实际上只有部分*.sh脚本定义有build_hook,如果该函数未定义的try_hook会直接return 0

2.3.4 入参为post_build_hook

build-helper脚本中入参为post_build_hook的函数调用如下:

try_hook post_build_hook "$POST_BUILD_CMDS" $@

由于<SDK>/device/rockchip/common/build-hooks目录下各个*.sh脚本中定义的post_build_hook函数、POST_BUILD_CMDS数组不相同,尽管$@参数一样但是执行的结果均是不同的。

注意:实际上只有部分*.sh脚本定义有post_build_hook,如果该函数未定义的try_hook会直接return 0

2.3 try_func

尝试调用某个函数,函数接收多个参数:

  • 参数1为函数将要调用的hook函数,比如usage_hookinit_hookpre_build_hook build_hook post_build_hook clean_hook;这些函数一般在查看使用说明、构建的初始化阶段、预构建、构建、构建后、以及清理时被触发;
  • 参数2为hook函数的入参,传入hook函数所支持的命令选项;

比如try_func init_hook default,将会执行init_hook default

# try_func init_hook default
try_func()
{# 检查名为init_hook的函数是否存在并且可执行,不存在或不可执行则return 0type $1 >/dev/null 2>/dev/null || return 0# Don't log these hookscase "${1%_hook}" in   # 第一个参数移除_hook,即initinit | pre_build)$@          # 执行init_hook defaultreturn 0;;esacif [ "$DRY_RUN" ]; thenDRY_FUNC=$1_dry         # 比如build_hook_drytype $DRY_FUNC >/dev/null 2>/dev/null || return 0# 将参数列表向左移动一位,去掉了第1个参数shift# 函数调用$DRY_FUNC $@return 0fi# 设置日志文件LOG_FILE="$(start_log ${LOG_FILE_NAME%%_*} $LOG_FILE_NAME)"、# 记录运行日志echo -e "# run func: $@\n" >> "$LOG_FILE"# 运行钩子并记录输出$@ 2>&1 | tee -a "$LOG_FILE"# 处理钩子执行结果FUNC_RET=${PIPESTATUS[0]}if [ $FUNC_RET -ne 0 ]; thenerr_handler $FUNC_RET "${FUNCNAME[0]} $*" "$@"exit $FUNC_RETfi
}

判断调用的是不是init_hookpre_build_hook函数,如果是执行该函数并将参数传入,执行完后return 0

如果是build_hook post_build_hook 等函数,

  • 如果$DRY_RUN存在将会执行$1_dry,比如build_hook_dry 参数2 ,执行完后return 0
  • 否则将会执行如下过程:
    • 设置日志文件:LOG_FILE=output/sessions/latest/$(*.sh脚本名称,比如00-config)-(参数1,比如support-cmds)_创建时间
    • 记录运行日志:将# run hook: $@打印到$LOG_FILE中;
    • 运行钩子并记录输出:
      • 调用钩子函数,并将标准错误(2)输出到(>)到标准输出(&1);
      • 将标准错误输出重定向到标准输出,这样可以确保错误信息也被tee命令捕获到;
      • tee -a将管道中的输出同时输出到标准输出和追加到$LOG_FILE文件中;
    • 处理钩子执行结果:将管道中上一个命令的退出状态保存在FUNC_RET变量中,PIPESTATUS[0]Bash 内置的数组,包含了最近一个管道中命令的退出状态;
      • 如果FUNC_RET不等于 0,则表示有错误发生;
      • 调用err_handler数处理错误,传递错误码、当前函数名以及传递给当前函数的所有参数;
      • FUNC_RET的值退出脚本,终止整个构建过程。

三、00-config.sh

00-config.sh脚本位于<SDK>/device/rockchip/common/build-hooks/目录,这里我们挑选一些重点内容介绍。

3.1 脚本入口

首先执行SDK/device/rockchip/common/build-hooks/build-helper脚本,会将这个脚本的内容直接加载到当前shell环境中执行,build-helper.sh 能够获取父文件中的变量和参数;

#  source SDK/device/rockchip/common/build-hooks/build-helper
source "${BUILD_HELPER:-$(dirname "$(realpath "$0")")/../build-hooks/build-helper}"init_hook $@

使用source命令执行脚本的一些注意事项:

  • 环境变量和函数的影响:被执行的脚本可以修改当前shell的环境变量和定义的函数,这些修改将持续影响到当前shell的会话,直到会话结束或者重新定义了这些变量和函数。
  • 退出状态:被执行的脚本的退出状态(即最后一个命令的退出状态)会影响到当前shell。可以通过$?变量来获取最近一次执行命令的退出状态;
  • 交互性:与直接执行脚本不同,使用source执行脚本时,不会创建新的shell环境,因此不会有新的子shell进程。这使得它适合于需要脚本和当前shell环境之间相互影响的场景,例如定义函数或设置环境变量;
3.2 usage_hook

usage_hook用于输出当前脚本支持的命令;

usage_hook()
{echo -e "chip[:<chip>[:<config>]]          \tchoose chip"echo -e "defconfig[:<config>]              \tchoose defconfig"echo -e " *_defconfig                      \tswitch to specified defconfig"echo "    available defconfigs:"ls "$CHIP_DIR/" | grep "defconfig$" | sed "s/^/\t/"echo -e " olddefconfig                     \tresolve any unresolved symbols in .config"echo -e " savedefconfig                    \tsave current config to defconfig"echo -e " menuconfig                       \tinteractive curses-based configurator"echo -e "config                            \tmodify SDK defconfig"
}
3.3 init_hook

init_hook函数在构建的初始化阶段被调用,用于处理INIT_CMDS命令;

INIT_CMDS="chip defconfig lunch .*_defconfig olddefconfig savedefconfig menuconfig config default"init_hook()
{case "${1:-default}" inchip) shift; choose_chip $@ ;;lunch|defconfig) shift; choose_defconfig $@ ;;*_defconfig) switch_defconfig "$1" ;;olddefconfig | savedefconfig | menuconfig)prepare_config$MAKE $1;;config)prepare_config$MAKE menuconfig$MAKE savedefconfig;;default) prepare_config ;; # End of init*) usage ;;esac
}

在调用init_hook函数时可以传入以上任一命令作为选项,init_hook将会执行不同的分支:

  • chip:执行 choose_chip $@函数;
  • lunch|defconfig:执行choose_defconfig $@函数;
  • _defconfig:执行switch_defconfig "$1" 函数;
  • olddefconfig | savedefconfig | menuconfig:执行prepare_config函数;
  • config:执行prepare_config函数、$MAKE menuconfig$MAKE savedefconfig
  • default:执行prepare_config函数;
  • 不满足以上命令,将会执行usage输出该脚本用法;
3.3.1 入参为default

执行prepare_config函数;

# 如果板载配置不存在在,选择板载配置
prepare_config()
{	# 如果<SDK>/device/rockchip/.chip路径不存在,则执行choose_chip选择芯片# 如果存在,其指向如下:.chip -> .chips/rk3588/[ -e "$CHIP_DIR" ] || choose_chip# 进入<SDK>/device/rockchipcd "$DEVICE_DIR"# 查找/device/rockchip/.chips目录下文件,即删除rk3588   rk3588 -> .chips/rk3588/rm -f $(ls "$CHIPS_DIR")   # ln -rsf .chips/rk3588 .ln -rsf "$(readlink "$CHIP_DIR")" .# 跳转到SDK目录cd "$SDK_DIR"# 如果<SDK>/output/defconfig文件不存在或不可读,则执行choose_chip选择芯片		if [ ! -r "$RK_DEFCONFIG_LINK" ]; thenecho "WARN: $RK_DEFCONFIG_LINK not exists"choose_defconfigreturn 0fi# 由<SDK>/output/defconfig -> ../device/rockchip/.chips/rk3588/rockchip_rk3588_sige7_defconfig,得到rockchip_rk3588_sige7_defconfigDEFCONFIG=$(basename "$(realpath "$RK_DEFCONFIG_LINK")")# 检查两个文件是否不是同一个文件if [ ! "$RK_DEFCONFIG_LINK" -ef "$CHIP_DIR/$DEFCONFIG" ]; thenecho "WARN: $RK_DEFCONFIG_LINK is invalid"choose_defconfigreturn 0fi# 检查文件$RK_CONFIG是否比$RK_DEFCONFIG_LINK更旧if [ "$RK_CONFIG" -ot "$RK_DEFCONFIG_LINK" ]; thenecho "WARN: $RK_CONFIG is out-dated"$MAKE $DEFCONFIG &>/dev/nullreturn 0fiCONFIG_DIR="$(dirname "$RK_CONFIG_IN")"if find "$CONFIG_DIR" -cnewer "$RK_CONFIG" | grep -q ""; thenecho "WARN: $CONFIG_DIR is updated"$MAKE $DEFCONFIG &>/dev/nullreturn 0fiCFG="RK_DEFCONFIG=\"$(realpath "$RK_DEFCONFIG_LINK")\""if ! grep -wq "$CFG" "$RK_CONFIG"; thenecho "WARN: $RK_CONFIG is invalid"$MAKE $DEFCONFIG &>/dev/nullreturn 0fiif [ "$RK_CONFIG" -nt "${RK_CONFIG}.old" ]; then$MAKE olddefconfig &>/dev/nulltouch "${RK_CONFIG}.old"fi
}
3.1.2 入参为_defconfig

执行switch_defconfig函数:

switch_defconfig()
{DEFCONFIG="$1"[ -f "$DEFCONFIG" ] || DEFCONFIG="$CHIP_DIR/$DEFCONFIG"if [ ! -f "$DEFCONFIG" ]; thenecho "No such defconfig: $1"exit 1fiecho "Switching to defconfig: $DEFCONFIG"rm -f "$RK_DEFCONFIG_LINK"ln -rsf "$DEFCONFIG" "$RK_DEFCONFIG_LINK"DEFCONFIG="$(realpath "$DEFCONFIG")"rm -rf "$CHIP_DIR"ln -rsf "$(dirname "$DEFCONFIG")" "$CHIP_DIR"$MAKE $(basename "$DEFCONFIG")
}

参考文章

[1] Rockchip_Developer_Guide_Linux_Software_CN.pdf

[2] repo介绍与使用

[3] Rockchip_Developer_Guide_Linux_Upgrade_CN.pdf

[4] Rockchip RK3588 - NanoPC-T6开发板介绍

[5] Rockchip RK3399 - NanoPC-T4开发板介绍

[6] Buildroot根文件系统构建

[7] Debian根文件系统构建

相关文章:

Rockchip RK3588 - Rockchip Linux SDK脚本分析

---------------------------------------------------------------------------------------------------------------------------- 开发板 &#xff1a;ArmSoM-Sige7开发板eMMC &#xff1a;64GBLPDDR4 &#xff1a;8GB 显示屏 &#xff1a;15.6英寸HDMI接口显示屏u-boot &a…...

【C++中resize和reserve的区别】

1. resize的用法 改变当前容器内含有元素的数量&#xff08;size()&#xff09;比如&#xff1a; vector<int> vct;int num vct.size();//之前的元素个数为num vct.resize(len);//现在的元素个数为len如果num < len &#xff0c;那么容器vct新增len - num个元素&am…...

计算机毕业设计Python深度学习游戏推荐系统 Django PySpark游戏可视化 游戏数据分析 游戏爬虫 Scrapy 机器学习 人工智能 大数据毕设

本论文的主要研究内容如下&#xff1a; 了解基于Spark的TapTap游戏数据分析系统的基本架构&#xff0c;掌握系统的开发方法&#xff0c;包括系统开发基本流程、开发环境的搭建、测试与运行等。 主要功能如下&#xff1a; &#xff08;1&#xff09;用户管理模块&#xff1a…...

Python面试题:如何在 Python 中进行正则表达式操作?

在 Python 中&#xff0c;正则表达式操作可以通过 re 模块来实现。以下是一些常用的正则表达式操作和示例&#xff1a; 1. 导入模块 import re2. 常见操作和示例 a. 匹配 使用 re.match() 来检查字符串的开头是否匹配某个模式。 pattern r\d # 匹配一个或多个数字 strin…...

C#面:简述什么是中间件(Middleware)?

中间件是组装到应⽤程序管道中以处理请求和响应的软件。 每个组件&#xff1a; 选择是否将请求传递给管道中的下⼀个组件。 可以在调⽤管道中的下⼀个组件之前和之后执⾏⼯作。 请求委托&#xff08;Request delegates&#xff09;⽤于构建请求管道&#xff0c;处理每个HTTP请…...

AWS Glue 与 Amazon Redshift 的安全通信配置

1. 引言 在 AWS 环境中,确保服务间的安全通信至关重要。本文将探讨 AWS Glue 与 Amazon Redshift 之间的安全通信配置,特别是为什么需要特定的安全组设置,以及如何正确实施这些配置。 2. 背景 AWS Glue:全托管的 ETL(提取、转换、加载)服务Amazon Redshift:快速、完全…...

nginx访问控制

最近部署consul服务&#xff0c;发现consul认证配置比较麻烦&#xff0c;于是上网查询发现nginx支持路由认证&#xff0c;在此做个记录。 1.Nginx访问控制模块类型 基于IP的访问控制&#xff1a;http_access_module基于用户的信任登录&#xff1a;http_auth_basic_module 2.…...

高效应对网络攻击,威胁检测响应(XDR)平台如何提升企业应急响应能力

在数字化时代&#xff0c;企业面临的网络攻击威胁持续增加&#xff0c;如恶意软件、勒索软件、钓鱼攻击、DDoS攻击等。这些威胁不仅危及企业数据安全、系统稳定&#xff0c;还损害了品牌形象和市场信任。随着云计算、大数据、物联网的广泛应用&#xff0c;企业网络攻击面扩大&a…...

多线程问题

什么是线程 线程是cpu调度和执行的单位&#xff0c;一个程序的运行伴随着的是一个进程的执行&#xff0c;而一个进程是由一个或多个线程来完成的&#xff0c;通过cpu调度资源在很短时间切换主线程和子线程并行&#xff0c;交替执行来做到看似多个线程同时进行的状态&#xff0…...

自动优化:SQL Server数据库自动收缩配置指南

自动优化&#xff1a;SQL Server数据库自动收缩配置指南 在数据库管理中&#xff0c;随着数据的增删&#xff0c;数据库文件的大小会不断变化&#xff0c;导致空间浪费和性能下降。SQL Server提供了自动收缩功能&#xff0c;帮助数据库文件保持最佳状态。本文将深入探讨如何在…...

华为机考真题 -- 密码解密

题目描述&#xff1a; 给定一段"密文"字符串 s, 其中字符都是经过"密码本"映射的&#xff0c;现需要将"密文"解密并且输出映射的规则 &#xff08;a - i)分别用(1 - 9)表示&#xff1b;(j - z)分别用(10* - 26*)表示约束&#xff1a;映射始终唯…...

ScrapySharp框架:小红书视频数据采集的API集成与应用

引言 随着大数据时代的到来&#xff0c;数据采集成为了互联网企业获取信息的重要手段。小红书作为一个集社交和电商于一体的平台&#xff0c;其丰富的用户生成内容&#xff08;UGC&#xff09;为数据采集提供了丰富的资源。本文将介绍如何使用ScrapySharp框架进行小红书视频数…...

PostgreSQL 数据库监控项

在维护和优化 PostgreSQL 数据库时&#xff0c;采集并监控数据库的各种静态和动态指标非常重要。这些指标包括数据库的配置信息、资源使用情况、性能指标等&#xff0c;能够帮助数据库管理员及时发现并解决潜在的问题&#xff0c;从而提高数据库的稳定性和性能。本文提供了一系…...

用python生成词频云图(python实例二十一)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.词频云图 3.1 代码构思 3.2 代码实例 3.3 运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&a…...

HTML 标签简写和全称及其对应的中文说明和实例

<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>HTML 标签简写及全称</title><style>…...

(2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)

前言 本系列最初的想法就是搭建一个多项目的环境, 包含nginx, nodejs, php, html, redis, MongoDB, mysql.本文使用的PHP镜像为php:7.3.6-apache, 这里可以使用上一篇文章中生成好的镜像.LAMP或包含react或vue的前端项目, 本文就各写了一个, 可以按照实际需求, 自行添加多个容…...

全网最适合入门的面向对象编程教程:13 类和对象的 Python 实现-可视化阅读代码神器 Sourcetrail 的安装使用

全网最适合入门的面向对象编程教程&#xff1a;13 类和对象的 Python 实现-可视化阅读代码神器 Sourcetrail 的安装使用 摘要&#xff1a; 本文主要介绍了可视化阅读代码神器Sourcetrail的安装与使用&#xff0c;包括软件简介和特性、下载地址、安装方式、新建工程和如何查看…...

Django 视图 - FBV 与 CBV

Django 视图 - FBV 与 CBV 在 Django 框架中&#xff0c;视图是处理 Web 请求和返回 Web 响应的核心组件。Django 提供了两种主要的视图编写方式&#xff1a;函数基础视图&#xff08;Function-Based Views&#xff0c;简称 FBV&#xff09;和类基础视图&#xff08;Class-Bas…...

AI机器人在未来的应用场景预测:是否会取代人类?华为、百度、特斯拉他们在AI领域都在做什么?

引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;AI机器人在各个领域的应用变得越来越普遍。从工业自动化到日常生活&#xff0c;AI机器人已经开始展现出强大的潜力和实际应用价值。本文将深入探讨AI机器人在未来的应用场景&#xff0c;并分析它们是否…...

第58期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…...

maven 依赖冲突

依赖冲突 1、对于 Maven 而言&#xff0c;同一个 groupId 同一个 artifactId 下&#xff0c;只能使用一个 version。 <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-math3 --><dependency><groupId>org.apache.commons</groupId&…...

demon drone 200无人机标定流程

demon drone 200无人机标定流程 一、飞控固件更新1.1 固件更新1.2 参数更新 二、imu标定2.1 安装imu标定工具&#xff08;在你自己的电脑上&#xff09;2.2 录制rosbag(在对应飞机上)2.3 运行标定程序&#xff08;在你自己的电脑上&#xff09; 三、双目及imu联合标定3.1 安装标…...

案例开发-日程管理-第一期

九 案例开发-日程管理-第一期 共7期 9.1 登录页及校验 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>.ht{text-align: center;color: cadetblue;font-family: 幼…...

【Java 注解,自定义注解,元注解,注解本质,注解解析】

文章目录 什么是注解&#xff1f;Java内置注解自定义注解元注解注解的本质注解解析 什么是注解&#xff1f; 注解是Java编程语言中的一种元数据&#xff0c;提供了有关程序的额外信息。注解以符号开始&#xff0c;紧跟着注解的名称和一对括号&#xff0c;括号内包含注解的参数…...

染色法判定二分图

什么是二分图&#xff1f; 二分图&#xff0c;也称作二部图&#xff0c;是图论中的一种特殊模型。在一个无向图G(V,E) 中&#xff0c;如果顶点集合 V 可以被分割成两个互不相交的子集 A 和 B&#xff0c;并且图中的每条边 (i,j) 关联的两个顶点 i 和 j 分别属于这两个不同的顶…...

自动气象站的主要功能优势

在科技日新月异的今天&#xff0c;我们生活的方方面面都受到了科技的影响。其中&#xff0c;自动气象站作为气象观测领域的重要一环&#xff0c;不仅提升了气象数据的准确性和时效性&#xff0c;还为我们的日常生活、农业生产、灾害预防等提供了重要的数据支持。 自动气象站概述…...

Java中实现二维数组(矩阵)的转置

在矩阵运算中&#xff0c;矩阵的转置是一个基本操作&#xff0c;即将矩阵的行变成列&#xff0c;列变成行。在Java中&#xff0c;我们可以通过编写一个方法来实现二维数组的转置。下面&#xff0c;我将详细介绍如何在Java中完成这一任务&#xff0c;并提供完整的代码示例。 编…...

Prometheus+Grafana主机运行数据

目录 介绍 安装Node Exporter 配置Prometheus 验证配置 导入仪表盘 介绍 Prometheus是一款开源的监控和警报工具&#xff0c;而Node Exporter是Prometheus的一个官方插件&#xff0c;用于采集主机上的各种系统和硬件指标。 安装Node Exporter 下载最新版本的Node Export…...

GraphQL在Postman中:释放API查询的强大潜能

&#x1f680; GraphQL在Postman中&#xff1a;释放API查询的强大潜能 Postman作为API开发和测试的领先工具&#xff0c;对GraphQL的支持为开发者提供了一种新的方式来查询和管理数据。GraphQL是一种查询语言&#xff0c;用于API&#xff0c;允许客户端明确指定他们需要哪些数…...

大语言模型里的微调vs RAG vs 模板提示词

文章目录 介绍微调&#xff08;Fine-tuning&#xff09;定义优点&#xff1a;缺点&#xff1a;应用场景&#xff1a;技术细节 检索增强生成&#xff08;RAG&#xff0c;Retrieval-Augmented Generation&#xff09;定义优点&#xff1a;缺点&#xff1a;应用场景&#xff1a;技…...

阿里云虚拟主机做企业网站/谷歌优化培训

SLF4J与Logback简介 Java日志框架众多&#xff0c;常用的有java.util.logging, log4j, logback&#xff0c;commons-logging等。 SLF4J (Simple Logging Facade For Java)&#xff0c;它是一个针对于各类Java日志框架的统一Facade抽象。SLF4J定义了统一的日志抽象接口&#xff…...

军事的网站应如何建设/外链平台有哪些

(*) 窗口的摆放在一个窗口上点右键&#xff0c;有floating, dockable, tabbed document这三种摆放方式。floating: 浮在最上&#xff0c;任何地方。dockable: 浮在最上&#xff0c;任何地方&#xff0c;且可以锚定在一侧。有了dockable之后其实floating就成鸡肋了。tabbed docu…...

wordpress导出主题/最近一周的重大热点新闻

前台 后台...

网站建设应用权限/百度搜索推广登录入口

为什么要写这个内容&#xff1f; 随着工作经历的积累&#xff0c;越来越感觉到&#xff0c;大量的关系型数据库的性能问题&#xff0c;其根源在于统计信息。这里说的是根源&#xff0c;其实很多时候大家觉得的那些什么索引失效等都只是表象。当然&#xff0c;不能一概而论&…...

建筑工程网是什么网站/读书网站排名

描述 如题&#xff0c;输入一个日期&#xff0c;格式如&#xff1a;2010 10 24 &#xff0c;判断这一天是这一年中的第几天。输入第一行输入一个数N&#xff08;0<N<100&#xff09;,表示有N组测试数据。后面的N行输入多组输入数据&#xff0c;每行的输入数据都是一个按题…...

网站建站手机/淘宝如何提升关键词排名

文章目录1.一层感知机结构输出数学推导代码测试2.多层感知机结构输出数学推导代码测试3.反向传播过程数学推导函数优化示例1.一层感知机结构输出 这个结构的含义是&#xff0c;n个x的输入&#xff0c;经过n个w的权值&#xff0c;进行一个线性的叠加求和∑XW得到一个输出结果x1…...