在《Rockchip RK3566 - orangepi-build编译》我们介绍了SDK的编译流程,本节将会对编译脚本进行深入的分析。
----------------------------------------------------------------------------------------------------------------------------
开发板 :Orange Pi 3B开发板eMMC :32GBLPDDR4 :8GB
显示屏 :15.6英寸HDMI接口显示屏u-boot :2017.09linux :5.10
----------------------------------------------------------------------------------------------------------------------------
一、build.sh分析
orangepi-build编译命令是由build.sh脚本实现的,其脚本相对来说比较长,这里我们去掉一些非重点代码(比如docker),内容如下:
点击查看代码
# 获取当前脚本所在的目录路径
SRC="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
# check for whitespace in ${SRC} and exit for safety reasons 空字符串检验
grep -q "[[:space:]]" <<<"${SRC}" && { echo "\"${SRC}\" contains whitespace. Not supported. Aborting." >&2 ; exit 1 ; }
cd "${SRC}" || exit
# 启用调用跟踪
if [[ "${ORANGEPI_ENABLE_CALL_TRACING}" == "yes" ]]; then
set -T # inherit return/debug traps
mkdir -p "${SRC}"/output/debug
echo -n "" > "${SRC}"/output/debug/calls.txt
trap 'echo "${BASH_LINENO[@]}|${BASH_SOURCE[@]}|${FUNCNAME[@]}" >> ${SRC}/output/debug/calls.txt ;' RETURN
fi
# 执行./script/general.sh脚本
if [[ -f "${SRC}"/scripts/general.sh ]]; then
# shellcheck source=scripts/general.sh
source "${SRC}"/scripts/general.sh
else
echo "Error: missing build directory structure"
echo "Please clone the full repository by https://github.com/orangepi-xunlong/orangepi-build"
exit 255
fi
# 校验第一个参数
if [[ "${EUID}" == "0" ]] || [[ "${1}" == "vagrant" ]]; then
:
elif [[ "${1}" == docker || "${1}" == dockerpurge || "${1}" == docker-shell ]] && grep -q "$(whoami)" <(getent group docker); then
:
else
# 以root身份执行脚本
display_alert "This script requires root privileges, trying to use sudo" "" "wrn"
sudo "${SRC}/build.sh" "$@"
exit $?
fi
# 走else分支,为宿主机ubuntu 22.04系统安装基础包,比如dialog、uuid、uuid-runtime等
if [ "$OFFLINE_WORK" == "yes" ]; then
echo -e "\n"
display_alert "* " "You are working offline."
display_alert "* " "Sources, time and host will not be checked"
echo -e "\n"
sleep 3s
else
# check and install the basic utilities here
prepare_host_basic
fi
EXTER="${SRC}/external"
# Create userpatches directory if not exists
mkdir -p "${SRC}"/userpatches
# Create example configs if none found in userpatches
if ! ls "${SRC}"/userpatches/{config-example.conf,config-docker.conf,config-vagrant.conf} 1> /dev/null 2>&1; then
# Migrate old configs
if ls "${SRC}"/*.conf 1> /dev/null 2>&1; then
display_alert "Migrate config files to userpatches directory" "all *.conf" "info"
cp "${SRC}"/*.conf "${SRC}"/userpatches || exit 1
rm "${SRC}"/*.conf
[[ ! -L "${SRC}"/userpatches/config-example.conf ]] && ln -fs config-example.conf "${SRC}"/userpatches/config-default.conf || exit 1
fi
display_alert "Create example config file using template" "config-default.conf" "info"
# Create example config
if [[ ! -f "${SRC}"/userpatches/config-example.conf ]]; then
cp "${EXTER}"/config/templates/config-example.conf "${SRC}"/userpatches/config-example.conf || exit 1
ln -fs config-example.conf "${SRC}"/userpatches/config-default.conf || exit 1
fi
# Create Docker config
if [[ ! -f "${SRC}"/userpatches/config-docker.conf ]]; then
cp "${EXTER}"/config/templates/config-docker.conf "${SRC}"/userpatches/config-docker.conf || exit 1
fi
# Create Docker file
if [[ ! -f "${SRC}"/userpatches/Dockerfile ]]; then
cp "${EXTER}"/config/templates/Dockerfile "${SRC}"/userpatches/Dockerfile || exit 1
fi
# Create Vagrant config
if [[ ! -f "${SRC}"/userpatches/config-vagrant.conf ]]; then
cp "${EXTER}"/config/templates/config-vagrant.conf "${SRC}"/userpatches/config-vagrant.conf || exit 1
fi
# Create Vagrant file
if [[ ! -f "${SRC}"/userpatches/Vagrantfile ]]; then
cp "${EXTER}"/config/templates/Vagrantfile "${SRC}"/userpatches/Vagrantfile || exit 1
fi
fi
# 不会进入
if [[ -z "${CONFIG}" && -n "$1" && -f "${SRC}/userpatches/config-$1.conf" ]]; then
CONFIG="userpatches/config-$1.conf"
shift
fi
# usind default if custom not found
if [[ -z "${CONFIG}" && -f "${SRC}/userpatches/config-default.conf" ]]; then
CONFIG="userpatches/config-default.conf"
fi
# source build configuration file
CONFIG_FILE="$(realpath "${CONFIG}")"
if [[ ! -f "${CONFIG_FILE}" ]]; then
display_alert "Config file does not exist" "${CONFIG}" "error"
exit 254
fi
CONFIG_PATH=$(dirname "${CONFIG_FILE}")
# Source the extensions manager library at this point, before sourcing the config.
# This allows early calls to enable_extension(), but initialization proper is done later.
# shellcheck source=scripts/extensions.sh
source "${SRC}"/scripts/extensions.sh
display_alert "Using config file" "${CONFIG_FILE}" "info"
pushd "${CONFIG_PATH}" > /dev/null || exit
# shellcheck source=/dev/null
source "${CONFIG_FILE}"
popd > /dev/null || exit
[[ -z "${USERPATCHES_PATH}" ]] && USERPATCHES_PATH="${CONFIG_PATH}"
# Script parameters handling
while [[ "${1}" == *=* ]]; do
parameter=${1%%=*}
value=${1##*=}
shift
display_alert "Command line: setting $parameter to" "${value:-(empty)}" "info"
eval "$parameter=\"$value\""
done
if [[ "${BUILD_ALL}" == "yes" || "${BUILD_ALL}" == "demo" ]]; then
# shellcheck source=scripts/build-all-ng.sh
source "${SRC}"/scripts/build-all-ng.sh
else
# shellcheck source=scripts/main.sh
source "${SRC}"/scripts/main.sh
fi
接下来我们针对该脚本内容从上往下依次分析。
1.1 开启调用追踪
如果我们需要启动调用跟踪,在执行命令时设置ORANGEPI_ENABLE_CALL_TRACING即可,比如:
ORANGEPI_ENABLE_CALL_TRACING=yes && ./build.sh
如果环境变量 ORANGEPI_ENABLE_CALL_TRACING 设置为 "yes",将启用函数调用跟踪;
if [[ "${ORANGEPI_ENABLE_CALL_TRACING}" == "yes" ]]; then
set -T # inherit return/debug traps
# 创建调试目录
mkdir -p "${SRC}"/output/debug
# 初始化一个空的calls.txt文件,用于存储调试信息
echo -n "" > "${SRC}"/output/debug/calls.txt
# 设置一个陷阱,在脚本退出时记录函数调用详情
trap 'echo "${BASH_LINENO[@]}|${BASH_SOURCE[@]}|${FUNCNAME[@]}" >> ${SRC}/output/debug/calls.txt ;' RETURN
fi
1.2 执行general.sh脚本
接着是使用source命令执行general.sh脚本,该脚本位于<SDK>/scripts目录下;
if [[ -f "${SRC}"/scripts/general.sh ]]; then
source "${SRC}"/scripts/general.sh
else
echo "错误:缺少构建目录结构"
echo "请通过 https://github.com/orangepi-xunlong/orangepi-build 克隆完整的存储库"
exit 255
fi
使用source命令执行脚本的一些注意事项:
- 环境变量和函数的影响:被执行的脚本可以修改当前
shell的环境变量和定义的函数,这些修改将持续影响到当前shell的会话,直到会话结束或者重新定义了这些变量和函数。 - 退出状态:被执行的脚本的退出状态(即最后一个命令的退出状态)会影响到当前
shell。可以通过$?变量来获取最近一次执行命令的退出状态; - 交互性:与直接执行脚本不同,使用
source执行脚本时,不会创建新的shell环境,因此不会有新的子shell进程。这使得它适合于需要脚本和当前shell环境之间相互影响的场景,例如定义函数或设置环境变量。
1.3 执行prepare_host_basic脚本
prepare_host_basic脚本是在general.sh中定义的,为宿主机ubuntu 22.04系统安装基础包,比如dialog、uuid 、uuid-runtime等。
1.4 创建userpatches目录
如果userpatches目录不存在则创建userpatches目录;
EXTER="${SRC}/external"
# Create userpatches directory if not exists
mkdir -p "${SRC}"/userpatches
1.4.1 创建example config
接着这段脚本代码主要用于检查和创建示例配置文件和相关文件,如果在 ${SRC}/userpatches 目录下找不到特定的配置文件,则创建相应的示例配置文件和相关文件;
# Create example configs if none found in userpatches 检查是否存在示例配置文件,如果都不存在则进入
if ! ls "${SRC}"/userpatches/{config-example.conf,config-docker.conf,config-vagrant.conf} 1> /dev/null 2>&1; then
# Migrate old configs 迁移旧配置文件,不会进入
if ls "${SRC}"/*.conf 1> /dev/null 2>&1; then
display_alert "Migrate config files to userpatches directory" "all *.conf" "info"
cp "${SRC}"/*.conf "${SRC}"/userpatches || exit 1
rm "${SRC}"/*.conf
[[ ! -L "${SRC}"/userpatches/config-example.conf ]] && ln -fs config-example.conf "${SRC}"/userpatches/config-default.conf || exit 1
fi
display_alert "Create example config file using template" "config-default.conf" "info"
# Create example config
if [[ ! -f "${SRC}"/userpatches/config-example.conf ]]; then
cp "${EXTER}"/config/templates/config-example.conf "${SRC}"/userpatches/config-example.conf || exit 1
ln -fs config-example.conf "${SRC}"/userpatches/config-default.conf || exit 1
fi
# Create Docker config
if [[ ! -f "${SRC}"/userpatches/config-docker.conf ]]; then
cp "${EXTER}"/config/templates/config-docker.conf "${SRC}"/userpatches/config-docker.conf || exit 1
fi
# Create Docker file
if [[ ! -f "${SRC}"/userpatches/Dockerfile ]]; then
cp "${EXTER}"/config/templates/Dockerfile "${SRC}"/userpatches/Dockerfile || exit 1
fi
# Create Vagrant config
if [[ ! -f "${SRC}"/userpatches/config-vagrant.conf ]]; then
cp "${EXTER}"/config/templates/config-vagrant.conf "${SRC}"/userpatches/config-vagrant.conf || exit 1
fi
# Create Vagrant file
if [[ ! -f "${SRC}"/userpatches/Vagrantfile ]]; then
cp "${EXTER}"/config/templates/Vagrantfile "${SRC}"/userpatches/Vagrantfile || exit 1
fi
fi
检查是否存在示例配置文件,如果都不存在:
- 迁移旧配置文件:正常不会进入该分支;
- 创建示例配置文件:如果
./userpatches/config-example.conf不存在,则从./external/config/templates/目录复制config-example.conf到./userpatches/目录,并创建一个指向config-example.conf的符号链接config-default.conf; - 创建
Docker相关文件:如果./userpatches/config-docker.conf和./userpatches/Dockerfile不存在,则分别从./external/config/templates/目录复制config-docker.conf和Dockerfile到./userpatches/目录; - 创建
Vagrant相关文件:如果./userpatches/config-vagrant.conf和./userpatches/Vagrantfile不存在,则分别从./external/config/templates/目录复制config-vagrant.conf和Vagrantfile到${SRC}/userpatches/目录。
因此执行完成后会在userpatches目录下创建config-default.conf、config-docker.conf、config-example.conf、config-vagrant.conf、Vagrantfile文件;
root@ubuntu:/work/sambashare/rk3566/orangepi-build$ ll userpatches/
lrwxrwxrwx 1 root root 19 7月 10 14:20 config-default.conf -> config-example.conf
-rw-r--r-- 1 root root 5846 7月 10 14:20 config-docker.conf
-rw-r--r-- 1 root root 1274 7月 10 17:57 config-example.conf
-rw-r--r-- 1 root root 715 7月 10 14:20 config-vagrant.conf
-rw-r--r-- 1 root root 3111 7月 10 14:20 Dockerfile
-rw-r--r-- 1 root root 1715 7月 10 14:20 Vagrantfile
1.4.2 使用默认配置
确定要使用的配置文件路径,并确保该配置文件存在。如果未找到任何自定义配置文件 ($1),则将使用默认配置文件 (config-default.conf);
# 检查自定义配置文件的存在性,由于参数1为指定因此不会进入
if [[ -z "${CONFIG}" && -n "$1" && -f "${SRC}/userpatches/config-$1.conf" ]]; then
CONFIG="userpatches/config-$1.conf"
shift
fi
# usind default if custom not found
if [[ -z "${CONFIG}" && -f "${SRC}/userpatches/config-default.conf" ]]; then
CONFIG="userpatches/config-default.conf"
fi
# source build configuration file, 获取配置文件绝对路径,<SDK>/userpatches/config-example.conf
CONFIG_FILE="$(realpath "${CONFIG}")"
# 检查配置文件的实际存在性,由于文件的确存在因此不会进入
if [[ ! -f "${CONFIG_FILE}" ]]; then
display_alert "Config file does not exist" "${CONFIG}" "error"
exit 254
fi
# 获取配置文件所在目录,<SDK>/userpatches
CONFIG_PATH=$(dirname "${CONFIG_FILE}")
1.5 执行extensions.sh脚本
接着是使用source命令执行extensions.sh脚本,该脚本位于<SDK>/scripts目录下;
# Source the extensions manager library at this point, before sourcing the config.
# This allows early calls to enable_extension(), but initialization proper is done later.
# shellcheck source=scripts/extensions.sh
source "${SRC}"/scripts/extensions.sh
1.6 加载配置文件
接着是输出当前使用的配置文件夹信息,然后切换工作目录并加载配置文件:
display_alert "Using config file" "${CONFIG_FILE}" "info"
# 将当前工作目录切换到 ${CONFIG_PATH}
pushd "${CONFIG_PATH}" > /dev/null || exit
# shellcheck source=/dev/null,加载${CONFIG_FILE}中的shell脚本
source "${CONFIG_FILE}"
# 恢复之前的工作目录
popd > /dev/null || exit
# 设置USERPATCHES_PATH=${CONFIG_PATH}
[[ -z "${USERPATCHES_PATH}" ]] && USERPATCHES_PATH="${CONFIG_PATH}"
# Script parameters handling,由于未指定参数1因此不会进入
while [[ "${1}" == *=* ]]; do
parameter=${1%%=*}
value=${1##*=}
shift
display_alert "Command line: setting $parameter to" "${value:-(empty)}" "info"
eval "$parameter=\"$value\""
done
1.6.1 config-example.conf
CONFIG_FILE被定义为了<SDK>/userpatches/config-example.conf,内容如下:
KERNEL_CONFIGURE="" # leave empty to select each time, set to "yes" or "no" to skip dialog prompt
CLEAN_LEVEL="debs,oldcache" # comma-separated list of clean targets: "make" = make clean for selected kernel and u-boot,
# "debs" = delete packages in "./output/debs" for current branch and family,
# "alldebs" = delete all packages in "./output/debs", "images" = delete "./output/images",
# "cache" = delete "./output/cache", "sources" = delete "./sources"
# "oldcache" = remove old cached rootfs except for the newest 8 files
DEST_LANG="en_US.UTF-8" # sl_SI.UTF-8, en_US.UTF-8
# advanced
EXTERNAL_NEW="prebuilt" # compile and install or install prebuilt additional packages
INSTALL_HEADERS="" # install kernel headers package
LIB_TAG="master" # change to "branchname" to use any branch currently available.
USE_TORRENT="yes" # use torrent network for faster toolchain and cache download
DOWNLOAD_MIRROR="china" # set to "china" to use mirrors.tuna.tsinghua.edu.cn
BOARD=""
BRANCH=""
RELEASE=""
WIREGUARD="no"
BUILD_KSRC="no"
INSTALL_KSRC="no"
IGNORE_UPDATES="yes"
COMPRESS_OUTPUTIMAGE="no"
NO_APT_CACHER="yes"
#install_balena_etcher="yes"
#install_zfs="yes"
#install_docker="yes"
#install_chromium="yes"
#install_firefox="yes"
该脚本中定义的一些变量将会被加载到当前shell中。
1.7 进入main.sh
脚本的最后使用source命令执行main.sh脚本;
if [[ "${BUILD_ALL}" == "yes" || "${BUILD_ALL}" == "demo" ]]; then
# shellcheck source=scripts/build-all-ng.sh
source "${SRC}"/scripts/build-all-ng.sh
else
# shellcheck source=scripts/main.sh
source "${SRC}"/scripts/main.sh
fi
二、general.sh分析
general.sh是一个通用脚本,该脚本位于<SDK>/scripts目录下,脚本提供了通用功能;
cleaning:exit_with_error:get_package_list_hash:create_sources_list:clean_up_git:waiter_local_git:fetch_from_repo:improved_git:distro_menu:addtorepo:repo-remove-old-packages:wait_for_package_manager:install_pkg_deb:prepare_host_basic:prepare_host:webseed:download_and_verify:show_developer_warning:show_checklist_variables:
2.1 display_alert
display_alert用于在终端中显示带有不同类型标签的警告信息;
#--------------------------------------------------------------------------------------------------------------------------------
# Let's have unique way of displaying alerts
#--------------------------------------------------------------------------------------------------------------------------------
display_alert()
{
# log function parameters to install.log
[[ -n "${DEST}" ]] && echo "Displaying message: $@" >> "${DEST}"/${LOG_SUBPATH}/output.log
local tmp=""
[[ -n $2 ]] && tmp="[\e[0;33m $2 \x1B[0m]"
# 根据第三个参数 $3 的不同取值,输出不同类型的警告信息
case $3 in
err)
# 红色文字
echo -e "[\e[0;31m error \x1B[0m] $1 $tmp"
;;
wrn)
# 洋红色文字
echo -e "[\e[0;35m warn \x1B[0m] $1 $tmp"
;;
ext)
# 绿色文字
echo -e "[\e[0;32m o.k. \x1B[0m] \e[1;32m$1\x1B[0m $tmp"
;;
info)
# 加粗绿色文字
echo -e "[\e[0;32m o.k. \x1B[0m] $1 $tmp"
;;
*)
# 绿色样式(作为通用信息)
echo -e "[\e[0;32m .... \x1B[0m] $1 $tmp"
;;
esac
}
2.2 prepare_host_basic
prepare_host_basic为宿主机ubuntu 22.04系统安装基础包,比如dialog、uuid 、uuid-runtime等;
# prepare_host_basic
#
# * installs only basic packages
#
prepare_host_basic()
{
# command:package1 package2 ...
# list of commands that are neeeded:packages where this command is
local check_pack install_pack
local checklist=(


1581

被折叠的 条评论
为什么被折叠?



