----------------------------------------------------------------------------------------------------------------------------
開發板 :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
函數代碼內容較長,我們只對重點內容進行分析:
點擊查看代碼
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
開啟調試模式,這樣腳本執行時會顯示每一行命令的執行情況。
錯誤處理:使用 trap
和 ERR
捕獲腳本中的錯誤。一旦發生錯誤,將調用 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-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%%:*}" ]; thenOPTIONS=$(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$"; thenOPTIONS=$(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; 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
執行清理工作; - 如果選項是
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" ]; 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.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
指定的選項中。
- 如果找到任何一個在任何一個選項中有匹配,則函數返回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=run
、option2=swim
、option3=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
:板載配置相關,支持的編譯選項有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.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
匹配到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
的值退出腳本,終止整個構建過程。
- 如果
在編譯過程中,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
支持的任意選項,比如all
、buildroot
、uboot
、kernel
等。
腳本入口代碼如下:
......# 跳轉到<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_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)# 將參數列表向左移動一位,去掉了第一個參數(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_hook
、init_hook
、pre_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_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
的值退出腳本,終止整個構建過程。
- 如果
- 設置日志文件:
三、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
根文件系統構建