Rockchip RK3588 - Rockchip Linux SDK脚本分析
目录
----------------------------------------------------------------------------------------------------------------------------
开发板 :ArmSoM-Sige7
开发板
eMMC
:64GB
LPDDR4
:8GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2017.09
linux
:5.10
----------------------------------------------------------------------------------------------------------------------------
在《Rockchip RK3588 - Rockchip Linux SDK
编译》我们介绍了SDK
的编译流程以及固件升级相关的内容,本节将会对编译脚本进行深入的分析。
一、build.sh
分析
Rockchip Linux SDK
编译命令是由build.sh
脚本实现的,其入口函数为main
函数。
1.1 main
函数
由于main
函数代码内容较长,我们只对重点内容进行分析:
View Code
main()
{
[ -z "$DEBUG" ] || set -x
trap 'err_handler' ERR
set -eE
# Save intial envionments
unset INITIAL_SESSION
INITIAL_ENV=$(mktemp -u)
if [ -z "$RK_SESSION" ]; then
INITIAL_SESSION=1
env > "$INITIAL_ENV"
fi
export LC_ALL=C
export 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 Makefile
case "$@" in
make-targets)
# Chip targets
ls "$CHIPS_DIR"
;&
make-usage)
run_build_hooks "$@"
rm -f "$INITIAL_ENV"
exit 0 ;;
esac
# Log SDK information
MANIFEST="$SDK_DIR/.repo/manifest.xml"
if [ -e "$MANIFEST" ]; then
if [ ! -L "$MANIFEST" ]; then
MANIFEST="$SDK_DIR/.repo/manifests/$(grep -o "[^\"]*\.xml" "$MANIFEST")"
fi
TAG="$(grep -o "linux-.*-gen-rkr[^.\"]*" "$MANIFEST" | \
head -n 1 || true)"
MANIFEST="$(basename "$(realpath "$MANIFEST")")"
echo
echo -e "\e[35m############### Rockchip Linux SDK ###############\e[0m"
echo
echo -e "\e[35mManifest: $MANIFEST\e[0m"
if [ "$TAG" ]; then
echo -e "\e[35mVersion: $TAG\e[0m"
fi
echo
fi
# Prepare firmware dirs
mkdir -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.xml
rm -f envsetup.sh
OPTIONS=${@:-allsave}
# Special handle for chip and defconfig
# e.g. ./build.sh rk3588:rockchip_defconfig
for opt in $OPTIONS; do
if [ -d "$CHIPS_DIR/${opt%%:*}" ]; then
OPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \
sed "s/^$opt$/chip:$opt/" | xargs)
elif echo "$opt" | grep -q "^[0-9a-z_]*_defconfig$"; then
OPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \
sed "s/^$opt$/defconfig:$opt/" | xargs)
fi
done
# Options checking
CMDS="$(run_build_hooks support-cmds all | xargs)"
for opt in $OPTIONS; do
case "$opt" in
help | h | -h | --help | usage | \?) usage ;;
clean:*)
# Check cleanup modules
for m in $(echo ${opt#clean:} | tr ':' ' '); do
grep -wq clean_hook \
"$SCRIPTS_DIR/mk-$m.sh" \
2>/dev/null || usage
done
;&
shell | cleanall)
# Check single options
if [ "$opt" = "$OPTIONS" ]; then
break
fi
echo "ERROR: $opt cannot combine with other options!"
;;
post-rootfs)
if [ "$opt" = "$1" -a -d "$2" ]; then
# Hide other args from build stages
OPTIONS=$opt
break
fi
echo "ERROR: $opt should be the first option followed by rootfs dir!"
;;
*)
# Make sure that all options are handled
if option_check "$CMDS" $opt; then
continue
fi
echo "ERROR: Unhandled option: $opt"
;;
esac
usage
done
# Prepare log dirs
if [ ! -d "$RK_LOG_DIR" ]; then
rm -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
# Drop old logs
cd "$RK_LOG_BASE_DIR"
rm -rf $(ls -t | sed '1,10d')
cd "$SDK_DIR"
# Save initial envionments
if [ "$INITIAL_SESSION" ]; then
rm -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 $OPTIONS
rm -f "$RK_OUTDIR/.tmpconfig*"
# 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
# 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 environments
sed -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 environments
rm -f "$RK_CUSTOM_ENV"
for cfg in $(grep "^RK_" "$RK_INITIAL_ENV" || true); do
env | grep -q "^${cfg//\"/}$" || \
echo "$cfg" >> "$RK_CUSTOM_ENV"
done
# Allow custom environments overriding
if [ -e "$RK_CUSTOM_ENV" ]; then
ln -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"; then
echo -e "\e[31mCustom RK_KERNEL_VERSION ignored!\e[0m"
load_config RK_KERNEL_VERSION
fi
if grep -q "^RK_ROOTFS_SYSTEM=" "$RK_CUSTOM_ENV"; then
echo -e "\e[31mCustom RK_ROOTFS_SYSTEM ignored!\e[0m"
load_config RK_ROOTFS_SYSTEM
fi
fi
fi
source "$PARTITION_HELPER"
rk_partition_init
set +a
export PYTHON3=/usr/bin/python3
export RK_KERNEL_VERSION_REAL=$(kernel_version_real)
# Handle special commands
case "$OPTIONS" in
cleanall)
run_build_hooks clean
rm -rf "$RK_OUTDIR" "$SDK_DIR/rockdev"
finish_build cleanall
exit 0 ;;
clean:*)
MODULES="$(echo ${OPTIONS#clean:} | tr ':' ' ')"
for m in $MODULES; do
"$SCRIPTS_DIR/mk-$m.sh" clean
done
finish_build clean - $MODULES
exit 0 ;;
post-rootfs)
shift
run_post_hooks $@
finish_build post-rootfs
exit 0 ;;
esac
# Save final environments
rm -f "$RK_FINAL_ENV"
env > "$RK_FINAL_ENV"
ln -rsf "$RK_FINAL_ENV" "$RK_OUTDIR/"
# 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.1.1 提示模式和错误处理
调试模式设置:首先通过判断是否设置了环境变量DEBUG
,决定是否启用调试模式。如果 DEBUG
被设置为非空值,则运行 set -x
开启调试模式,这样脚本执行时会显示每一行命令的执行情况。
错误处理:使用 trap
和 ERR
捕获脚本中的错误。一旦发生错误,将调用 err_handler
函数;同时,set -eE
确保如果任何命令失败,脚本会立即退出,并且支持 ERR
跟踪。
保存初始环境变量:接着尝试保存当前的环境变量到一个临时文件中。首先,它解除定义了 INITIAL_SESSION
变量(如果存在),然后使用 mktemp -u
生成一个唯一的临时文件路径。如果 RK_SESSION
变量未设置,则认为这是一个新的会话,并将所有当前环境变量保存到生成的临时文件中。
1.1.2 设置环境变量
接着是设置一系列环境变,这些变量定义了一些重要的目录路径,通常在脚本执行过程中会用到这些路径。
export LC_ALL=C
export 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-targets
和make-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
变量进行特殊处理,用于处理 chip
和defconfig
的情况。它会检查每个参数,并根据特定的条件对其进行转换。
首先:
if [ -d "$CHIPS_DIR/${opt%%:*}" ]; then
OPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \
sed "s/^$opt$/chip:$opt/" | xargs)
检查是否存在以$CHIPS_DIR
开头、后面跟着$opt
变量:
分隔之前的字符串作为目录名, 如果满足;修改 $OPTIONS
变量,替换内容$opt
为 chip:$opt
;
- 比如
./builsh rk3588:rockchip_defconfig
,opt=rk3588:rockchip_defconfig
,处理后OPTIONS=chip:rk3588:rockchip_defconfig
;
接着:
elif echo "$opt" | grep -q "^[0-9a-z_]*_defconfig$"; then
OPTIONS=$(echo "$OPTIONS" | xargs -n 1 | \
sed "s/^$opt$/defconfig:$opt/" | xargs)
fi
如果是以_defconfig
结尾命名,修改 $OPTIONS
变量,替换内容$opt
为 defconfig:$opt
;
- 比如
./builsh rockchip_defconfig
,opt=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; do
case "$opt" in
help | h | -h | --help | usage | \?) usage ;;
clean:*)
# Check cleanup modules
for m in $(echo ${opt#clean:} | tr ':' ' '); do
grep -wq clean_hook \
"$SCRIPTS_DIR/mk-$m.sh" \
2>/dev/null || usage
done
;&
shell | cleanall)
# Check single options
if [ "$opt" = "$OPTIONS" ]; then
break
fi
echo "ERROR: $opt cannot combine with other options!"
;;
post-rootfs)
if [ "$opt" = "$1" -a -d "$2" ]; then
# Hide other args from build stages
OPTIONS=$opt
break
fi
echo "ERROR: $opt should be the first option followed by rootfs dir!"
;;
*)
# Make sure that all options are handled
if option_check "$CMDS" $opt; then
continue
fi
echo "ERROR: Unhandled option: $opt"
;;
esac
usage
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
执行清理工作; - 如果选项是
shell
或cleanall
,执行函数run_build_hooks clean
,该函数则会遍历device/rockchip/common/scripts
下shell
脚本文件,并传入clean
参数函数依次执行; - 如果选项是
post-rootfs
,并且满足上面的条件,则设置OPTIONS=$opt
; - 如果不满足以上条件,则调用
option_check
函数检查给定的命令选项是否在指定的编译选项列表中,如果匹配失败,则输出错误信息,并调用usage
函数。
1.1.6 准备日志目录
接着是创建编译日志目录:
# Prepare log dirs
if [ ! -d "$RK_LOG_DIR" ]; then
rm -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" ]; then
rm -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.sh
在pre-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
中指定的编译选项。
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 environments
sed -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 environments
rm -f "$RK_CUSTOM_ENV"
for cfg in $(grep "^RK_" "$RK_INITIAL_ENV" || true); do
env | grep -q "^${cfg//\"/}$" || \
echo "$cfg" >> "$RK_CUSTOM_ENV"
done
# Allow custom environments overriding
if [ -e "$RK_CUSTOM_ENV" ]; then
ln -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"; then
echo -e "\e[31mCustom RK_KERNEL_VERSION ignored!\e[0m"
load_config RK_KERNEL_VERSION
fi
if grep -q "^RK_ROOTFS_SYSTEM=" "$RK_CUSTOM_ENV"; then
echo -e "\e[31mCustom RK_ROOTFS_SYSTEM ignored!\e[0m"
load_config RK_ROOTFS_SYSTEM
fi
fi
fi
source "$PARTITION_HELPER"
rk_partition_init
set +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" in
cleanall)
run_build_hooks clean
rm -rf "$RK_OUTDIR" "$SDK_DIR/rockdev"
finish_build cleanall
exit 0 ;;
clean:*)
MODULES="$(echo ${OPTIONS#clean:} | tr ':' ' ')"
for m in $MODULES; do
"$SCRIPTS_DIR/mk-$m.sh" clean
done
finish_build clean - $MODULES
exit 0 ;;
post-rootfs)
shift
run_post_hooks $@
finish_build post-rootfs
exit 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 辅助函数
1.2.1 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 0
done
done
return 1
}
比如我们给函数传入:
commands="run jump swim"
option_check "$commands" run swim sleep
在这个示例中:
$commands
被传递给CMDS
变量,即CMDS="run jump swim
;run swim sleep
是要检查的选项,即option1=run
、option2=swim
、option3=sleep
。
函数会逐个检查每个编译选项是否在编译选项列表CMDS
中,如果没有找到任意匹配的编译,则返回状态码0;否则返回状态码1。
1.2.2 check_config
check_config
主要功能是检查传入的参数列表中的环境变量是否已经设置,如果有未设置的环境变量,则输出相应的信息并返回错误码。
check_config()
{
unset missing
for var in $@; do
# 获取变量的值,如果该变量已经设置(非空),则继续下一个循环迭代;
eval [ \$$var ] && continue
missing="$missing $var"
done
[ -z "$missing" ] && return 0
echo "Skipping $(basename "${BASH_SOURCE[1]}") - ${FUNCNAME[1]} for missing configs: $missing."
return 1
}
1.2.3 load_config
load_config
函数遍历函数调用时传递的每一个参数,从$RK_CONFIG
配置文件中查找与这些变量名对应的配置项,并将这些配置项的值导出为环境变量,供当前shell
会话使用。
load_config()
{
# RK_CONFIG = $(OUTDIR)/.config
[ -r "$RK_CONFIG" ] || return 0
for var in $@; do
export $(grep "^$var=" "$RK_CONFIG" | \
tr -d '"' || true) &>/dev/null
done
}
1.3 run_build_hooks
run_build_hooks
函数作用是运行一些构建过程中的钩子(hooks
),并且记录执行日志。
run_build_hooks()
{
# Don't log these hooks
case "$1" in
init | pre-build | make-* | usage | support-cmds)
run_hooks "$RK_BUILD_HOOK_DIR" $@ || true
return 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 ]; then
err_handler $HOOK_RET "${FUNCNAME[0]} $*" "$@"
exit $HOOK_RET
fi
}
函数执行流程如下:
-
根据传递给函数的第一个参数
$1
进行匹配,如果$1
匹配到init
、pre-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
的值退出脚本,终止整个构建过程。
- 如果
1.3.1 run_hooks
run_hooks
在指定目录中查找并执行所有以 .sh
结尾的脚本文件。如果执行任何一个脚本失败,它将调用错误处理函数 err_handler
处理错误,并以失败的返回码退出整个脚本。
run_hooks()
{
# 将第一个参数$1赋值给变量DIR
DIR="$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则执行continue
HOOK_RET=$?
err_handler $HOOK_RET "${FUNCNAME[0]} $*" "$hook $*"
exit $HOOK_RET
done
done
}
比如我们给函数传入:
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
:板载配置相关,支持的编译选项有chip
、defconfig
、menuconfig
等;05-partitions.sh
:分区配置相关,支持的编译选项有:print-parts
、mod-parts
、edit-parts
等;10-kernel.sh
:内核编译相关,支持的编译选项有:kernel
、modules
、linux-headers
等;20-wifibt.sh
:wifibt
编译相关;30-rootfs.sh
:根文件系统编译相关,支持的编译选项有:buildroot
、yocto
、debian
等;40-recovery.sh
:recovery
编译相关;70-loader.sh
:uboot
编译相关,支持的编译选项有:loader[:cmds]
、uboot[:cmds]
、uefi[:cmds]
等;80-firmware.sh
:分区镜像打包相关;90-updateimg.sh
:统一固件打包相关;99-all.sh
:所有,支持的编译选项有:all
、save
等;
在该示例中将会按顺序依次执行*.sh
脚本,并传递参数support-cmds all
,有关这些*.sh
脚本的执行我们在后面介绍。
1.3.2 命令./build.sh recovery
我们以./build.sh recovery
为例,该命令用于编译recovery.img
(是由kernel+dtb+ramdisk
组成的根文件系统,主要用于升级操作),在执行main
函数中会设置OPTIONS=recovery
;在编译过程中,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-build recovery
。
run_build_hooks
会调用<SDK>/device/rockchip/common/build-hooks
下的所有*.sh
脚本,并将参数传入。
1.3.3 命令./build.sh buildroot
我们以./build.sh buildroot
为例,该命令用于编译Buildroot
根文件系统,在执行main
函数中会设置OPTIONS=buildroot
;在编译过程中,main
函数多次调用run_build_hooks
函数的,其执行顺序依次为;
run_build_hooks support-cmds all
;run_build_hooks init buildroot
;run_build_hooks support-cmds pre-build build post-build
;run_build_hooks pre-build buildroot
;run_build_hooks support-cmds build post-build
;run_build_hooks build buildroot
;run_build_hooks support-cmds post-build
;run_build_hooks post-build buildroot
。
run_build_hooks
会调用<SDK>/device/rockchip/common/build-hooks
下的所有*.sh
脚本,并将参数传入。
1.3.4 命令./build.sh kernel
我们以./build.sh kernel
为例,该命令用于编译linux
内核,在执行main
函数中会设置OPTIONS=kernel
;在编译过程中,main
函数多次调用run_build_hooks
函数的,其执行顺序依次为;
run_build_hooks support-cmds all
;run_build_hooks init kernel
;run_build_hooks support-cmds pre-build build post-build
;run_build_hooks pre-build kernel
;run_build_hooks support-cmds build post-build
;run_build_hooks build kernel
;run_build_hooks support-cmds post-build
;run_build_hooks post-build kernel
。
run_build_hooks
会调用<SDK>/device/rockchip/common/build-hooks
下的所有*.sh
脚本,并将参数传入。
二、build-helper
build-helper
从文件名字不难猜测出来这是一个编译辅助脚本,其位于<SDK>/device/rockchip/common/build-hooks
目录下,提供了若干个辅助函数。
build-helper
脚本会被<SDK>/device/rockchip/common/build-hooks
下的所有*.sh
脚本调用;
source "${BUILD_HELPER:-$(dirname "$(realpath "$0")")/../build-hooks/build-helper}"
#init_hook/pre-build_hook/build_hook/post-build_hook
xxxx_hook $@
$@
为传入的参数,假设执行的命令为./build.sh recovery
,那么$@
可能是:
support-cmds all
:输出支持的所有编译选项;init recovery
:初始化阶段(init
)执行,调用init_hook
函数,入参为recovery
;support-cmds pre-build build post-build
:输出预编译、编译、编译后三个阶段支持的编译选项;pre-build recovery
:预编译阶段(pre-build
)执行,调用pre_build_hook
函数,入参为recovery
;support-cmds build post-build
:输出编译、编译后这两个阶段支持的编译选项;build recovery
:编译阶段(build
)执行,调用build_hook
函数,入参为recovery
support-cmds post-build
:输出编编译后阶段支持的编译选项;post-build recovery
:编译后阶段(post-build
)执行,调用post_build_hook
函数,入参为recovery
;
其中:recovery
可以替换成./build.sh
支持的任意编译选项,比如all
、buildroot
、uboot
、kernel
等。
2. 1 脚本入口
脚本入口代码如下:
......
# 跳转到<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" in
help | 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}; do
case $stage in
init) 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 config
all) echo "$ALL_CMDS" ;;
esac
done
} | xargs -n 1 | grep -v "^default$" | xargs || true # 过滤掉单独包含 "default" 的行,然后将剩余的行作为参数依次传递给下一个命令
exit 0
;;
clean)
try_func clean_hook
exit 0
;;
init)
shift
try_hook init_hook "$INIT_CMDS" $@
exit 0
;;
pre-build)
shift
try_hook pre_build_hook "$PRE_BUILD_CMDS" $@
exit 0
;;
build)
shift
try_hook build_hook "$BUILD_CMDS" $@
exit 0
;;
post-build)
shift
try_hook post_build_hook "$POST_BUILD_CMDS" $@
exit 0
;;
esac
if [ "$DRY_RUN" ]; then
echo "Environment 'DRY_RUN' ignored!"
unset DRY_RUN
fi
if [ "$2" = cmds ]; then
export 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}; do
case $stage in
init) 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 config
all) echo "$ALL_CMDS" ;;
esac
done
} | 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_CMDS
、PRE_BUILD_CMDS
、BUILD_CMDS
、POST_BUILD_CMDS
;pre-build
:输出PRE_BUILD_CMDS
;build
:输出BUILD_CMDS
;post-build
:输出POST_BUILD_CMDS
。
由于<SDK>/device/rockchip/common/build-hooks
目录下各个*.sh
脚本中支持的INIT_CMDS
、PRE_BUILD_CMDS
、BUILD_CMDS
、POST_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)
# 将参数列表向左移动一位,去掉了第一个参数(init),剩下的参数存储在$@中(即recovery)
shift
try_hook init_hook "$INIT_CMDS" $@
exit 0
;;
接着会执行try_hook
函数,具体参考《try_hook
分析》。
2.1.3 入参为pre-build recovery
如果入参为pre-build recovery
,代码会进入pre-build
分支;
pre-build)
shift
try_hook pre_build_hook "$PRE_BUILD_CMDS" $@
exit 0
;;
接着会执行try_hook
函数,具体参考《try_hook
分析》。
2.1.4 入参为build recovery
如果入参为build recovery
,代码会进入build
分支;
build)
shift
try_hook build_hook "$BUILD_CMDS" $@
exit 0
;;
接着会执行try_hook
函数,具体参考《try_hook
分析》。
2.1.5 入参为post-build recovery
如果入参为post-build recovery
,代码会进入post-build
分支;
post-build)
shift
try_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))); do
SOURCE="${BASH_SOURCE[$i]}"
LINE=${BASH_LINENO[$(( $i - 1 ))]}
echo " $(basename "$SOURCE"): ${FUNCNAME[$i]}($LINE)"
done
exit $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( |$)"; then
OPTS="default $@"
else
# 一般走这里
OPTS="$@ default"
fi
# 检查名为$FUNC的函数是否存在并且可执行,不存在或不可执行则return 0
type $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相等的项
# 如果opt=defconfig:rockchip_rk3588_sige7_defconfig,cmd=defconfig,执行后返回defconfig rockchip_rk3588_sige7_defconfig
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 $ARGS
done
done
}
这里我们姑且认为双层循环就是查找在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
,如果该函数未定义的init_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
,如果该函数未定义的pre_build_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
,如果该函数未定义的build_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
,如果该函数未定义的post_build_hook
会直接return 0
。
2.4 try_func
尝试调用某个函数,函数接收多个参数:
- 参数1为函数将要调用的
hook
函数,比如usage_hook
、init_hook
、pre_build_hook
、build_hook
、post_build_hook
、clean_hook
;这些函数通常在查看使用说明、构建的初始化阶段(init
)、预构建(pre-build
)、构建(build
)、构建后(post-build
)、以及清理时被触发; - 参数2为
hook
函数的入参,传入hook
函数所支持的编译选项;
比如try_func init_hook default
,将会执行init_hook default
;
# try_func init_hook default
try_func()
{
# 检查名为init_hook的函数是否存在并且可执行,不存在或不可执行则return 0
type $1 >/dev/null 2>/dev/null || return 0
# Don't log these hooks
case "${1%_hook}" in # 第一个参数移除_hook,即init
init | pre_build)
$@ # 执行init_hook default
return 0
;;
esac
if [ "$DRY_RUN" ]; then
DRY_FUNC=$1_dry # 比如build_hook_dry
type $DRY_FUNC >/dev/null 2>/dev/null || return 0
# 将参数列表向左移动一位,去掉了第1个参数
shift
# 函数调用
$DRY_FUNC $@
return 0
fi
# 设置日志文件
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 ]; then
err_handler $FUNC_RET "${FUNCNAME[0]} $*" "$@"
exit $FUNC_RET
fi
}
判断调用的是不是init_hook
、pre_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
的值退出脚本,终止整个构建过程。
- 如果
- 设置日志文件:
2.5 make_usage
make_usage
用于执行usage_hook
函数;
make_usage()
{
usage_hook | grep "^[a-z]" | grep -v "^[a-z0-9_.-]*:" | \
sed 's/\(^[a-z0-9_.-]*\).*\t\(.*$\)/\1@\2/' | \
while read LINE; do
TARGET=$(echo $LINE | grep -o "^[a-z0-9_.-]*")
USAGE=$(echo $LINE | grep -oE "[^@]*$" || true)
printf " %-22s - %s\n" "$TARGET" "$USAGE"
done
}
三、00-config.sh
00-config.sh
脚本位于<SDK>/device/rockchip/common/build-hooks/
目录,其实现的编译选项有:
chip
;*_defconfig
;savedefconfig
;menuconfig
;config
等;
该脚本主要用于选择芯片级配置,由于该文件内容比较多,这里我们挑选一些重点介绍。
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 $@
有关build-helper
我们已经具体分析了,这里不再重复。
使用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"
}
通过以上输出的信息,我们不难猜测出00-config.sh
脚本是用来实现SDK
配置的,其提供了选择芯片、选择板级配置、切换板级配置、保存板级配置、用户自定义配置等功能。
该函数调用链路如下;
./build.sh make-usage
run_build_hooks make-usage
run_hooks <SDK>/device/rockchip/common/build-hooks make-usage
......
<SDK>/device/rockchip/common/build-hooks/00-config.sh make-usage
<SDK>/device/rockchip/common/build-hooks/build-helper make-usage
make_usage # 位于build-helper
usage_hook # 位于00-config.sh
......
3.2.1 rockchip_rk3588_sige7_defconfig
对于ArmSoM-Sige7
开发板,我们选择的板级配置文件为rockchip_rk3588_sige7_defconfig
,位于<SDK>/device/rockchip/.chips/rk3588
目录下。
当我们执行make rockchip_rk3588_sige7_defconfig
或者./build.sh ${target}
选择rockchip_rk3588_sige7_defconfig
时均会编译生成<SDK>/output/.config
配置文件。
这里我们以./build.sh rockchip_rk3588_sige7_defconfig
命令为例;
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp# DEBUG=true ./build.sh rockchip_rk3588_sige7_defconfig
在输出日志中,可以定位到如下日志:
<SDK>/device/rockchip/common/build-hooks/00-config.sh init defconfig:rockchip_rk3588_sige7_defconfig
注意:其它*.sh
脚本的执行日志忽略,因为只有00-config.sh
才会处理*_defconfig
编译选项。
即触发阶段为init
,入参为defconfig rockchip_rk3588_sige7_defconfig
,init
阶段触发的hook
函数为init_hook
,当入参为defconfig rockchip_rk3588_sige7_defconfig
时将会执行choose_defconfig
函数。
注意:调用init_hook
函数时入参是defconfig rockchip_rk3588_sige7_defconfig
,并不是defconfig:rockchip_rk3588_sige7_defconfig
。
3.3 init_hook
init_hook
函数在构建的初始化阶段(init
阶段)被调用,用于处理INIT_CMDS
命令;
INIT_CMDS="chip defconfig lunch .*_defconfig olddefconfig savedefconfig menuconfig config default"
init_hook()
{
case "${1:-default}" in
chip) 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_CMDS
选项有多个;该函数调用链路如下;
./build.sh menuconfig
......
run_build_hooks init menuconfig
run_hooks <SDK>/device/rockchip/common/build-hooks init menuconfig
......
<SDK>/device/rockchip/common/build-hooks/00-config.sh init menuconfig
<SDK>/device/rockchip/common/build-hooks/build-helper init menuconfig
try_hook init_hook 'chip defconfig lunch .*_defconfig olddefconfig savedefconfig menuconfig config default ' menuconfig
try_func init_hook menuconfig
init_hook menuconfig
......
......
在调用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/rockchip
cd "$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" ]; then
echo "WARN: $RK_DEFCONFIG_LINK not exists"
choose_defconfig
return 0
fi
# 由<SDK>/output/defconfig -> ../device/rockchip/.chips/rk3588/rockchip_rk3588_sige7_defconfig,得到rockchip_rk3588_sige7_defconfig
DEFCONFIG=$(basename "$(realpath "$RK_DEFCONFIG_LINK")")
# 检查两个文件是否不是同一个文件
if [ ! "$RK_DEFCONFIG_LINK" -ef "$CHIP_DIR/$DEFCONFIG" ]; then
echo "WARN: $RK_DEFCONFIG_LINK is invalid"
choose_defconfig
return 0
fi
# 检查文件$RK_CONFIG是否比$RK_DEFCONFIG_LINK更旧
if [ "$RK_CONFIG" -ot "$RK_DEFCONFIG_LINK" ]; then
echo "WARN: $RK_CONFIG is out-dated"
$MAKE $DEFCONFIG &>/dev/null
return 0
fi
CONFIG_DIR="$(dirname "$RK_CONFIG_IN")"
if find "$CONFIG_DIR" -cnewer "$RK_CONFIG" | grep -q ""; then
echo "WARN: $CONFIG_DIR is updated"
$MAKE $DEFCONFIG &>/dev/null
return 0
fi
CFG="RK_DEFCONFIG=\"$(realpath "$RK_DEFCONFIG_LINK")\""
if ! grep -wq "$CFG" "$RK_CONFIG"; then
echo "WARN: $RK_CONFIG is invalid"
$MAKE $DEFCONFIG &>/dev/null
return 0
fi
if [ "$RK_CONFIG" -nt "${RK_CONFIG}.old" ]; then
$MAKE olddefconfig &>/dev/null
touch "${RK_CONFIG}.old"
fi
}
3.3.2 入参为defconfig
通过上面的分析我们知道执行./build.sh rockchip_rk3588_sige7_defconfig
命令会执行choose_defconfig
函数,函数的入参为rockchip_rk3588_sige7_defconfig
;
# 列出当前SDK支持的板载配置
rockchip_defconfigs()
{
# <SDK>/device/rockchip/.chip
cd "$CHIP_DIR"
ls rockchip_defconfig 2>/dev/null || true
# 输出所有*_defconfig文件,比如rockchip_defconfig、rockchip_rk3588s_evb1_lp4x_v10_defconfig等
ls *_defconfig | grep -v rockchip_defconfig || true
}
choose_defconfig()
{
# rockchip_defconfigs为支持的板载配置数组,判断是否支持rockchip_rk3588_sige7_defconfig
DEFCONFIG_ARRAY=( $(rockchip_defconfigs | grep "$1" || true) )
# 获取数组长度,这里为1
DEFCONFIG_ARRAY_LEN=${#DEFCONFIG_ARRAY[@]}
case $DEFCONFIG_ARRAY_LEN in
0)
echo "No available defconfigs${1:+" for: $1"}"
return 1
;;
1) DEFCONFIG=${DEFCONFIG_ARRAY[0]} ;; # 保存当前选中的板载配置文件
*)
if [ "$1" = ${DEFCONFIG_ARRAY[0]} ]; then
# Prefer exact-match
DEFCONFIG="$1"
else
echo "Pick a defconfig:"
echo ""
echo ${DEFCONFIG_ARRAY[@]} | xargs -n 1 | \
sed "=" | sed "N;s/\n/. /"
local INDEX
read -p "Which would you like? [1]: " INDEX
INDEX=$((${INDEX:-1} - 1))
DEFCONFIG="${DEFCONFIG_ARRAY[$INDEX]}"
fi
;;
esac
# 执行switch_defconfig rockchip_rk3588_sige7_defconfig
switch_defconfig $DEFCONFIG
}
3.3.3 入参为*_defconfig
执行switch_defconfig
函数:
switch_defconfig()
{
# rockchip_rk3588_sige7_defconfig
DEFCONFIG="$1"
# DEFCONFIG=<SDK>/device/rockchip/.chip/rockchip_rk3588_sige7_defconfig
[ -f "$DEFCONFIG" ] || DEFCONFIG="$CHIP_DIR/$DEFCONFIG"
if [ ! -f "$DEFCONFIG" ]; then
echo "No such defconfig: $1"
exit 1
fi
echo "Switching to defconfig: $DEFCONFIG"
# 删除<SDK>/output/defconfig
rm -f "$RK_DEFCONFIG_LINK"
# 创建<SDK>/output/defconfig -> <SDK>/device/rockchip/.chip/rockchip_rk3588_sige7_defconfig
ln -rsf "$DEFCONFIG" "$RK_DEFCONFIG_LINK"
DEFCONFIG="$(realpath "$DEFCONFIG")"
# 删除<SDK>/device/rockchip/.chip
rm -rf "$CHIP_DIR"
# 创建<SDK>/device/rockchip/.chip -> <SDK>/device/rockchip/.chips/rk3588
ln -rsf "$(dirname "$DEFCONFIG")" "$CHIP_DIR"
# 执行make V=1 -C device/rockchip/common rockchip_rk3588_sige7_defconfig
$MAKE $(basename "$DEFCONFIG")
}
可以看到分析到最后就是执行了make V=1 -C device/rockchip/common rockchip_rk3588_sige7_defconfig
。
因此我们需要进入<SDK>/Makefile
分析<SDK>/output/.config
文件是如何生成的。
3.4 .config
配置文件
我们定位到<SDK>/Makefile
;
# <SDK>/device/rockchip/.chi -> <SDK>/device/rockchip/.chips/rk3588,目录下有板级配置rockchip_rk3588_sige7_defconfig
CHIP_DIR := $(DEVICE_DIR)/.chip
# <SDK>/device/rockchip/common/configs
CONFIG_DIR := $(COMMON_DIR)/configs
CONFIG_CONFIG_IN = Config.in
# <SDK>/device/rockchip/common/kconfig
CONFIG = $(COMMON_DIR)/kconfig
# <SDK>/output/.config
RK_CONFIG = $(OUTDIR)/.config
# <SDK>/output/kconf
BUILD_DIR := $(OUTDIR)/kconf
#<SDK>/output/kconf/conf工具生成命令
$(BUILD_DIR)/%onf:
# 创建目录<SDK>/output/kconf/lxdialog
mkdir -p $(@D)/lxdialog
# make CC=gcc HOSTCC=g++ obj=<SDK>/output/kconf/conf -C <SDK>/device/rockchip/common/kconfig -f Makefile.br conf 切换到<SDK>/device/rockchip/common/kconfig目录,编译生成conf
make CC="$(HOSTCC)" HOSTCC="$(HOSTCC)" \
obj=$(@D) -C $(CONFIG) -f Makefile.br $(@F)
......
COMMON_CONFIG_ENV = \
RK_DEFCONFIG='$(call qstrip,$(RK_DEFCONFIG))' \
RK_CHIP_FAMILY='$(call qstrip,$(RK_CHIP_FAMILY))' \
RK_CHIP='$(call qstrip,$(RK_CHIP))' \
KCONFIG_AUTOCONFIG=$(BUILD_DIR)/auto.conf \
KCONFIG_AUTOHEADER=$(BUILD_DIR)/autoconf.h \
KCONFIG_TRISTATE=$(BUILD_DIR)/tristate.config \
srctree=$(CONFIG_DIR) RK_CONFIG=$(RK_CONFIG)
# .config配置文件生成命令
%_defconfig: $(BUILD_DIR)/conf $(CHIP_DIR)/%_defconfig
$(Q)$(COMMON_CONFIG_ENV) $< --defconfig=$(CHIP_DIR)/$@ $(CONFIG_CONFIG_IN)
比如我们执行如下命令:
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ sudo make V=1 -C device/rockchip/common rockchip_rk3588_sige7_defconfig
make: 进入目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/device/rockchip/common”
RK_DEFCONFIG='/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/device/rockchip/.chips/rk3588/rockchip_rk3588_sige7_defconfig' RK_CHIP_FAMILY='rk3588' RK_CHIP='rk3588' KCONFIG_AUTOCONFIG=/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/output/kconf/auto.conf KCONFIG_AUTOHEADER=/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/output/kconf/autoconf.h KCONFIG_TRISTATE=/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/output/kconf/tristate.config srctree=/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/device/rockchip/common/configs RK_CONFIG=/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/output/.config /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/output/kconf/conf --defconfig=/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/device/rockchip/.chip/rockchip_rk3588_sige7_defconfig Config.in
#
# configuration written to /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/output/.config
#
make: 离开目录“/work/sambashare/rk3588/armsom/armsom-rk3588-bsp/device/rockchip/common”
编译生成配置文件<SDK>/output/.config
,.config
文件是一个至关重要的配置文件,它包含了SDK
编译所需的配置信息,比如Rootfs
、u-boot
、Kernel
等。
<SDK>/output/.config
通过<SDK/output/kconf/conf
可执行文件生成的,而<SDK/>output/kconf/conf
工具是由device/rockchip/common/kconfig
源码编译生成的。
该工具接受的参数分别是:
--defconfig=<SDK>/device/rockchip/.chip/rockchip_rk3588_sige7_defconfig
;Config.in
;
SDK
使用Kconfig
系统来管理其配置选项。Kconfig
是一个用于内核配置的脚本语言,它允许开发者定义配置选项、条件依赖关系以及帮助文本。
在SDK
的源代码中,会有一系列的Kconfig
文件(位于<SDK>device/rockchip/common/configs
目录),这些文件定义了可用的配置选项。
因此不难猜测.config
文件中的配置项主要来源于板级的默认配置、Kconfig
系统定义的可选配置以及用户的自定义配置(make menuconfig
配置)。
四、40-recovery.sh
00-config.sh
脚本位于<SDK>/device/rockchip/common/build-hooks/
目录,其实现的编译选项只有recovery
。
该脚本主要用来编译recovery.img
镜像,在OTA
升级中会使用到。
4.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}"
build_hook $@
有关build-helper
我们已经具体分析了,这里不再重复。
4.2 usage_hook
usage_hook
输出当前脚本支持的编译选项信息;
usage_hook()
{
echo -e "recovery \tbuild recovery"
}
该函数调用链路如下;
./build.sh make-usage
run_build_hooks make-usage
run_hooks <SDK>/device/rockchip/common/build-hooks make-usage
......
<SDK>/device/rockchip/common/build-hooks/40-recovery.sh make-usage
<SDK>/device/rockchip/common/build-hooks/build-helper make-usage
make_usage # 位于build-helper
usage_hook # 位于40-recovery.sh
......
4.3 clean_hook
clean_hook
用于进行清理工作;
clean_hook()
{
check_config RK_RECOVERY_CFG || return 0
rm -rf buildroot/output/$RK_RECOVERY_CFG
rm -rf "$RK_OUTDIR/recovery"
}
删除板载配置RK_RECOVERY_CFG目录
编译的文件, 文件位于<SDK>/buildroot/output/$RK_RECOVERY_CFG
目录,。
RK_RECOVERY_CFG
定义在<SDK>/device/rockchip/common/configs/Config.in.recovery
;
config RK_RECOVERY_CFG
string
default "rockchip_${RK_RECOVERY_BASE_CFG}_recovery"
比如我们使用rk3588
开发板编译时,RK_RECOVERY_CFG=rockchip_rk3588_recovery
,
删除<SDK>/output/recovery
目录下的文件,比如:
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ ll output/recovery
lrwxrwxrwx 1 root root 51 6月 11 22:59 output/recovery -> ../buildroot/output/rockchip_rk3588_recovery/images/
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ ll output/recovery/
-rw-r--r-- 1 root root 44296704 6月 11 23:00 recovery.img
-rw-r--r-- 1 root root 15763968 6月 11 23:00 rootfs.cpio
-rw-r--r-- 1 root root 6673958 6月 11 23:00 rootfs.cpio.gz
-rw-r--r-- 1 root root 43187200 6月 11 23:00 rootfs.ext2
lrwxrwxrwx 1 root root 11 6月 11 23:00 rootfs.ext4 -> rootfs.ext2
-rw-r--r-- 1 root root 6635520 6月 11 23:00 rootfs.squashfs
-rw-r--r-- 1 root root 16844800 6月 11 23:00 rootfs.tar
4.4 build_hook
build_hook
用于recovery
构建,构建步骤如下:
buildroot
文件系统编译,编译输出目录为<SDK>/buildroot/output/rockchip_rk3588_recovery/
需要注意的是编译buildroot
使用的单板配置文件是rockchip_rk3588_recovery_defconfig
;- 构建系统镜像
recovery.img
(FIT uImage
类型内核镜像),由Image
+dtb
+ramdisk
(buildroot
编译得到的ramdisk
文件系统rootfs.cpio.gz
)组成; - 创建链接:
output/firmware/recovery.img
-><SDK>/output/rockchip_rk3588_recovery/images/recovery.img
。
源码如下:
# 构建阶段的编译选项
BUILD_CMDS="recovery"
build_hook()
{
# 在<SDK>/output/.config中查找,# RK_AB_UPDATE is not set
[ -z "$RK_AB_UPDATE" ] || return 0
# 检查RK_RECOVERY_CFG变量是否已经设置
check_config RK_RECOVERY_CFG || return 0
echo "=========================================="
echo " Start building recovery(buildroot)"
echo "=========================================="
# 设置输出目录<SDK>/output/recovery
DST_DIR="$RK_OUTDIR/recovery"
# 调用<SDK>/device/rockchip/common/scripts/mk-buildroot.sh rockchip_rk3588_recovery <SDK>/output/recovery
/usr/bin/time -f "you take %E to build recovery(buildroot)" \
"$SCRIPTS_DIR/mk-buildroot.sh" $RK_RECOVERY_CFG "$DST_DIR"
# 调用<SDK>/device/rockchip/common/scripts/mk-ramdisk.sh <SDK>/output/recovery/rootfs.cpio.gz <SDK>/output/recovery/recovery.img <SDK>/device/rockchip/.chips/rk3588/boot4recovery.its
/usr/bin/time -f "you take %E to pack recovery image" \
"$SCRIPTS_DIR/mk-ramdisk.sh" "$DST_DIR/rootfs.cpio.gz" \
"$DST_DIR/recovery.img" "$RK_RECOVERY_FIT_ITS"
# 创建链接<SDK>/output/firmware/recovery.img -> <SDK>/output/recovery/recovery.img -> <SDK>/output/rockchip_rk3588_recovery/images/recovery.img
ln -rsf "$DST_DIR/recovery.img" "$RK_FIRMWARE_DIR"
finish_build build_recovery
}
该函数调用链路如下;
./build.sh recovery
......
run_build_hooks build recovery
run_hooks <SDK>/device/rockchip/common/build-hooks build recovery
......
<SDK>/device/rockchip/common/build-hooks/40-recovery.sh build recovery
<SDK>/device/rockchip/common/build-hooks/build-helper build recovery
try_hook build_hook 'recovery' recovery
try_func build_hook recovery
build_hook recovery
......
......
build_hook
调用了mk-buildroot.sh
,mk-ramdisk.sh
等脚本,因此我们还需要对这些脚本进行分析。
4.4.1 mk-buildroot.sh
mk-buildroot.sh
脚本位于<SDK>/device/rockchip/common/scripts
目录下,用于编译 building recovery(buildroot)
,实际上就是进入<SDK>/buildroot
目录去编译buildroot
。
编译buildroot
使用的单板配置文件是rockchip_rk3588_recovery_defconfig
,文件位于<SDK>/buildroot/configs
目录。
<SDK>/device/rockchip/common/scripts/mk-buildroot.sh rockchip_rk3588_recovery <SDK>/output/recovery
脚本核心代码:
# <SDK>/device/rockchip/common/script
SCRIPTS_DIR="${SCRIPTS_DIR:-$(dirname "$(realpath "$0")")}"
# <SDK>
SDK_DIR="${SDK_DIR:-$SCRIPTS_DIR/../../../..}"
# rockchip_rk3588_recovery
BUILDROOT_BOARD=$1
# <SDK>/output/recovery
ROOTFS_OUTPUT_DIR="${2:-$SDK_DIR/output/buildroot}"
# <SDK>/buildroot
BUILDROOT_DIR="$SDK_DIR/buildroot"
"$SCRIPTS_DIR/check-buildroot.sh"
# 设置编译输出目录 <SDK>/buildroot/output/rockchip_rk3588_recovery
BUILDROOT_OUTPUT_DIR="$BUILDROOT_DIR/output/$BUILDROOT_BOARD"
# <SDK>/buildroot/output/rockchip_rk3588_recovery/.config
BUILDROOT_CONFIG="$BUILDROOT_OUTPUT_DIR/.config"
# <SDK>/buildroot/output/rockchip_rk3588_recovery/.config.orig
BUILDROOT_CONFIG_ORIG="$BUILDROOT_OUTPUT_DIR/.config.orig"
......
# Save the original .config if exists 判断.config文件存在并且可读,复制.config为.config.orig,即保存之前的配置文件
if [ -r "$BUILDROOT_CONFIG" ] && [ ! -r "$BUILDROOT_CONFIG_ORIG" ]; then
cp "$BUILDROOT_CONFIG" "$BUILDROOT_CONFIG_ORIG"
fi
# 1. 配置:进入<SDK>/buildroot目录,执行make,配置文件为rockchip_rk3588_recovery_defconfig,输出目录为<SDK>/buildroot/output/rockchip_rk3588_recovery,执行完成会生成.config文件。
make -C "$BUILDROOT_DIR" O="$BUILDROOT_OUTPUT_DIR" ${BUILDROOT_BOARD}_defconfig
# Warn about config changes 判断.config.orig文件存在并且可读
if [ -r "$BUILDROOT_CONFIG_ORIG" ]; then
# 比较.config和.config.orig文件差异,如果配置文件发生改变了,提示需要先进行clean工作
if ! diff "$BUILDROOT_CONFIG" "$BUILDROOT_CONFIG_ORIG"; then
echo -e "\e[35m"
echo "Buildroot config changed!"
echo "You might need to clean it before building:"
echo "rm -rf $BUILDROOT_OUTPUT_DIR"
echo -e "\e[0m"
echo
fi
fi
# 设置编译镜像文件存放目录 <SDK>/buildroot/output/rockchip_rk3588_recovery/images
IMAGE_DIR="$BUILDROOT_OUTPUT_DIR/images"
# <SDK>/output/recovery
rm -rf "$ROOTFS_OUTPUT_DIR"
# 创建编译镜像文件存放目录
mkdir -p "$IMAGE_DIR"
# 创建链接: <SDK>/output/recovery -> <SDK>/buildroot/output/rockchip_rk3588_recovery/images
ln -rsf "$IMAGE_DIR" "$ROOTFS_OUTPUT_DIR"
# 进入目录<SDK>/output/sessions/创建时间
cd "${RK_LOG_DIR:-$ROOTFS_OUTPUT_DIR}"
# br-rockchip_rk3588_recovery
LOG_PREFIX="br-$(basename "$BUILDROOT_OUTPUT_DIR")"
# 设置日志输出文件:<SDK>/output/sessions/创建时间/br-rockchip_rk3588_recovery_创建时间.log
LOG_FILE="$(start_log "$LOG_PREFIX" 2>/dev/null || echo $PWD/$LOG_PREFIX.log)"
# br.log -> $LOG_FILE
ln -rsf "$LOG_FILE" br.log
# Buildroot doesn't like it
unset LD_LIBRARY_PATH
# 2.编译 <SDK>/buildroot/utils/brmake -C <SDK>/buildroot O=<SDK>/buildroot/output/rockchip_rk3588_recovery
if ! "$BUILDROOT_DIR"/utils/brmake -C "$BUILDROOT_DIR" O="$BUILDROOT_OUTPUT_DIR"; then
echo "Failed to build $BUILDROOT_BOARD:"
tail -n 100 "$LOG_FILE"
echo -e "\e[35m"
echo "Please check details in $LOG_FILE"
echo -e "\e[0m"
exit 1
fi
echo "Log saved on $LOG_FILE"
echo "Generated images:"
# 查看<SDK>/output/recovery/rootfs.*文件
ls "$ROOTFS_OUTPUT_DIR"/rootfs.*
编译后会在<SDK>/buildroot/output/rockchip_rk3588_recovery
目录生成下生成.config
以及若干镜像文件;
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ ll buildroot/output/rockchip_rk3588_recovery/
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.init
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.jpeg
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.linux
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.menus
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.openssl
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.paths
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.skeleton
-rw-r--r-- 1 root root 82 7月 8 23:05 .br2-external.in.toolchains
-rw-r--r-- 1 root root 162 7月 8 23:05 .br2-external.mk
drwxr-xr-x 96 root root 4096 7月 8 23:06 build/ # 除了交叉编译的工具链之外的所有组件
-rw-r--r-- 1 root root 114043 7月 8 23:05 .config # 生成的配置文件
-rw------- 1 root root 3019 7月 8 23:05 .config.in
-rw-r--r-- 1 root root 114043 6月 23 16:57 .config.new
-rw-r--r-- 1 root root 114043 7月 8 22:45 .config.old
-rw-r--r-- 1 root root 114043 7月 8 22:43 .config.orig
-rw-r--r-- 1 root root 105270 7月 8 23:05 ..config.tmp
drwxr-xr-x 11 root root 4096 6月 18 01:46 host/ # 包含为主机编译的工具的安装
drwxr-xr-x 2 root root 4096 7月 8 23:06 images/ # 有镜像(文件系统,比如ext2/4、squashfs、cpio等格式镜像)存储目录
-rw-r--r-- 1 root root 681 7月 8 23:05 Makefile
lrwxrwxrwx 1 root root 131 6月 18 01:48 staging -> /work/sambashare/rk3588/armsom/armsom-rk3588-bsp/buildroot/output/rockchip_rk3588_recovery/host/aarch64-buildroot-linux-gnu/sysroot/
drwxr-xr-x 17 root root 4096 7月 8 23:06 target/ # 几乎包含了目标的完整根文件系统
root@ubuntu:/work/sambashare/rk3588/armsom/armsom-rk3588-bsp$ ll buildroot/output/rockchip_rk3588_recovery/images/
-rw-r--r-- 1 root root 44336128 7月 8 23:06 recovery.img # 最终需要的系统镜像
-rw-r--r-- 1 root root 15856640 7月 8 23:06 rootfs.cpio # rootfs.ext2经过cpio压缩的根文件系统镜像
-rw-r--r-- 1 root root 6713350 7月 8 23:06 rootfs.cpio.gz # 经过cpio和gzip压缩的根文件系统镜像,常用于创建初始RAM磁盘 (initramfs)
-rw-r--r-- 1 root root 43342848 7月 8 23:06 rootfs.ext2 # buildroot编译生成的文件系统镜像,ext2格式
lrwxrwxrwx 1 root root 11 7月 8 23:06 rootfs.ext4 -> rootfs.ext2
-rw-r--r-- 1 root root 6676480 7月 8 23:06 rootfs.squashfs
-rw-r--r-- 1 root root 16947200 7月 8 23:06 rootfs.tar # rootfs.ext2经过tar打包的根文件系统镜像
关于编译的具体细节,以及生成的目录介绍具体可以参考:
4.4.2 mk-ramdisk.sh
mk-ramdisk.sh
脚本位于<SDK>/device/rockchip/common/scripts
目录下,用于构建recovery
系统镜像。
<SDK>/device/rockchip/common/scripts/mk-ramdisk.sh \
<SDK>/output/recovery/rootfs.cpio.gz \
<SDK>/output/recovery/recovery.img \
<SDK>/device/rockchip/.chips/rk3588/boot4recovery.its
脚本核心代码:
# ramsidk根文件系统:<SDK>/output/recovery/rootfs.cpio.gz
RAMDISK_IMG="$1"
# recovery系统镜像:<SDK>/output/recovery/recovery.img
TARGET_IMG="$2"
# <SDK>/device/rockchip/.chips/rk3588/boot4recovery.its
ITS="$3"
# 校验ramsidk根文件系统文件是否存在
if [ ! -f "$RAMDISK_IMG" ]; then
echo "$RAMDISK_IMG doesn't exist"
exit 0
fi
# 内核镜像<SDK>/kernel/arch/arm64/boot/Image
KERNEL_IMG="$RK_KERNEL_IMG"
# 校验内核镜像是否存在
if [ ! -f "$KERNEL_IMG" ]; then
echo "Build kernel for initrd"
"$SCRIPTS_DIR/mk-kernel.sh"
fi
# 检验ramsidk根文件系统是否以.romfs结尾,如果是,则对$RAMDISK_IMG和$KERNEL_IMG分别进行gzip压缩,
if echo $RAMDISK_IMG | grep -q ".romfs$"; then
cat "$RAMDISK_IMG" | gzip -n -f -9 > "$RAMDISK_IMG.gz"
cat "$KERNEL_IMG" | gzip -n -f -9 > "$KERNEL_IMG.gz"
RAMDISK_IMG="$RAMDISK_IMG.gz"
KERNEL_IMG="$KERNEL_IMG.gz"
fi
# 开始打包
echo "Packing $RAMDISK_IMG to $TARGET_IMG"
# 判断its存在
if [ -n "$ITS" ]; then
# 走这里
"$SCRIPTS_DIR/mk-fitimage.sh" "$TARGET_IMG" "$ITS" \
"$KERNEL_IMG" "$RAMDISK_IMG"
else
kernel/scripts/mkbootimg --kernel "$KERNEL_IMG" \
--ramdisk "$RAMDISK_IMG" --second "kernel/resource.img" \
-o "$TARGET_IMG"
fi
看到最后我们实际上就已经很熟悉了,这里就是调用mk-fitimage.sh
脚本去生成FIT uImage
。
这里我们校验介绍一下FIT uImage
:FIT uImage
是在Legacy uImage
的基础上,为了满足Linux Flattened Device Tree(FDT)
的标准,而重新改进和定义出来的一种镜像文件格式;它一般将kernel
、dtb
、ramdisk
等等镜像打包到一个itb
镜像文件中;u-boot
只要获得了这个镜像文件,就可以得到kernel
、dtb
、ramdisk
等等镜像的具体信息和内容。
4.4.3 mk-fitimage.sh
这里我们还是来看一下mk-fitimage.sh
脚本,脚本位于<SDK>/device/rockchip/common/scripts
目录下,该脚本根据its
文件中的描述来打包镜像生成itb
文件(FIT uImage
)。
# FIT uImage镜像:<SDK>/output/recovery/recovery.img
TARGET_IMG="$1"
# its文件: <SDK>/device/rockchip/.chips/rk3588/boot4recovery.its
ITS="$CHIP_DIR/$2"
# 内核镜像:<SDK>/kernel/arch/arm64/boot/Image
KERNEL_IMG="$3"
# ramsidk根文件系统:<SDK>/output/recovery/rootfs.cpio.gz
RAMDISK_IMG="$4"
# 内核设备树:<SDK>/kernel/arch/arm64/boot/dts/rockchip/rk3588-armsom-sige7.dtb
KERNEL_DTB="$RK_KERNEL_DTB"
# 资源:<SDK>/kernel/resource.img
RESOURCE_IMG=kernel/resource.img
# 判断its文件存在
if [ ! -f "$ITS" ]; then
echo "$ITS not exists!"
exit 1
fi
# 生成临时文件,复制its到临时文件
TMP_ITS=$(mktemp)
cp "$ITS" "$TMP_ITS"
if [ "$RK_SECURITY" ]; then
echo "Security boot enabled, removing uboot-ignore ..."
sed -i "/uboot-ignore/d" "$TMP_ITS"
fi
# 使用sed命令替换一个临时文件$TMP_ITS中的占位符, 比如替换@KERNEL_DTB@-><SDK>/kernel/arch/arm64/boot/dts/rockchip/rk3588-armsom-sige7.dtb
sed -i -e "s~@KERNEL_DTB@~$(realpath -q "$KERNEL_DTB")~" \
-e "s~@KERNEL_IMG@~$(realpath -q "$KERNEL_IMG")~" \
-e "s~@RAMDISK_IMG@~$(realpath -q "$RAMDISK_IMG")~" \
-e "s~@RESOURCE_IMG@~$(realpath -q "$RESOURCE_IMG")~" "$TMP_ITS"
# 使用mkimage工具编译,这里一定要指定-E参数
rkbin/tools/mkimage -f "$TMP_ITS" -E -p 0x800 "$TARGET_IMG"
# 删除临时文件
rm -f "$TMP_ITS"
这里重点关注一下its
文件,路径为device/rockchip/.chip/boot4recovery.its
;
/*
* Copyright (C) 2021 Rockchip Electronics Co., Ltd
*
* SPDX-License-Identifier: GPL-2.0
*/
/dts-v1/;
/ {
description = "U-Boot FIT source file for arm";
images {
fdt {
data = /incbin/("@KERNEL_DTB@");
type = "flat_dt";
arch = "arm64";
compression = "none";
load = <0xffffff00>;
hash {
algo = "sha256";
};
};
# 注意这里未指定压缩方式,因此内核镜像使用Image
kernel {
data = /incbin/("@KERNEL_IMG@");
type = "kernel";
arch = "arm64";
os = "linux";
compression = "none";
entry = <0xffffff01>;
load = <0xffffff01>;
hash {
algo = "sha256";
};
};
ramdisk {
data = /incbin/("@RAMDISK_IMG@");
type = "ramdisk";
arch = "arm64";
os = "linux";
compression = "none";
load = <0xffffff02>;
hash {
algo = "sha256";
};
};
resource {
data = /incbin/("@RESOURCE_IMG@");
type = "multi";
arch = "arm64";
compression = "none";
hash {
algo = "sha256";
};
};
};
configurations {
default = "conf";
conf {
rollback-index = <0x00>;
fdt = "fdt";
kernel = "kernel";
ramdisk = "ramdisk";
multi = "resource";
signature {
algo = "sha256,rsa2048";
padding = "pss";
key-name-hint = "dev";
sign-images = "fdt", "kernel", "ramdisk", "multi";
};
};
};
};
关于FIT uImage
镜像的具体制作可以参考:《Rockchip RK3399
- 移植linux 5.2.8
》。
五、10-kernel.sh
10-kernel.sh
脚本位于<SDK>/device/rockchip/common/build-hooks/
目录,其实现的编译选项有:
kernel[:cmds]
;modules[:cmds]
;linux-headers[:cmds]
;kernel-config[:cmds]
;kernel-make[:<arg1>:<arg2>]
;
该脚本实现了内核配置、编译功能,接下来我们以内核编译命令./build.sh kernel
为例进行讲解。
5.1 脚本入口
首先执行SDK/device/rockchip/common/build-hooks/build-helper
脚本,会将这个脚本的内容直接加载到当前shell
环境中执行,build-helper.sh
能够获取父文件中的变量和参数;
source "${BUILD_HELPER:-$(dirname "$(realpath "$0")")/../build-hooks/build-helper}"
case "${1:-kernel}" in
kernel-config | kernel-make | kmake) pre_build_hook $@ ;;
kernel* | modules)
init_hook $@
build_hook ${@:-kernel}
;;
linux-headers) post_build_hook $@ ;;
*) usage ;;
esac
有关build-helper
我们已经具体分析了,这里不再重复。
5.2 usage_hook
usage_hook
输出当前脚本支持的编译选项信息;
usage_hook()
{
for k in $KERNELS; do
echo -e "$k[:cmds] \tbuild kernel ${k#kernel-}"
done
echo -e "kernel[:cmds] \tbuild kernel"
echo -e "modules[:cmds] \tbuild kernel modules"
echo -e "linux-headers[:cmds] \tbuild linux-headers"
echo -e "kernel-config[:cmds] \tmodify kernel defconfig"
echo -e "kernel-make[:<arg1>:<arg2>] \trun kernel make (alias kmake)"
}
该函数调用链路如下;
./build.sh make-usage
run_build_hooks make-usage
run_hooks <SDK>/device/rockchip/common/build-hooks make-usage
......
<SDK>/device/rockchip/common/build-hooks/10-kernel.sh make-usage
<SDK>/device/rockchip/common/build-hooks/build-helper make-usage
make_usage # 位于build-helper
usage_hook # 位于10-kernel.sh
......
5.3 clean_hook
clean_hook
用于进行清理工作,切换到kernel
目录执行清理工作;
clean_hook()
{
[ ! -d kernel ] || make -C kernel distclean
rm -f "$RK_OUTDIR/linux-headers.tar"
}
同时删除<SDK>/output
目录下的linux-headers.tar
。
5.4 init_hook
init_hook
函数在构建的初始化阶段(init
阶段)被调用,用于处理INIT_CMDS
命令;
KERNELS=$(ls | grep kernel- || true)
# default
INIT_CMDS="default $KERNELS"
init_hook()
{
load_config RK_KERNEL_CFG
check_config RK_KERNEL_CFG &>/dev/null || return 0
# Priority: cmdline > custom env > .config > current kernel/ symlink
if echo $1 | grep -q "^kernel-"; then
export RK_KERNEL_VERSION=${1#kernel-}
echo "Using kernel version($RK_KERNEL_VERSION) from cmdline"
elif [ "$RK_KERNEL_VERSION" ]; then
export RK_KERNEL_VERSION=${RK_KERNEL_VERSION//\"/}
echo "Using kernel version($RK_KERNEL_VERSION) from environment"
else
load_config RK_KERNEL_VERSION
fi
update_kernel
}
INIT_CMDS
只有一个选项,即default
;该函数调用链路如下;
./build.sh kernel
......
run_build_hooks init kernel
run_hooks <SDK>/device/rockchip/common/build-hooks init kernel
......
<SDK>/device/rockchip/common/build-hooks/10-kernel.sh init kernel
<SDK>/device/rockchip/common/build-hooks/build-helper init kernel
try_hook init_hook 'default ' kernel
try_func init_hook default
init_hook default
......
......
在调用init_hook
函数时传入default
,走else
分支。
5.4.1 load_config
调用load_config
函数从$(OUTDIR)/.config
文件中加载配置项RK_KERNEL_VERSION
;
RK_KERNEL_VERSION="5.10"
并将该变量export
到当前shell
。
5.4.2 update_kernel
这个 update_kernel
函数的主要作用是更新或切换到指定版本的内核,并确保配置文件和目录结构一致;
update_kernel()
{
# Fallback to current kernel
RK_KERNEL_VERSION=${RK_KERNEL_VERSION:-$(kernel_version)}
# Fallback to 5.10 kernel
RK_KERNEL_VERSION=${RK_KERNEL_VERSION:-5.10}
# Update .config KERNEL_CONFIG='RK_KERNEL_VERSION="5.10"'
KERNEL_CONFIG="RK_KERNEL_VERSION=\"$RK_KERNEL_VERSION\""
# 在$(OUTDIR)/.config文件查找RK_KERNEL_VERSION="5.10"
if ! grep -q "^$KERNEL_CONFIG$" "$RK_CONFIG"; then
sed -i "s/^RK_KERNEL_VERSION=.*/$KERNEL_CONFIG/" "$RK_CONFIG"
"$SCRIPTS_DIR/mk-config.sh" olddefconfig &>/dev/null
fi
# kernel_version函数用于从kernel文件夹获取内核版本,这里是5.10,两者匹配因此这里返回
[ "$(kernel_version)" != "$RK_KERNEL_VERSION" ] || return 0
# Update kernel
KERNEL_DIR=kernel-$RK_KERNEL_VERSION
echo "switching to $KERNEL_DIR"
if [ ! -d "$KERNEL_DIR" ]; then
echo "$KERNEL_DIR not exist!"
exit 1
fi
rm -rf kernel
ln -rsf $KERNEL_DIR kernel
}
5.5 pre_build_hook
pre_build_hook
函数在构建前(pre-build
阶段)被调用,用于处理PRE_BUILD_CMDS
命令;
PRE_BUILD_CMDS="kernel-config kernel-make kmake"
pre_build_hook()
{
check_config RK_KERNEL_CFG || return 0
source "$SCRIPTS_DIR/kernel-helper"
echo "Toolchain for kernel:"
echo "${RK_KERNEL_TOOLCHAIN:-gcc}"
echo
case "$1" in
kernel-make | kmake)
shift
[ "$1" != cmds ] || shift
if [ "$DRY_RUN" ]; then
echo -e "\e[35mCommands of building ${@:-stuff}:\e[0m"
else
echo "=========================================="
echo " Start building $@"
echo "=========================================="
fi
if [ ! -r kernel/.config ]; then
run_command $KMAKE $RK_KERNEL_CFG \
$RK_KERNEL_CFG_FRAGMENTS
fi
run_command $KMAKE $@
;;
kernel-config)
do_build $@
;;
esac
if [ -z "$DRY_RUN" ]; then
finish_build $@
fi
}
PRE_BUILD_CMDS
选项有三个;该函数调用链路如下;
./build.sh kernel
......
run_build_hooks pre-build kernel
run_hooks <SDK>/device/rockchip/common/build-hooks pre-build kernel
......
<SDK>/device/rockchip/common/build-hooks/10-kernel.sh pre-build kernel
<SDK>/device/rockchip/common/build-hooks/build-helper pre-build kernel
try_hook pre_build_hook 'kernel-config kernel-make kmake' kernel
kernel不在支持的命令选项中,因此什么也不做
......
......
在调用pre_build__hook
函数时可以传入kernel-config kernel-make kmake
任一命令作为选项,pre_build_hook
将会执行不同的分支。
然而这里传入的是kernel
,并不在PRE_BUILD_CMDS
选项中,因此这里什么也不做。
5.6 build_hook
build_hook
函数在构建(build
阶段)被调用,用于处理BUILD_CMDS
命令;
# 构建阶段的编译选项
BUILD_CMDS="$KERNELS kernel modules"
build_hook()
{
# 检查配置项是否存在
check_config RK_KERNEL_DTS_NAME RK_KERNEL_CFG RK_BOOT_IMG || return 0
source "$SCRIPTS_DIR/kernel-helper"
# 输出工具链信息
echo "Toolchain for kernel:"
echo "${RK_KERNEL_TOOLCHAIN:-gcc}"
echo
# 不会匹配
if echo $1 | grep -q "^kernel-"; then
if [ "$RK_KERNEL_VERSION" != "${1#kernel-}" ]; then
echo -ne "\e[35m"
echo "Kernel version overrided: " \
"$RK_KERNEL_VERSION -> ${1#kernel-}"
echo -ne "\e[0m"
fi
fi
# 执行构建操作
do_build $@
if [ "$DRY_RUN" ]; then
return 0
fi
# 传入kernel,匹配
if echo $1 | grep -q "^kernel"; then
# 创建链接文件<SDK>/output>/firmware/boot.img -> <SDK>/kernel/boot.img
ln -rsf "kernel/$RK_BOOT_IMG" "$RK_FIRMWARE_DIR/boot.img"
"$SCRIPTS_DIR/check-power-domain.sh"
fi
finish_build build_$1
}
BUILD_CMDS
选项有两个;该函数调用链路如下;
./build.sh kernel
......
run_build_hooks build kernel
run_hooks <SDK>/device/rockchip/common/build-hooks build kernel
......
<SDK>/device/rockchip/common/build-hooks/10-kernel.sh build kernel
<SDK>/device/rockchip/common/build-hooks/build-helper build kernel
try_hook build_hook 'kernel modules' kernel
try_func build_hook kernel
build_hook kernel
......
......
在调用build_hook
函数时可以传入kernel modules
任一命令作为选项。
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
2023-07-09 Rockchip RK3399 - ALSA Proc Info