程序项目代做,有需求私信(vue、React、Java、爬虫、电路板设计、嵌入式linux等)

Rockchip RK3566 - orangepi-build脚本分析

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

开发板 :Orange Pi 3B开发板
eMMC32GB
LPDDR42GB
显示屏 :15.6英寸HDMI接口显示屏
u-boot2017.09
linux6.6
----------------------------------------------------------------------------------------------------------------------------

在《Rockchip RK3566 - orangepi-build编译》我们介绍了SDK的编译流程,本节将会对编译脚本进行深入的分析。

一、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",将启用函数调用跟踪,并将日志记录到./output/debug/calls.txt

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系统安装基础包,比如dialoguuiduuid-runtime等,这个我们在单独介绍general.sh时再来说。

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.confDockerfile./userpatches/目录;
  • 创建Vagrant相关文件:如果./userpatches/config-vagrant.conf./userpatches/Vagrantfile不存在,则分别从 ./external/config/templates/目录复制config-vagrant.confVagrantfile${SRC}/userpatches/目录。

因此执行完成后会在userpatches目录下创建config-default.confconfig-docker.confconfig-example.confconfig-vagrant.confVagrantfile文件;

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 使用config-default.conf

确定要使用的配置文件路径,并确保该配置文件存在。如果未找到任何自定义配置文件 ($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, 获取配置文件绝对路径,由于config-default.conf链接到了config-example.conf,因此此处值为<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

CONFIG_FILE被定义为了<SDK>/userpatches/config-example.conf,该脚本中定义的一些变量将会被加载到当前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

二、main.sh分析

main.sh脚本位于<SDK>/scripts目录下,该脚本比较重要,可以认为就是编译的主程序。

这里我们去掉一些非重点代码(比如docker),内容如下:

点击查看代码
#!/bin/bash
#
# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
#
# Main program
#
cleanup_list() {
        local varname="${1}"
        local list_to_clean="${!varname}"
        list_to_clean="${list_to_clean#"${list_to_clean%%[![:space:]]*}"}"
        list_to_clean="${list_to_clean%"${list_to_clean##*[![:space:]]}"}"
        echo ${list_to_clean}
}

# default umask for root is 022 so parent directories won't be group writeable without this
# this is used instead of making the chmod in prepare_host() recursive
# 设置文件创建的默认权限
umask 002

# destination 确定目标路径
if [ -d "$CONFIG_PATH/output" ]; then
        DEST="${CONFIG_PATH}"/output
else
        DEST="${SRC}"/output
fi

# 进入
[[ -z $REVISION ]] && REVISION="3.0.8"

# 进入
[[ $DOWNLOAD_MIRROR == "china" ]] && NTP_SERVER="cn.pool.ntp.org"

# 进入,根据终端的当前尺寸设置 TTY_X(宽度)和 TTY_Y(高度)
if [[ $BUILD_ALL != "yes" ]]; then
        # override stty size
        [[ -n $COLUMNS ]] && stty cols $COLUMNS
        [[ -n $LINES ]] && stty rows $LINES
        TTY_X=$(($(stty size | awk '{print $2}')-6))                    # determine terminal width
        TTY_Y=$(($(stty size | awk '{print $1}')-6))                    # determine terminal height
fi

# We'll use this title on all menus  设置标题和菜单
backtitle="Orange Pi building script, http://www.orangepi.org"
titlestr="Choose an option"

# Warnings mitigation  设置语言和终端的字符集编码
[[ -z $LANGUAGE ]] && export LANGUAGE="en_US:en"            # set to english if not set
[[ -z $CONSOLE_CHAR ]] && export CONSOLE_CHAR="UTF-8"       # set console to UTF-8 if not set
# Libraries include

# shellcheck source=debootstrap.sh
source "${SRC}"/scripts/debootstrap.sh  # system specific install
# shellcheck source=image-helpers.sh
source "${SRC}"/scripts/image-helpers.sh        # helpers for OS image building
# shellcheck source=distributions.sh
source "${SRC}"/scripts/distributions.sh        # system specific install
# shellcheck source=desktop.sh
source "${SRC}"/scripts/desktop.sh              # desktop specific install
# shellcheck source=compilation.sh
source "${SRC}"/scripts/compilation.sh  # patching and compilation of kernel, uboot, ATF
# shellcheck source=compilation-prepare.sh
#source "${SRC}"/scripts/compilation-prepare.sh # drivers that are not upstreamed
# shellcheck source=makeboarddeb.sh
source "${SRC}"/scripts/makeboarddeb.sh         # board support package
# shellcheck source=general.sh
source "${SRC}"/scripts/general.sh              # general functions
# shellcheck source=chroot-buildpackages.sh
source "${SRC}"/scripts/chroot-buildpackages.sh # chroot packages building
# shellcheck source=pack.sh
source "${SRC}"/scripts/pack-uboot.sh

# set log path 设置输出日志路径
LOG_SUBPATH=${LOG_SUBPATH:=debug}

# compress and remove old logs
mkdir -p "${DEST}"/${LOG_SUBPATH}
(cd "${DEST}"/${LOG_SUBPATH} && tar -czf logs-"$(<timestamp)".tgz ./*.log) > /dev/null 2>&1
rm -f "${DEST}"/${LOG_SUBPATH}/*.log > /dev/null 2>&1
date +"%d_%m_%Y-%H_%M_%S" > "${DEST}"/${LOG_SUBPATH}/timestamp

# delete compressed logs older than 7 days
(cd "${DEST}"/${LOG_SUBPATH} && find . -name '*.tgz' -mtime +7 -delete) > /dev/null

# 设置缓存目录
SHOW_WARNING=yes
if [[ $USE_CCACHE != no ]]; then
        CCACHE=ccache
        export PATH="/usr/lib/ccache:$PATH"
        # private ccache directory to avoid permission issues when using build script with "sudo"
        # see https://ccache.samba.org/manual.html#_sharing_a_cache for alternative solution
        [[ $PRIVATE_CCACHE == yes ]] && export CCACHE_DIR=$EXTER/cache/ccache
else
        CCACHE=""
fi

# if BUILD_OPT, KERNEL_CONFIGURE, BOARD, BRANCH or RELEASE are not set, display selection menu
# 创建了一个用户界面,用户可以从菜单中选择构建选项(u-boot、kernel、rootfs、image)
if [[ -z $BUILD_OPT ]]; then

        options+=("u-boot"       "U-boot package")
        options+=("kernel"       "Kernel package")
        options+=("rootfs"       "Rootfs and all deb packages")
        options+=("image"        "Full OS image for flashing")

        menustr="Compile image | rootfs | kernel | u-boot"
        BUILD_OPT=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" --notags \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)

        unset options
        [[ -z $BUILD_OPT ]] && exit_with_error "No option selected"
        [[ $BUILD_OPT == rootfs ]] && ROOT_FS_CREATE_ONLY="yes"
fi

# 如果选择了kernel或image则进入,选择内核配置
if [[ ${BUILD_OPT} =~ kernel|image ]]; then

        if [[ -z $KERNEL_CONFIGURE ]]; then

                options+=("no" "Do not change the kernel configuration")
                options+=("yes" "Show a kernel configuration menu before compilation")

                menustr="Select the kernel configuration."
                KERNEL_CONFIGURE=$(whiptail --title "${titlestr}" --backtitle "$backtitle" --notags \
                                                 --menu "${menustr}" $TTY_Y $TTY_X $((TTY_Y - 8)) \
                                                 --cancel-button Exit --ok-button Select "${options[@]}" \
                                                 3>&1 1>&2 2>&3)

                unset options
                [[ -z $KERNEL_CONFIGURE ]] && exit_with_error "No option selected"
        fi
fi

# 进入,选择开发板型号
if [[ -z $BOARD ]]; then
        #options+=("orangepir1"                 "Allwinner H2+ quad core 256MB RAM WiFi SPI 2xETH")
        #options+=("orangepizero"               "Allwinner H2+ quad core 256MB/512MB RAM WiFi SPI")
        #options+=("orangepipc"                 "Allwinner H3 quad core 1GB RAM")
        #options+=("orangepipcplus"             "Allwinner H3 quad core 1GB RAM WiFi eMMC")
        #options+=("orangepione"                        "Allwinner H3 quad core 512MB RAM")
        #options+=("orangepilite"               "Allwinner H3 quad core 512MB RAM WiFi")
        #options+=("orangepiplus"               "Allwinner H3 quad core 1GB/2GB RAM WiFi GBE eMMC")
        #options+=("orangepiplus2e"             "Allwinner H3 quad core 2GB RAM WiFi GBE eMMC")
        #options+=("orangepizeroplus2h3"        "Allwinner H3 quad core 512MB RAM WiFi/BT eMMC")
        #options+=("orangepipch5"                "Allwinner H5 quad core 1GB RAM")
        #options+=("orangepipc2"                        "Allwinner H5 quad core 1GB RAM GBE SPI")
        #options+=("orangepioneh5"               "Allwinner H5 quad core 512MB/1GB RAM")
        #options+=("orangepiprime"              "Allwinner H5 quad core 2GB RAM GBE WiFi/BT")
        #options+=("orangepizeroplus"           "Allwinner H5 quad core 512MB RAM GBE WiFi SPI")
        #options+=("orangepizeroplus2h5"                "Allwinner H5 quad core 512MB RAM WiFi/BT eMMC")
        options+=("orangepi3"                   "Allwinner H6 quad core 1GB/2GB RAM GBE WiFi/BT eMMC USB3")
        options+=("orangepi3-lts"               "Allwinner H6 quad core 2GB RAM GBE WiFi/BT-AW859A eMMC USB3")
        #options+=("orangepilite2"              "Allwinner H6 quad core 1GB RAM WiFi/BT USB3")
        #options+=("orangepioneplus"            "Allwinner H6 quad core 1GB RAM GBE")
        options+=("orangepizero2"               "Allwinner H616 quad core 512MB/1GB RAM WiFi/BT GBE SPI")
        #options+=("orangepizero2-b"            "Allwinner H616 quad core 512MB/1GB RAM WiFi/BT GBE SPI")
        #options+=("orangepizero2-lts"           "Allwinner H616 quad core 1.5GB RAM WiFi/BT GBE SPI")
        options+=("orangepizero3"               "Allwinner H618 quad core 1GB/1.5GB/2GB/4GB RAM WiFi/BT GBE SPI")
        options+=("orangepizero2w"              "Allwinner H618 quad core 1GB/1.5GB/2GB/4GB RAM WiFi/BT SPI")
        #options+=("orangepir1b"                        "Allwinner H618 quad core 1.5GB/2GB/4GB RAM WiFi/BT GBE SPI")
        #options+=("orangepi400"                        "Allwinner H616 quad core 4GB RAM WiFi/BT GBE eMMC VGA")
        options+=("orangepi4"                   "Rockchip  RK3399 hexa core 4GB RAM GBE eMMC USB3 USB-C WiFi/BT")
        options+=("orangepi4-lts"                 "Rockchip  RK3399 hexa core 4GB RAM GBE eMMC USB3 USB-C WiFi/BT")
        options+=("orangepi800"                 "Rockchip  RK3399 hexa core 4GB RAM GBE eMMC USB3 USB-C WiFi/BT VGA")
        options+=("orangepi5"                 "Rockchip  RK3588S octa core 4-16GB RAM GBE USB3 USB-C NVMe")
        options+=("orangepicm5"                 "Rockchip  RK3588S octa core 4-16GB RAM GBE USB3 USB-C")
        options+=("orangepicm5-tablet"           "Rockchip  RK3588S octa core 4-16GB RAM USB3 USB-C WiFi/BT")
        options+=("orangepi5b"                 "Rockchip  RK3588S octa core 4-16GB RAM GBE USB3 USB-C WiFi/BT eMMC")
        #options+=("orangepitab"                 "Rockchip  RK3588S octa core 4-16GB RAM USB-C WiFi/BT NVMe")
        #options+=("orangepi900"                 "Rockchip  RK3588 octa core 4-16GB RAM 2.5GBE USB3 USB-C WiFi/BT NVMe")
        options+=("orangepi5pro"                 "Rockchip  RK3588S octa core 4-16GB RAM GBE USB3 WiFi/BT NVMe eMMC")
        options+=("orangepi5max"                 "Rockchip  RK3588 octa core 4-16GB RAM 2.5GBE USB3 WiFi/BT NVMe eMMC")
        options+=("orangepi5plus"                 "Rockchip  RK3588 octa core 4-32GB RAM 2.5GBE USB3 USB-C WiFi/BT NVMe eMMC")
        options+=("orangepicm4"                 "Rockchip  RK3566 quad core 2-8GB RAM GBE eMMC USB3 NvMe WiFi/BT")
        options+=("orangepi3b"                  "Rockchip  RK3566 quad core 2-8GB RAM GBE eMMC USB3 NvMe WiFi/BT")
        #options+=("orangepir1plus"              "Rockchip  RK3328 quad core 1GB RAM 2xGBE USB2 SPI")
        #options+=("orangepi3plus"              "Amlogic S905D3 quad core 2/4GB RAM SoC eMMC GBE USB3 SPI WiFi/BT")

        menustr="Please choose a Board."
        BOARD=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)

        unset options
        [[ -z $BOARD ]] && exit_with_error "No option selected"
fi

# 加载板载配置文件external/config/boards/orangepi3b.conf
BOARD_TYPE="conf"
# shellcheck source=/dev/null
source "${EXTER}/config/boards/${BOARD}.${BOARD_TYPE}"
LINUXFAMILY="${BOARDFAMILY}"

[[ -z $KERNEL_TARGET ]] && exit_with_error "Board configuration does not define valid kernel config"

# 进入,选择内核版本
if [[ -z $BRANCH ]]; then
        options=()
        [[ $KERNEL_TARGET == *current* ]] && options+=("current" "Recommended. Come with best support")
        [[ $KERNEL_TARGET == *legacy* ]] && options+=("legacy" "Old stable / Legacy")
        [[ $KERNEL_TARGET == *next* ]] && options+=("next" "Use the latest kernel")

        menustr="Select the target kernel branch\nExact kernel versions depend on selected board"
        # do not display selection dialog if only one kernel branch is available
        if [[ "${#options[@]}" == 2 ]]; then
                BRANCH="${options[0]}"
        else
                BRANCH=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" \
                                  --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                                  --cancel-button Exit --ok-button Select "${options[@]}" \
                                  3>&1 1>&2 2>&3)
        fi
        unset options
        [[ -z $BRANCH ]] && exit_with_error "No kernel branch selected"
        [[ $BRANCH == dev && $SHOW_WARNING == yes ]] && show_developer_warning

fi

# 如果选择了kernel或image,并且未设置RELEASE则进入选择Linux发行版的类型
if [[ $BUILD_OPT =~ rootfs|image && -z $RELEASE ]]; then
        options=()
        distros_options
        menustr="Select the target OS release package base"
        RELEASE=$(whiptail --title "Choose a release package base" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)
        #echo "options : ${options}"
        [[ -z $RELEASE ]] && exit_with_error "No release selected"
        unset options
fi

# don't show desktop option if we choose minimal build,如果设置了Minimal版本版本
[[ $BUILD_MINIMAL == yes ]] && BUILD_DESKTOP=no

# 选择镜像的类型,有桌面和无桌面
if [[ $BUILD_OPT =~ rootfs|image && -z $BUILD_DESKTOP ]]; then
        # read distribution support status which is written to the orangepi-release file
        set_distribution_status

        options=()
        options+=("no" "Image with console interface (server)")
        options+=("yes" "Image with desktop environment")

        menustr="Select the target image type"
        BUILD_DESKTOP=$(whiptail --title "Choose image type" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)
        unset options
        [[ -z $BUILD_DESKTOP ]] && exit_with_error "No option selected"
        if [[ ${BUILD_DESKTOP} == "yes" ]]; then
                BUILD_MINIMAL=no
                SELECTED_CONFIGURATION="desktop"
        fi

fi

# 对于无桌面的镜像,选择Standard版本或者Minimal版本
if [[ $BUILD_OPT =~ rootfs|image && $BUILD_DESKTOP == no && -z $BUILD_MINIMAL ]]; then
        options=()
        options+=("no" "Standard image with console interface")
        options+=("yes" "Minimal image with console interface")
        menustr="Select the target image type"
        BUILD_MINIMAL=$(whiptail --title "Choose image type" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)
        unset options
        [[ -z $BUILD_MINIMAL ]] && exit_with_error "No option selected"
        if [[ $BUILD_MINIMAL == "yes" ]]; then
                SELECTED_CONFIGURATION="cli_minimal"
        else
                SELECTED_CONFIGURATION="cli_standard"
        fi

fi

#prevent conflicting setup 处理冲突配置
if [[ $BUILD_DESKTOP == "yes" ]]; then
        BUILD_MINIMAL=no
        SELECTED_CONFIGURATION="desktop"
elif [[ $BUILD_MINIMAL != "yes" || -z "${BUILD_MINIMAL}" ]]; then
        BUILD_MINIMAL=no # Just in case BUILD_MINIMAL is not defined
        BUILD_DESKTOP=no
        SELECTED_CONFIGURATION="cli_standard"
elif [[ $BUILD_MINIMAL == "yes" ]]; then
        BUILD_DESKTOP=no
        SELECTED_CONFIGURATION="cli_minimal"
fi

#shellcheck source=configuration.sh
source "${SRC}"/scripts/configuration.sh

# optimize build time with 100% CPU usage 定系统中的CPU核心数,并根据USEALLCORES的设置动态调整并行编译线程数
CPUS=$(grep -c 'processor' /proc/cpuinfo)
if [[ $USEALLCORES != no ]]; then
        CTHREADS="-j$((CPUS + CPUS/2))"
else
        CTHREADS="-j1"
fi

call_extension_method "post_determine_cthreads" "config_post_determine_cthreads" << 'POST_DETERMINE_CTHREADS'
*give config a chance modify CTHREADS programatically. A build server may work better with hyperthreads-1 for example.*
Called early, before any compilation work starts.
POST_DETERMINE_CTHREADS

if [[ $BETA == yes ]]; then
        IMAGE_TYPE=nightly
elif [[ $BETA != "yes" && $BUILD_ALL == yes && -n $GPG_PASS ]]; then
        IMAGE_TYPE=stable
else
        IMAGE_TYPE=user-built        # 走这里
fi

branch2dir() {
        [[ "${1}" == "head" ]] && echo "HEAD" || echo "${1##*:}"
}

BOOTSOURCEDIR="${BOOTDIR}/$(branch2dir "${BOOTBRANCH}")"
LINUXSOURCEDIR="${KERNELDIR}/$(branch2dir "${KERNELBRANCH}")"
[[ -n $ATFSOURCE ]] && ATFSOURCEDIR="${ATFDIR}/$(branch2dir "${ATFBRANCH}")"

BSP_CLI_PACKAGE_NAME="orangepi-bsp-cli-${BOARD}"
BSP_CLI_PACKAGE_FULLNAME="${BSP_CLI_PACKAGE_NAME}_${REVISION}_${ARCH}"
BSP_DESKTOP_PACKAGE_NAME="orangepi-bsp-desktop-${BOARD}"
BSP_DESKTOP_PACKAGE_FULLNAME="${BSP_DESKTOP_PACKAGE_NAME}_${REVISION}_${ARCH}"

CHOSEN_UBOOT=linux-u-boot-${BRANCH}-${BOARD}
CHOSEN_KERNEL=linux-image-${BRANCH}-${LINUXFAMILY}
CHOSEN_ROOTFS=${BSP_CLI_PACKAGE_NAME}
CHOSEN_DESKTOP=orangepi-${RELEASE}-desktop-${DESKTOP_ENVIRONMENT}
CHOSEN_KSRC=linux-source-${BRANCH}-${LINUXFAMILY}

do_default() {

start=$(date +%s)

# Check and install dependencies, directory structure and settings
# The OFFLINE_WORK variable inside the function
prepare_host

[[ "${JUST_INIT}" == "yes" ]] && exit 0

[[ $CLEAN_LEVEL == *sources* ]] && cleaning "sources"

# fetch_from_repo <url> <dir> <ref> <subdir_flag>
# ignore updates help on building all images - for internal purposes
if [[ ${IGNORE_UPDATES} != yes ]]; then

        display_alert "Downloading sources" "" "info"

        [[ $BUILD_OPT =~ u-boot|image ]] && fetch_from_repo "$BOOTSOURCE" "$BOOTDIR" "$BOOTBRANCH" "yes"
        [[ $BUILD_OPT =~ kernel|image ]] && fetch_from_repo "$KERNELSOURCE" "$KERNELDIR" "$KERNELBRANCH" "yes"

        if [[ -n ${ATFSOURCE} ]]; then
                [[ ${BUILD_OPT} =~ u-boot|image ]] && fetch_from_repo "$ATFSOURCE" "${EXTER}/cache/sources/$ATFDIR" "$ATFBRANCH" "yes"
        fi

        if [[ ${BOARDFAMILY} == "rockchip-rk356x" && $RELEASE =~ bullseye|focal|jammy|raspi ]]; then
                [[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/rk35xx_packages" "branch:rk35xx_packages"
        fi

        if [[ ${BOARD} =~ orangepi3|orangepi3-lts && $RELEASE =~ bullseye && $BRANCH == current ]]; then
                [[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/ffmpeg_kodi_${RELEASE}" "branch:ffmpeg_kodi_${RELEASE}"
        fi

        call_extension_method "fetch_sources_tools"  <<- 'FETCH_SOURCES_TOOLS'
        *fetch host-side sources needed for tools and build*
        Run early to fetch_from_repo or otherwise obtain sources for needed tools.
        FETCH_SOURCES_TOOLS

        call_extension_method "build_host_tools"  <<- 'BUILD_HOST_TOOLS'
        *build needed tools for the build, host-side*
        After sources are fetched, build host-side tools needed for the build.
        BUILD_HOST_TOOLS
        if [[ ${BOARDFAMILY} == "rockchip-rk3588" ]]; then
                local rkbin_url="https://github.com/orangepi-xunlong/rk-rootfs-build/raw/rkbin/rk35"
                wget -nc -P ${EXTER}/cache/sources/rkbin-tools/rk35/ ${rkbin_url}/rk3588_bl31_v1.45_20240422.elf
        fi

fi

for option in $(tr ',' ' ' <<< "$CLEAN_LEVEL"); do
        [[ $option != sources ]] && cleaning "$option"
done

# Compile u-boot if packed .deb does not exist or use the one from Orange Pi
if [[ $BUILD_OPT == u-boot || $BUILD_OPT == image ]]; then
        if [[ ! -f "${DEB_STORAGE}"/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb ]]; then
                [[ -n "${ATFSOURCE}" && "${REPOSITORY_INSTALL}" != *u-boot* ]] && compile_atf
                [[ ${REPOSITORY_INSTALL} != *u-boot* ]] && compile_uboot
        fi

        if [[ $BUILD_OPT == "u-boot" ]]; then
                unset BUILD_MINIMAL BUILD_DESKTOP COMPRESS_OUTPUTIMAGE
                display_alert "U-boot build done" "@host" "info"
                display_alert "Target directory" "${DEB_STORAGE}/u-boot" "info"
                display_alert "File name" "${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" "info"
        fi
fi

# Compile kernel if packed .deb does not exist or use the one from Orange Pi
if [[ $BUILD_OPT == kernel || $BUILD_OPT == image ]]; then
        if [[ ! -f ${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb ]]; then
                KDEB_CHANGELOG_DIST=$RELEASE
                [[ "${REPOSITORY_INSTALL}" != *kernel* ]] && compile_kernel
        fi

        if [[ $BUILD_OPT == "kernel" ]]; then
                unset BUILD_MINIMAL BUILD_DESKTOP COMPRESS_OUTPUTIMAGE
                display_alert "Kernel build done" "@host" "info"
                display_alert "Target directory" "${DEB_STORAGE}/" "info"
                display_alert "File name" "${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" "info"
        fi
fi
if [[ $BUILD_OPT == rootfs || $BUILD_OPT == image ]]; then
        # Compile orangepi-config if packed .deb does not exist or use the one from Orange Pi
        if [[ ! -f ${DEB_STORAGE}/orangepi-config_${REVISION}_all.deb ]]; then
                [[ "${REPOSITORY_INSTALL}" != *orangepi-config* ]] && compile_orangepi-config
        fi

        # Compile orangepi-zsh if packed .deb does not exist or use the one from repository
        if [[ ! -f ${DEB_STORAGE}/orangepi-zsh_${REVISION}_all.deb ]]; then
                [[ "${REPOSITORY_INSTALL}" != *orangepi-zsh* ]] && compile_orangepi-zsh
        fi

        # Compile plymouth-theme-orangepi if packed .deb does not exist or use the one from repository
        if [[ ! -f ${DEB_STORAGE}/plymouth-theme-orangepi_${REVISION}_all.deb ]]; then
                [[ "${REPOSITORY_INSTALL}" != *plymouth-theme-orangepi* ]] && compile_plymouth-theme-orangepi
        fi

        # Compile orangepi-firmware if packed .deb does not exist or use the one from repository
        if [[ "${REPOSITORY_INSTALL}" != *orangepi-firmware* ]]; then
                if ! ls "${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb" 1> /dev/null 2>&1; then
                        FULL=""
                        REPLACE="-full"
                        compile_firmware
                fi
        fi

        overlayfs_wrapper "cleanup"

        # create board support package
        [[ -n $RELEASE && ! -f ${DEB_STORAGE}/$RELEASE/${BSP_CLI_PACKAGE_FULLNAME}.deb ]] && create_board_package

        # create desktop package
        [[ -n $RELEASE && $DESKTOP_ENVIRONMENT ]] && create_desktop_package
        [[ -n $RELEASE && $DESKTOP_ENVIRONMENT ]] && create_bsp_desktop_package

        # build additional packages
        [[ $EXTERNAL_NEW == compile ]] && chroot_build_packages

        [[ $BSP_BUILD != yes ]] && debootstrap_ng

fi
# hook for function to run after build, i.e. to change owner of $SRC
# NOTE: this will run only if there were no errors during build process
[[ $(type -t run_after_build) == function ]] && run_after_build || true

end=$(date +%s)
runtime=$(((end-start)/60))
display_alert "Runtime" "$runtime min" "info"

# Make it easy to repeat build by displaying build options used
[ "$(systemd-detect-virt)" == 'docker' ] && BUILD_CONFIG='docker'

display_alert "Repeat Build Options" "sudo ./build.sh ${BUILD_CONFIG} BOARD=${BOARD} BRANCH=${BRANCH} \
$([[ -n $BUILD_OPT ]] && echo "BUILD_OPT=${BUILD_OPT} ")\
$([[ -n $RELEASE ]] && echo "RELEASE=${RELEASE} ")\
$([[ -n $BUILD_MINIMAL ]] && echo "BUILD_MINIMAL=${BUILD_MINIMAL} ")\
$([[ -n $BUILD_DESKTOP ]] && echo "BUILD_DESKTOP=${BUILD_DESKTOP} ")\
$([[ -n $KERNEL_CONFIGURE ]] && echo "KERNEL_CONFIGURE=${KERNEL_CONFIGURE} ")\
$([[ -n $DESKTOP_ENVIRONMENT ]] && echo "DESKTOP_ENVIRONMENT=${DESKTOP_ENVIRONMENT} ")\
$([[ -n $DESKTOP_ENVIRONMENT_CONFIG_NAME  ]] && echo "DESKTOP_ENVIRONMENT_CONFIG_NAME=${DESKTOP_ENVIRONMENT_CONFIG_NAME} ")\
$([[ -n $DESKTOP_APPGROUPS_SELECTED ]] && echo "DESKTOP_APPGROUPS_SELECTED=\"${DESKTOP_APPGROUPS_SELECTED}\" ")\
$([[ -n $DESKTOP_APT_FLAGS_SELECTED ]] && echo "DESKTOP_APT_FLAGS_SELECTED=\"${DESKTOP_APT_FLAGS_SELECTED}\" ")\
$([[ -n $COMPRESS_OUTPUTIMAGE ]] && echo "COMPRESS_OUTPUTIMAGE=${COMPRESS_OUTPUTIMAGE} ")\
" "ext"

} # end of do_default()

if [[ -z $1 ]]; then
        do_default
else
        eval "$@"
f

接下来我们针对该脚本内容从上往下依次分析。

2.1 准备工作

main.sh脚本开始是一系列的准备工作,具体如下。

2.1.1 确定目标路径

确定编译输出目标路径,设置为<SDK>/output

# destination 确定目标路径
if [ -d "$CONFIG_PATH/output" ]; then
        DEST="${CONFIG_PATH}"/output
else
        DEST="${SRC}"/output  # 走这里
fi
2.1.2 加载若干脚本函数

接着是使用source命令执行若干*.sh脚本,脚本均位于<SDK>/scripts目录下;

# shellcheck source=debootstrap.sh
source "${SRC}"/scripts/debootstrap.sh  # system specific install
# shellcheck source=image-helpers.sh
source "${SRC}"/scripts/image-helpers.sh        # helpers for OS image building
# shellcheck source=distributions.sh
source "${SRC}"/scripts/distributions.sh        # system specific install
# shellcheck source=desktop.sh
source "${SRC}"/scripts/desktop.sh              # desktop specific install
# shellcheck source=compilation.sh
source "${SRC}"/scripts/compilation.sh  # patching and compilation of kernel, uboot, ATF
# shellcheck source=compilation-prepare.sh
#source "${SRC}"/scripts/compilation-prepare.sh # drivers that are not upstreamed
# shellcheck source=makeboarddeb.sh
source "${SRC}"/scripts/makeboarddeb.sh         # board support package
# shellcheck source=general.sh
source "${SRC}"/scripts/general.sh              # general functions
# shellcheck source=chroot-buildpackages.sh
source "${SRC}"/scripts/chroot-buildpackages.sh # chroot packages building
# shellcheck source=pack.sh
source "${SRC}"/scripts/pack-uboot.sh

这些脚本都是定义了若干个函数,这些脚本中定义的函数将会被加载到当前shell中。

2.1.3 设置日志路径

接着设置日志输出路径:

# set log path
LOG_SUBPATH=${LOG_SUBPATH:=debug}

# compress and remove old logs  创建目录
mkdir -p "${DEST}"/${LOG_SUBPATH}

# 压缩并删除旧的日志文件,并将其压缩后的文件存档
(cd "${DEST}"/${LOG_SUBPATH} && tar -czf logs-"$(<timestamp)".tgz ./*.log) > /dev/null 2>&1
rm -f "${DEST}"/${LOG_SUBPATH}/*.log > /dev/null 2>&1
date +"%d_%m_%Y-%H_%M_%S" > "${DEST}"/${LOG_SUBPATH}/timestamp

# delete compressed logs older than 7 days  删除超过一周的旧日志压缩文件
(cd "${DEST}"/${LOG_SUBPATH} && find . -name '*.tgz' -mtime +7 -delete) > /dev/null

日志输出路径被设置为debug,位于<SDK>/output目录下;

root@ubuntu:/work/sambashare/rk3566/orangepi-build$ ll output/debug/
-rw-rw-r-- 1 root sudo  6100  7月 10 15:24 logs-10_07_2024-14_20_17.tgz
-rw-rw-r-- 1 root sudo   246  7月 10 15:26 logs-10_07_2024-15_24_55.tgz
-rw-rw-r-- 1 root sudo   254  7月 10 15:30 logs-10_07_2024-15_26_21.tgz
-rw-rw-r-- 1 root sudo  2253  7月 10 15:42 logs-10_07_2024-15_30_06.tgz
-rw-rw-r-- 1 root sudo  6296  7月 10 16:53 logs-10_07_2024-15_42_43.tgz
-rw-rw-r-- 1 root sudo   246  7月 10 16:55 logs-10_07_2024-16_53_05.tgz
-rw-rw-r-- 1 root sudo  4630  7月 10 17:40 logs-10_07_2024-16_55_36.tgz
-rw-rw-r-- 1 root sudo  3659  7月 10 17:45 logs-10_07_2024-17_40_27.tgz
-rw-rw-r-- 1 root sudo  3714  7月 10 17:57 logs-10_07_2024-17_45_03.tgz
-rw-rw-r-- 1 root sudo 30427  7月 10 20:20 logs-10_07_2024-17_57_14.tgz
-rw-rw-r-- 1 root root    45  7月 10 14:20 logs-.tgz
-rw-rw-r-- 1 root sudo   198  7月 10 20:20 output.log
-rw-rw-r-- 1 root root    20  7月 10 20:20 timestamp

在该路径下存放在一周内的编译日志。

2.1.4 设置缓存路径

这段脚本片段的作用是根据条件设置ccache的相关环境变量和路径:

SHOW_WARNING=yes
if [[ $USE_CCACHE != no ]]; then
        CCACHE=ccache
        export PATH="/usr/lib/ccache:$PATH"
        # private ccache directory to avoid permission issues when using build script with "sudo"
        # see https://ccache.samba.org/manual.html#_sharing_a_cache for alternative solution
        [[ $PRIVATE_CCACHE == yes ]] && export CCACHE_DIR=$EXTER/cache/ccache
else
        CCACHE=""
fi

由于未设置USE_CCACHE,因此CCACHE=""

2.2 支持用户选择

接下来就是可视化交互页面,允许用户选择编译u-bootkernelrootfsimage以及版本等信息。

2.2.1 编译选项

接着创建了一个用户界面,用户可以从菜单中选择构建选项(u-bootkernelrootfsimage);

# if BUILD_OPT, KERNEL_CONFIGURE, BOARD, BRANCH or RELEASE are not set, display selection menu
if [[ -z $BUILD_OPT ]]; then

        options+=("u-boot"       "U-boot package")
        options+=("kernel"       "Kernel package")
        options+=("rootfs"       "Rootfs and all deb packages")
        options+=("image"        "Full OS image for flashing")

        menustr="Compile image | rootfs | kernel | u-boot"
        BUILD_OPT=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" --notags \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)

        unset options
        # 未选择,则退出
        [[ -z $BUILD_OPT ]] && exit_with_error "No option selected"
        [[ $BUILD_OPT == rootfs ]] && ROOT_FS_CREATE_ONLY="yes"
fi

假如我们选择了Full OS image for flashing,则会设置BUILD_OPT=image

img
2.2.2 选择内核配置

假如上一步我们选择了Linux镜像,接下来会让我们选择内核配置;

if [[ ${BUILD_OPT} =~ kernel|image ]]; then

        if [[ -z $KERNEL_CONFIGURE ]]; then

                options+=("no" "Do not change the kernel configuration")
                options+=("yes" "Show a kernel configuration menu before compilation")

                menustr="Select the kernel configuration."
                KERNEL_CONFIGURE=$(whiptail --title "${titlestr}" --backtitle "$backtitle" --notags \
                                                 --menu "${menustr}" $TTY_Y $TTY_X $((TTY_Y - 8)) \
                                                 --cancel-button Exit --ok-button Select "${options[@]}" \
                                                 3>&1 1>&2 2>&3)

                unset options
                [[ -z $KERNEL_CONFIGURE ]] && exit_with_error "No option selected"
        fi
fi

如果不需要修改内核配置,则选择第一个即可,如果需要修改内核配置,则选择第二个;

img

假如我们选择了第一个,那么KERNEL_CONFIGURE=no

2.2.3 选择开发板型号

接着是选择开发板的型号;

if [[ -z $BOARD ]]; then
        #options+=("orangepir1"                 "Allwinner H2+ quad core 256MB RAM WiFi SPI 2xETH")
        #options+=("orangepizero"               "Allwinner H2+ quad core 256MB/512MB RAM WiFi SPI")
        #options+=("orangepipc"                 "Allwinner H3 quad core 1GB RAM")
        #options+=("orangepipcplus"             "Allwinner H3 quad core 1GB RAM WiFi eMMC")
        #options+=("orangepione"                        "Allwinner H3 quad core 512MB RAM")
        #options+=("orangepilite"               "Allwinner H3 quad core 512MB RAM WiFi")
		......
        options+=("orangepi3b"                  "Rockchip  RK3566 quad core 2-8GB RAM GBE eMMC USB3 NvMe WiFi/BT")
        #options+=("orangepir1plus"              "Rockchip  RK3328 quad core 1GB RAM 2xGBE USB2 SPI")
        #options+=("orangepi3plus"              "Amlogic S905D3 quad core 2/4GB RAM SoC eMMC GBE USB3 SPI WiFi/BT")

        menustr="Please choose a Board."
        BOARD=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)
        unset options
        [[ -z $BOARD ]] && exit_with_error "No option selected"
fi

假如我们选择了orangepi3b,那么将会设置BOARD=orangepi3b

2.2.4 加载板载配置

假如我们选择的开发板型号为orangepi3b,那么将会加载板载配置文件external/config/boards/orangepi3b.conf

BOARD_TYPE="conf"
# shellcheck source=/dev/null
source "${EXTER}/config/boards/${BOARD}.${BOARD_TYPE}"

# 设置为rockchip-rk356x
LINUXFAMILY="${BOARDFAMILY}"

# 不会进入
[[ -z $KERNEL_TARGET ]] && exit_with_error "Board configuration does not define valid kernel config"

orangepi3b.conf内容如下:

# Rockchip RK3566 hexa core 4GB RAM SoC GBE eMMC USB3 USB-C WiFi/BT
BOARD_NAME="OPI 3B"
BOARDFAMILY="rockchip-rk356x"
BOOTCONFIG="orangepi-3b-rk3566_defconfig"
KERNEL_TARGET="legacy,current"
BOOT_LOGO="desktop"
BOOT_SUPPORT_SPI="yes"
DISTRIB_TYPE_LEGACY="focal jammy bullseye bookworm raspi"
BOOTFS_TYPE="fat"
IMAGE_PARTITION_TABLE="gpt"
REVISION="1.0.6"
2.2.5 选择内核版本

接着是选择内核版本;

# 进入,选择源码分支
if [[ -z $BRANCH ]]; then
        # 定义数组
        options=()
        # 根据支持项定义菜单选项
        [[ $KERNEL_TARGET == *current* ]] && options+=("current" "Recommended. Come with best support")
        [[ $KERNEL_TARGET == *legacy* ]] && options+=("legacy" "Old stable / Legacy")
        [[ $KERNEL_TARGET == *next* ]] && options+=("next" "Use the latest kernel")

        menustr="Select the target kernel branch\nExact kernel versions depend on selected board"
        # do not display selection dialog if only one kernel branch is available
        if [[ "${#options[@]}" == 2 ]]; then
                BRANCH="${options[0]}"
        else
                BRANCH=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" \
                                  --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                                  --cancel-button Exit --ok-button Select "${options[@]}" \
                                  3>&1 1>&2 2>&3)
        fi
        unset options
        [[ -z $BRANCH ]] && exit_with_error "No kernel branch selected"
        [[ $BRANCH == dev && $SHOW_WARNING == yes ]] && show_developer_warning
fi

由于KERNEL_TARGET="legacy,current",因此支持的选项如下;

img

假如我们选择了current,那么BRANCH=current

2.2.6 选择Linux发行版的类型
2.2.6.1 Linux发行版

首先是选择Linux发行版的类型;

if [[ $BUILD_OPT =~ rootfs|image && -z $RELEASE ]]; then
        options=()
        distros_options
        menustr="Select the target OS release package base"
        RELEASE=$(whiptail --title "Choose a release package base" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)
        #echo "options : ${options}"
        [[ -z $RELEASE ]] && exit_with_error "No release selected"
        unset options
fi

distros_options定义在general.sh中,用于获取Linux发行版的类型;

img

假如我们选择了bullseye Debian 11 Bullseye,那么RELEASE=bullseye

2.2.6.2 镜像类型

接着是选择镜像的类型;

if [[ $BUILD_OPT =~ rootfs|image && -z $BUILD_DESKTOP ]]; then
        # read distribution support status which is written to the orangepi-release file
        set_distribution_status
	options=()
    options+=("no" "Image with console interface (server)")
    options+=("yes" "Image with desktop environment")

    menustr="Select the target image type"
    BUILD_DESKTOP=$(whiptail --title "Choose image type" --backtitle "${backtitle}" \
                      --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                      --cancel-button Exit --ok-button Select "${options[@]}" \
                      3>&1 1>&2 2>&3)
    unset options
    [[ -z $BUILD_DESKTOP ]] && exit_with_error "No option selected"
    if [[ ${BUILD_DESKTOP} == "yes" ]]; then
            BUILD_MINIMAL=no
            SELECTED_CONFIGURATION="desktop"
    fi

其中:

  • Image with console interface (server)表示服务器版的镜像,体积比较小;

  • Image with desktop environmen表示带桌面的镜像,体积比较大。

如果我们选择Image with console interface (server)BUILD_DESKTOP=no

img
2.2.6.3 Standard/Minimal

如果是编译服务器版的镜像,还可以选择编译Standard版本或者Minimal版本,Minimal版本预装的软件会比Standard版本少很多(没特殊需求请不要选择Minimal版本,因为很多东西默认没有预装,部分功能可能用不了);

if [[ $BUILD_OPT =~ rootfs|image && $BUILD_DESKTOP == no && -z $BUILD_MINIMAL ]]; then
        options=()
        options+=("no" "Standard image with console interface")
        options+=("yes" "Minimal image with console interface")
        menustr="Select the target image type"
        BUILD_MINIMAL=$(whiptail --title "Choose image type" --backtitle "${backtitle}" \
                          --menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          --cancel-button Exit --ok-button Select "${options[@]}" \
                          3>&1 1>&2 2>&3)
        unset options
        [[ -z $BUILD_MINIMAL ]] && exit_with_error "No option selected"
        if [[ $BUILD_MINIMAL == "yes" ]]; then
                SELECTED_CONFIGURATION="cli_minimal"
        else
                SELECTED_CONFIGURATION="cli_standard"
        fi
fi

如果我们选择了Standard image with console interfaceBUILD_MINIMAL=noSELECTED_CONFIGURATION=cli_standard

img

2.3 加载configuration.sh

接着是使用source命令执行configuration.sh脚本,脚本位于<SDK>/scripts目录下,该脚本内容也比较多,后面我们单独介绍。

#shellcheck source=configuration.sh
source "${SRC}"/scripts/configuration.sh

2.4 变量定义

接着是一些变量定义,比如定义了BSP_CLI包名、BSP_DESKTOP包名;

branch2dir() {
        [[ "${1}" == "head" ]] && echo "HEAD" || echo "${1##*:}"
}

# <SDK>/u-boot/v2017.09-rk3588
BOOTSOURCEDIR="${BOOTDIR}/$(branch2dir "${BOOTBRANCH}")"
# <SDK>/u-boot/orange-pi-6.6-rk35xx
LINUXSOURCEDIR="${KERNELDIR}/$(branch2dir "${KERNELBRANCH}")"
[[ -n $ATFSOURCE ]] && ATFSOURCEDIR="${ATFDIR}/$(branch2dir "${ATFBRANCH}")"

# orangepi-bsp-cli-orangepi3b
BSP_CLI_PACKAGE_NAME="orangepi-bsp-cli-${BOARD}"
# orangepi-bsp-cli-orangepi3b_1.0.6_arm64
BSP_CLI_PACKAGE_FULLNAME="${BSP_CLI_PACKAGE_NAME}_${REVISION}_${ARCH}"
# orangepi-bsp-desktop--orangepi3b
BSP_DESKTOP_PACKAGE_NAME="orangepi-bsp-desktop-${BOARD}"
# orangepi-bsp-desktop--orangepi3b_1.0.6_arm64
BSP_DESKTOP_PACKAGE_FULLNAME="${BSP_DESKTOP_PACKAGE_NAME}_${REVISION}_${ARCH}"

# linux-u-boot-current-orangepi3b
CHOSEN_UBOOT=linux-u-boot-${BRANCH}-${BOARD}
# linux-image-current-rockchip-rk356x
CHOSEN_KERNEL=linux-image-${BRANCH}-${LINUXFAMILY}
# orangepi-bsp-cli-orangepi3b
CHOSEN_ROOTFS=${BSP_CLI_PACKAGE_NAME}

CHOSEN_DESKTOP=orangepi-${RELEASE}-desktop-${DESKTOP_ENVIRONMENT}
# linux-source-current-rockchip-rk356x
CHOSEN_KSRC=linux-source-${BRANCH}-${LINUXFAMILY}

2.5 do_default

文件最后调用了do_default函数,我们将该函数分为以下几段莱介绍。

2.5.1 源码下载

如果没有设置IGNORE_UPDATES="yes",将会通过调用fetch_from_repo 依次下载:

  • u-boot:从https://github.com/orangepi-xunlong/u-boot-orangepi.git下载u-boot源码;
  • kernel;从https://github.com/orangepi-xunlong/linux-orangepi.git下载内核源码;
  • rootfs:从https://github.com/orangepi-xunlong/rk-rootfs-build.git下载根文件系统;

脚本如下:

# fetch_from_repo <url> <dir> <ref> <subdir_flag>
# ignore updates help on building all images - for internal purposes
if [[ ${IGNORE_UPDATES} != yes ]]; then
        display_alert "Downloading sources" "" "info"
        # 下载u-boot源码
        [[ $BUILD_OPT =~ u-boot|image ]] && fetch_from_repo "$BOOTSOURCE" "$BOOTDIR" "$BOOTBRANCH" "yes"
        # 下载kernel源码
        [[ $BUILD_OPT =~ kernel|image ]] && fetch_from_repo "$KERNELSOURCE" "$KERNELDIR" "$KERNELBRANCH" "yes"

		# ATFSOURCE并没有定义 不会执行
        if [[ -n ${ATFSOURCE} ]]; then
                [[ ${BUILD_OPT} =~ u-boot|image ]] && fetch_from_repo "$ATFSOURCE" "${EXTER}/cache/sources/$ATFDIR" "$ATFBRANCH" "yes"
        fi

		# rk356x系列 下载Linux发行版
        if [[ ${BOARDFAMILY} == "rockchip-rk356x" && $RELEASE =~ bullseye|focal|jammy|raspi ]]; then
                [[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/rk35xx_packages" "branch:rk35xx_packages"
        fi

        if [[ ${BOARD} =~ orangepi3|orangepi3-lts && $RELEASE =~ bullseye && $BRANCH == current ]]; then
                [[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/ffmpeg_kodi_${RELEASE}" "branch:ffmpeg_kodi_${RELEASE}"
        fi

        call_extension_method "fetch_sources_tools"  <<- 'FETCH_SOURCES_TOOLS'
        *fetch host-side sources needed for tools and build*
        Run early to fetch_from_repo or otherwise obtain sources for needed tools.
        FETCH_SOURCES_TOOLS

        call_extension_method "build_host_tools"  <<- 'BUILD_HOST_TOOLS'
        *build needed tools for the build, host-side*
        After sources are fetched, build host-side tools needed for the build.
        BUILD_HOST_TOOLS
fi
2.5.1.1 u-boot源码

下载命令:

fetch_from_repo "$BOOTSOURCE" "$BOOTDIR" "$BOOTBRANCH" "yes"

BOOTSOURCEBOOTDIRBOOTBRANCH定义在<SDK>/external/config/sources/arm64.conf

GIT_SERVER="https://github.com/orangepi-xunlong"
# 源码下载地址https://github.com/orangepi-xunlong/u-boot-orangepi.git
[[ -z $BOOTSOURCE ]]            && BOOTSOURCE="${GIT_SERVER}/u-boot-orangepi.git"
# 下载保存目录<SDK>/u-boot
[[ -z $BOOTDIR ]]               && BOOTDIR="${SRC}/u-boot"
# 下载分支branch:v2020.04,这个值会被<SDK>/external/config/sources/families/rockchip-rk356x.conf覆盖为branch:v2017.09-rk3588
[[ -z $BOOTBRANCH ]]            && BOOTBRANCH='branch:v2020.04'

因此会从https://github.com/orangepi-xunlong/u-boot-orangepi.git下载v2017.09-rk3588分支源码并保存到<SDK>/u-boot目录下;

root@ubuntu:/work/sambashare/rk3566/orangepi-build# ll u-boot
drwxrwxr-x 27 root root 4096  7月 27 23:26 v2017.09-rk3588/
2.5.1.2 kernel源码

下载命令:

fetch_from_repo "$KERNELSOURCE" "$KERNELDIR" "$KERNELBRANCH" "yes"

KERNELSOURCEKERNELDIRKERNELBRANCH定义在<SDK>/external/config/sources/arm64.conf

GIT_SERVER="https://github.com/orangepi-xunlong"
# 源码下载地址https://github.com/orangepi-xunlong/linux-orangepi.git
[[ -z $KERNELSOURCE ]]          && KERNELSOURCE="${GIT_SERVER}/linux-orangepi.git"
# 下载保存目录<SDK>/kernel
[[ -z $KERNELDIR ]]             && KERNELDIR="${SRC}/kernel"
# 下载分支branch:orange-pi-5.4,这个值会被<SDK>/external/config/sources/families/rockchip-rk356x.conf覆盖为branch:orange-pi-6.6-rk35xx
[[ -z $KERNELBRANCH ]]          && KERNELBRANCH='branch:orange-pi-5.4'

因此会从https://github.com/orangepi-xunlong/linux-orangepi.git下载orange-pi-6.6-rk35xx分支源码并保存到<SDK>/kernel目录下;

root@ubuntu:/work/sambashare/rk3566/orangepi-build# ll kernel/
-rw-rw-r--  1 root root 7034  7月 27 23:29 linux-6.6.0-rc5-rockchip-rk356x_1.0.6_arm64.buildinfo
-rw-rw-r--  1 root root 2574  7月 27 23:29 linux-6.6.0-rc5-rockchip-rk356x_1.0.6_arm64.changes
drwxrwxr-x 28 root root 4096  7月 27 23:29 orange-pi-6.6-rk35xx/
2.5.1.3 rootfs源码

下载命令:

fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/rk35xx_packages" "branch:rk35xx_packages"

fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/ffmpeg_kodi_${RELEASE}" "branch:ffmpeg_kodi_${RELEASE}"

首先会从https://github.com/orangepi-xunlong/rk-rootfs-build.git下载rk35xx_packages分支源码并保存到<SDK>/cache/sources/rk35xx_packages目录下;

接着会从https://github.com/orangepi-xunlong/rk-rootfs-build.git下载ffmpeg_kodi_bullseye分支源码并保存到<SDK>/cache/sources/ffmpeg_kodi_bullseye目录下;

不过我们发现<SDK>/external/cache/sources/rk35xx_packages竟然是空的,并且没有创建ffmpeg_kodi_bullseye目录。

root@ubuntu:/work/sambashare/rk3566/orangepi-build# ll external/cache/sources/rk35xx_packages
drwxrwxr-x  7 root root 4096  7月 10 17:55 .git/
root@ubuntu:/work/sambashare/rk3566/orangepi-build# ll external/cache/sources/ffmpeg_kodi_bullseye
ls: 无法访问 'external/cache/sources/ffmpeg_kodi_bullseye': 没有那个文件或目录
2.5.2 清理工作

下载完源码之后,是进行上一次编译的清理工作;

for option in $(tr ',' ' ' <<< "$CLEAN_LEVEL"); do
        [[ $option != sources ]] && cleaning "$option"
done

CLEAN_LEVEL默认被配置为debs,oldcache,定义在<SDK>/userpatches/config-default.conf

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

这里使用tr命令将CLEAN_LEVEL中的逗号替换为空格,这样可以将逗号分隔的选项转换成空格分隔的选项列表。

然后执行cleaning函数,入参依次外debsoldcache,该函数定义在scripts/general.sh

如果我们没有对u-bootkernel进行修改,我们就可以不清理u-bootkernel编译生成的deb包,即修改:

CLEAN_LEVEL="oldcache"  

这样就不会重新编译u-bootkernel

2.5.3 编译u-boot

接着是进行u-boot的编译,首先判断u-boot deb包是否存在,如果不存在无非是进入u-boot源码执行配置、编译;

# Compile u-boot if packed .deb does not exist or use the one from Orange Pi
if [[ $BUILD_OPT == u-boot || $BUILD_OPT == image ]]; then
		# 判断 <SDK>/output/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb文件是否存在,如果不存在
        if [[ ! -f "${DEB_STORAGE}"/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb ]]; then
        		# ATFSOURCE未定义
                [[ -n "${ATFSOURCE}" && "${REPOSITORY_INSTALL}" != *u-boot* ]] && compile_atf
                # REPOSITORY_INSTALL未定义,因此执行compile_uboot
                [[ ${REPOSITORY_INSTALL} != *u-boot* ]] && compile_uboot
        fi

        if [[ $BUILD_OPT == "u-boot" ]]; then
                unset BUILD_MINIMAL BUILD_DESKTOP COMPRESS_OUTPUTIMAGE
                display_alert "U-boot build done" "@host" "info"
                display_alert "Target directory" "${DEB_STORAGE}/u-boot" "info"
                display_alert "File name" "${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" "info"
        fi
fi

我们查看编译生成的u-boot deb包:

root@ubuntu:/work/sambashare/rk3566/orangepi-build# ll output/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb
-rw-r--r-- 1 root sudo 553488  7月 27 23:26 output/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb
2.5.4 编译kernel

接着是进行kernel的编译,首先判断kernel deb包是否存在,如果不存在无非是进入kernel 源码执行配置、编译;

# Compile kernel if packed .deb does not exist or use the one from Orange Pi
if [[ $BUILD_OPT == kernel || $BUILD_OPT == image ]]; then
		# 判断 <SDK>/output/debs/linux-image-current-rockchip-rk356x_1.0.6_arm64.deb文件是否存在,如果不存在
        if [[ ! -f ${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb ]]; then
        		# KDEB_CHANGELOG_DIST=bullseye
                KDEB_CHANGELOG_DIST=$RELEASE
                # REPOSITORY_INSTALL未定义,因此执行compile_kernel
                [[ "${REPOSITORY_INSTALL}" != *kernel* ]] && compile_kernel
        fi

        if [[ $BUILD_OPT == "kernel" ]]; then
                unset BUILD_MINIMAL BUILD_DESKTOP COMPRESS_OUTPUTIMAGE
                display_alert "Kernel build done" "@host" "info"
                display_alert "Target directory" "${DEB_STORAGE}/" "info"
                display_alert "File name" "${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" "info"
        fi
fi

我们查看编译生成的kernel deb包:

root@ubuntu:/work/sambashare/rk3566/orangepi-build# ll output/debs/linux-image-current-rockchip-rk356x_1.0.6_arm64.deb
-rw-r--r-- 1 root sudo 40466004  7月 27 23:29 output/debs/linux-image-current-rockchip-rk356x_1.0.6_arm64.deb
2.5.5 构建rootfsLinux镜像

接着是进行Linux 镜像的构建,这里会判断如下包是否存在,不存在则会编译生成;

  • <SDK>/output/debs/orangepi-config_1.0.6_all.debcompile_orangepi编译生成;

  • <SDK>/output/debs/orangepi-zsh_1.0.6_all.debcompile_orangepi编译生成;

  • <SDK>/output/debs/orangepi-plymouth-theme_1.0.6_all.debcompile_plymouth-theme-orangepi编译生成;

  • <SDK>/output/debs/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64.debcreate_board_package编译生成;

最后调用debootstrap_ng开始构建rootfs文件系统以及Linux镜像文件(统一固件)。

脚本如下:

if [[ $BUILD_OPT == rootfs || $BUILD_OPT == image ]]; then
        # Compile orangepi-config if packed .deb does not exist or use the one from Orange Pi
        # <SDK>/output/debs/orangepi-config_1.0.6_all.deb
        if [[ ! -f ${DEB_STORAGE}/orangepi-config_${REVISION}_all.deb ]]; then
                [[ "${REPOSITORY_INSTALL}" != *orangepi-config* ]] && compile_orangepi-config
        fi

        # Compile orangepi-zsh if packed .deb does not exist or use the one from repository
        # <SDK>/output/debs/orangepi-zsh_1.0.6_all.deb
        if [[ ! -f ${DEB_STORAGE}/orangepi-zsh_${REVISION}_all.deb ]]; then
                [[ "${REPOSITORY_INSTALL}" != *orangepi-zsh* ]] && compile_orangepi-zsh
        fi

        # Compile plymouth-theme-orangepi if packed .deb does not exist or use the one from repository
        # <SDK>/output/debs/orangepi-plymouth-theme_1.0.6_all.deb
        if [[ ! -f ${DEB_STORAGE}/plymouth-theme-orangepi_${REVISION}_all.deb ]]; then
                [[ "${REPOSITORY_INSTALL}" != *plymouth-theme-orangepi* ]] && compile_plymouth-theme-orangepi
        fi

        # Compile orangepi-firmware if packed .deb does not exist or use the one from repository
        # <SDK>/output/debs/orangepi-firmware_1.0.6_all.deb
        if [[ "${REPOSITORY_INSTALL}" != *orangepi-firmware* ]]; then
                if ! ls "${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb" 1> /dev/null 2>&1; then
                        FULL=""
                        REPLACE="-full"
                        compile_firmware
                fi
        fi

        overlayfs_wrapper "cleanup"

        # create board support package, <SDK>/output/debs/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
        [[ -n $RELEASE && ! -f ${DEB_STORAGE}/$RELEASE/${BSP_CLI_PACKAGE_FULLNAME}.deb ]] && create_board_package

        # create desktop package,我们未配置桌面环境,因此不会进入
        [[ -n $RELEASE && $DESKTOP_ENVIRONMENT ]] && create_desktop_package
        [[ -n $RELEASE && $DESKTOP_ENVIRONMENT ]] && create_bsp_desktop_package

        # build additional packages,EXTERNAL_NEW="prebuilt"因此也不会进入
        [[ $EXTERNAL_NEW == compile ]] && chroot_build_packages

        [[ $BSP_BUILD != yes ]] && debootstrap_ng
fi
2.5.5 其它

最后执行用户自定义的hook函数run_after_build,并输出构建完成的日志信息;

# hook for function to run after build, i.e. to change owner of $SRC
# NOTE: this will run only if there were no errors during build process
[[ $(type -t run_after_build) == function ]] && run_after_build || true

end=$(date +%s)
runtime=$(((end-start)/60))
display_alert "Runtime" "$runtime min" "info"

# Make it easy to repeat build by displaying build options used
[ "$(systemd-detect-virt)" == 'docker' ] && BUILD_CONFIG='docker'

display_alert "Repeat Build Options" "sudo ./build.sh ${BUILD_CONFIG} BOARD=${BOARD} BRANCH=${BRANCH} \
$([[ -n $BUILD_OPT ]] && echo "BUILD_OPT=${BUILD_OPT} ")\
$([[ -n $RELEASE ]] && echo "RELEASE=${RELEASE} ")\
$([[ -n $BUILD_MINIMAL ]] && echo "BUILD_MINIMAL=${BUILD_MINIMAL} ")\
$([[ -n $BUILD_DESKTOP ]] && echo "BUILD_DESKTOP=${BUILD_DESKTOP} ")\
$([[ -n $KERNEL_CONFIGURE ]] && echo "KERNEL_CONFIGURE=${KERNEL_CONFIGURE} ")\
$([[ -n $DESKTOP_ENVIRONMENT ]] && echo "DESKTOP_ENVIRONMENT=${DESKTOP_ENVIRONMENT} ")\
$([[ -n $DESKTOP_ENVIRONMENT_CONFIG_NAME  ]] && echo "DESKTOP_ENVIRONMENT_CONFIG_NAME=${DESKTOP_ENVIRONMENT_CONFIG_NAME} ")\
$([[ -n $DESKTOP_APPGROUPS_SELECTED ]] && echo "DESKTOP_APPGROUPS_SELECTED=\"${DESKTOP_APPGROUPS_SELECTED}\" ")\
$([[ -n $DESKTOP_APT_FLAGS_SELECTED ]] && echo "DESKTOP_APT_FLAGS_SELECTED=\"${DESKTOP_APT_FLAGS_SELECTED}\" ")\
$([[ -n $COMPRESS_OUTPUTIMAGE ]] && echo "COMPRESS_OUTPUTIMAGE=${COMPRESS_OUTPUTIMAGE} ")\
" "ext"

三、general.sh分析

general.sh是一个通用脚本,该脚本位于<SDK>/scripts目录下,脚本提供了通用功能;cleaningexit_with_errorget_package_list_hashcreate_sources_listclean_up_gitwaiter_local_gitfetch_from_repoimproved_gitdistro_menuaddtoreporepo-remove-old-packageswait_for_package_managerinstall_pkg_debprepare_host_basicprepare_hostwebseeddownload_and_verifyshow_developer_warningshow_checklist_variables

3.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
}

3.2 prepare_host_basic

prepare_host_basic为宿主机ubuntu 22.04系统安装基础包,比如dialoguuiduuid-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=(
                        "whiptail:whiptail"
                        "dialog:dialog"
                        "fuser:psmisc"
                        "getfacl:acl"
                        "uuid:uuid uuid-runtime"
                        "curl:curl"
                        "gpg:gnupg"
                        "gawk:gawk"
                        "git:git"
                        )

        for check_pack in "${checklist[@]}"; do
                # 使用which命令检查 ${check_pack%:*}是否存在,如果不存在(即返回非零退出码),则将${check_pack#*:}添加到install_pack变量中。
                # ${check_pack%:*}:删除最后一个冒号:及其右侧的部分,保留左侧的命令名称部分
                # ${check_pack#*:}:删除第一个冒号:及其左侧的部分,保留右侧的软件包名称部分
                if ! which ${check_pack%:*} >/dev/null; then local install_pack+=${check_pack#*:}" "; fi
        done

		# 不为空,则显示安装基本软件包
        if [[ -n $install_pack ]]; then
                display_alert "Installing basic packages" "$install_pack"
                sudo bash -c "apt-get -qq update && apt-get install -qq -y --no-install-recommends $install_pack"
        fi
}

3.3 distros_options

distros_options函数用于获取Linux发行版的类型;

DISTRIBUTIONS_DESC_DIR="external/config/distributions"

function distro_menu ()
{
# create a select menu for choosing a distribution based EXPERT status

        local distrib_dir="${1}"
		# 查目录 ${distrib_dir}是否存在且包含文件${distrib_dir}/support
        if [[ -d "${distrib_dir}" && -f "${distrib_dir}/support" ]]; then
		        # 读取 ${distrib_dir}/support文件中的支持级别
                local support_level="$(cat "${distrib_dir}/support")"
                # 如果分发的支持级别不是"supported"并且EXPERT不等于"yes",则跳过该分发
                if [[ "${support_level}" != "supported" && $EXPERT != "yes" ]]; then
                        :
                else
                		# 读取分发的名称和全名
                        local distro_codename="$(basename "${distrib_dir}")"
                        local distro_fullname="$(cat "${distrib_dir}/name")"
                        local expert_infos=""
                        
                        # 不会进入
                        [[ $EXPERT == "yes" ]] && expert_infos="(${support_level})"
                        
                        # 根据BRANCH的值设置DISTRIB_TYPE变量
                        if [[ "${BRANCH}" == "legacy" ]]; then
                                DISTRIB_TYPE="${DISTRIB_TYPE_LEGACY}"
                                [[ -z "${DISTRIB_TYPE_LEGACY}" ]] && DISTRIB_TYPE="buster bionic focal"
                        elif [[ "${BRANCH}" == "current" ]]; then
                                DISTRIB_TYPE="${DISTRIB_TYPE_CURRENT}"
                                [[ -z "${DISTRIB_TYPE_CURRENT}" ]] && DISTRIB_TYPE="bullseye bookworm focal jammy"
                        elif [[ "${BRANCH}" == "next" ]]; then
                                if [[ -n "${DISTRIB_TYPE_NEXT}" ]]; then
                                        DISTRIB_TYPE="${DISTRIB_TYPE_NEXT}"
                                else
                                        DISTRIB_TYPE="${DISTRIB_TYPE_CURRENT}"
                                        [[ -z "${DISTRIB_TYPE_CURRENT}" ]] && DISTRIB_TYPE="bullseye bookworm focal jammy"
                                fi
                        fi

                        if [[ "${DISTRIB_TYPE}" =~ "${distro_codename}" ]]; then
                                options+=("${distro_codename}" "${distro_fullname} ${expert_infos}")
                        fi
                fi
        fi
}

function distros_options() {
        for distrib_dir in "${DISTRIBUTIONS_DESC_DIR}/"*; do
                distro_menu "${distrib_dir}"
        done
}

我们查看external/config/distributions目录;

root@ubuntu:/work/sambashare/rk3566/orangepi-build$ ll external/config/distributions
drwxr-xr-x  2 root root 4096  7月 10 13:40 bionic/
drwxr-xr-x  2 root root 4096  7月 10 13:40 bookworm/
drwxr-xr-x  2 root root 4096  7月 10 13:40 bullseye/
drwxr-xr-x  2 root root 4096  7月 10 13:40 buster/
drwxr-xr-x  2 root root 4096  7月 10 13:40 focal/
drwxr-xr-x  2 root root 4096  7月 10 13:40 jammy/
drwxr-xr-x  2 root root 4096  7月 10 13:40 raspi/
-rw-r--r--  1 root root  297  7月 10 13:40 README.md
drwxr-xr-x  2 root root 4096  7月 10 13:40 sid/
drwxr-xr-x  2 root root 4096  7月 10 13:40 stretch/
drwxr-xr-x  2 root root 4096  7月 10 13:40 xenial/

bullseye目录为例;

root@ubuntu:/work/sambashare/rk3566/orangepi-build$ ll external/config/distributions/bullseye/
-rw-r--r--  1 root root   19  7月 10 13:40 name
-rw-r--r--  1 root root   10  7月 10 13:40 support

查看support文件内容,这里为supported,所以support_level=supported

查看name文件内容,这里为Debian 11 Bullseye,所以distro_codename=bullseyedistro_fullname=Debian 11 Bullseye

所以当BRANCH=current时,设置DISTRIB_TYPE=bullseye bookworm focal jammy

DISTRIB_TYPE="${DISTRIB_TYPE_CURRENT}"
[[ -z "${DISTRIB_TYPE_CURRENT}" ]] && DISTRIB_TYPE="bullseye bookworm focal jammy"

函数最后设置:

options+=("${distro_codename}" "${distro_fullname} ${expert_infos}")
即:
bullseye Debian 11 Bullseye

3.4 fetch_from_repo

fetch_from_repo用于源码下载,接收三个参数:

  • url:源码下载地址
  • directory:源码下载下来的存放目录;
  • ref:下载的branch/tag名称;
  • ref_subdir:是否为该branch/tag创建子目录;

由于源码较多,就不一一分析了;

点击查看代码
# fetch_from_repo <url> <directory> <ref> <ref_subdir>
# <url>: remote repository URL
# <directory>: local directory; subdir for branch/tag will be created
# <ref>:
#       branch:name
#       tag:name
#       head(*)
#       commit:hash
#
# *: Implies ref_subdir=no
#
# <ref_subdir>: "yes" to create subdirectory for tag or branch name
#
fetch_from_repo()
{
        local url=$1
        local dir=$2
        local ref=$3
        local ref_subdir=$4

        # Set GitHub mirror before anything else touches $url
        url=${url//'https://github.com/'/$GITHUB_SOURCE'/'}

        # The 'offline' variable must always be set to 'true' or 'false'
        if [ "$OFFLINE_WORK" == "yes" ]; then
                local offline=true
        else
                local offline=false
        fi

        [[ -z $ref || ( $ref != tag:* && $ref != branch:* && $ref != head && $ref != commit:* ) ]] && exit_with_error "Error in configuration"
        local ref_type=${ref%%:*}
        if [[ $ref_type == head ]]; then
                local ref_name=HEAD
        else
                local ref_name=${ref##*:}
        fi

        display_alert "Checking git sources" "$dir $ref_name" "info"

        # get default remote branch name without cloning
        # local ref_name=$(git ls-remote --symref $url HEAD | grep -o 'refs/heads/\S*' | sed 's%refs/heads/%%')
        # for git:// protocol comparing hashes of "git ls-remote -h $url" and "git ls-remote --symref $url HEAD" is needed

        if [[ $ref_subdir == yes ]]; then
                local workdir=$dir/$ref_name
        else
                local workdir=$dir
        fi

        mkdir -p "${workdir}" 2>/dev/null || \
                exit_with_error "No path or no write permission" "${workdir}"

        cd "${workdir}" || exit

        # check if existing remote URL for the repo or branch does not match current one
        # may not be supported by older git versions
        #  Check the folder as a git repository.
        #  Then the target URL matches the local URL.

        if [[ "$(git rev-parse --git-dir 2>/dev/null)" == ".git" && \
                  "$url" != *"$(git remote get-url origin | sed 's/^.*@//' | sed 's/^.*\/\///' 2>/dev/null)" ]]; then
                display_alert "Remote URL does not match, removing existing local copy"
                rm -rf .git ./*
        fi

        if [[ "$(git rev-parse --git-dir 2>/dev/null)" != ".git" ]]; then
                display_alert "Creating local copy"
                git init -q .
                git remote add origin "${url}"
                # Here you need to upload from a new address
                offline=false
        fi

        local changed=false

        # when we work offline we simply return the sources to their original state
        if ! $offline; then
                local local_hash
                local_hash=$(git rev-parse @ 2>/dev/null)

                case $ref_type in
                        branch)
                        # TODO: grep refs/heads/$name
                        local remote_hash
                        remote_hash=$(improved_git ls-remote -h "${url}" "$ref_name" | head -1 | cut -f1)
                        [[ -z $local_hash || "${local_hash}" != "${remote_hash}" ]] && changed=true
                        ;;

                        tag)
                        local remote_hash
                        remote_hash=$(improved_git ls-remote -t "${url}" "$ref_name" | cut -f1)
                        if [[ -z $local_hash || "${local_hash}" != "${remote_hash}" ]]; then
                                remote_hash=$(improved_git ls-remote -t "${url}" "$ref_name^{}" | cut -f1)
                                [[ -z $remote_hash || "${local_hash}" != "${remote_hash}" ]] && changed=true
                        fi
                        ;;

                        head)
                        local remote_hash
                        remote_hash=$(improved_git ls-remote "${url}" HEAD | cut -f1)
                        [[ -z $local_hash || "${local_hash}" != "${remote_hash}" ]] && changed=true
                        ;;

                        commit)
                        [[ -z $local_hash || $local_hash == "@" ]] && changed=true
                        ;;
                esac

        fi # offline

        if [[ $changed == true ]]; then

                # remote was updated, fetch and check out updates
                display_alert "Fetching updates"
                case $ref_type in
                        branch) improved_git fetch --depth 200 origin "${ref_name}" ;;
                        tag) improved_git fetch --depth 200 origin tags/"${ref_name}" ;;
                        head) improved_git fetch --depth 200 origin HEAD ;;
                esac

                # commit type needs support for older git servers that doesn't support fetching id directly
                if [[ $ref_type == commit ]]; then

                        improved_git fetch --depth 200 origin "${ref_name}"

                        # cover old type
                        if [[ $? -ne 0 ]]; then

                                display_alert "Commit checkout not supported on this repository. Doing full clone." "" "wrn"
                                improved_git pull
                                git checkout -fq "${ref_name}"
                                display_alert "Checkout out to" "$(git --no-pager log -2 --pretty=format:"$ad%s [%an]" | head -1)" "info"

                        else

                                display_alert "Checking out"
                                git checkout -f -q FETCH_HEAD
                                git clean -qdf

                        fi
                else

                        display_alert "Checking out"
                        git checkout -f -q FETCH_HEAD
                        git clean -qdf

                fi
        elif [[ -n $(git status -uno --porcelain --ignore-submodules=all) ]]; then
                # working directory is not clean
                display_alert " Cleaning .... " "$(git status -s | wc -l) files"

                # Return the files that are tracked by git to the initial state.
                git checkout -f -q HEAD

                # Files that are not tracked by git and were added
                # when the patch was applied must be removed.
                git clean -qdf
        else
                # working directory is clean, nothing to do
                display_alert "Up to date"
        fi

        if [[ -f .gitmodules ]]; then
                display_alert "Updating submodules" "" "ext"
                # FML: http://stackoverflow.com/a/17692710
                for i in $(git config -f .gitmodules --get-regexp path | awk '{ print $2 }'); do
                        cd "${workdir}" || exit
                        local surl sref
                        surl=$(git config -f .gitmodules --get "submodule.$i.url")
                        sref=$(git config -f .gitmodules --get "submodule.$i.branch")
                        if [[ -n $sref ]]; then
                                sref="branch:$sref"
                        else
                                sref="head"
                        fi
                        fetch_from_repo "$surl" "$workdir/$i" "$sref"
                done
        fi
} #############################################################################

3.5 cleaning

cleaning执行一些清理工作,支持的选项有:

  • debs:删除u-bootkernelbspdeb包,这些包均位于<SDK>/output/debs目录;

  • ubootdebs:删除u-boot deb包,比如我们编译生成的<SDK>/output/debs/linux-image-current-rockchip-rk356x_1.0.6_arm64.deb

  • extras:删除额外的deb包,这些包位于<SDK>/output/debs/extra/bullseye 目录;

  • alldebs:删除所有的deb包,即直接清理<SDK>/output/debs/目录;

  • cache:删除缓存的rootfs,即直接清理<SDK>/external/cache/rootfs目录下的文件;

  • images:删除<SDK>/output/images目录下的文件,比如我们编译生成的Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5/目录;

  • sources:删除缓存的源码,即直接清理<SDK>/external/cache/sources目录下的文件;

  • oldcache:删除缓存早期的rootfs,即清理<SDK>/external/cache/rootfs目录下除了最新的 ${ROOTFS_CACHE_MAX}.lz4 文件之外的.lz4 文件;

脚本如下:

# cleaning <target>
#
# target: what to clean
# "make" - "make clean" for selected kernel and u-boot
# "debs" - delete output/debs for board&branch
# "ubootdebs" - delete output/debs for uboot&board&branch
# "alldebs" - delete output/debs
# "cache" - delete output/cache
# "oldcache" - remove old output/cache
# "images" - delete output/images
# "sources" - delete output/sources
#

cleaning()
{
        case $1 in
		        # delete output/debs for board&branch
                debs) # delete ${DEB_STORAGE} for current branch and family,DEB_STORAGE=<SDK>/output/debs
                if [[ -d "${DEB_STORAGE}" ]]; then
                        display_alert "Cleaning ${DEB_STORAGE} for" "$BOARD $BRANCH" "info"
                        # easier than dealing with variable expansion and escaping dashes in file names
                        # 删除u-boot deb包,linux-u-boot-current-orangepi3b_1.0.6_arm64.deb
                        find "${DEB_STORAGE}" -name "${CHOSEN_UBOOT}_*.deb" -delete
                        # 删除kernel deb包,linux-image-current-rockchip-rk356x_1.0.6_arm64.deb
                        find "${DEB_STORAGE}" \( -name "${CHOSEN_KERNEL}_*.deb" -o \
                                -name "orangepi-*.deb" -o \
                                -name "plymouth-theme-orangepi_*.deb" -o \
                                -name "${CHOSEN_KERNEL/image/dtb}_*.deb" -o \
                                -name "${CHOSEN_KERNEL/image/headers}_*.deb" -o \
                                -name "${CHOSEN_KERNEL/image/source}_*.deb" -o \
                                -name "${CHOSEN_KERNEL/image/firmware-image}_*.deb" \) -delete
                        # 删除BSP包,<SDK>/output/debs/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
                        [[ -n $RELEASE ]] && rm -f "${DEB_STORAGE}/${RELEASE}/${CHOSEN_ROOTFS}"_*.deb
                        [[ -n $RELEASE ]] && rm -f "${DEB_STORAGE}/${RELEASE}/orangepi-desktop-${RELEASE}"_*.deb
                fi
                ;;

                ubootdebs) # delete ${DEB_STORAGE} for uboot, current branch and family
                if [[ -d "${DEB_STORAGE}" ]]; then
                        display_alert "Cleaning ${DEB_STORAGE} for u-boot" "$BOARD $BRANCH" "info"
                        # easier than dealing with variable expansion and escaping dashes in file names
                        find "${DEB_STORAGE}" -name "${CHOSEN_UBOOT}_*.deb" -delete
                fi
                ;;

                extras) # delete ${DEB_STORAGE}/extra/$RELEASE for all architectures
                if [[ -n $RELEASE && -d ${DEB_STORAGE}/extra/$RELEASE ]]; then
                        display_alert "Cleaning ${DEB_STORAGE}/extra for" "$RELEASE" "info"
                        rm -rf "${DEB_STORAGE}/extra/${RELEASE}"
                fi
                ;;

                alldebs) # delete output/debs
                [[ -d "${DEB_STORAGE}" ]] && display_alert "Cleaning" "${DEB_STORAGE}" "info" && rm -rf "${DEB_STORAGE}"/*
                ;;

                cache) # delete output/cache
                [[ -d $EXTER/cache/rootfs ]] && display_alert "Cleaning" "rootfs cache (all)" "info" && find $EXTER/cache/rootfs -type f -delete
                ;;


                images) # delete output/images
                [[ -d "${DEST}"/images ]] && display_alert "Cleaning" "output/images" "info" && rm -rf "${DEST}"/images/*
                ;;

                sources) # delete output/sources and output/buildpkg
                [[ -d $EXTER/cache/sources ]] && display_alert "Cleaning" "sources" "info" && rm -rf $EXTER/cache/sources/* "${DEST}"/buildpkg/*
                ;;

                oldcache) # remove old `cache/rootfs` except for the newest 8 files
                if [[ -d $EXTER/cache/rootfs && $(ls -1 $EXTER/cache/rootfs/*.lz4 2> /dev/null | wc -l) -gt "${ROOTFS_CACHE_MAX}" ]]; then
                        display_alert "Cleaning" "rootfs cache (old)" "info"
                        (cd $EXTER/cache/rootfs; ls -t *.lz4 | sed -e "1,${ROOTFS_CACHE_MAX}d" | xargs -d '\n' rm -f)
                        # Remove signatures if they are present. We use them for internal purpose
                        (cd $EXTER/cache/rootfs; ls -t *.asc | sed -e "1,${ROOTFS_CACHE_MAX}d" | xargs -d '\n' rm -f)
                fi
                ;;
        esac
}

四、compilation.sh分析

compilation.sh脚本位于<SDK>/scripts目录下,实现了各个功能模块源码的编译,如compile_atfcompile_ubootcompile_kernelcompile_firmwarecompile_orangepi-configcompile_sunxi_toolsinstall_rkbin_toolsgrab_versionfind_toolchainadvanced_patchprocess_patch_fileuserpatch_createoverlayfs_wrapper

4.1 compile_uboot

compile_uboot函数实现了u-boot源码的编译并生成deb软件包。由于脚本内容较多,以下脚本会提出一些不会执行的代码,具体如下:

点击查看代码
compile_uboot()
{

        if [[ ${BOARDFAMILY} == "sun50iw9" && ${BRANCH} =~ legacy|current && $(dpkg --print-architecture) == arm64 ]]; then
			.......
        else

        # not optimal, but extra cleaning before overlayfs_wrapper should keep sources directory clean
        if [[ $CLEAN_LEVEL == *make* ]]; then
                display_alert "Cleaning" "$BOOTSOURCEDIR" "info"
                (cd $BOOTSOURCEDIR; make clean > /dev/null 2>&1)
        fi

        if [[ $USE_OVERLAYFS == yes ]]; then
                local ubootdir
                ubootdir=$(overlayfs_wrapper "wrap" "$BOOTSOURCEDIR" "u-boot_${LINUXFAMILY}_${BRANCH}")
        else
                local ubootdir="$BOOTSOURCEDIR"
        fi
        cd "${ubootdir}" || exit

        # read uboot version
        local version hash
        version=$(grab_version "$ubootdir")
        hash=$(improved_git --git-dir="$ubootdir"/.git rev-parse HEAD)

        display_alert "Compiling u-boot" "v$version" "info"

        # build aarch64
        if [[ $(dpkg --print-architecture) == amd64 ]]; then
                local toolchain
                # 查找交叉编译工具链,UBOOT_COMPILER=aarch64-none-linux-gnu-  UBOOT_USE_GCC='> 8.0'
                toolchain=$(find_toolchain "$UBOOT_COMPILER" "$UBOOT_USE_GCC")
                [[ -z $toolchain ]] && exit_with_error "Could not find required toolchain" "${UBOOT_COMPILER}gcc $UBOOT_USE_GCC"

				# 未定义
                if [[ -n $UBOOT_TOOLCHAIN2 ]]; then
                       ......
                fi

                # build aarch64
        fi
        display_alert "Compiler version" "${UBOOT_COMPILER}gcc $(eval env PATH="${toolchain}:${toolchain2}:${PATH}" "${UBOOT_COMPILER}gcc" -dumpversion)" "info"
        
        # 未定义
        [[ -n $toolchain2 ]] && display_alert "Additional compiler version" "${toolchain2_type}gcc $(eval env PATH="${toolchain}:${toolchain2}:${PATH}" "${toolchain2_type}gcc" -dumpversion)" "info"

        # create directory structure for the .deb package  创建临时目录
        uboottempdir=$(mktemp -d)
        chmod 700 ${uboottempdir}
        trap "ret=\$?; rm -rf \"${uboottempdir}\" ; exit \$ret" 0 1 2 3 15
        # linux-u-boot-current-orangepi3b_1.0.6_arm64
        local uboot_name=${CHOSEN_UBOOT}_${REVISION}_${ARCH}
        rm -rf $uboottempdir/$uboot_name
        
        # 创建/tmp/xxxx/linux-u-boot-current-orangepi3b_1.0.6_arm64/usr/lib/u-boot、tmp/xxxx/linux-u-boot-current-orangepi3b_1.0.6_arm64/usr/lib/linux-u-boot-current-orangepi3b_1.0.6_arm64等目录
        mkdir -p $uboottempdir/$uboot_name/usr/lib/{u-boot,$uboot_name} $uboottempdir/$uboot_name/DEBIAN

        # process compilation for one or multiple targets,读取UBOOT_TARGET_MAP变量值,一次读取一行
        while read -r target; do
                local target_make target_patchdir target_files
                # BL31=external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb
                target_make=$(cut -d';' -f1 <<< "${target}")
                # target_patchdir=
                target_patchdir=$(cut -d';' -f2 <<< "${target}")
                # idbloader.img u-boot.itb
                target_files=$(cut -d';' -f3 <<< "${target}")

                # needed for multiple targets and for calling compile_uboot directly
                #display_alert "Checking out to clean sources"
                #improved_git checkout -f -q HEAD

                if [[ $CLEAN_LEVEL == *make* ]]; then
                        display_alert "Cleaning" "$BOOTSOURCEDIR" "info"
                        (cd "${BOOTSOURCEDIR}"; make clean > /dev/null 2>&1)
                fi

                advanced_patch "u-boot" "$BOOTPATCHDIR" "$BOARD" "$target_patchdir" "$BRANCH" "${LINUXFAMILY}-${BOARD}-${BRANCH}"

                # create patch for manual source changes
                [[ $CREATE_PATCHES == yes ]] && userpatch_create "u-boot"

                if [[ -n $ATFSOURCE ]]; then
                        cp -Rv "${atftempdir}"/*.bin .
                        rm -rf "${atftempdir}"
                fi

				# == u-boot make orangepi-3b-rk3566_defconfig ==
                echo -e "\n\t== u-boot make $BOOTCONFIG ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
                
                ## 先将交叉编译工具添加到环境变量PATH,然后执行make -j4  orangepi-3b-rk3566_defconfig CROSS_COMPILE=aarch64-none-linux-gnu-
                eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \
                        'make $CTHREADS $BOOTCONFIG \
                        CROSS_COMPILE="$CCACHE $UBOOT_COMPILER"' 2>> "${DEST}"/${LOG_SUBPATH}/compilation.log \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}

				# 修改.config配置文件,比如配置BOOTDELAY
                if [[ "${version}" != 2014.07 ]]; then
                       # orangepi specifics u-boot settings
                        [[ -f .config ]] && sed -i 's/CONFIG_LOCALVERSION=""/CONFIG_LOCALVERSION="-orangepi"/g' .config
                        [[ -f .config ]] && sed -i 's/CONFIG_LOCALVERSION_AUTO=.*/# CONFIG_LOCALVERSION_AUTO is not set/g' .config

                        # for modern kernel and non spi targets
                        if [[ ${BOOTBRANCH} =~ ^tag:v201[8-9](.*) && ${target} != "spi" && -f .config ]]; then
								......

                        fi

                        if [[ ${BOARDFAMILY} == "sun50iw9" && ${BRANCH} == "next" ]]; then
                                ......
                        fi

                        [[ -f tools/logos/udoo.bmp ]] && cp "${EXTER}"/packages/blobs/splash/udoo.bmp tools/logos/udoo.bmp
                        touch .scmversion

                        # $BOOTDELAY can be set in board family config, ensure autoboot can be stopped even if set to 0
                        [[ $BOOTDELAY == 0 ]] && echo -e "CONFIG_ZERO_BOOTDELAY_CHECK=y" >> .config
                        [[ -n $BOOTDELAY ]] && sed -i "s/^CONFIG_BOOTDELAY=.*/CONFIG_BOOTDELAY=${BOOTDELAY}/" .config || [[ -f .config ]] && echo "CONFIG_BOOTDELAY=${BOOTDELAY}" >> .config

                fi

                # workaround when two compilers are needed
                cross_compile="CROSS_COMPILE=$CCACHE $UBOOT_COMPILER";
                [[ -n $UBOOT_TOOLCHAIN2 ]] && cross_compile="ORANGEPI=foe"; # empty parameter is not allowed

				#  == u-boot make BL31=/work/sambashare/rk3566/orangepi-build/external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb ==
                echo -e "\n\t== u-boot make $target_make ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
                ## 先将交叉编译工具添加到环境变量PATH,然后执行make BL31=/work/sambashare/rk3566/orangepi-build/external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb -j4 CROSS_COMPILE=aarch64-none-linux-gnu-
                eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \                
                        'make $target_make $CTHREADS \
                        "${cross_compile}"' 2>>"${DEST}"/${LOG_SUBPATH}/compilation.log \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a "${DEST}"/${LOG_SUBPATH}/compilation.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Compiling u-boot..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "U-boot compilation failed"

				# 执行uboot_custom_postprocess函数
                [[ $(type -t uboot_custom_postprocess) == function ]] && uboot_custom_postprocess
                # copy files to build directory,拷贝编译出来的文件到构建目录
                for f in $target_files; do
                        local f_src
                        f_src=$(cut -d':' -f1 <<< "${f}")
                        if [[ $f == *:* ]]; then
                                local f_dst
                                f_dst=$(cut -d':' -f2 <<< "${f}")
                        else
                                local f_dst
                                f_dst=$(basename "${f_src}")
                        fi
                        [[ ! -f $f_src ]] && exit_with_error "U-boot file not found" "$(basename "${f_src}")"
                        if [[ "${version}" =~ 2014.07|2011.09 ]]; then
                                cp "${f_src}" "$uboottempdir/packout/${f_dst}"
                        else
                                cp "${f_src}" "$uboottempdir/${uboot_name}/usr/lib/${uboot_name}/${f_dst}"
                        fi
                done
        done <<< "$UBOOT_TARGET_MAP"

		# 未定义
        if [[ $PACK_UBOOT == "yes" ]];then
                if [[ $BOARDFAMILY =~ sun50iw1 ]]; then
    					......
                else
                        pack_uboot
                        cp $uboottempdir/packout/{boot0_sdcard.fex,boot_package.fex} "${SRC}/.tmp/${uboot_name}/usr/lib/${uboot_name}/"
                        cp $uboottempdir/packout/dts/${BOARD}-u-boot.dts "${SRC}/.tmp/${uboot_name}/usr/lib/u-boot/"
                fi
                cd "${ubootdir}" || exit
        fi

        # declare -f on non-defined function does not do anything
        cat <<-EOF > "$uboottempdir/${uboot_name}/usr/lib/u-boot/platform_install.sh"
        DIR=/usr/lib/$uboot_name
        $(declare -f write_uboot_platform)
        $(declare -f write_uboot_platform_mtd)
        $(declare -f setup_write_uboot_platform)
        EOF

        # set up control file
        cat <<-EOF > "$uboottempdir/${uboot_name}/DEBIAN/control"
        Package: linux-u-boot-${BOARD}-${BRANCH}
        Version: $REVISION
        Architecture: $ARCH
        Maintainer: $MAINTAINER <$MAINTAINERMAIL>
        Installed-Size: 1
        Section: kernel
        Priority: optional
        Provides: orangepi-u-boot
        Replaces: orangepi-u-boot
        Conflicts: orangepi-u-boot, u-boot-sunxi
        Description: Uboot loader $version
        EOF

        # copy config file to the package
        # useful for FEL boot with overlayfs_wrapper
        [[ -f .config && -n $BOOTCONFIG ]] && cp .config "$uboottempdir/${uboot_name}/usr/lib/u-boot/${BOOTCONFIG}"
        # copy license files from typical locations
        [[ -f COPYING ]] && cp COPYING "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
        [[ -f Licenses/README ]] && cp Licenses/README "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
        [[ -n $atftempdir && -f $atftempdir/license.md ]] && cp "${atftempdir}/license.md" "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE.atf"

        display_alert "Building deb" "${uboot_name}.deb" "info"
        fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "$uboottempdir/${uboot_name}" "$uboottempdir/${uboot_name}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
        rm -rf "$uboottempdir/${uboot_name}"
        [[ -n $atftempdir ]] && rm -rf "${atftempdir}"

        [[ ! -f $uboottempdir/${uboot_name}.deb ]] && exit_with_error "Building u-boot package failed"

        rsync --remove-source-files -rq "$uboottempdir/${uboot_name}.deb" "${DEB_STORAGE}/u-boot/"
        rm -rf "$uboottempdir"

        fi
}

u-boot编译包含两部分:配置和编译。

4.1.1 配置

先将交叉编译工具添加到环境变量PATH,然后执行make -j4 orangepi-3b-rk3566_defconfig CROSS_COMPILE=aarch64-none-linux-gnu-

eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \
    'make $CTHREADS $BOOTCONFIG \
     CROSS_COMPILE="$CCACHE $UBOOT_COMPILER"' 2>> "${DEST}"/${LOG_SUBPATH}/compilation.log \
     ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
     ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}

eval 命令在shell中用于执行参数中的命令,并将结果作为一个命令来执行。主要功能包括:

  • 执行字符串: eval命令会将其参数作为shell命令执行,这在需要动态生成命令或者执行复杂的命令字符串时非常有用;
  • 变量展开:如果参数中包含变量,eval命令会先展开这些变量再执行命令。这使得可以在运行时动态地构造命令;
  • 处理引号: eval可以处理包括引号在内的特殊字符,使得shell可以正确解析参数中的命令和变量。

env 命令主要用于设置和显示环境变量。它的主要功能包括:

  • 设置环境变量:可以在命令执行前设置一个或多个环境变量;
  • 显示当前环境变量:如果不带参数,env命令会显示当前shell的所有环境变量及其值;
  • 运行命令时设置环境变量:可以在命令执行时暂时设置环境变量,而不是永久更改shell环境。
4.1.2 编译

先将交叉编译工具添加到环境变量PATH,然后执行make BL31=/work/sambashare/rk3566/orangepi-build/external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb -j4 CROSS_COMPILE=aarch64-none-linux-gnu-

eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \                
       'make $target_make $CTHREADS \
       "${cross_compile}"' 2>>"${DEST}"/${LOG_SUBPATH}/compilation.log \
       ${PROGRESS_LOG_TO_FILE:+' | tee -a "${DEST}"/${LOG_SUBPATH}/compilation.log'} \
       ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Compiling u-boot..." $TTY_Y $TTY_X'} \
        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
4.1.3 uboot_custom_postprocess

首先执行uboot_custom_postprocess,该函数会在u-boot编译完成后执行,主要做一些后置处理;

[[ $(type -t uboot_custom_postprocess) == function ]] && uboot_custom_postprocess
# copy files to build directory
# target_files=idbloader.img u-boot.itb
for f in $target_files; do
		local f_src
		f_src=$(cut -d':' -f1 <<< "${f}")
		if [[ $f == *:* ]]; then
				local f_dst
				f_dst=$(cut -d':' -f2 <<< "${f}")
		else
				local f_dst
				f_dst=$(basename "${f_src}")
		fi
		[[ ! -f $f_src ]] && exit_with_error "U-boot file not found" "$(basename "${f_src}")"
		if [[ "${version}" =~ 2014.07|2011.09 ]]; then
				cp "${f_src}" "$uboottempdir/packout/${f_dst}"
		else
				cp "${f_src}" "$uboottempdir/${uboot_name}/usr/lib/${uboot_name}/${f_dst}"
		fi
done

接着从<SDK>/u-boot/v2017.09-rk3588拷贝idbloader.imgu-boot.itb$uboottempdir/linux-u-boot-current-orangepi3b_1.0.6_arm64/usr/lib/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64目录。

uboot_custom_postprocess定义在<SDK>/external/config/sources/families/include/rockchip64_common.inc

u-boot源码编译后会生成tpl/u-boot-tpl.binspl/u-boot-spl.bin文件,如果配置了BOOT_SUPPORT_SPI

  • 那么uboot_custom_postprocess调用mkimage根据external/cache/sources/rkbin-tools/rk35/rk3566_ddr_1056MHz_v1.10.binspl/u-boot-spl.bin这两个文件生成idbloader.img

  • 此外还创建了rkspi_loader.img镜像文件,该文件包含两个分区:

    • idbloader分区:位于0x40~0x3FF扇区,写入idbloader.img镜像文件;

    • uboot分区:位于0x400~0x1BFF扇区,写入u-boot.itb镜像文件;

脚本如下:

uboot_custom_postprocess()
{
		# BOOT_SUPPORT_SPI="yes",进入用于生成idbloader.img、rkspi_loader.img
        if [[ $BOOT_SUPPORT_SPI == yes ]]; then
                if [[ $BOARDFAMILY == "rockchip-rk3588" ]]; then
					......
				# BOARDFAMILY="rockchip-rk356x",进入
                elif [[ $BOARDFAMILY == "rockchip-rk356x" ]]; then
                		# external/cache/sources/rkbin-tools/rk35/rk3566_ddr_1056MHz_v1.10.bin:spl/u-boot-spl.bin -> idbloader.img
                        tools/mkimage -n rk3568 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                        # 生成空白的 rkspi_loader.img 文件,大小4M
                        dd if=/dev/zero of=rkspi_loader.img bs=1M count=0 seek=4
                        # 使用parted创建GPT分区表
                        /sbin/parted -s rkspi_loader.img mklabel gpt
                        # 使用parted工具在rkspi_loader.img文件上创建一个名为idbloader的分区,起始扇区从0x40开始,终止扇区为0x3FF
                        /sbin/parted -s rkspi_loader.img unit s mkpart idbloader 64 1023
                        # 使用parted工具在rkspi_loader.img文件上创建一个名为uboot的分区,起始扇区从0x400开始,终止扇区为0x1BFF
                        /sbin/parted -s rkspi_loader.img unit s mkpart uboot 1024 7167
                        # 使用dd命令将idbloader.img文件的内容写入到rkspi_loader.img文件中idbloader分区
                        dd if=idbloader.img of=rkspi_loader.img seek=64 conv=notrunc
                        # 使用dd命令将u-boot.itb文件的内容写入到rkspi_loader.img文件中uboot分区
                        dd if=u-boot.itb of=rkspi_loader.img seek=1024 conv=notrunc
                fi
        fi

        if [[ $BOOT_USE_MAINLINE_ATF == yes || $BOOT_USE_TPL_SPL_BLOB == yes ]]; then
                ......
        elif [[ $BOOT_USE_BLOBS == yes ]]; then
                ......
		# BOOT_SCENARIO="spl-blobs",进入
        elif [[ $BOOT_SCENARIO == "spl-blobs" || $BOOT_SCENARIO == "tpl-blob-atf-mainline" ]]; then
        		# BOARD="orangepi3b",进入
                if [[ $BOARD =~ orangepicm4|orangepi3b ]]; then
		                # external/cache/sources/rkbin-tools/rk35/rk3566_ddr_1056MHz_v1.10.bin:spl/u-boot-spl.bin -> idbloader.img
                        tools/mkimage -n rk3568 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                elif [[ $BOARD =~ orangepi4-lts|orangepi800 ]]; then
                        tools/mkimage -n rk3399 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                fi
                :
        else
                echo "[uboot_custom_postprocess]: Unsupported u-boot processing configuration!"
                exit 1
        fi

		# MERGE_UBOOT未定义不会进入
        if [[ ${MERGE_UBOOT} ]]; then
               	......
        fi
}
4.1.4 platform_install.sh

接着会在uboottempdir目录下创建linux-u-boot-current-orangepi3b_1.0.6_arm64/usr/lib/u-boot/platform_install.sh文件;

# declare -f on non-defined function does not do anything
cat <<-EOF > "$uboottempdir/${uboot_name}/usr/lib/u-boot/platform_install.sh"
DIR=/usr/lib/$uboot_name
$(declare -f write_uboot_platform)
$(declare -f write_uboot_platform_mtd)
$(declare -f setup_write_uboot_platform)
EOF

这里会将DIR变量以及write_uboot_platformwrite_uboot_platform_mtdsetup_write_uboot_platform函数写入。

这些函数定义在<SDK>/external/config/sources/families/include/rockchip64_common.inc

4.1.5 control

接着会在uboottempdir目录下创建linux-u-boot-current-orangepi3b_1.0.6_arm64/DEBIAN/control文件;

# set up control file
cat <<-EOF > "$uboottempdir/${uboot_name}/DEBIAN/control"
Package: linux-u-boot-${BOARD}-${BRANCH}
Version: $REVISION
Architecture: $ARCH
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Installed-Size: 1
Section: kernel
Priority: optional
Provides: orangepi-u-boot
Replaces: orangepi-u-boot
Conflicts: orangepi-u-boot, u-boot-sunxi
Description: Uboot loader $version
EOF

control文件描述软件包的名称(Package),版本(Version),描述(Description)等,是deb包必须剧本的描述性文件,以便于软件的安装管理和索引。

4.1.6 生成deb

最后就是构建一个名为linux-u-boot-current-orangepi3b_1.0.6_arm64Debian软件包,并将其复制到指定的目标存储位置 <SDK>/output/debs/u-boot/

# copy config file to the package
# useful for FEL boot with overlayfs_wrapper, 将.config 复制到 ${uboottempdir}/${uboot_name}/usr/lib/u-boot/orangepi-3b-rk3566_defconfig
[[ -f .config && -n $BOOTCONFIG ]] && cp .config "$uboottempdir/${uboot_name}/usr/lib/u-boot/${BOOTCONFIG}"

# copy license files from typical locations,将COPYING复制${uboot_name}/usr/lib/u-boot/LICENSE
[[ -f COPYING ]] && cp COPYING "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"

# 无
[[ -f Licenses/README ]] && cp Licenses/README "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
# 无
[[ -n $atftempdir && -f $atftempdir/license.md ]] && cp "${atftempdir}/license.md" "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE.atf"

# 使用fakeroot命令打包${uboottempdir}/${uboot_name}目录为${uboottempdir}/${uboot_name}.deb 文件
display_alert "Building deb" "${uboot_name}.deb" "info"
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "$uboottempdir/${uboot_name}" "$uboottempdir/${uboot_name}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1

# 清理临时目录
rm -rf "$uboottempdir/${uboot_name}"
[[ -n $atftempdir ]] && rm -rf "${atftempdir}"
[[ ! -f $uboottempdir/${uboot_name}.deb ]] && exit_with_error "Building u-boot package failed"

# 将构建好的软件包复制到目标存储位置
rsync --remove-source-files -rq "$uboottempdir/${uboot_name}.deb" "${DEB_STORAGE}/u-boot/"
# 最终清理临时目录
rm -rf "$uboottempdir"

经过上面一系列的操作,会在临时目录$uboottempdir下创建一个linux-u-boot-current-orangepi3b_1.0.6_arm64文件夹,内部包含如下文件;

usr/ 
└── lib
    ├── linux-u-boot-current-orangepi3b_1.0.6_arm64
    │   ├── idbloader.img
    │   ├── rkspi_loader.img
    │   └── u-boot.itb
    └── u-boot
        ├── LICENSE
        ├── orangepi-3b-rk3566_defconfig
        └── platform_install.sh

最终会通过fakeroot工具将linux-u-boot-current-orangepi3b_1.0.6_arm64目录打包成linux-u-boot-current-orangepi3b_1.0.6_arm64.deb,并拷贝到<SDK>/output/debs/u-boot/

4.2 compile_kernel

compile_kernel函数实现了kernel源码的编译并生成deb软件包。由于脚本内容较多,以下脚本会提出一些不会执行的代码,具体如下:

点击查看代码
compile_kernel()
{
        if [[ $CLEAN_LEVEL == *make* ]]; then
                display_alert "Cleaning" "$LINUXSOURCEDIR" "info"
                (cd ${LINUXSOURCEDIR}; make ARCH="${ARCHITECTURE}" clean >/dev/null 2>&1)
        fi

        if [[ $USE_OVERLAYFS == yes ]]; then
                local kerneldir
                kerneldir=$(overlayfs_wrapper "wrap" "$LINUXSOURCEDIR" "kernel_${LINUXFAMILY}_${BRANCH}")
        else
                local kerneldir="$LINUXSOURCEDIR"
        fi
        cd "${kerneldir}" || exit

        rm -f localversion

        # read kernel version
        local version hash
        version=$(grab_version "$kerneldir")

        # read kernel git hash
        hash=$(improved_git --git-dir="$kerneldir"/.git rev-parse HEAD)

        # Apply a series of patches if a series file exists
        if test -f "${EXTER}"/patch/kernel/${KERNELPATCHDIR}/series.conf; then
                display_alert "series.conf file visible. Apply"
                series_conf="${SRC}"/patch/kernel/${KERNELPATCHDIR}/series.conf

                # apply_patch_series <target dir> <full path to series file>
                apply_patch_series "${kerneldir}" "$series_conf"
        fi

        # build 3rd party drivers
        # compilation_prepare

        advanced_patch "kernel" "$KERNELPATCHDIR" "$BOARD" "" "$BRANCH" "$LINUXFAMILY-$BRANCH"

        # create patch for manual source changes in debug mode
        [[ $CREATE_PATCHES == yes ]] && userpatch_create "kernel"

        # re-read kernel version after patching
        local version
        version=$(grab_version "$kerneldir")

        display_alert "Compiling $BRANCH kernel" "$version" "info"

        # compare with the architecture of the current Debian node
        # if it matches we use the system compiler
        if $(dpkg-architecture -e "${ARCH}"); then
                display_alert "Native compilation"
        elif [[ $(dpkg --print-architecture) == amd64 ]]; then
                local toolchain
                toolchain=$(find_toolchain "$KERNEL_COMPILER" "$KERNEL_USE_GCC")
                [[ -z $toolchain ]] && exit_with_error "Could not find required toolchain" "${KERNEL_COMPILER}gcc $KERNEL_USE_GCC"
        else
                exit_with_error "Architecture [$ARCH] is not supported"
        fi

        display_alert "Compiler version" "${KERNEL_COMPILER}gcc $(eval env PATH="${toolchain}:${PATH}" "${KERNEL_COMPILER}gcc" -dumpversion)" "info"

        # copy kernel config
        if [[ $KERNEL_KEEP_CONFIG == yes && -f "${DEST}"/config/$LINUXCONFIG.config ]]; then
                display_alert "Using previous kernel config" "${DEST}/config/$LINUXCONFIG.config" "info"
                cp -p "${DEST}/config/${LINUXCONFIG}.config" .config
        else
                if [[ -f $USERPATCHES_PATH/$LINUXCONFIG.config ]]; then
                        display_alert "Using kernel config provided by user" "userpatches/$LINUXCONFIG.config" "info"
                        cp -p "${USERPATCHES_PATH}/${LINUXCONFIG}.config" .config
                else
                        display_alert "Using kernel config file" "${EXTER}/config/kernel/$LINUXCONFIG.config" "info"
                        cp -p "${EXTER}/config/kernel/${LINUXCONFIG}.config" .config
                fi
        fi

        call_extension_method "custom_kernel_config" << 'CUSTOM_KERNEL_CONFIG'
*Kernel .config is in place, still clean from git version*
Called after ${LINUXCONFIG}.config is put in place (.config).
Before any olddefconfig any Kconfig make is called.
A good place to customize the .config directly.
CUSTOM_KERNEL_CONFIG


        # hack for deb builder. To pack what's missing in headers pack.
        cp "$EXTER"/patch/misc/headers-debian-byteshift.patch /tmp

        if [[ $KERNEL_CONFIGURE != yes ]]; then
                if [[ $BRANCH == legacy && ! $BOARDFAMILY =~ "rockchip-rk3588"|"rockchip-rk356x" ]]; then
                        eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                                'make ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" silentoldconfig'
                else
                        # TODO: check if required
                        eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                                'make ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" olddefconfig'
                fi
        else
                eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                        'make $CTHREADS ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" oldconfig'
                eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                        'make $CTHREADS ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" ${KERNEL_MENUCONFIG:-menuconfig}'

                [[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Error kernel menuconfig failed"

                # store kernel config in easily reachable place
                display_alert "Exporting new kernel config" "$DEST/config/$LINUXCONFIG.config" "info"
                cp .config "${DEST}/config/${LINUXCONFIG}.config"
                cp .config "${EXTER}/config/kernel/${LINUXCONFIG}.config"
                # export defconfig too if requested
                if [[ $KERNEL_EXPORT_DEFCONFIG == yes ]]; then
                        eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                                'make ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" savedefconfig'
                        [[ -f defconfig ]] && cp defconfig "${DEST}/config/${LINUXCONFIG}.defconfig"
                fi
        fi

        # create linux-source package - with already patched sources
        # We will build this package first and clear the memory.
        if [[ $BUILD_KSRC != no ]]; then
                create_linux-source_package
        fi

        echo -e "\n\t== kernel ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
        eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                'make $CTHREADS ARCH=$ARCHITECTURE \
                CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" \
                $SRC_LOADADDR \
                LOCALVERSION="-$LINUXFAMILY" \
                $KERNEL_IMAGE_TYPE ${KERNEL_EXTRA_TARGETS:-modules dtbs} 2>>$DEST/${LOG_SUBPATH}/compilation.log' \
                ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
                ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" \
                --progressbox "Compiling kernel..." $TTY_Y $TTY_X'} \
                ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}

        if [[ ${PIPESTATUS[0]} -ne 0 || ! -f arch/$ARCHITECTURE/boot/$KERNEL_IMAGE_TYPE ]]; then
                grep -i error $DEST/${LOG_SUBPATH}/compilation.log
                exit_with_error "Kernel was not built" "@host"
        fi

        # different packaging for 4.3+
        if linux-version compare "${version}" ge 4.3; then
                local kernel_packing="bindeb-pkg"
        else
                local kernel_packing="deb-pkg"
        fi

        #if [[ $BRANCH == legacy && $LINUXFAMILY =~ sun50iw2|sun50iw6|sun50iw9 ]]; then
        #       make -C modules/gpu LICHEE_MOD_DIR=${SRC}/.tmp/gpu_modules_${LINUXFAMILY} LICHEE_KDIR=${kerneldir} CROSS_COMPILE=$toolchain/$KERNEL_COMPILER ARCH=$ARCHITECTURE
        #fi

        display_alert "Creating packages"

        # produce deb packages: image, headers, firmware, dtb
        echo -e "\n\t== deb packages: image, headers, firmware, dtb ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
        eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
                'make $CTHREADS $kernel_packing \
                KDEB_PKGVERSION=$REVISION \
                KDEB_COMPRESS=${DEB_COMPRESS} \
                BRANCH=$BRANCH \
                LOCALVERSION="-${LINUXFAMILY}" \
                KBUILD_DEBARCH=$ARCH \
                ARCH=$ARCHITECTURE \
                DEBFULLNAME="$MAINTAINER" \
                DEBEMAIL="$MAINTAINERMAIL" \
                CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" 2>>$DEST/${LOG_SUBPATH}/compilation.log' \
                ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
                ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Creating kernel packages..." $TTY_Y $TTY_X'} \
                ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}

        cd .. || exit
        # remove firmare image packages here - easier than patching ~40 packaging scripts at once
        rm -f linux-firmware-image-*.deb

        rsync --remove-source-files -rq ./*.deb "${DEB_STORAGE}/" || exit_with_error "Failed moving kernel DEBs"     
}

编译完成之后,会生成linux-image-current-rockchip-rk356x_1.0.6_arm64.deb ,该文件是由<SDK>/kernel/orange-pi-6.6-rk35xx/debian/tmp/打包生成的;

root@ubuntu:/work/sambashare/rk3566/orangepi-build/kernel/orange-pi-6.6-rk35xx/$ ll debian/tmp/
drwxr-xr-x 2 root root 4096  8月  4 09:37 boot/
drwxr-xr-x 2 root root 4096  8月  4 09:37 DEBIAN/
drwxr-xr-x 3 root root 4096  8月  4 09:37 etc/
drwxr-xr-x 3 root root 4096  8月  4 09:37 lib/
drwxr-xr-x 4 root root 4096  8月  4 09:37 usr/=

可以看到这个目录和linux-image-current-rockchip-rk356x_1.0.6_arm64.deb解压后的内容基本是一致的。有关kernel deb更多实现细节可以查看./scripts/package/builddeb脚本。

4.3 compile_firmware

点击查看代码
compile_firmware()
{
        display_alert "Merging and packaging linux firmware" "@host" "info"

        local firmwaretempdir plugin_dir

        firmwaretempdir=$(mktemp -d)
        chmod 700 ${firmwaretempdir}
        trap "ret=\$?; rm -rf \"${firmwaretempdir}\" ; exit \$ret" 0 1 2 3 15
        plugin_dir="orangepi-firmware${FULL}"
        mkdir -p "${firmwaretempdir}/${plugin_dir}/lib/firmware"

        [[ $IGNORE_UPDATES != yes ]] && fetch_from_repo "https://github.com/orangepi-xunlong/firmware" "${EXTER}/cache/sources/orangepi-firmware-git" "branch:master"
        if [[ -n $FULL ]]; then
                [[ $IGNORE_UPDATES != yes ]] && fetch_from_repo "$MAINLINE_FIRMWARE_SOURCE" "${EXTER}/cache/sources/linux-firmware-git" "branch:master"
                # cp : create hardlinks
                cp -af --reflink=auto "${EXTER}"/cache/sources/linux-firmware-git/* "${firmwaretempdir}/${plugin_dir}/lib/firmware/"
        fi
        # overlay our firmware
        # cp : create hardlinks
        cp -af --reflink=auto "${EXTER}"/cache/sources/orangepi-firmware-git/* "${firmwaretempdir}/${plugin_dir}/lib/firmware/"

        # cleanup what's not needed for sure
        rm -rf "${firmwaretempdir}/${plugin_dir}"/lib/firmware/{amdgpu,amd-ucode,radeon,nvidia,matrox,.git}
        cd "${firmwaretempdir}/${plugin_dir}" || exit

        # set up control file
        mkdir -p DEBIAN
        cat <<-END > DEBIAN/control
        Package: orangepi-firmware${FULL}
        Version: $REVISION
        Architecture: all
        Maintainer: $MAINTAINER <$MAINTAINERMAIL>
        Installed-Size: 1
        Replaces: linux-firmware, firmware-brcm80211, firmware-ralink, firmware-samsung, firmware-realtek, orangepi-firmware${REPLACE}
        Section: kernel
        Priority: optional
        Description: Linux firmware${FULL}
        END

        cd "${firmwaretempdir}" || exit
        # pack
        mv "orangepi-firmware${FULL}" "orangepi-firmware${FULL}_${REVISION}_all"
        display_alert "Building firmware package" "orangepi-firmware${FULL}_${REVISION}_all" "info"
        fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "orangepi-firmware${FULL}_${REVISION}_all" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        mv "orangepi-firmware${FULL}_${REVISION}_all" "orangepi-firmware${FULL}"
        rsync -rq "orangepi-firmware${FULL}_${REVISION}_all.deb" "${DEB_STORAGE}/"

        # remove temp directory
        rm -rf "${firmwaretempdir}"
}

4.4 compile_orangepi-config

compile_orangepi-config函数是用来编译和打包Orange Pi的配置工具 orangepi-config 到一个Debian 包 ;

compile_orangepi-config()
{
        local tmpdir=${SRC}/.tmp/orangepi-config_${REVISION}_all

        display_alert "Building deb" "orangepi-config" "info"


        mkdir -p "${tmpdir}"/{DEBIAN,usr/bin/,usr/sbin/,usr/lib/orangepi-config/}

        # set up control file
        cat <<-END > "${tmpdir}"/DEBIAN/control
        Package: orangepi-config
        Version: $REVISION
        Architecture: all
        Maintainer: $MAINTAINER <$MAINTAINERMAIL>
        Replaces: orangepi-bsp
        Depends: bash, iperf3, psmisc, curl, bc, expect, dialog, pv, \
        debconf-utils, unzip, build-essential, html2text, apt-transport-https, html2text, dirmngr, software-properties-common
        Recommends: orangepi-bsp
        Suggests: libpam-google-authenticator, qrencode, network-manager, sunxi-tools
        Section: utils
        Priority: optional
        Description: Orange Pi configuration utility
        END

        install -m 755 $EXTER/cache/sources/orangepi-config/scripts/tv_grab_file $tmpdir/usr/bin/tv_grab_file
        install -m 755 $EXTER/cache/sources/orangepi-config/debian-config $tmpdir/usr/sbin/orangepi-config
        install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-jobs $tmpdir/usr/lib/orangepi-config/jobs.sh
        install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-submenu $tmpdir/usr/lib/orangepi-config/submenu.sh
        install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-functions $tmpdir/usr/lib/orangepi-config/functions.sh
        install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-functions-network $tmpdir/usr/lib/orangepi-config/functions-network.sh
        install -m 755 $EXTER/cache/sources/orangepi-config/softy $tmpdir/usr/sbin/softy
        # fallback to replace orangepi-config in BSP
        ln -sf /usr/sbin/orangepi-config $tmpdir/usr/bin/orangepi-config
        ln -sf /usr/sbin/softy "${tmpdir}"/usr/bin/softy

        fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "${tmpdir}" >/dev/null
        rsync --remove-source-files -rq "${tmpdir}.deb" "${DEB_STORAGE}/"
        rm -rf "${tmpdir}"
}

五、makeboarddeb.sh

makeboarddeb.sh文件只实现了 create_board_package函数,用于构建BSPorangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb,需要注意的是这个包并不是rootfs镜像文件。

点击查看代码
#!/bin/bash

create_board_package()
{
        display_alert "Creating board support package for CLI" "$CHOSEN_ROOTFS" "info"

		# 创建临时目录
        bsptempdir=$(mktemp -d)
        # 修改权限
        chmod 700 ${bsptempdir}
        # 设置信号处理器,当收到信号0/1/2/3/15 删除临时目录
        trap "rm -rf \"${bsptempdir}\" ; exit 0" 0 1 2 3 15
        # 设置目标目录{bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
        local destination=${bsptempdir}/${RELEASE}/${BSP_CLI_PACKAGE_FULLNAME}
        # 创建目录{bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64/DEBIAN
        mkdir -p "${destination}"/DEBIAN
        cd $destination

        # copy general overlay from packages/bsp-cli
        copy_all_packages_files_for "bsp-cli"

        # install copy of boot script & environment file
        if [[ "${BOOTCONFIG}" != "none" ]]; then
                # @TODO: add extension method bsp_prepare_bootloader(), refactor into u-boot extension
                local bootscript_src=${BOOTSCRIPT%%:*}
                local bootscript_dst=${BOOTSCRIPT##*:}
                mkdir -p "${destination}"/usr/share/orangepi/

                # create extlinux config file
                if [[ $SRC_EXTLINUX != yes ]]; then
                        if [ -f "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" ]; then
                          cp "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
                        else
                          cp "${EXTER}/config/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
                        fi
                        [[ -n $BOOTENV_FILE && -f $SRC/config/bootenv/$BOOTENV_FILE ]] && \
                                cp "${EXTER}/config/bootenv/${BOOTENV_FILE}" "${destination}"/usr/share/orangepi/orangepiEnv.txt
                fi

                # add configuration for setting uboot environment from userspace with: fw_setenv fw_printenv
                if [[ -n $UBOOT_FW_ENV ]]; then
                        UBOOT_FW_ENV=($(tr ',' ' ' <<< "$UBOOT_FW_ENV"))
                        mkdir -p "${destination}"/etc
                        echo "# Device to access      offset           env size" > "${destination}"/etc/fw_env.config
                        echo "/dev/mmcblk0      ${UBOOT_FW_ENV[0]}      ${UBOOT_FW_ENV[1]}" >> "${destination}"/etc/fw_env.config
                fi
        fi

        # Replaces: base-files is needed to replace /etc/update-motd.d/ files on Xenial
        # Replaces: unattended-upgrades may be needed to replace /etc/apt/apt.conf.d/50unattended-upgrades
        # (distributions provide good defaults, so this is not needed currently)
        # Depends: linux-base is needed for "linux-version" command in initrd cleanup script
        # Depends: fping is needed for orangepimonitor to upload orangepi-hardware-monitor.log
        cat <<-EOF > "${destination}"/DEBIAN/control
        Package: ${BSP_CLI_PACKAGE_NAME}
        Version: $REVISION
        Architecture: $ARCH
        Maintainer: $MAINTAINER <$MAINTAINERMAIL>
        Installed-Size: 1
        Section: kernel
        Priority: optional
        Depends: bash, linux-base, u-boot-tools, initramfs-tools, lsb-release, fping
        Provides: linux-${RELEASE}-root-legacy-$BOARD, linux-${RELEASE}-root-current-$BOARD, linux-${RELEASE}-root-next-$BOARD
        Suggests: orangepi-config
        Replaces: zram-config, base-files, orangepi-tools-$RELEASE, linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
        Breaks: linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
        Recommends: bsdutils, parted, util-linux, toilet
        Description: OrangePi board support files for $BOARD
        EOF

        # set up pre install script
        cat <<-EOF > "${destination}"/DEBIAN/preinst
        #!/bin/sh

        # tell people to reboot at next login
        [ "\$1" = "upgrade" ] && touch /var/run/.reboot_required

        # convert link to file
        if [ -L "/etc/network/interfaces" ]; then

            cp /etc/network/interfaces /etc/network/interfaces.tmp
            rm /etc/network/interfaces
            mv /etc/network/interfaces.tmp /etc/network/interfaces

        fi

        # fixing ramdisk corruption when using lz4 compression method
        sed -i "s/^COMPRESS=.*/COMPRESS=gzip/" /etc/initramfs-tools/initramfs.conf

        # swap
        grep -q vm.swappiness /etc/sysctl.conf
        case \$? in
        0)
            sed -i 's/vm\.swappiness.*/vm.swappiness=100/' /etc/sysctl.conf
            ;;
        *)
            echo vm.swappiness=100 >>/etc/sysctl.conf
            ;;
        esac
        sysctl -p >/dev/null 2>&1

        # disable deprecated services
        [ -f "/etc/profile.d/activate_psd_user.sh" ] && rm /etc/profile.d/activate_psd_user.sh
        [ -f "/etc/profile.d/check_first_login.sh" ] && rm /etc/profile.d/check_first_login.sh
        [ -f "/etc/profile.d/check_first_login_reboot.sh" ] && rm /etc/profile.d/check_first_login_reboot.sh
        [ -f "/etc/profile.d/ssh-title.sh" ] && rm /etc/profile.d/ssh-title.sh
        [ -f "/etc/update-motd.d/10-header" ] && rm /etc/update-motd.d/10-header
        [ -f "/etc/update-motd.d/30-sysinfo" ] && rm /etc/update-motd.d/30-sysinfo
        [ -f "/etc/update-motd.d/35-tips" ] && rm /etc/update-motd.d/35-tips
        [ -f "/etc/update-motd.d/40-updates" ] && rm /etc/update-motd.d/40-updates
        [ -f "/etc/update-motd.d/98-autoreboot-warn" ] && rm /etc/update-motd.d/98-autoreboot-warn
        [ -f "/etc/update-motd.d/99-point-to-faq" ] && rm /etc/update-motd.d/99-point-to-faq
        [ -f "/etc/update-motd.d/80-esm" ] && rm /etc/update-motd.d/80-esm
        [ -f "/etc/update-motd.d/80-livepatch" ] && rm /etc/update-motd.d/80-livepatch
        [ -f "/etc/apt/apt.conf.d/02compress-indexes" ] && rm /etc/apt/apt.conf.d/02compress-indexes
        [ -f "/etc/apt/apt.conf.d/02periodic" ] && rm /etc/apt/apt.conf.d/02periodic
        [ -f "/etc/apt/apt.conf.d/no-languages" ] && rm /etc/apt/apt.conf.d/no-languages
        [ -f "/etc/init.d/armhwinfo" ] && rm /etc/init.d/armhwinfo
        [ -f "/etc/logrotate.d/armhwinfo" ] && rm /etc/logrotate.d/armhwinfo
        [ -f "/etc/init.d/firstrun" ] && rm /etc/init.d/firstrun
        [ -f "/etc/init.d/resize2fs" ] && rm /etc/init.d/resize2fs
        [ -f "/lib/systemd/system/firstrun-config.service" ] && rm /lib/systemd/system/firstrun-config.service
        [ -f "/lib/systemd/system/firstrun.service" ] && rm /lib/systemd/system/firstrun.service
        [ -f "/lib/systemd/system/resize2fs.service" ] && rm /lib/systemd/system/resize2fs.service
        [ -f "/usr/lib/orangepi/apt-updates" ] && rm /usr/lib/orangepi/apt-updates
        [ -f "/usr/lib/orangepi/firstrun-config.sh" ] && rm /usr/lib/orangepi/firstrun-config.sh
        # fix for https://bugs.launchpad.net/ubuntu/+source/lightdm-gtk-greeter/+bug/1897491
        [ -d "/var/lib/lightdm" ] && (chown -R lightdm:lightdm /var/lib/lightdm ; chmod 0750 /var/lib/lightdm)
        exit 0
        EOF

        chmod 755 "${destination}"/DEBIAN/preinst

        # postrm script
        cat <<-EOF > "${destination}"/DEBIAN/postrm
        #!/bin/sh
        if [ remove = "\$1" ] || [ abort-install = "\$1" ]; then

            systemctl disable orangepi-hardware-monitor.service orangepi-hardware-optimize.service >/dev/null 2>&1
            systemctl disable orangepi-zram-config.service orangepi-ramlog.service >/dev/null 2>&1

        fi
        exit 0
        EOF

        chmod 755 "${destination}"/DEBIAN/postrm

        # set up post install script
        cat <<-EOF > "${destination}"/DEBIAN/postinst
        #!/bin/sh
        #
        # ${BOARD} BSP post installation script
        #

        [ -f /etc/lib/systemd/system/orangepi-ramlog.service ] && systemctl --no-reload enable orangepi-ramlog.service

        # check if it was disabled in config and disable in new service
        if [ -n "\$(grep -w '^ENABLED=false' /etc/default/log2ram 2> /dev/null)" ]; then

             sed -i "s/^ENABLED=.*/ENABLED=false/" /etc/default/orangepi-ramlog

        fi

        # fix boot delay "waiting for suspend/resume device"
        if [ -f "/etc/initramfs-tools/initramfs.conf" ]; then

            if ! grep --quiet "RESUME=none" /etc/initramfs-tools/initramfs.conf; then
                 echo "RESUME=none" >> /etc/initramfs-tools/initramfs.conf
            fi

        fi

        EOF
        # install bootscripts if they are not present. Fix upgrades from old images
        if [[ $FORCE_BOOTSCRIPT_UPDATE == yes ]]; then
            cat <<-EOF >> "${destination}"/DEBIAN/postinst
        if [ true ]; then

    # this package recreate boot scripts
        EOF
        else
            cat <<-EOF >> "${destination}"/DEBIAN/postinst
        if [ ! -f /boot/$bootscript_dst ]; then

        # if boot script does not exits its recreated
        EOF
        fi
        cat <<-EOF >> "${destination}"/DEBIAN/postinst
    # move bootscript to /usr/share/orangepi
    # create a backup
    [ -f /etc/orangepi-release ] &&  . /etc/orangepi-release
    [ -z \${VERSION} ] && VERSION=$(echo \`date +%s\`)
    if [ -f /boot/$bootscript_dst ]; then
       cp /boot/$bootscript_dst /usr/share/orangepi/${bootscript_dst}-\${VERSION} >/dev/null 2>&1
       echo "NOTE: You can find previous bootscript versions in /usr/share/orangepi !"
    fi

    # cleanup old bootscript backup
    ls /usr/share/orangepi/boot.cmd-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --
    ls /usr/share/orangepi/boot.ini-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --

    echo "Recreating boot script"
    cp /usr/share/orangepi/$bootscript_dst /boot  >/dev/null 2>&1
    rootdev=\$(sed -e 's/^.*root=//' -e 's/ .*\$//' < /proc/cmdline)
    rootfstype=\$(sed -e 's/^.*rootfstype=//' -e 's/ .*$//' < /proc/cmdline)

    # recreate orangepiEnv.txt if it and extlinux does not exists
    if [ ! -f /boot/orangepiEnv.txt ] && [ ! -f /boot/extlinux/extlinux.conf ]; then
      cp /usr/share/orangepi/orangepiEnv.txt /boot  >/dev/null 2>&1
      echo "rootdev="\$rootdev >> /boot/orangepiEnv.txt
      echo "rootfstype="\$rootfstype >> /boot/orangepiEnv.txt
    fi

    [ -f /boot/boot.ini ] && sed -i "s/setenv rootdev.*/setenv rootdev \\"\$rootdev\\"/" /boot/boot.ini
    [ -f /boot/boot.ini ] && sed -i "s/setenv rootfstype.*/setenv rootfstype \\"\$rootfstype\\"/" /boot/boot.ini
    [ -f /boot/boot.cmd ] && mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr  >/dev/null 2>&1

        fi

        [ ! -f "/etc/network/interfaces" ] && [ -f "/etc/network/interfaces.default" ] && cp /etc/network/interfaces.default /etc/network/interfaces
        ln -sf /var/run/motd /etc/motd
        rm -f /etc/update-motd.d/00-header /etc/update-motd.d/10-help-text
        if [ -f "/boot/bin/$BOARD.bin" ] && [ ! -f "/boot/script.bin" ]; then ln -sf bin/$BOARD.bin /boot/script.bin >/dev/null 2>&1 || cp /boot/bin/$BOARD.bin /boot/script.bin; fi
        if [ ! -f "/etc/default/orangepi-motd" ]; then
                mv /etc/default/orangepi-motd.dpkg-dist /etc/default/orangepi-motd
        fi
        if [ ! -f "/etc/default/orangepi-ramlog" ] && [ -f /etc/default/orangepi-ramlog.dpkg-dist ]; then
                mv /etc/default/orangepi-ramlog.dpkg-dist /etc/default/orangepi-ramlog
        fi
        if [ ! -f "/etc/default/orangepi-zram-config" ] && [ -f /etc/default/orangepi-zram-config.dpkg-dist ]; then
                mv /etc/default/orangepi-zram-config.dpkg-dist /etc/default/orangepi-zram-config
        fi

        if [ -L "/usr/lib/chromium-browser/master_preferences.dpkg-dist" ]; then
                mv /usr/lib/chromium-browser/master_preferences.dpkg-dist /usr/lib/chromium-browser/master_preferences
        fi

        # Read release value
        if [ -f /etc/lsb-release ]; then
                RELEASE=\$(cat /etc/lsb-release | grep CODENAME | cut -d"=" -f2 | sed 's/.*/\u&/')
                sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${VENDOR} $REVISION "\${RELEASE}"\"/" /etc/os-release
                echo "${VENDOR} ${REVISION} \${RELEASE} \\l \n" > /etc/issue
                echo "${VENDOR} ${REVISION} \${RELEASE}" > /etc/issue.net
        fi

        # Reload services
        systemctl --no-reload enable orangepi-hardware-monitor.service orangepi-hardware-optimize.service orangepi-zram-config.service >/dev/null 2>&1
        exit 0
        EOF

        chmod 755 "${destination}"/DEBIAN/postinst

        # won't recreate files if they were removed by user
        # TODO: Add proper handling for updated conffiles
        #cat <<-EOF > "${destination}"/DEBIAN/conffiles
        #EOF

        # copy common files from a premade directory structure
        rsync -a "${EXTER}"/packages/bsp/common/* ${destination}

        # trigger uInitrd creation after installation, to apply
        # /etc/initramfs/post-update.d/99-uboot
        cat <<-EOF > "${destination}"/DEBIAN/triggers
        activate update-initramfs
        EOF

        # copy distribution support status
        local releases=($(find ${EXTER}/config/distributions -mindepth 1 -maxdepth 1 -type d))
        for i in ${releases[@]}
        do
                echo "$(echo $i | sed 's/.*\///')=$(cat $i/support)" >> "${destination}"/etc/orangepi-distribution-status
        done

        # armhwinfo, firstrun, orangepimonitor, etc. config file
        cat <<-EOF > "${destination}"/etc/orangepi-release
        # PLEASE DO NOT EDIT THIS FILE
        BOARD=${BOARD}
        BOARD_NAME="$BOARD_NAME"
        BOARDFAMILY=${BOARDFAMILY}
        BUILD_REPOSITORY_URL=${BUILD_REPOSITORY_URL}
        BUILD_REPOSITORY_COMMIT=${BUILD_REPOSITORY_COMMIT}
        DISTRIBUTION_CODENAME=${RELEASE}
        DISTRIBUTION_STATUS=${DISTRIBUTION_STATUS}
        VERSION=${REVISION}
        LINUXFAMILY=${LINUXFAMILY}
        ARCH=${ARCHITECTURE}
        IMAGE_TYPE=$IMAGE_TYPE
        BOARD_TYPE=$BOARD_TYPE
        INITRD_ARCH=${INITRD_ARCH}
        KERNEL_IMAGE_TYPE=${KERNEL_IMAGE_TYPE}
        BRANCH=${BRANCH}
        EOF

        # this is required for NFS boot to prevent deconfiguring the network on shutdown
        sed -i 's/#no-auto-down/no-auto-down/g' "${destination}"/etc/network/interfaces.default

        if [[ ( $LINUXFAMILY == sun8i ) && $BRANCH == legacy ]]; then
                # add mpv config for vdpau_sunxi
                mkdir -p "${destination}"/etc/mpv/
                cp "${EXTER}"/packages/bsp/mpv/mpv_sunxi.conf "${destination}"/etc/mpv/mpv.conf
                echo "export VDPAU_OSD=1" > "${destination}"/etc/profile.d/90-vdpau.sh
                chmod 755 "${destination}"/etc/profile.d/90-vdpau.sh
        fi
        if [[ $LINUXFAMILY == sunxi* ]]; then
                # add mpv config for x11 output - slow, but it works compared to no config at all
                # TODO: Test which output driver is better with DRM
                mkdir -p "${destination}"/etc/mpv/
                cp "${EXTER}"/packages/bsp/mpv/mpv_mainline.conf "${destination}"/etc/mpv/mpv.conf
        fi

        case $RELEASE in
        xenial)
                if [[ $BRANCH == legacy && $LINUXFAMILY == sun8i ]]; then
                        # this is required only for old kernels
                        # not needed for Stretch since there will be no Stretch images with kernels < 4.4
                        mkdir -p "${destination}"/lib/systemd/system/haveged.service.d/
                        cp "${EXTER}"/packages/bsp/10-no-new-privileges.conf "${destination}"/lib/systemd/system/haveged.service.d/
                fi
        ;;
        esac
        # execute $LINUXFAMILY-specific tweaks
        [[ $(type -t family_tweaks_bsp) == function ]] && family_tweaks_bsp

        call_extension_method "post_family_tweaks_bsp" << 'POST_FAMILY_TWEAKS_BSP'
*family_tweaks_bsp overrrides what is in the config, so give it a chance to override the family tweaks*
This should be implemented by the config to tweak the BSP, after the board or family has had the chance to.
POST_FAMILY_TWEAKS_BSP

        # add some summary to the image
        fingerprint_image "${destination}/etc/orangepi.txt"

        # fixing permissions (basic), reference: dh_fixperms
        find "${destination}" -print0 2>/dev/null | xargs -0r chown --no-dereference 0:0
        find "${destination}" ! -type l -print0 2>/dev/null | xargs -0r chmod 'go=rX,u+rw,a-s'

        # create board DEB file
        fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "${destination}" "${destination}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
        mkdir -p "${DEB_STORAGE}/${RELEASE}/"
        rsync --remove-source-files -rq "${destination}.deb" "${DEB_STORAGE}/${RELEASE}/"

        # cleanup
        rm -rf ${bsptempdir}
}

接下来我们对该函数进行一一分析。

5.1 准备工作

最开始是一系列的准备工作,具体如下。

5.1.1 确定目标路径

创建BSP临时目录,全路径为${bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64

# 创建临时目录
bsptempdir=$(mktemp -d)
# 修改权限
chmod 700 ${bsptempdir}
# 设置信号处理器,当收到信号0/1/2/3/15 删除临时目录
trap "rm -rf \"${bsptempdir}\" ; exit 0" 0 1 2 3 15
# 设置目标目录${bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
local destination=${bsptempdir}/${RELEASE}/${BSP_CLI_PACKAGE_FULLNAME}
# 创建目录{bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64/DEBIAN
mkdir -p "${destination}"/DEBIAN
cd $destination

注意:后文我们就把$destination这个目录称为bsp目录。

5.1.2 拷贝bsp-cli

接着是将若干个目录复制到bsp目录中;

# copy general overlay from packages/bsp-cli
copy_all_packages_files_for "bsp-cli"

copy_all_packages_files_for函数定义在scripts/image-helpers.sh

# copy_all_packages_files_for <folder> to package
#
copy_all_packages_files_for()
{
		# bsp-cli
        local package_name="${1}"
        # 遍历以下目录
        # <SDK>/external/packages            => 不存在子目录bsp-cli
        # <SDK>/external/config/optional/_any_board/_packages   => 不存在子目录bsp-cli
        # <SDK>/external/config/optional/architectures/arm64/_packages   => 不存在子目录bsp-cli
        # <SDK>/external/config/optional/families/rockchip-rk356x/_packages  
        # <SDK>/external/config/optional/boards/orangepi3b/_packages   => 不存在子目录bsp-cli
        for package_src_dir in ${PACKAGES_SEARCH_ROOT_ABSOLUTE_DIRS};
        do        		
        		# ..../bsp-cli
                local package_dirpath="${package_src_dir}/${package_name}"
                # 如果目录存在,则拷贝到${bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
                if [ -d "${package_dirpath}" ];
                then
                        cp -r "${package_dirpath}/"* "${destination}/" 2> /dev/null
                        display_alert "Adding files from" "${package_dirpath}"
                fi
        done
}

这里只有external/config/optional/boards/orangepi3b/_packages/bsp-cli/目录是存在的;

root@ubuntu:/work/sambashare/rk3566/orangepi-build# tree external/config/optional/boards/orangepi3b/_packages/bsp-cli/
external/config/optional/boards/orangepi3b/_packages/bsp-cli/
└── usr
    └── bin
        └── hciattach_opi
5.1.3 拷贝boot脚本/环境遍历文件

接着是拷贝boot脚本以及环境变量文件到bsp目录;

# install copy of boot script & environment file,  BOOTCONFIG="orangepi-3b-rk3566_defconfig"
if [[ "${BOOTCONFIG}" != "none" ]]; then
		# @TODO: add extension method bsp_prepare_bootloader(), refactor into u-boot extension
		# 提取冒号前的部分boot-rockchip64.cmd
		local bootscript_src=${BOOTSCRIPT%%:*}
		# 提取冒号后的部分boot.cmd
		local bootscript_dst=${BOOTSCRIPT##*:}
		# 在rootfs创建/usr/share/orangepi/目录
		mkdir -p "${destination}"/usr/share/orangepi/

		# create extlinux config file
		if [[ $SRC_EXTLINUX != yes ]]; then
				# 不会进入
				if [ -f "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" ]; then
				  cp "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
				else
				  # 进入
				  cp "${EXTER}/config/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
				fi
				# BOOTENV_FILE='rockchip.txt',$SRC/config/bootenv/$BOOTENV_FILE文件不存在,不会进入
				[[ -n $BOOTENV_FILE && -f $SRC/config/bootenv/$BOOTENV_FILE ]] && \
						cp "${EXTER}/config/bootenv/${BOOTENV_FILE}" "${destination}"/usr/share/orangepi/orangepiEnv.txt
		fi

		# add configuration for setting uboot environment from userspace with: fw_setenv fw_printenv,不会进入
		if [[ -n $UBOOT_FW_ENV ]]; then
				UBOOT_FW_ENV=($(tr ',' ' ' <<< "$UBOOT_FW_ENV"))
				mkdir -p "${destination}"/etc
				echo "# Device to access      offset           env size" > "${destination}"/etc/fw_env.config
				echo "/dev/mmcblk0      ${UBOOT_FW_ENV[0]}      ${UBOOT_FW_ENV[1]}" >> "${destination}"/etc/fw_env.config
		fi
fi

该段脚本会就是复制<SDK>/external/config/bootscripts/boot-rockchip64.cmd到根文件系统/usr/share/orangepi/boot.cmd

5.2 DEBIAN目录

5.2.1 创建/DEBIAN/control

接着生成一个Debian包的控制文件 (control),包含了必要的元数据、依赖关系和建议项,确保正确的安装和管理;

# Replaces: base-files is needed to replace /etc/update-motd.d/ files on Xenial
# Replaces: unattended-upgrades may be needed to replace /etc/apt/apt.conf.d/50unattended-upgrades
# (distributions provide good defaults, so this is not needed currently)
# Depends: linux-base is needed for "linux-version" command in initrd cleanup script
# Depends: fping is needed for orangepimonitor to upload orangepi-hardware-monitor.log
cat <<-EOF > "${destination}"/DEBIAN/control
Package: ${BSP_CLI_PACKAGE_NAME}
Version: $REVISION
Architecture: $ARCH
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Installed-Size: 1
Section: kernel
Priority: optional
Depends: bash, linux-base, u-boot-tools, initramfs-tools, lsb-release, fping
Provides: linux-${RELEASE}-root-legacy-$BOARD, linux-${RELEASE}-root-current-$BOARD, linux-${RELEASE}-root-next-$BOARD
Suggests: orangepi-config
Replaces: zram-config, base-files, orangepi-tools-$RELEASE, linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
Breaks: linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
Recommends: bsdutils, parted, util-linux, toilet
Description: OrangePi board support files for $BOARD
EOF
5.2.1 创建/DEBIAN/preinst

接着是生成一个Debian软件包的preinst脚本,用于在软件包安装之前执行一些操作;

# set up pre install script
cat <<-EOF > "${destination}"/DEBIAN/preinst
#!/bin/sh

# tell people to reboot at next login
[ "\$1" = "upgrade" ] && touch /var/run/.reboot_required

# convert link to file
if [ -L "/etc/network/interfaces" ]; then
	cp /etc/network/interfaces /etc/network/interfaces.tmp
	rm /etc/network/interfaces
	mv /etc/network/interfaces.tmp /etc/network/interfaces
fi

# fixing ramdisk corruption when using lz4 compression method
sed -i "s/^COMPRESS=.*/COMPRESS=gzip/" /etc/initramfs-tools/initramfs.conf

# swap
grep -q vm.swappiness /etc/sysctl.conf
case \$? in
0)
	sed -i 's/vm\.swappiness.*/vm.swappiness=100/' /etc/sysctl.conf
	;;
*)
	echo vm.swappiness=100 >>/etc/sysctl.conf
	;;
esac
sysctl -p >/dev/null 2>&1

# disable deprecated services
[ -f "/etc/profile.d/activate_psd_user.sh" ] && rm /etc/profile.d/activate_psd_user.sh
[ -f "/etc/profile.d/check_first_login.sh" ] && rm /etc/profile.d/check_first_login.sh
[ -f "/etc/profile.d/check_first_login_reboot.sh" ] && rm /etc/profile.d/check_first_login_reboot.sh
[ -f "/etc/profile.d/ssh-title.sh" ] && rm /etc/profile.d/ssh-title.sh
[ -f "/etc/update-motd.d/10-header" ] && rm /etc/update-motd.d/10-header
[ -f "/etc/update-motd.d/30-sysinfo" ] && rm /etc/update-motd.d/30-sysinfo
[ -f "/etc/update-motd.d/35-tips" ] && rm /etc/update-motd.d/35-tips
[ -f "/etc/update-motd.d/40-updates" ] && rm /etc/update-motd.d/40-updates
[ -f "/etc/update-motd.d/98-autoreboot-warn" ] && rm /etc/update-motd.d/98-autoreboot-warn
[ -f "/etc/update-motd.d/99-point-to-faq" ] && rm /etc/update-motd.d/99-point-to-faq
[ -f "/etc/update-motd.d/80-esm" ] && rm /etc/update-motd.d/80-esm
[ -f "/etc/update-motd.d/80-livepatch" ] && rm /etc/update-motd.d/80-livepatch
[ -f "/etc/apt/apt.conf.d/02compress-indexes" ] && rm /etc/apt/apt.conf.d/02compress-indexes
[ -f "/etc/apt/apt.conf.d/02periodic" ] && rm /etc/apt/apt.conf.d/02periodic
[ -f "/etc/apt/apt.conf.d/no-languages" ] && rm /etc/apt/apt.conf.d/no-languages
[ -f "/etc/init.d/armhwinfo" ] && rm /etc/init.d/armhwinfo
[ -f "/etc/logrotate.d/armhwinfo" ] && rm /etc/logrotate.d/armhwinfo
[ -f "/etc/init.d/firstrun" ] && rm /etc/init.d/firstrun
[ -f "/etc/init.d/resize2fs" ] && rm /etc/init.d/resize2fs
[ -f "/lib/systemd/system/firstrun-config.service" ] && rm /lib/systemd/system/firstrun-config.service
[ -f "/lib/systemd/system/firstrun.service" ] && rm /lib/systemd/system/firstrun.service
[ -f "/lib/systemd/system/resize2fs.service" ] && rm /lib/systemd/system/resize2fs.service
[ -f "/usr/lib/orangepi/apt-updates" ] && rm /usr/lib/orangepi/apt-updates
[ -f "/usr/lib/orangepi/firstrun-config.sh" ] && rm /usr/lib/orangepi/firstrun-config.sh
# fix for https://bugs.launchpad.net/ubuntu/+source/lightdm-gtk-greeter/+bug/1897491
[ -d "/var/lib/lightdm" ] && (chown -R lightdm:lightdm /var/lib/lightdm ; chmod 0750 /var/lib/lightdm)
exit 0
EOF

chmod 755 "${destination}"/DEBIAN/preinst
5.2.3 创建/DEBIAN/postinst

接着是生成一个Debian软件包的postinst脚本,用于在软件包安装之后执行一些操作;

# 第一段
# set up post install script
cat <<-EOF > "${destination}"/DEBIAN/postinst
#!/bin/sh
#
# ${BOARD} BSP post installation script
#

[ -f /etc/lib/systemd/system/orangepi-ramlog.service ] && systemctl --no-reload enable orangepi-ramlog.service

# check if it was disabled in config and disable in new service
if [ -n "\$(grep -w '^ENABLED=false' /etc/default/log2ram 2> /dev/null)" ]; then
	 sed -i "s/^ENABLED=.*/ENABLED=false/" /etc/default/orangepi-ramlog
fi

# fix boot delay "waiting for suspend/resume device"
if [ -f "/etc/initramfs-tools/initramfs.conf" ]; then
	if ! grep --quiet "RESUME=none" /etc/initramfs-tools/initramfs.conf; then
		 echo "RESUME=none" >> /etc/initramfs-tools/initramfs.conf
	fi
fi
EOF

# install bootscripts if they are not present. Fix upgrades from old images
if [[ $FORCE_BOOTSCRIPT_UPDATE == yes ]]; then
	# 第二段
	cat <<-EOF >> "${destination}"/DEBIAN/postinst
	if [ true ]; then
		# this package recreate boot scripts
		EOF
	else
		cat <<-EOF >> "${destination}"/DEBIAN/postinst
		if [ ! -f /boot/$bootscript_dst ]; then

		# if boot script does not exits its recreated
		EOF
	fi
	
	# 第二段
	cat <<-EOF >> "${destination}"/DEBIAN/postinst
	# move bootscript to /usr/share/orangepi
	# create a backup
	[ -f /etc/orangepi-release ] &&  . /etc/orangepi-release
	[ -z \${VERSION} ] && VERSION=$(echo \`date +%s\`)
	if [ -f /boot/$bootscript_dst ]; then
	   cp /boot/$bootscript_dst /usr/share/orangepi/${bootscript_dst}-\${VERSION} >/dev/null 2>&1
	   echo "NOTE: You can find previous bootscript versions in /usr/share/orangepi !"
	fi

	# cleanup old bootscript backup
	ls /usr/share/orangepi/boot.cmd-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --
	ls /usr/share/orangepi/boot.ini-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --

	echo "Recreating boot script"
	cp /usr/share/orangepi/$bootscript_dst /boot  >/dev/null 2>&1
	rootdev=\$(sed -e 's/^.*root=//' -e 's/ .*\$//' < /proc/cmdline)
	rootfstype=\$(sed -e 's/^.*rootfstype=//' -e 's/ .*$//' < /proc/cmdline)

	# recreate orangepiEnv.txt if it and extlinux does not exists
	if [ ! -f /boot/orangepiEnv.txt ] && [ ! -f /boot/extlinux/extlinux.conf ]; then
	  cp /usr/share/orangepi/orangepiEnv.txt /boot  >/dev/null 2>&1
	  echo "rootdev="\$rootdev >> /boot/orangepiEnv.txt
	  echo "rootfstype="\$rootfstype >> /boot/orangepiEnv.txt
	fi

	[ -f /boot/boot.ini ] && sed -i "s/setenv rootdev.*/setenv rootdev \\"\$rootdev\\"/" /boot/boot.ini
	[ -f /boot/boot.ini ] && sed -i "s/setenv rootfstype.*/setenv rootfstype \\"\$rootfstype\\"/" /boot/boot.ini
	[ -f /boot/boot.cmd ] && mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr  >/dev/null 2>&1
fi

[ ! -f "/etc/network/interfaces" ] && [ -f "/etc/network/interfaces.default" ] && cp /etc/network/interfaces.default /etc/network/interfaces
ln -sf /var/run/motd /etc/motd
rm -f /etc/update-motd.d/00-header /etc/update-motd.d/10-help-text
if [ -f "/boot/bin/$BOARD.bin" ] && [ ! -f "/boot/script.bin" ]; then ln -sf bin/$BOARD.bin /boot/script.bin >/dev/null 2>&1 || cp /boot/bin/$BOARD.bin /boot/script.bin; fi
if [ ! -f "/etc/default/orangepi-motd" ]; then
		mv /etc/default/orangepi-motd.dpkg-dist /etc/default/orangepi-motd
fi
if [ ! -f "/etc/default/orangepi-ramlog" ] && [ -f /etc/default/orangepi-ramlog.dpkg-dist ]; then
		mv /etc/default/orangepi-ramlog.dpkg-dist /etc/default/orangepi-ramlog
fi
if [ ! -f "/etc/default/orangepi-zram-config" ] && [ -f /etc/default/orangepi-zram-config.dpkg-dist ]; then
		mv /etc/default/orangepi-zram-config.dpkg-dist /etc/default/orangepi-zram-config
fi

if [ -L "/usr/lib/chromium-browser/master_preferences.dpkg-dist" ]; then
		mv /usr/lib/chromium-browser/master_preferences.dpkg-dist /usr/lib/chromium-browser/master_preferences
fi

# Read release value
if [ -f /etc/lsb-release ]; then
		RELEASE=\$(cat /etc/lsb-release | grep CODENAME | cut -d"=" -f2 | sed 's/.*/\u&/')
		sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${VENDOR} $REVISION "\${RELEASE}"\"/" /etc/os-release
		echo "${VENDOR} ${REVISION} \${RELEASE} \\l \n" > /etc/issue
		echo "${VENDOR} ${REVISION} \${RELEASE}" > /etc/issue.net
fi

# Reload services
systemctl --no-reload enable orangepi-hardware-monitor.service orangepi-hardware-optimize.service orangepi-zram-config.service >/dev/null 2>&1
exit 0
EOF

5.3 其它

5.3.1 orangepi-distribution-status

接着是将这段脚本片段的作用是将<external/config/distributions/>路径下的各个发行版支持状态信息复制到/etc/orangepi-distribution-status

# copy distribution support status
local releases=($(find ${EXTER}/config/distributions -mindepth 1 -maxdepth 1 -type d))
for i in ${releases[@]}
do
		# 读取每个发行版目录中的support文件的内容,然后将发行版名称和其支持状态写入到orangepi-distribution-status
		echo "$(echo $i | sed 's/.*\///')=$(cat $i/support)" >> "${destination}"/etc/orangepi-distribution-status
done

最终得到的orangepi-distribution-status文件内容;

sid=csc
jammy=supported
focal=supported
bullseye=supported
bionic=supported
stretch=eos
xenial=eos
buster=supported
raspi=supported
bookworm=supported
5.3.2 orangepi-release

接着向/etc/orangepi-release写入一些配置信息;

# armhwinfo, firstrun, orangepimonitor, etc. config file
cat <<-EOF > "${destination}"/etc/orangepi-release
# PLEASE DO NOT EDIT THIS FILE
BOARD=${BOARD}
BOARD_NAME="$BOARD_NAME"
BOARDFAMILY=${BOARDFAMILY}
BUILD_REPOSITORY_URL=${BUILD_REPOSITORY_URL}
BUILD_REPOSITORY_COMMIT=${BUILD_REPOSITORY_COMMIT}
DISTRIBUTION_CODENAME=${RELEASE}
DISTRIBUTION_STATUS=${DISTRIBUTION_STATUS}
VERSION=${REVISION}
LINUXFAMILY=${LINUXFAMILY}
ARCH=${ARCHITECTURE}
IMAGE_TYPE=$IMAGE_TYPE
BOARD_TYPE=$BOARD_TYPE
INITRD_ARCH=${INITRD_ARCH}
KERNEL_IMAGE_TYPE=${KERNEL_IMAGE_TYPE}
BRANCH=${BRANCH}
EOF

最终得到的orangepi-release文件内容;

# PLEASE DO NOT EDIT THIS FILE
BOARD=orangepi3b
BOARD_NAME="OPI 3B"
BOARDFAMILY=rockchip-rk356x
BUILD_REPOSITORY_URL=https://kkgithub.com/orangepi-xunlong/orangepi-build.git
BUILD_REPOSITORY_COMMIT=5262437
DISTRIBUTION_CODENAME=bullseye
DISTRIBUTION_STATUS=supported
VERSION=1.0.6
LINUXFAMILY=rockchip-rk356x
ARCH=arm64
IMAGE_TYPE=user-built
BOARD_TYPE=conf
INITRD_ARCH=arm64
KERNEL_IMAGE_TYPE=Image
BRANCH=current
5.3.3 生成deb

函数最后先是将/etc/network/interfaces.default文件中的#no-auto-down替换为no-auto-down

接着执行了family_tweaks_bsp函数,其定义在<SDK>/external/config/sources/families/rockchip-rk356x.conf

然后设置bsp目录下的所有文件和目录的所有者、组以及权限;

最后就是使用fakerootbsp目录打包成deb包,包名为orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb,并拷贝到<SDK>/output/debs/bullseye目录;

# this is required for NFS boot to prevent deconfiguring the network on shutdown
sed -i 's/#no-auto-down/no-auto-down/g' "${destination}"/etc/network/interfaces.default

# execute $LINUXFAMILY-specific tweaks
[[ $(type -t family_tweaks_bsp) == function ]] && family_tweaks_bsp

call_extension_method "post_family_tweaks_bsp" << 'POST_FAMILY_TWEAKS_BSP'
*family_tweaks_bsp overrrides what is in the config, so give it a chance to override the family tweaks*
This should be implemented by the config to tweak the BSP, after the board or family has had the chance to.
POST_FAMILY_TWEAKS_BSP

# add some summary to the image
fingerprint_image "${destination}/etc/orangepi.txt"

# fixing permissions (basic), reference: dh_fixperms,将根文件系统下的所有文件和目录的所有者和组设置为root
find "${destination}" -print0 2>/dev/null | xargs -0r chown --no-dereference 0:0
# 将不是符号链接的文件和目录,权限设置为为755(目录)和644(文件)
find "${destination}" ! -type l -print0 2>/dev/null | xargs -0r chmod 'go=rX,u+rw,a-s'

# create board DEB file
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "${destination}" "${destination}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
mkdir -p "${DEB_STORAGE}/${RELEASE}/"
rsync --remove-source-files -rq "${destination}.deb" "${DEB_STORAGE}/${RELEASE}/"

# cleanup
rm -rf ${bsptempdir}

orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb包解压,包含如下文件;

点击查看代码
root@ubuntu:/work/sambashare/rk3566/orangepi-build/output/debs/bullseye$ sudo dpkg -x orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb ./
root@ubuntu:/work/sambashare/rk3566/orangepi-build/output/debs/bullseye$ ll
drwxr-xr-x 17 root root   4096  7月 10 18:01 etc/
drwxr-xr-x  4 root root   4096  7月 10 13:40 lib/
-rw-r--r--  1 root sudo 781316  7月 10 18:01 orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
drwxr-xr-x  8 root root   4096  7月 10 13:40 usr/
drwxr-xr-x  3 root root   4096  7月 10 13:40 var/

root@ubuntu:/work/sambashare/rk3566/orangepi-build/output/debs/bullseye# tree
.
├── etc
│   ├── apt
│   │   └── apt.conf.d
│   │       ├── 02-orangepi-compress-indexes
│   │       ├── 02-orangepi-periodic
│   │       ├── 71-orangepi-no-recommends
│   │       └── 81-orangepi-no-languages
│   ├── cron.d
│   │   ├── orangepi-truncate-logs
│   │   └── orangepi-updates
│   ├── cron.daily
│   │   └── orangepi-ram-logging
│   ├── default
│   │   ├── orangepi-motd.dpkg-dist
│   │   ├── orangepi-ramlog.dpkg-dist
│   │   └── orangepi-zram-config.dpkg-dist
│   ├── initramfs
│   │   └── post-update.d
│   │       └── 99-uboot
│   ├── initramfs-tools
│   │   └── hooks
│   │       └── bootsplash.sh
│   ├── kernel
│   │   ├── postinst.d
│   │   │   └── xx-initramfs-cleanup
│   │   └── postrm.d
│   │       └── xx-initramfs-cleanup
│   ├── modprobe.d
│   │   ├── 8189fs.conf
│   │   └── r8723bs.conf
│   ├── network
│   │   └── interfaces.default
│   ├── NetworkManager
│   │   └── conf.d
│   │       ├── 10-override-wifi-random-mac-disable.conf
│   │       └── 20-override-wifi-powersave-disable.conf
│   ├── orangepi-distribution-status
│   ├── orangepi-release
│   ├── orangepi.txt
│   ├── profile.d
│   │   ├── orangepi-activate-profile-sync-daemon.sh
│   │   ├── orangepi-check-first-login-reboot.sh
│   │   ├── orangepi-check-first-login.sh
│   │   ├── orangepi-lang.sh
│   │   └── orangepi-ssh-title.sh
│   ├── skel
│   ├── systemd
│   │   └── system
│   │       └── logrotate.service
│   ├── udev
│   │   └── rules.d
│   │       ├── 10-wifi-disable-powermanagement.rules
│   │       └── 50-usb-realtek-net.rules
│   └── update-motd.d
│       ├── 10-orangepi-header
│       ├── 30-orangepi-sysinfo
│       ├── 35-orangepi-tips
│       ├── 40-orangepi-updates
│       ├── 41-orangepi-config
│       └── 98-orangepi-autoreboot-warn
├── lib
│   ├── systemd
│   │   └── system
│   │       ├── bootsplash-ask-password-console.path
│   │       ├── bootsplash-ask-password-console.service
│   │       ├── bootsplash-hide-when-booted.service
│   │       ├── bootsplash-show-on-shutdown.service
│   │       ├── getty@tty1.service.d
│   │       │   └── 10-noclear.conf
│   │       ├── orangepi-disable-autologin.service
│   │       ├── orangepi-disable-autologin.timer
│   │       ├── orangepi-firstrun-config.service
│   │       ├── orangepi-firstrun.service
│   │       ├── orangepi-hardware-monitor.service
│   │       ├── orangepi-hardware-optimize.service
│   │       ├── orangepi-ramlog.service
│   │       ├── orangepi-resize-filesystem.service
│   │       ├── orangepi-zram-config.service
│   │       ├── serial-getty@.service.d
│   │       │   └── 10-term.conf
│   │       ├── systemd-journald.service.d
│   │       │   └── override.conf
│   │       └── systemd-modules-load.service.d
│   │           └── 10-timeout.conf
│   └── udev
│       └── rules.d
│           └── 71-axp-power-button.rules
├── orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
├── usr
│   ├── bin
│   │   ├── adbd
│   │   ├── hciattach_opi
│   │   ├── memtester.sh
│   │   └── orangepimonitor
│   ├── lib
│   │   ├── chromium-browser
│   │   │   └── master_preferences.dpkg-dist -> ../../share/chromium/master_preferences
│   │   ├── nand-sata-install
│   │   │   └── exclude.txt
│   │   └── orangepi
│   │       ├── orangepi-apt-updates
│   │       ├── orangepi-audio-config
│   │       ├── orangepi-common
│   │       ├── orangepi-firstlogin
│   │       ├── orangepi-firstrun
│   │       ├── orangepi-firstrun-config
│   │       ├── orangepi-hardware-monitor
│   │       ├── orangepi-hardware-optimization
│   │       ├── orangepi-ramlog
│   │       ├── orangepi-resize-filesystem
│   │       ├── orangepi-truncate-logs
│   │       └── orangepi-zram-config
│   ├── local
│   │   └── bin
│   │       └── test_pwm.sh
│   ├── sbin
│   │   ├── blink_all_gpio
│   │   ├── burn_to_emmc
│   │   ├── nand-sata-install
│   │   └── orangepi-add-overlay
│   ├── share
│   │   ├── initramfs-tools
│   │   │   ├── conf-hooks.d
│   │   │   │   └── orangepi-plymouth
│   │   │   └── hooks
│   │   │       └── usb_modeswitch
│   │   └── orangepi
│   │       └── boot.cmd
│   └── src
│       └── hello
│           ├── hello.c
│           └── Makefile
└── var
    └── lib
        └── polkit-1
            └── localauthority
                └── 50-local.d
                    ├── backlight.pkla
                    ├── networkmanager.pkla
                    ├── plugdev.pkla
                    ├── power.pkla
                    └── printing.pkla

54 directories, 88 files

上面我们看到了一大堆文件,但是在create_board_package中并没有创建那么多文件,那么其它一些文件哪里来的呢?这个我们后续会介绍到。

debootstrap.sh

debootstrap.sh脚本文件实现了rootfs文件系统以及Linux镜像文件(统一固件)的制作,该文件主要提供了debootstrap_ngcreate_rootfs_cacheprepare_partitionsupdate_initramfscreate_image函数。

6.1 debootstrap_ng

点击查看代码
debootstrap_ng()
{
        display_alert "Starting rootfs and image building process for" "${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED:-null} ${DESKTOP_ENVIRONMENT:-null} ${BUILD_MINIMAL}" "info"

		# ROOTFS_TYPE默认为ext4 
        [[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming $BOARD $BRANCH kernel supports $ROOTFS_TYPE" "" "wrn"

        # trap to unmount stuff in case of error/manual interruption,
        trap unmount_on_exit INT TERM EXIT

        # stage: clean and create directories
        rm -rf $SDCARD $MOUNT
        mkdir -p $SDCARD $MOUNT $DEST/images $EXTER/cache/rootfs

        # stage: verify tmpfs configuration and mount
        # CLI needs ~1.5GiB, desktop - ~3.5GiB
        # calculate and set tmpfs mount to use 9/10 of available RAM+SWAP
        local phymem=$(( (($(awk '/MemTotal/ {print $2}' /proc/meminfo) + $(awk '/SwapTotal/ {print $2}' /proc/meminfo))) / 1024 * 9 / 10 )) # MiB
        if [[ $BUILD_DESKTOP == yes ]]; then local tmpfs_max_size=3500; else local tmpfs_max_size=1500; fi # MiB
        if [[ $FORCE_USE_RAMDISK == no ]]; then local use_tmpfs=no
        elif [[ $FORCE_USE_RAMDISK == yes || $phymem -gt $tmpfs_max_size ]]; then
                local use_tmpfs=yes
        fi
        [[ -n $FORCE_TMPFS_SIZE ]] && phymem=$FORCE_TMPFS_SIZE

        [[ $use_tmpfs == yes ]] && mount -t tmpfs -o size=${phymem}M tmpfs $SDCARD

        # stage: prepare basic rootfs: unpack cache or create from scratch
        create_rootfs_cache

        call_extension_method "pre_install_distribution_specific" "config_pre_install_distribution_specific" << 'PRE_INSTALL_DISTRIBUTION_SPECIFIC'
*give config a chance to act before install_distribution_specific*
Called after `create_rootfs_cache` (_prepare basic rootfs: unpack cache or create from scratch_) but before `install_distribution_specific` (_install distribution and board specific applications_).
PRE_INSTALL_DISTRIBUTION_SPECIFIC

        # stage: install kernel and u-boot packages
        # install distribution and board specific applications

        if [[ ${RELEASE} == "raspi" ]]; then
                install_opi_specific
        else
                install_distribution_specific
                install_common

                # install locally built packages or install pre-built packages from orangepi
                [[ $EXTERNAL_NEW == compile || $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages_local

                #[[ $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages "yes"

                # stage: user customization script
                # NOTE: installing too many packages may fill tmpfs mount
                customize_image

                # remove packages that are no longer needed. Since we have intrudoced uninstall feature, we might want to clean things that are no longer needed
                display_alert "No longer needed packages" "purge" "info"
                chroot $SDCARD /bin/bash -c "apt-get autoremove -y"  >/dev/null 2>&1

                # create list of installed packages for debug purposes
                chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > $DEST/${LOG_SUBPATH}/installed-packages-${RELEASE}$([[ ${BUILD_MINIMAL} == yes ]] && echo "-minimal")$([[ ${BUILD_DESKTOP} == yes  ]] && echo "-desktop").list 2>&1

        fi

        # clean up / prepare for making the image
        umount_chroot "$SDCARD"
        post_debootstrap_tweaks

        if [[ $ROOTFS_TYPE == fel ]]; then
                FEL_ROOTFS=$SDCARD/
                display_alert "Starting FEL boot" "$BOARD" "info"
                source $SRC/scripts/fel-load.sh
        else
                prepare_partitions
                create_image
        fi

        # stage: unmount tmpfs
        umount $SDCARD 2>&1
        if [[ $use_tmpfs = yes ]]; then
                while grep -qs "$SDCARD" /proc/mounts
                do
                        umount $SDCARD
                        sleep 5
                done
        fi
        rm -rf $SDCARD

        # remove exit trap
        trap - INT TERM EXIT
} #############################################################################
6.1.1 准备工作

函数最开始是一些准备工作,比如:

  • 输出构建rootfs开始的日志信息;
  • 设置陷阱,当接收到INT/TERM/EXIT信号时执行unmount_on_exit
  • 删除$SDCARD目录,SDCARD被设置为<SDK>/.tmp/rootfs-$(uuidgen):用于挂载tmpfs文件系统,存放根文件系统文件(对于tmpfs文件系统,数据是放在内存的);
  • 删除$MOUNT目录,MOUNT被设置为<SDK>/.tmp/mount-$(uuidgen):创建根文件系统镜像<SDK>/.tmp/rootfs-$(uuidgen).raw时的挂载点;
  • 创建临时目录: $SDCARD$MOUNT <SDK>/output/images<SDK>/external/cache/rootfs
  • 设置phymem=RAM+SWAP内存空间的9/10,比如我使用的宿主机这个大小为8949MB
  • 设置tmpfs_max_size=1500(根文件系统在临时文件系统占用的最大空间)、use_tmpfs=yes
  • 最后将一个大小为 phymemtmpfs文件系统挂载到$SDCARD目录;tmpfs是一种存在于内存中的临时文件系统,用于存储临时数据,因此要求phymem>tmpfs_max_size

比如我们在编译过程中可以查看<SDK>/.tmp目录;

root@ubuntu:/work/sambashare/rk3566/orangepi-build/.tmp$ ll
-rw-rw-r--  1 root root  219  8月  4 11:31 extension_function_cleanup.sh
drwxrwxr-x  3 root root 4096  8月  4 11:31 .extensions/
drwxrwxr-x  2 root root 4096  8月  4 11:31 mount-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/
drwxrwxrwt 18 root root  420  8月  4 11:31 rootfs-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/
root@ubuntu:/work/sambashare/rk3566/orangepi-build/.tmp$ ls rootfs-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/
bin  boot  dev  etc  home  lib  media  mnt  opt  proc  root  run  sbin  selinux  srv  sys  tmp  usr  var

root@ubuntu:/work/sambashare/rk3566/orangepi-build/.tmp$ df -hT
文件系统         类型   大小  已用  可用 已用% 挂载点
......
tmpfs          tmpfs  8.8G  1.2G  7.6G   14% /work/sambashare/rk3566/orangepi-build/.tmp/rootfs-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1
/dev/loop18p2  ext4   2.8G  2.0G  776M   73% /work/sambashare/rk3566/orangepi-build/.tmp/mount-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1
/dev/loop18p1  vfat  1022M   67M  956M    7% /work/sambashare/rk3566/orangepi-build/.tmp/mount-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/boot

相关脚本如下:

display_alert "Starting rootfs and image building process for" "${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED:-null} ${DESKTOP_ENVIRONMENT:-null} ${BUILD_MINIMAL}" "info"

[[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming $BOARD $BRANCH kernel supports $ROOTFS_TYPE" "" "wrn"

# trap to unmount stuff in case of error/manual interruption,设置信号处理器,当收到信号INT/TERM/EXIT取消挂载
trap unmount_on_exit INT TERM EXIT

# stage: clean and create directories
rm -rf $SDCARD $MOUNT
mkdir -p $SDCARD $MOUNT $DEST/images $EXTER/cache/rootfs

# stage: verify tmpfs configuration and mount
# CLI needs ~1.5GiB, desktop - ~3.5GiB
# calculate and set tmpfs mount to use 9/10 of available RAM+SWAP
local phymem=$(( (($(awk '/MemTotal/ {print $2}' /proc/meminfo) + $(awk '/SwapTotal/ {print $2}' /proc/meminfo))) / 1024 * 9 / 10 )) # MiB

# 无桌面环境,因此设置tmpfs_max_size=1500
if [[ $BUILD_DESKTOP == yes ]]; then local tmpfs_max_size=3500; else local tmpfs_max_size=1500; fi # MiB

# 判断是否使用tmpfs文件系统
if [[ $FORCE_USE_RAMDISK == no ]]; then local use_tmpfs=no
elif [[ $FORCE_USE_RAMDISK == yes || $phymem -gt $tmpfs_max_size ]]; then
local use_tmpfs=yes
fi
[[ -n $FORCE_TMPFS_SIZE ]] && phymem=$FORCE_TMPFS_SIZE

# 将一个大小为phymem的tmpfs文件系统挂载到$SDCARD目录
[[ $use_tmpfs == yes ]] && mount -t tmpfs -o size=${phymem}M tmpfs $SDCARD

unmount_on_exit定义在scripts/image-helpers.sh

umount_chroot()
{

        local target=$1
        display_alert "Unmounting" "$target" "info"
        # 检查并卸载chroot环境中的${target}/dev、${target}/proc和${target}/sys目录下的挂载点
        while grep -Eq "${target}.*(dev|proc|sys)" /proc/mounts
        do
                umount -l --recursive "${target}"/dev >/dev/null 2>&1
                umount -l "${target}"/proc >/dev/null 2>&1
                umount -l "${target}"/sys >/dev/null 2>&1
                sleep 5
        done
}

unmount_on_exit()
{
        trap - INT TERM EXIT
        local stacktrace="$(get_extension_hook_stracktrace "${BASH_SOURCE[*]}" "${BASH_LINENO[*]}")"
        display_alert "unmount_on_exit() called!" "$stacktrace" "err"
        if [[ "${ERROR_DEBUG_SHELL}" == "yes" ]]; then
                ERROR_DEBUG_SHELL=no # dont do it twice
                display_alert "MOUNT" "${MOUNT}" "err"
                display_alert "SDCARD" "${SDCARD}" "err"
                display_alert "ERROR_DEBUG_SHELL=yes, starting a shell." "ERROR_DEBUG_SHELL" "err"
                bash < /dev/tty || true
        fi

		# 卸载chroot环境中的挂载点
        umount_chroot "${SDCARD}/"
        umount -l "${SDCARD}"/tmp >/dev/null 2>&1
        umount -l "${SDCARD}" >/dev/null 2>&1
        umount -l "${MOUNT}"/boot >/dev/null 2>&1
        umount -l "${MOUNT}" >/dev/null 2>&1
        [[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose "${ROOT_MAPPER}"
        losetup -d "${LOOP}" >/dev/null 2>&1
        rm -rf --one-file-system "${SDCARD}"
        exit_with_error "debootstrap-ng was interrupted" || true # don't trigger again
}
6.1.2 create_rootfs_cache

接着调用create_rootfs_cache准备基本的根文件系统:解压缓存或从头开始创建。

6.1.3 install_common

install_common定义在scripts/distributions.sh

点击查看代码
install_common()
{
        display_alert "Applying common tweaks" "" "info"

        # install rootfs encryption related packages separate to not break packages cache
        if [[ $CRYPTROOT_ENABLE == yes ]]; then
                display_alert "Installing rootfs encryption related packages" "cryptsetup" "info"
                chroot "${SDCARD}" /bin/bash -c "apt-get -y -qq --no-install-recommends install cryptsetup" \
                >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                if [[ $CRYPTROOT_SSH_UNLOCK == yes ]]; then
                        display_alert "Installing rootfs encryption related packages" "dropbear-initramfs" "info"
                        chroot "${SDCARD}" /bin/bash -c "apt-get -y -qq --no-install-recommends install dropbear-initramfs cryptsetup-initramfs" \
                        >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                fi

        fi

        # add dummy fstab entry to make mkinitramfs happy
        echo "/dev/mmcblk0p1 / $ROOTFS_TYPE defaults 0 1" >> "${SDCARD}"/etc/fstab
        # required for initramfs-tools-core on Stretch since it ignores the / fstab entry
        echo "/dev/mmcblk0p2 /usr $ROOTFS_TYPE defaults 0 2" >> "${SDCARD}"/etc/fstab

        # adjust initramfs dropbear configuration
        # needs to be done before kernel installation, else it won't be in the initrd image
        if [[ $CRYPTROOT_ENABLE == yes && $CRYPTROOT_SSH_UNLOCK == yes ]]; then
                # Set the port of the dropbear ssh daemon in the initramfs to a different one if configured
                # this avoids the typical 'host key changed warning' - `WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!`
                [[ -f "${SDCARD}"/etc/dropbear-initramfs/config ]] && \
                sed -i 's/^#DROPBEAR_OPTIONS=/DROPBEAR_OPTIONS="-p '"${CRYPTROOT_SSH_UNLOCK_PORT}"'"/' \
                "${SDCARD}"/etc/dropbear-initramfs/config

                # setup dropbear authorized_keys, either provided by userpatches or generated
                if [[ -f $USERPATCHES_PATH/dropbear_authorized_keys ]]; then
                        cp "$USERPATCHES_PATH"/dropbear_authorized_keys "${SDCARD}"/etc/dropbear-initramfs/authorized_keys
                else
                        # generate a default ssh key for login on dropbear in initramfs
                        # this key should be changed by the user on first login
                        display_alert "Generating a new SSH key pair for dropbear (initramfs)" "" ""
                        ssh-keygen -t ecdsa -f "${SDCARD}"/etc/dropbear-initramfs/id_ecdsa \
                        -N '' -O force-command=cryptroot-unlock -C 'AUTOGENERATED_BY_ARMBIAN_BUILD'  >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

                        # /usr/share/initramfs-tools/hooks/dropbear will automatically add 'id_ecdsa.pub' to authorized_keys file
                        # during mkinitramfs of update-initramfs
                        #cat "${SDCARD}"/etc/dropbear-initramfs/id_ecdsa.pub > "${SDCARD}"/etc/dropbear-initramfs/authorized_keys
                        # The version of the Linux kernel
                        CRYPTROOT_SSH_UNLOCK_KEY_NAME="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${SELECTED_CONFIGURATION}_linux"$(grab_version "$LINUXSOURCEDIR")"".key
                        # copy dropbear ssh key to image output dir for convenience
                        cp "${SDCARD}"/etc/dropbear-initramfs/id_ecdsa "${DEST}/images/${CRYPTROOT_SSH_UNLOCK_KEY_NAME}"
                        display_alert "SSH private key for dropbear (initramfs) has been copied to:" \
                        "$DEST/images/$CRYPTROOT_SSH_UNLOCK_KEY_NAME" "info"
                fi
        fi

        # create modules file
        local modules=MODULES_${BRANCH^^}
        if [[ -n "${!modules}" ]]; then
                tr ' ' '\n' <<< "${!modules}" > "${SDCARD}"/etc/modules
        elif [[ -n "${MODULES}" ]]; then
                tr ' ' '\n' <<< "${MODULES}" > "${SDCARD}"/etc/modules
        fi

        # create blacklist files
        local blacklist=MODULES_BLACKLIST_${BRANCH^^}
        if [[ -n "${!blacklist}" ]]; then
                tr ' ' '\n' <<< "${!blacklist}" | sed -e 's/^/blacklist /' > "${SDCARD}/etc/modprobe.d/blacklist-${BOARD}.conf"
        elif [[ -n "${MODULES_BLACKLIST}" ]]; then
                tr ' ' '\n' <<< "${MODULES_BLACKLIST}" | sed -e 's/^/blacklist /' > "${SDCARD}/etc/modprobe.d/blacklist-${BOARD}.conf"
        fi

        # configure MIN / MAX speed for cpufrequtils
        cat <<-EOF > "${SDCARD}"/etc/default/cpufrequtils
        ENABLE=true
        MIN_SPEED=$CPUMIN
        MAX_SPEED=$CPUMAX
        GOVERNOR=$GOVERNOR
        EOF

        # remove default interfaces file if present
        # before installing board support package
        rm -f "${SDCARD}"/etc/network/interfaces

        # disable selinux by default
        mkdir -p "${SDCARD}"/selinux
        [[ -f "${SDCARD}"/etc/selinux/config ]] && sed "s/^SELINUX=.*/SELINUX=disabled/" -i "${SDCARD}"/etc/selinux/config

        # remove Ubuntu's legal text
        [[ -f "${SDCARD}"/etc/legal ]] && rm "${SDCARD}"/etc/legal

        # Prevent loading paralel printer port drivers which we don't need here.
        # Suppress boot error if kernel modules are absent
        if [[ -f "${SDCARD}"/etc/modules-load.d/cups-filters.conf ]]; then
                sed "s/^lp/#lp/" -i "${SDCARD}"/etc/modules-load.d/cups-filters.conf
                sed "s/^ppdev/#ppdev/" -i "${SDCARD}"/etc/modules-load.d/cups-filters.conf
                sed "s/^parport_pc/#parport_pc/" -i "${SDCARD}"/etc/modules-load.d/cups-filters.conf
        fi

        # console fix due to Debian bug
        sed -e 's/CHARMAP=".*"/CHARMAP="'$CONSOLE_CHAR'"/g' -i "${SDCARD}"/etc/default/console-setup

        # add the /dev/urandom path to the rng config file
        echo "HRNGDEVICE=/dev/urandom" >> "${SDCARD}"/etc/default/rng-tools

        # ping needs privileged action to be able to create raw network socket
        # this is working properly but not with (at least) Debian Buster
        chroot "${SDCARD}" /bin/bash -c "chmod u+s /bin/ping"

        # change time zone data
        echo "${TZDATA}" > "${SDCARD}"/etc/timezone
        chroot "${SDCARD}" /bin/bash -c "dpkg-reconfigure -f noninteractive tzdata >/dev/null 2>&1"

        # set root password
        chroot "${SDCARD}" /bin/bash -c "(echo $ROOTPWD;echo $ROOTPWD;) | passwd root >/dev/null 2>&1"

        # enable automated login to console(s)
        #mkdir -p "${SDCARD}"/etc/systemd/system/getty@.service.d/
        #mkdir -p "${SDCARD}"/etc/systemd/system/serial-getty@.service.d/
        #cat <<-EOF > "${SDCARD}"/etc/systemd/system/serial-getty@.service.d/override.conf
        #[Service]
        #ExecStartPre=/bin/sh -c 'exec /bin/sleep 10'
        #ExecStart=
        #ExecStart=-/sbin/agetty --noissue --autologin root %I \$TERM
        #Type=idle
        #EOF
        #cp "${SDCARD}"/etc/systemd/system/serial-getty@.service.d/override.conf "${SDCARD}"/etc/systemd/system/getty@.service.d/override.conf

        # force change root password at first login
        #chroot "${SDCARD}" /bin/bash -c "chage -d 0 root"

        # change console welcome text
        echo -e "${VENDOR} ${REVISION} ${RELEASE^} \\l \n" > "${SDCARD}"/etc/issue
        echo "${VENDOR} ${REVISION} ${RELEASE^}" > "${SDCARD}"/etc/issue.net
        sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${VENDOR} $REVISION "${RELEASE^}"\"/" "${SDCARD}"/etc/os-release

        # enable few bash aliases enabled in Ubuntu by default to make it even
        sed "s/#alias ll='ls -l'/alias ll='ls -l'/" -i "${SDCARD}"/etc/skel/.bashrc
        sed "s/#alias la='ls -A'/alias la='ls -A'/" -i "${SDCARD}"/etc/skel/.bashrc
        sed "s/#alias l='ls -CF'/alias l='ls -CF'/" -i "${SDCARD}"/etc/skel/.bashrc
        # root user is already there. Copy bashrc there as well
        cp "${SDCARD}"/etc/skel/.bashrc "${SDCARD}"/root

        # display welcome message at first root login
        touch "${SDCARD}"/root/.not_logged_in_yet

        if [[ ${DESKTOP_AUTOLOGIN} != no ]]; then
                # set desktop autologin
                touch "${SDCARD}"/root/.desktop_autologin
        fi

        # NOTE: this needs to be executed before family_tweaks
        local bootscript_src=${BOOTSCRIPT%%:*}
        local bootscript_dst=${BOOTSCRIPT##*:}

        # create extlinux config file
        if [[ $SRC_EXTLINUX == yes ]]; then
                mkdir -p $SDCARD/boot/extlinux
                cat <<-EOF > "$SDCARD/boot/extlinux/extlinux.conf"
                label ${VENDOR}
                  kernel /boot/$NAME_KERNEL
                  initrd /boot/$NAME_INITRD
        EOF
                if [[ -n $BOOT_FDT_FILE ]]; then
                        if [[ $BOOT_FDT_FILE != "none" ]]; then
                                echo "  fdt /boot/dtb/$BOOT_FDT_FILE" >> "$SDCARD/boot/extlinux/extlinux.conf"
                        fi
                else
                        echo "  fdtdir /boot/dtb/" >> "$SDCARD/boot/extlinux/extlinux.conf"
                fi
        else

                if [[ "${BOOTCONFIG}" != "none" ]]; then
                        if [ -f "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" ]; then
                                cp "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" "${SDCARD}/boot/${bootscript_dst}"
                        else
                                cp "${EXTER}/config/bootscripts/${bootscript_src}" "${SDCARD}/boot/${bootscript_dst}"
                        fi
                fi

                if [[ -n $BOOTENV_FILE ]]; then
                        if [[ -f $USERPATCHES_PATH/bootenv/$BOOTENV_FILE ]]; then
                                cp "$USERPATCHES_PATH/bootenv/${BOOTENV_FILE}" "${SDCARD}"/boot/orangepiEnv.txt
                        elif [[ -f $EXTER/config/bootenv/$BOOTENV_FILE ]]; then
                                cp "${EXTER}/config/bootenv/${BOOTENV_FILE}" "${SDCARD}"/boot/orangepiEnv.txt
                        fi
                fi

                # TODO: modify $bootscript_dst or orangepiEnv.txt to make NFS boot universal
                # instead of copying sunxi-specific template
                if [[ $ROOTFS_TYPE == nfs ]]; then
                        display_alert "Copying NFS boot script template"
                        if [[ -f $USERPATCHES_PATH/nfs-boot.cmd ]]; then
                                cp "$USERPATCHES_PATH"/nfs-boot.cmd "${SDCARD}"/boot/boot.cmd
                        else
                                cp "${EXTER}"/config/templates/nfs-boot.cmd.template "${SDCARD}"/boot/boot.cmd
                        fi
                fi

                [[ -n $OVERLAY_PREFIX && -f "${SDCARD}"/boot/orangepiEnv.txt && ($BRANCH =~ current|next || $BOARDFAMILY =~ "rockchip-rk3588"|"rockchip-rk356x") ]] && \
                        echo "overlay_prefix=$OVERLAY_PREFIX" >> "${SDCARD}"/boot/orangepiEnv.txt

                [[ -n $DEFAULT_OVERLAYS && -f "${SDCARD}"/boot/orangepiEnv.txt && ($BRANCH =~ current|next || $BOARDFAMILY =~ "rockchip-rk3588"|"rockchip-rk356x") ]] && \
                        echo "overlays=${DEFAULT_OVERLAYS//,/ }" >> "${SDCARD}"/boot/orangepiEnv.txt

                [[ -n $BOOT_FDT_FILE && -f "${SDCARD}"/boot/orangepiEnv.txt ]] && \
                        echo "fdtfile=${BOOT_FDT_FILE}" >> "${SDCARD}/boot/orangepiEnv.txt"

        fi

        # initial date for fake-hwclock
        date -u '+%Y-%m-%d %H:%M:%S' > "${SDCARD}"/etc/fake-hwclock.data

        echo "${HOST}" > "${SDCARD}"/etc/hostname

        # set hostname in hosts file
        cat <<-EOF > "${SDCARD}"/etc/hosts
        127.0.0.1   localhost
        127.0.1.1   $HOST
        ::1         localhost $HOST ip6-localhost ip6-loopback
        fe00::0     ip6-localnet
        ff00::0     ip6-mcastprefix
        ff02::1     ip6-allnodes
        ff02::2     ip6-allrouters
        EOF

        cd $SRC

        # Prepare and export caching-related params common to all apt calls below, to maximize apt-cacher-ng usage
        export APT_EXTRA_DIST_PARAMS=""
        [[ $NO_APT_CACHER != yes ]] && APT_EXTRA_DIST_PARAMS="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\" -o Acquire::http::Proxy::localhost=\"DIRECT\""

        display_alert "Cleaning" "package lists"
        chroot "${SDCARD}" /bin/bash -c "apt-get clean"

        display_alert "Updating" "package lists"
        chroot "${SDCARD}" /bin/bash -c "apt-get ${APT_EXTRA_DIST_PARAMS} update" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

        display_alert "Temporarily disabling" "initramfs-tools hook for kernel"
        chroot "${SDCARD}" /bin/bash -c "chmod -v -x /etc/kernel/postinst.d/initramfs-tools" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

        # install family packages
        if [[ -n ${PACKAGE_LIST_FAMILY} ]]; then
                display_alert "Installing PACKAGE_LIST_FAMILY packages" "${PACKAGE_LIST_FAMILY}"
                chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive  apt-get ${APT_EXTRA_DIST_PARAMS} -yqq --no-install-recommends install $PACKAGE_LIST_FAMILY" >> "${DEST}"/${LOG_SUBPATH}/install.log
        fi

        # install board packages
        if [[ -n ${PACKAGE_LIST_BOARD} ]]; then
                display_alert "Installing PACKAGE_LIST_BOARD packages" "${PACKAGE_LIST_BOARD}"
                chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive  apt-get ${APT_EXTRA_DIST_PARAMS} -yqq --no-install-recommends install $PACKAGE_LIST_BOARD" >> "${DEST}"/${LOG_SUBPATH}/install.log || { display_alert "Failed to install PACKAGE_LIST_BOARD" "${PACKAGE_LIST_BOARD}" "err"; exit 2; }
        fi

        # remove family packages
        if [[ -n ${PACKAGE_LIST_FAMILY_REMOVE} ]]; then
                display_alert "Removing PACKAGE_LIST_FAMILY_REMOVE packages" "${PACKAGE_LIST_FAMILY_REMOVE}"
                chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive  apt-get ${APT_EXTRA_DIST_PARAMS} -yqq remove --auto-remove $PACKAGE_LIST_FAMILY_REMOVE" >> "${DEST}"/${LOG_SUBPATH}/install.log
        fi

        # remove board packages
        if [[ -n ${PACKAGE_LIST_BOARD_REMOVE} ]]; then
                display_alert "Removing PACKAGE_LIST_BOARD_REMOVE packages" "${PACKAGE_LIST_BOARD_REMOVE}"
                for PKG_REMOVE in ${PACKAGE_LIST_BOARD_REMOVE}; do
                        chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt-get ${APT_EXTRA_DIST_PARAMS} -yqq remove --auto-remove ${PKG_REMOVE}" >> "${DEST}"/${LOG_SUBPATH}/install.log
                done
        fi

        # install u-boot
        # @TODO: add install_bootloader() extension method, refactor into u-boot extension
        [[ "${BOOTCONFIG}" != "none" ]] && {
                if [[ "${REPOSITORY_INSTALL}" != *u-boot* ]]; then
                        UBOOT_VER=$(dpkg --info "${DEB_STORAGE}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" | grep Descr | awk '{print $(NF)}')
                        install_deb_chroot "${DEB_STORAGE}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb"
                else
                        UBOOT_VER=$(dpkg --info "${DEB_ORANGEPI}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" | grep Descr | awk '{print $(NF)}')
                        install_deb_chroot "${DEB_ORANGEPI}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" "orangepi"
                fi
        }

        call_extension_method "pre_install_kernel_debs"  << 'PRE_INSTALL_KERNEL_DEBS'
*called before installing the Armbian-built kernel deb packages*
It is not too late to `unset KERNELSOURCE` here and avoid kernel install.
PRE_INSTALL_KERNEL_DEBS

        # install kernel
        [[ -n $KERNELSOURCE ]] && {
                if [[ "${REPOSITORY_INSTALL}" != *kernel* ]]; then
                        VER=$(dpkg --info "${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" | awk -F"-" '/Source:/{print $2}')

                        install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb"
                        if [[ -f ${DEB_STORAGE}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb ]]; then
                                install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb"
                        fi
                        if [[ $INSTALL_HEADERS == yes ]]; then
                                install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KERNEL/image/headers}_${REVISION}_${ARCH}.deb"
                        else
                                cp "${DEB_STORAGE}/${CHOSEN_KERNEL/image/headers}_${REVISION}_${ARCH}.deb" "${SDCARD}"/opt/
                        fi
                else
                        VER=$(dpkg --info "${DEB_ORANGEPI}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" | grep Descr | awk '{print $(NF)}')
                        VER="${VER/-$LINUXFAMILY/}"

                        install_deb_chroot "${DEB_ORANGEPI}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" "orangepi"

                        if [[ -f ${DEB_ORANGEPI}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb ]]; then
                                install_deb_chroot "${DEB_ORANGEPI}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb" "orangepi"
                        fi

                        if [[ $INSTALL_HEADERS == yes ]]; then
                                install_deb_chroot "${DEB_ORANGEPI}/${CHOSEN_KERNEL/image/headers}_${REVISION}_${ARCH}.deb" "orangepi"
                        fi
                fi
        }

        call_extension_method "post_install_kernel_debs" << 'POST_INSTALL_KERNEL_DEBS'
*allow config to do more with the installed kernel/headers*
Called after packages, u-boot, kernel and headers installed in the chroot, but before the BSP is installed.
If `KERNELSOURCE` is (still?) unset after this, Armbian-built firmware will not be installed.
POST_INSTALL_KERNEL_DEBS

        # install board support packages
        if [[ "${REPOSITORY_INSTALL}" != *bsp* ]]; then
                install_deb_chroot "${DEB_STORAGE}/$RELEASE/${BSP_CLI_PACKAGE_FULLNAME}.deb"
        else
                install_deb_chroot "${DEB_ORANGEPI}/$RELEASE/${CHOSEN_ROOTFS}_${BSP_CLI_PACKAGE_FULLNAME}.deb" "orangepi"
        fi

        # install orangepi-desktop
        if [[ "${REPOSITORY_INSTALL}" != *orangepi-desktop* ]]; then
                if [[ $BUILD_DESKTOP == yes ]]; then
                        install_deb_chroot "${DEB_STORAGE}/${RELEASE}/${CHOSEN_DESKTOP}_${REVISION}_all.deb"
                        install_deb_chroot "${DEB_STORAGE}/${RELEASE}/${BSP_DESKTOP_PACKAGE_FULLNAME}.deb"
                        # install display manager and PACKAGE_LIST_DESKTOP_FULL packages if enabled per board
                        desktop_postinstall
                fi
        else
                if [[ $BUILD_DESKTOP == yes ]]; then
                        install_deb_chroot "${CHOSEN_DESKTOP}" "orangepi"
                        # install display manager and PACKAGE_LIST_DESKTOP_FULL packages if enabled per board
                        desktop_postinstall
                fi
        fi

        # install orangepi-firmware
        if [[ "${REPOSITORY_INSTALL}" != *orangepi-firmware* ]]; then
                if [[ -f ${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb ]]; then
                        install_deb_chroot "${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb"
                fi
        else
                if [[ -f ${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb ]]; then
                        install_deb_chroot "${DEB_ORANGEPI}/orangepi-firmware_${REVISION}_all.deb" "orangepi"
                fi
        fi

        # install orangepi-config
        if [[ "${PACKAGE_LIST_RM}" != *orangepi-config* ]]; then
                if [[ "${REPOSITORY_INSTALL}" != *orangepi-config* ]]; then
                        if [[ $BUILD_MINIMAL != yes ]]; then
                                install_deb_chroot "${DEB_STORAGE}/orangepi-config_${REVISION}_all.deb"
                        fi
                else
                        if [[ $BUILD_MINIMAL != yes ]]; then
                                install_deb_chroot "${DEB_ORANGEPI}/orangepi-config_${REVISION}_all.deb" "orangepi"
                        fi
                fi
        fi

        # install orangepi-zsh
        if [[ "${PACKAGE_LIST_RM}" != *orangepi-zsh* ]]; then
                if [[ "${REPOSITORY_INSTALL}" != *orangepi-zsh* ]]; then
                        if [[ $BUILD_MINIMAL != yes ]]; then
                                install_deb_chroot "${DEB_STORAGE}/orangepi-zsh_${REVISION}_all.deb"
                        fi
                else
                        if [[ $BUILD_MINIMAL != yes ]]; then
                                install_deb_chroot "orangepi-zsh" "remote"
                        fi
                fi
        fi

        # install plymouth-theme-orangepi
        if [[ $PLYMOUTH == yes && $BUILD_DESKTOP == yes && $RELEASE != buster ]]; then
                if [[ "${REPOSITORY_INSTALL}" != *plymouth-theme-orangepi* ]]; then
                        install_deb_chroot "${DEB_STORAGE}/orangepi-plymouth-theme_${REVISION}_all.deb"
                else
                        install_deb_chroot "orangepi-plymouth-theme" "remote"
                fi
        fi

        # install kernel sources
        if [[ -f ${DEB_STORAGE}/${CHOSEN_KSRC}_${REVISION}_all.deb && $INSTALL_KSRC == yes ]]; then
                install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KSRC}_${REVISION}_all.deb"
        elif [[ $INSTALL_KSRC == yes ]]; then
                display_alert "Please set BUILD_KSRC=yes to generate the kernel source package" "" "wrn"
        fi

        # install wireguard tools
        if [[ $WIREGUARD == yes ]]; then
                chroot "${SDCARD}" /bin/bash -c "apt-get -y -qq install wireguard-tools --no-install-recommends" >> "${DEST}"/debug/install.log 2>&1
        fi

        # freeze orangepi packages
        if [[ $BSPFREEZE == yes ]]; then
                display_alert "Freezing Orange Pi packages" "$BOARD" "info"
                chroot "${SDCARD}" /bin/bash -c "apt-mark hold ${CHOSEN_KERNEL} ${CHOSEN_KERNEL/image/headers} \
                linux-u-boot-${BOARD}-${BRANCH} ${CHOSEN_KERNEL/image/dtb}" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        fi

        # add orangepi user
        chroot "${SDCARD}" /bin/bash -c "adduser --quiet --disabled-password --shell /bin/bash --home /home/${OPI_USERNAME} --gecos ${OPI_USERNAME} ${OPI_USERNAME}"
        chroot "${SDCARD}" /bin/bash -c "(echo ${OPI_PWD};echo ${OPI_PWD};) | passwd "${OPI_USERNAME}" >/dev/null 2>&1"
        for additionalgroup in sudo netdev audio video disk tty users games dialout plugdev input bluetooth systemd-journal ssh; do
                chroot "${SDCARD}" /bin/bash -c "usermod -aG ${additionalgroup} ${OPI_USERNAME} 2>/dev/null"
        done

        # fix for gksu in Xenial
        touch ${SDCARD}/home/${OPI_USERNAME}/.Xauthority
        chroot "${SDCARD}" /bin/bash -c "chown ${OPI_USERNAME}:${OPI_USERNAME} /home/${OPI_USERNAME}/.Xauthority"
        # set up profile sync daemon on desktop systems
        chroot "${SDCARD}" /bin/bash -c "which psd >/dev/null 2>&1"
        if [ $? -eq 0 ]; then
                echo -e "${OPI_USERNAME} ALL=(ALL) NOPASSWD: /usr/bin/psd-overlay-helper" >> ${SDCARD}/etc/sudoers
                touch ${SDCARD}/home/${OPI_USERNAME}/.activate_psd
                chroot "${SDCARD}" /bin/bash -c "chown $OPI_USERNAME:$OPI_USERNAME /home/${OPI_USERNAME}/.activate_psd"
        fi

        # remove deb files
        rm -f "${SDCARD}"/root/*.deb

        # copy boot splash images
        cp "${EXTER}"/packages/blobs/splash/orangepi-u-boot.bmp "${SDCARD}"/boot/boot.bmp
        cp "${EXTER}"/packages/blobs/splash/logo.bmp "${SDCARD}"/boot/logo.bmp

        # copy audio.wav and mute.wav
        cp "${EXTER}"/packages/blobs/audio_wav/audio.wav "${SDCARD}"/usr/share/sounds/alsa/
        cp "${EXTER}"/packages/blobs/audio_wav/mute.wav "${SDCARD}"/usr/share/sounds/alsa/

        cp "${EXTER}"/packages/blobs/test.mp4 "${SDCARD}"/usr/local/

        # copy watchdog test programm
        cp "${EXTER}"/packages/blobs/watchdog/watchdog_test_${ARCH} "${SDCARD}"/usr/local/bin/watchdog_test

        [[ -f "${SDCARD}"/usr/bin/gnome-session ]] && sed -i "s/user-session.*/user-session=ubuntu-wayland/" ${SDCARD}/etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf
        [[ -f "${SDCARD}"/usr/bin/startplasma-x11 ]] && sed -i "s/user-session.*/user-session=plasma-x11/" ${SDCARD}/etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf

        # execute $LINUXFAMILY-specific tweaks
        [[ $(type -t family_tweaks) == function ]] && family_tweaks

        call_extension_method "post_family_tweaks" << 'FAMILY_TWEAKS'
*customize the tweaks made by $LINUXFAMILY-specific family_tweaks*
It is run after packages are installed in the rootfs, but before enabling additional services.
It allows implementors access to the rootfs (`${SDCARD}`) in its pristine state after packages are installed.
FAMILY_TWEAKS

        # enable additional services
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-firstrun.service >/dev/null 2>&1"
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-firstrun-config.service >/dev/null 2>&1"
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-zram-config.service >/dev/null 2>&1"
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-hardware-optimize.service >/dev/null 2>&1"
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-ramlog.service >/dev/null 2>&1"
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-resize-filesystem.service >/dev/null 2>&1"
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-hardware-monitor.service >/dev/null 2>&1"

        # copy "first run automated config, optional user configured"
        cp ${EXTER}/packages/bsp/orangepi_first_run.txt.template "${SDCARD}"/boot/orangepi_first_run.txt.template

        ## switch to beta repository at this stage if building nightly images
        #[[ $IMAGE_TYPE == nightly ]] \
        #&& echo "deb http://beta.orangepi.com $RELEASE main ${RELEASE}-utils ${RELEASE}-desktop" \
        #> "${SDCARD}"/etc/apt/sources.list.d/orangepi.list

        # Cosmetic fix [FAILED] Failed to start Set console font and keymap at first boot
        [[ -f "${SDCARD}"/etc/console-setup/cached_setup_font.sh ]] \
        && sed -i "s/^printf '.*/printf '\\\033\%\%G'/g" "${SDCARD}"/etc/console-setup/cached_setup_font.sh
        [[ -f "${SDCARD}"/etc/console-setup/cached_setup_terminal.sh ]] \
        && sed -i "s/^printf '.*/printf '\\\033\%\%G'/g" "${SDCARD}"/etc/console-setup/cached_setup_terminal.sh
        [[ -f "${SDCARD}"/etc/console-setup/cached_setup_keyboard.sh ]] \
        && sed -i "s/-u/-x'/g" "${SDCARD}"/etc/console-setup/cached_setup_keyboard.sh

        # fix for https://bugs.launchpad.net/ubuntu/+source/blueman/+bug/1542723
        chroot "${SDCARD}" /bin/bash -c "chown root:messagebus /usr/lib/dbus-1.0/dbus-daemon-launch-helper"
        chroot "${SDCARD}" /bin/bash -c "chmod u+s /usr/lib/dbus-1.0/dbus-daemon-launch-helper"

        # disable samba NetBIOS over IP name service requests since it hangs when no network is present at boot
        chroot "${SDCARD}" /bin/bash -c "systemctl --quiet disable nmbd 2> /dev/null"

        # disable low-level kernel messages for non betas
        if [[ -z $BETA ]]; then
                sed -i "s/^#kernel.printk*/kernel.printk/" "${SDCARD}"/etc/sysctl.conf
        fi

        # disable repeated messages due to xconsole not being installed.
        [[ -f "${SDCARD}"/etc/rsyslog.d/50-default.conf ]] && \
        sed '/daemon\.\*\;mail.*/,/xconsole/ s/.*/#&/' -i "${SDCARD}"/etc/rsyslog.d/50-default.conf

        # disable deprecated parameter
        sed '/.*$KLogPermitNonKernelFacility.*/,// s/.*/#&/' -i "${SDCARD}"/etc/rsyslog.conf

        # enable getty on multiple serial consoles
        # and adjust the speed if it is defined and different than 115200
        #
        # example: SERIALCON="ttyS0:15000000,ttyGS1"
        #
        ifs=$IFS
        for i in $(echo "${SERIALCON:-'ttyS0'}" | sed "s/,/ /g")
        do
                IFS=':' read -r -a array <<< "$i"
                [[ "${array[0]}" == "tty1" ]] && continue # Don't enable tty1 as serial console.
                display_alert "Enabling serial console" "${array[0]}" "info"
                # add serial console to secure tty list
                [ -z "$(grep -w '^${array[0]}' "${SDCARD}"/etc/securetty 2> /dev/null)" ] && \
                echo "${array[0]}" >>  "${SDCARD}"/etc/securetty
                if [[ ${array[1]} != "115200" && -n ${array[1]} ]]; then
                        # make a copy, fix speed and enable
                        cp "${SDCARD}"/lib/systemd/system/serial-getty@.service \
                        "${SDCARD}/lib/systemd/system/serial-getty@${array[0]}.service"
                        sed -i "s/--keep-baud 115200/--keep-baud ${array[1]},115200/" \
                        "${SDCARD}/lib/systemd/system/serial-getty@${array[0]}.service"
                fi
                chroot "${SDCARD}" /bin/bash -c "systemctl daemon-reload" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable serial-getty@${array[0]}.service" \
                >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                if [[ "${array[0]}" == "ttyGS0" && $LINUXFAMILY == sun8i && $BRANCH == legacy ]]; then
                        mkdir -p "${SDCARD}"/etc/systemd/system/serial-getty@ttyGS0.service.d
                        cat <<-EOF > "${SDCARD}"/etc/systemd/system/serial-getty@ttyGS0.service.d/10-switch-role.conf
                        [Service]
                        ExecStartPre=-/bin/sh -c "echo 2 > /sys/bus/platform/devices/sunxi_usb_udc/otg_role"
                        EOF
                fi
        done
        IFS=$ifs

        [[ $LINUXFAMILY == sun*i ]] && mkdir -p "${SDCARD}"/boot/overlay-user

        # to prevent creating swap file on NFS (needs specific kernel options)
        # and f2fs/btrfs (not recommended or needs specific kernel options)
        [[ $ROOTFS_TYPE != ext4 ]] && touch "${SDCARD}"/var/swap

        # install initial asound.state if defined
        mkdir -p "${SDCARD}"/var/lib/alsa/
        [[ -n $ASOUND_STATE ]] && cp "${EXTER}/packages/blobs/asound.state/${ASOUND_STATE}" "${SDCARD}"/var/lib/alsa/asound.state

        # save initial orangepi-release state
        cp "${SDCARD}"/etc/orangepi-release "${SDCARD}"/etc/orangepi-image-release

        # DNS fix. package resolvconf is not available everywhere
        if [ -d /etc/resolvconf/resolv.conf.d ] && [ -n "$NAMESERVER" ]; then
                echo "nameserver $NAMESERVER" > "${SDCARD}"/etc/resolvconf/resolv.conf.d/head
        fi

        # permit root login via SSH for the first boot
        sed -i 's/#\?PermitRootLogin .*/PermitRootLogin yes/' "${SDCARD}"/etc/ssh/sshd_config

        # enable PubkeyAuthentication
        sed -i 's/#\?PubkeyAuthentication .*/PubkeyAuthentication yes/' "${SDCARD}"/etc/ssh/sshd_config

        if [ -f "${SDCARD}"/etc/NetworkManager/NetworkManager.conf ]; then
                # configure network manager
                sed "s/managed=\(.*\)/managed=true/g" -i "${SDCARD}"/etc/NetworkManager/NetworkManager.conf

                # remove network manager defaults to handle eth by default
                rm -f "${SDCARD}"/usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf

                # most likely we don't need to wait for nm to get online
                chroot "${SDCARD}" /bin/bash -c "systemctl disable NetworkManager-wait-online.service" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

                # Just regular DNS and maintain /etc/resolv.conf as a file
                sed "/dns/d" -i "${SDCARD}"/etc/NetworkManager/NetworkManager.conf
                sed "s/\[main\]/\[main\]\ndns=default\nrc-manager=file/g" -i "${SDCARD}"/etc/NetworkManager/NetworkManager.conf
                if [[ -n $NM_IGNORE_DEVICES ]]; then
                        mkdir -p "${SDCARD}"/etc/NetworkManager/conf.d/
                        cat <<-EOF > "${SDCARD}"/etc/NetworkManager/conf.d/10-ignore-interfaces.conf
                        [keyfile]
                        unmanaged-devices=$NM_IGNORE_DEVICES
                        EOF
                fi

        elif [ -d "${SDCARD}"/etc/systemd/network ]; then
                # configure networkd
                rm "${SDCARD}"/etc/resolv.conf
                ln -s /run/systemd/resolve/resolv.conf "${SDCARD}"/etc/resolv.conf

                # enable services
                chroot "${SDCARD}" /bin/bash -c "systemctl enable systemd-networkd.service systemd-resolved.service" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

                if  [ -e /etc/systemd/timesyncd.conf ]; then
                        chroot "${SDCARD}" /bin/bash -c "systemctl enable systemd-timesyncd.service" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                fi
                umask 022
                cat > "${SDCARD}"/etc/systemd/network/eth0.network <<- __EOF__
                [Match]
                Name=eth0

                [Network]
                #MACAddress=
                DHCP=ipv4
                LinkLocalAddressing=ipv4
                #Address=192.168.1.100/24
                #Gateway=192.168.1.1
                #DNS=192.168.1.1
                #Domains=example.com
                NTP=0.pool.ntp.org 1.pool.ntp.org
                __EOF__

        fi

        # avahi daemon defaults if exists
        [[ -f "${SDCARD}"/usr/share/doc/avahi-daemon/examples/sftp-ssh.service ]] && \
        cp "${SDCARD}"/usr/share/doc/avahi-daemon/examples/sftp-ssh.service "${SDCARD}"/etc/avahi/services/
        [[ -f "${SDCARD}"/usr/share/doc/avahi-daemon/examples/ssh.service ]] && \
        cp "${SDCARD}"/usr/share/doc/avahi-daemon/examples/ssh.service "${SDCARD}"/etc/avahi/services/

        # nsswitch settings for sane DNS behavior: remove resolve, assure libnss-myhostname support
        sed "s/hosts\:.*/hosts:          files mymachines dns myhostname/g" -i "${SDCARD}"/etc/nsswitch.conf

        # build logo in any case
        boot_logo

        # disable MOTD for first boot - we want as clean 1st run as possible
        chmod -x "${SDCARD}"/etc/update-motd.d/*

}
6.1.4 chroot_installpackages_local

chroot_installpackages_local定义在scripts/chroot-buildpackages.sh

chroot_installpackages_local()
{
        local conf=$EXTER/config/aptly-temp.conf
        rm -rf /tmp/aptly-temp/
        mkdir -p /tmp/aptly-temp/
        aptly -config="${conf}" repo create temp >> "${DEST}"/${LOG_SUBPATH}/install.log
        # NOTE: this works recursively
        if [[ $EXTERNAL_NEW == prebuilt ]]; then
                aptly -config="${conf}" repo add temp "${DEB_ORANGEPI}/extra/${RELEASE}-desktop/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                aptly -config="${conf}" repo add temp "${DEB_ORANGEPI}/extra/${RELEASE}-utils/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        else
                aptly -config="${conf}" repo add temp "${DEB_STORAGE}/extra/${RELEASE}-desktop/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                aptly -config="${conf}" repo add temp "${DEB_STORAGE}/extra/${RELEASE}-utils/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        fi

        # -gpg-key="925644A6"
        [[ ! -d /root/.gnupg ]] && mkdir -p /root/.gnupg
        aptly -keyring="$EXTER/packages/extras-buildpkgs/buildpkg-public.gpg" -secret-keyring="$EXTER/packages/extras-buildpkgs/buildpkg.gpg" -batch=true -config="${conf}" \
                 -gpg-key="925644A6" -passphrase="testkey1234" -component=temp -distribution="${RELEASE}" publish repo temp >> "${DEST}"/${LOG_SUBPATH}/install.log
        #aptly -config="${conf}" -listen=":8189" serve &
        aptly -config="${conf}" -listen=":8189" serve >> "${DEST}"/debug/install.log 2>&1 &
        local aptly_pid=$!
        cp $EXTER/packages/extras-buildpkgs/buildpkg.key "${SDCARD}"/tmp/buildpkg.key
        cat <<-'EOF' > "${SDCARD}"/etc/apt/preferences.d/90-orangepi-temp.pref
        Package: *
        Pin: origin "localhost"
        Pin-Priority: 550
        EOF
        cat <<-EOF > "${SDCARD}"/etc/apt/sources.list.d/orangepi-temp.list
        deb http://localhost:8189/ $RELEASE temp
        EOF
        chroot_installpackages
        kill "${aptly_pid}" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
} #############################################################################
6.1.5 customize_image

customize_image定义在scripts/image-helpers.sh;这里相当于给用户开放了一个钩子,使得用户可以在<SDK>/userpatches目录下提供若干个hook文件:

  • customize-image-host.sh:在当前环境执行,在该脚本中可以获取到Linux SDK编译过程中的上下文信息(变量);
  • customize-image.sh:在chroot环境(可以理解为运行环境切换到为了根文件系统中)中执行,因此可以在该脚本中配置根文件系统,安装一些软件包等;比如执行cp /tmp/overlay/usr/bin/xx /usr/bin/xx
  • overlay:该目录会挂载到${SDCARD}/tmp/overlay,因此在customize-image.sh脚本执行时可以通过/tmp/overlay路径获取到该目录内的数据;

脚本源码如下:

customize_image()
{

        # for users that need to prepare files at host,在当前环境执行customize-image-host.sh
        [[ -f $USERPATCHES_PATH/customize-image-host.sh ]] && source "$USERPATCHES_PATH"/customize-image-host.sh

        call_extension_method "pre_customize_image" "image_tweaks_pre_customize" << 'PRE_CUSTOMIZE_IMAGE'
*run before customize-image.sh*
This hook is called after `customize-image-host.sh` is called, but before the overlay is mounted.
It thus can be used for the same purposes as `customize-image-host.sh`.
PRE_CUSTOMIZE_IMAGE

        cp "$USERPATCHES_PATH"/customize-image.sh "${SDCARD}"/tmp/customize-image.sh
        chmod +x "${SDCARD}"/tmp/customize-image.sh
        mkdir -p "${SDCARD}"/tmp/overlay
        # util-linux >= 2.27 required,挂载overlay
        mount -o bind,ro "$USERPATCHES_PATH"/overlay "${SDCARD}"/tmp/overlay
        display_alert "Calling image customization script" "customize-image.sh" "info"
        
        # 切换到chroot环境执行customize-image.sh
        chroot "${SDCARD}" /bin/bash -c "/tmp/customize-image.sh $RELEASE $LINUXFAMILY $BOARD $BUILD_DESKTOP $ARCH"
        CUSTOMIZE_IMAGE_RC=$?
        
        # 卸载挂载overlay
        umount -i "${SDCARD}"/tmp/overlay >/dev/null 2>&1
        mountpoint -q "${SDCARD}"/tmp/overlay || rm -r "${SDCARD}"/tmp/overlay
        
        if [[ $CUSTOMIZE_IMAGE_RC != 0 ]]; then
                exit_with_error "customize-image.sh exited with error (rc: $CUSTOMIZE_IMAGE_RC)"
        fi

        call_extension_method "post_customize_image" "image_tweaks_post_customize" << 'POST_CUSTOMIZE_IMAGE'
*post customize-image.sh hook*
Run after the customize-image.sh script is run, and the overlay is unmounted.
POST_CUSTOMIZE_IMAGE
}
6.1.6 chroot环境
# remove packages that are no longer needed. Since we have intrudoced uninstall feature, we might want to clean things that are no longer needed
display_alert "No longer needed packages" "purge" "info"
chroot $SDCARD /bin/bash -c "apt-get autoremove -y"  >/dev/null 2>&1

# create list of installed packages for debug purposes
chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > $DEST/${LOG_SUBPATH}/installed-packages-${RELEASE}$([[ ${BUILD_MINIMAL} == yes ]] && echo "-minimal")$([[ ${BUILD_DESKTOP} == yes  ]] && echo "-desktop").list 2>&1

这段脚本的功能分为两部分:

  • chroot环境中执行命令:移除不再需要的软件包;
  • chroot环境中执行命令:创建已安装软件包列表,将处理后的软件包列表保存到指定路径中;
6.1.7 准备分区/创建镜像

接着执行如下操作:

  • 卸载chroot环境中的挂载点;
  • 调用每个post_debootstrap_tweaks函数移除一些启动服务;
  • 创建分区;
  • 创建根文件系统镜像;

脚本如下:

# clean up / prepare for making the image, 卸载chroot环境中的挂载点
umount_chroot "$SDCARD"
post_debootstrap_tweaks

if [[ $ROOTFS_TYPE == fel ]]; then
		......
else
		prepare_partitions
		create_image
fi

post_debootstrap_tweaks函数定义在scripts/distributions.sh

post_debootstrap_tweaks()
{
        # remove service start blockers and QEMU binary
        rm -f "${SDCARD}"/sbin/initctl "${SDCARD}"/sbin/start-stop-daemon
        chroot "${SDCARD}" /bin/bash -c "dpkg-divert --quiet --local --rename --remove /sbin/initctl"
        chroot "${SDCARD}" /bin/bash -c "dpkg-divert --quiet --local --rename --remove /sbin/start-stop-daemon"
        rm -f "${SDCARD}"/usr/sbin/policy-rc.d "${SDCARD}/usr/bin/${QEMU_BINARY}"

        call_extension_method "post_post_debootstrap_tweaks" "config_post_debootstrap_tweaks" << 'POST_POST_DEBOOTSTRAP_TWEAKS'
*run after removing diversions and qemu with chroot unmounted*
Last chance to touch the `${SDCARD}` filesystem before it is copied to the final media.
It is too late to run any chrooted commands, since the supporting filesystems are already unmounted.
POST_POST_DEBOOTSTRAP_TWEAKS

}
6.1.8 清理工作

最后执行文件系统的卸载工作,以及临时目录的清理工作;

# stage: unmount tmpfs,卸载指定的文件系统
umount $SDCARD 2>&1
if [[ $use_tmpfs = yes ]]; then
		# 查找/proc/mounts是否还存在$SDCARD挂载信息,如果存在尝试卸载
		while grep -qs "$SDCARD" /proc/mounts
		do
				umount $SDCARD
				sleep 5
		done
fi
# 删除临时目录
rm -rf $SDCARD

# remove exit trap,取消或者清除对这些信号的处理
trap - INT TERM EXIT

6.2 create_rootfs_cache

点击查看代码
# create_rootfs_cache
#
# unpacks cached rootfs for $RELEASE or creates one
#
create_rootfs_cache()
{

        local packages_hash=$(get_package_list_hash "$ROOTFSCACHE_VERSION")
        local cache_type="cli"
        [[ ${BUILD_DESKTOP} == yes ]] && local cache_type="xfce-desktop"
        [[ -n ${DESKTOP_ENVIRONMENT} ]] && local cache_type="${DESKTOP_ENVIRONMENT}"
        [[ ${BUILD_MINIMAL} == yes ]] && local cache_type="minimal"
        local cache_name=${RELEASE}-${cache_type}-${ARCH}.$packages_hash.tar.lz4
        local cache_fname=${EXTER}/cache/rootfs/${cache_name}
        local display_name=${RELEASE}-${cache_type}-${ARCH}.${packages_hash:0:3}...${packages_hash:29}.tar.lz4

        if [[ -f $cache_fname && "$ROOT_FS_CREATE_ONLY" != "force" ]]; then
                local date_diff=$(( ($(date +%s) - $(stat -c %Y $cache_fname)) / 86400 ))
                display_alert "Extracting $display_name" "$date_diff days old" "info"
                pv -p -b -r -c -N "[ .... ] $display_name" "$cache_fname" | lz4 -dc | tar xp --xattrs -C $SDCARD/
                [[ $? -ne 0 ]] && rm $cache_fname && exit_with_error "Cache $cache_fname is corrupted and was deleted. Restart."
                rm $SDCARD/etc/resolv.conf
                echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf
                create_sources_list "$RELEASE" "$SDCARD/"
        elif [[ $RELEASE == "raspi" ]]; then
                display_alert "local not found" "Creating new rootfs cache for $RELEASE" "info"

                cd $SDCARD # this will prevent error sh: 0: getcwd() failed

                bootstrap bullseye "$SDCARD" "https://mirrors.ustc.edu.cn/debian/"

                mount_chroot "$SDCARD"

                display_alert "Diverting" "initctl/start-stop-daemon" "info"
                # policy-rc.d script prevents starting or reloading services during image creation
                printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d
                LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl" &> /dev/null
                LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon" &> /dev/null
                printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon
                printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl
                chmod 755 $SDCARD/usr/sbin/policy-rc.d
                chmod 755 $SDCARD/sbin/initctl
                chmod 755 $SDCARD/sbin/start-stop-daemon

                install_raspi_specific

                umount_chroot "$SDCARD"

                tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
                        --exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -5 -c > $cache_fname
        else
                display_alert "local not found" "Creating new rootfs cache for $RELEASE" "info"

                # stage: debootstrap base system
                if [[ $NO_APT_CACHER != yes ]]; then
                        # apt-cacher-ng apt-get proxy parameter
                        local apt_extra="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\""
                        local apt_mirror="http://${APT_PROXY_ADDR:-localhost:3142}/$APT_MIRROR"
                else
                        local apt_mirror="http://$APT_MIRROR"
                fi

                # fancy progress bars
                [[ -z $OUTPUT_DIALOG ]] && local apt_extra_progress="--show-progress -o DPKG::Progress-Fancy=1"

                # Ok so for eval+PIPESTATUS.
                # Try this on your bash shell:
                # ONEVAR="testing" eval 'bash -c "echo value once $ONEVAR && false && echo value twice $ONEVAR"' '| grep value'  '| grep value' ; echo ${PIPESTATUS[*]}
                # Notice how PIPESTATUS has only one element. and it is always true, although we failed explicitly with false in the middle of the bash.
                # That is because eval itself is considered a single command, no matter how many pipes you put in there, you'll get a single value, the return code of the LAST pipe.
                # Lets export the value of the pipe inside eval so we know outside what happened:
                # ONEVAR="testing" eval 'bash -e -c "echo value once $ONEVAR && false && echo value twice $ONEVAR"' '| grep value'  '| grep value' ';EVALPIPE=(${PIPESTATUS[@]})' ; echo ${EVALPIPE[*]}

                display_alert "Installing base system" "Stage 1/2" "info"
                cd $SDCARD # this will prevent error sh: 0: getcwd() failed

                eval 'debootstrap --variant=minbase --include=${DEBOOTSTRAP_LIST// /,} ${PACKAGE_LIST_EXCLUDE:+ --exclude=${PACKAGE_LIST_EXCLUDE// /,}} \
                        --arch=$ARCH --components=${DEBOOTSTRAP_COMPONENTS} $DEBOOTSTRAP_OPTION --foreign $RELEASE $SDCARD/ $apt_mirror' \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 1/2)..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${EVALPIPE[0]} -ne 0 || ! -f $SDCARD/debootstrap/debootstrap ]] && exit_with_error "Debootstrap base system for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} first stage failed"

                cp /usr/bin/$QEMU_BINARY $SDCARD/usr/bin/

                mkdir -p $SDCARD/usr/share/keyrings/
                cp /usr/share/keyrings/*-archive-keyring.gpg $SDCARD/usr/share/keyrings/

                display_alert "Installing base system" "Stage 2/2" "info"
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "/debootstrap/debootstrap --second-stage"' \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 2/2)..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${EVALPIPE[0]} -ne 0 || ! -f $SDCARD/bin/bash ]] && exit_with_error "Debootstrap base system for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} second stage failed"

                mount_chroot "$SDCARD"

                display_alert "Diverting" "initctl/start-stop-daemon" "info"
                # policy-rc.d script prevents starting or reloading services during image creation
                printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d
                LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl" &> /dev/null
                LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon" &> /dev/null
                printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon
                printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl
                chmod 755 $SDCARD/usr/sbin/policy-rc.d
                chmod 755 $SDCARD/sbin/initctl
                chmod 755 $SDCARD/sbin/start-stop-daemon

                # stage: configure language and locales
                display_alert "Configuring locales" "$DEST_LANG" "info"

                [[ -f $SDCARD/etc/locale.gen ]] && sed -i "s/^# $DEST_LANG/$DEST_LANG/" $SDCARD/etc/locale.gen
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "locale-gen $DEST_LANG"' ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "update-locale LANG=$DEST_LANG LANGUAGE=$DEST_LANG LC_MESSAGES=$DEST_LANG"' \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
                if [[ -f $SDCARD/etc/default/console-setup ]]; then
                        sed -e 's/CHARMAP=.*/CHARMAP="UTF-8"/' -e 's/FONTSIZE=.*/FONTSIZE="8x16"/' \
                                -e 's/CODESET=.*/CODESET="guess"/' -i $SDCARD/etc/default/console-setup
                        eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "setupcon --save --force"'
                fi

                # stage: create apt-get sources list
                create_sources_list "$RELEASE" "$SDCARD/"

                # add armhf arhitecture to arm64, unless configured not to do so.
                if [[ "a${ARMHF_ARCH}" != "askip" ]]; then
                        [[ $ARCH == arm64 ]] && eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg --add-architecture armhf"'
                fi

                # this should fix resolvconf installation failure in some cases
                chroot $SDCARD /bin/bash -c 'echo "resolvconf resolvconf/linkify-resolvconf boolean false" | debconf-set-selections'

                # stage: update packages list
                display_alert "Updating package list" "$RELEASE" "info"
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "apt-get -q -y $apt_extra update"' \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Updating package lists..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${EVALPIPE[0]} -ne 0 ]] && display_alert "Updating package lists" "failed" "wrn"

                # stage: upgrade base packages from xxx-updates and xxx-backports repository branches
                display_alert "Upgrading base packages" "Orange Pi" "info"
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
                        $apt_extra $apt_extra_progress upgrade"' \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Upgrading base packages..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                # Myy: Dividing the desktop packages installation steps into multiple
                # ones. We first install the "ADDITIONAL_PACKAGES" in order to get
                # access to software-common-properties installation.
                # THEN we add the APT sources and install the Desktop packages.
                # TODO : Find a way to add APT sources WITHOUT software-common-properties

                [[ ${EVALPIPE[0]} -ne 0 ]] && display_alert "Upgrading base packages" "failed" "wrn"

                # stage: install additional packages
                display_alert "Installing the main packages for" "Orange Pi" "info"
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
                        $apt_extra $apt_extra_progress --no-install-recommends install $PACKAGE_MAIN_LIST"' \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Orange Pi main packages..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi main packages for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} failed"

                if [[ $BUILD_DESKTOP == "yes" ]]; then
                        # FIXME Myy : Are we keeping this only for Desktop users,
                        # or should we extend this to CLI users too ?
                        # There might be some clunky boards that require Debian packages from
                        # specific repos...
                        display_alert "Adding apt sources for Desktop packages"
                        add_desktop_package_sources

                        local apt_desktop_install_flags=""
                        if [[ ! -z ${DESKTOP_APT_FLAGS_SELECTED+x} ]]; then
                                for flag in ${DESKTOP_APT_FLAGS_SELECTED}; do
                                        apt_desktop_install_flags+=" --install-${flag}"
                                done
                        else
                                # Myy : Using the previous default option, if the variable isn't defined
                                # And ONLY if it's not defined !
                                apt_desktop_install_flags+=" --no-install-recommends"
                        fi

                        display_alert "Installing the desktop packages for" "Orange Pi" "info"
                        eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
                                $apt_extra $apt_extra_progress install ${apt_desktop_install_flags} $PACKAGE_LIST_DESKTOP"' \
                                ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                                ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Orange Pi desktop packages..." $TTY_Y $TTY_X'} \
                                ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                        [[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi desktop packages for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} failed"
                fi

                # Remove packages from packages.uninstall

                display_alert "Uninstall packages" "$PACKAGE_LIST_UNINSTALL" "info"
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq \
                        $apt_extra $apt_extra_progress purge $PACKAGE_LIST_UNINSTALL"' \
                        ${PROGRESS_LOG_TO_FILE:+' >> $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Removing packages.uninstall packages..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi packages failed"

                # stage: purge residual packages
                display_alert "Purging residual packages for" "Orange Pi" "info"
                PURGINGPACKAGES=$(chroot $SDCARD /bin/bash -c "dpkg -l | grep \"^rc\" | awk '{print \$2}' | tr \"\n\" \" \"")
                eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
                        $apt_extra $apt_extra_progress remove --purge $PURGINGPACKAGES"' \
                        ${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
                        ${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Purging residual Orange Pi packages..." $TTY_Y $TTY_X'} \
                        ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'

                [[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "Purging of residual Orange Pi packages failed"

                # stage: remove downloaded packages
                chroot $SDCARD /bin/bash -c "apt-get -y autoremove; apt-get clean"

                # DEBUG: print free space
                local freespace=$(LC_ALL=C df -h)
                echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log
                display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"
                display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"

                # create list of installed packages for debug purposes
                chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > ${cache_fname}.list 2>&1

                # creating xapian index that synaptic runs faster
                if [[ $BUILD_DESKTOP == yes ]]; then
                        display_alert "Recreating Synaptic search index" "Please wait" "info"
                        chroot $SDCARD /bin/bash -c "[[ -f /usr/sbin/update-apt-xapian-index ]] && /usr/sbin/update-apt-xapian-index -u"
                fi

                # this is needed for the build process later since resolvconf generated file in /run is not saved
                rm $SDCARD/etc/resolv.conf
                echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf

                # stage: make rootfs cache archive
                display_alert "Ending debootstrap process and preparing cache" "$RELEASE" "info"
                sync
                # the only reason to unmount here is compression progress display
                # based on rootfs size calculation
                umount_chroot "$SDCARD"

                tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
                        --exclude='./sys/*' --exclude='./home/*' --exclude='./root/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -5 -c > $cache_fname

                # sign rootfs cache archive that it can be used for web cache once. Internal purposes
                if [[ -n "${GPG_PASS}" && "${SUDO_USER}" ]]; then
                        [[ -n ${SUDO_USER} ]] && sudo chown -R ${SUDO_USER}:${SUDO_USER} "${DEST}"/images/
                        echo "${GPG_PASS}" | sudo -H -u ${SUDO_USER} bash -c "gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${cache_fname}" || exit 1
                fi

                # needed for backend to keep current only
                touch $cache_fname.current

        fi

        # used for internal purposes. Faster rootfs cache rebuilding
        if [[ -n "$ROOT_FS_CREATE_ONLY" ]]; then
                umount --lazy "$SDCARD"
                rm -rf $SDCARD

                display_alert "Rootfs build done" "@host" "info"
                display_alert "Target directory" "${EXTER}/cache/rootfs" "info"
                display_alert "File name" "${cache_name}" "info"

                # remove exit trap
                trap - INT TERM EXIT
        exit
        fi

        mount_chroot "$SDCARD"
} #############################################################################

6.3 prepare_partitions

prepare_partitions函数中我们主要实现如下功能:

  • 创建了空白镜像:${SDCARD}.raw,即<SDK>/.tmp/rootfs-$(uuidgen).raw
  • 为镜像创建分区表,分区表定义了镜像每个分区的位置和大小;这里分区格式设置为gpt,并写入了bootfsrootfs分区信息;
  • 为镜像写入分区表后,需要格式化分区设备以创建文件系统;
    • 这里将bootfs分区格式化为vfat文件系统,并将其挂载到了$MOUNT/boot;其中MOUNT<SDK>/.tmp/mount-$(uuidgen)
    • 这里将rootfs分区格式化为ext4文件系统,并将其挂载到了$MOUNT
  • 最后调整boot/orangepiEnv.txt文件以及生成boot script文件boot.scr

完整脚本如下:

点击查看代码
# prepare_partitions
#
# creates image file, partitions and fs
# and mounts it to local dir
# FS-dependent stuff (boot and root fs partition types) happens here
#
prepare_partitions() {
        display_alert "Preparing image file for rootfs" "$BOARD $RELEASE" "info"

        # possible partition combinations
        # /boot: none, ext4, ext2, fat (BOOTFS_TYPE)
        # root: ext4, btrfs, f2fs, nfs (ROOTFS_TYPE)

        # declare makes local variables by default if used inside a function
        # NOTE: mountopts string should always start with comma if not empty

        # array copying in old bash versions is tricky, so having filesystems as arrays
        # with attributes as keys is not a good idea
        declare -A parttype mkopts mkopts_label mkfs mountopts

        parttype[ext4]=ext4
        parttype[ext2]=ext2
        parttype[fat]=fat16
        parttype[f2fs]=ext4 # not a copy-paste error
        parttype[btrfs]=btrfs
        parttype[xfs]=xfs
        # parttype[nfs] is empty

        # metadata_csum and 64bit may need to be disabled explicitly when migrating to newer supported host OS releases
        if [[ $HOSTRELEASE =~ buster|bullseye|bookworm|bionic|focal|jammy|kinetic|sid ]]; then
                mkopts[ext4]="-q -m 2 -O ^64bit,^metadata_csum"
        fi
        # mkopts[fat] is empty
        mkopts[ext2]='-q'
        # mkopts[f2fs] is empty
        mkopts[btrfs]='-m dup'
        # mkopts[xfs] is empty
        # mkopts[nfs] is empty

        mkopts_label[ext4]='-L '
        mkopts_label[ext2]='-L '
        mkopts_label[fat]='-n '
        mkopts_label[f2fs]='-l '
        mkopts_label[btrfs]='-L '
        mkopts_label[xfs]='-L '
        # mkopts_label[nfs] is empty

        mkfs[ext4]=ext4
        mkfs[ext2]=ext2
        mkfs[fat]=vfat
        mkfs[f2fs]=f2fs
        mkfs[btrfs]=btrfs
        mkfs[xfs]=xfs
        # mkfs[nfs] is empty

        mountopts[ext4]=',commit=600,errors=remount-ro'
        # mountopts[ext2] is empty
        # mountopts[fat] is empty
        # mountopts[f2fs] is empty
        mountopts[btrfs]=',commit=600'
        # mountopts[xfs] is empty
        # mountopts[nfs] is empty

        # default BOOTSIZE to use if not specified
        DEFAULT_BOOTSIZE=1024 # MiB
        # size of UEFI partition. 0 for no UEFI. Don't mix UEFISIZE>0 and BOOTSIZE>0
        UEFISIZE=${UEFISIZE:-0}
        BIOSSIZE=${BIOSSIZE:-0}
        UEFI_MOUNT_POINT=${UEFI_MOUNT_POINT:-/boot/efi}
        UEFI_FS_LABEL="${UEFI_FS_LABEL:-opi_efi}"
        ROOT_FS_LABEL="${ROOT_FS_LABEL:-opi_root}"
        BOOT_FS_LABEL="${BOOT_FS_LABEL:-opi_boot}"

        call_extension_method "pre_prepare_partitions" "prepare_partitions_custom" << 'PRE_PREPARE_PARTITIONS'
*allow custom options for mkfs*
Good time to change stuff like mkfs opts, types etc.
PRE_PREPARE_PARTITIONS

        # stage: determine partition configuration
        local next=1
        # Check if we need UEFI partition
        if [[ $UEFISIZE -gt 0 ]]; then
                # Check if we need BIOS partition
                [[ $BIOSSIZE -gt 0 ]] && local biospart=$((next++))
                local uefipart=$((next++))
        fi
        # Check if we need boot partition
        if [[ -n $BOOTFS_TYPE || $ROOTFS_TYPE != ext4 || $CRYPTROOT_ENABLE == yes ]]; then
                local bootpart=$((next++))
                local bootfs=${BOOTFS_TYPE:-ext4}
                [[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=${DEFAULT_BOOTSIZE}
        else
                BOOTSIZE=0
        fi
        # Check if we need root partition
        [[ $ROOTFS_TYPE != nfs ]] &&
                local rootpart=$((next++))

        # stage: calculate rootfs size
        export rootfs_size=$(du -sm $SDCARD/ | cut -f1) # MiB
        display_alert "Current rootfs size" "$rootfs_size MiB" "info"

        call_extension_method "prepare_image_size" "config_prepare_image_size" << 'PREPARE_IMAGE_SIZE'
*allow dynamically determining the size based on the $rootfs_size*
Called after `${rootfs_size}` is known, but before `${FIXED_IMAGE_SIZE}` is taken into account.
A good spot to determine `FIXED_IMAGE_SIZE` based on `rootfs_size`.
UEFISIZE can be set to 0 for no UEFI partition, or to a size in MiB to include one.
Last chance to set `USE_HOOK_FOR_PARTITION`=yes and then implement create_partition_table hook_point.
PREPARE_IMAGE_SIZE

        if [[ -n $FIXED_IMAGE_SIZE && $FIXED_IMAGE_SIZE =~ ^[0-9]+$ ]]; then
                display_alert "Using user-defined image size" "$FIXED_IMAGE_SIZE MiB" "info"
                local sdsize=$FIXED_IMAGE_SIZE
                # basic sanity check
                if [[ $ROOTFS_TYPE != nfs && $sdsize -lt $rootfs_size ]]; then
                        exit_with_error "User defined image size is too small" "$sdsize <= $rootfs_size"
                fi
        else
                local imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE)) # MiB
                # Hardcoded overhead +25% is needed for desktop images,
                # for CLI it could be lower. Align the size up to 4MiB
                if [[ $BUILD_DESKTOP == yes ]]; then
                        local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.35) / 1 + 0) / 4 + 1) * 4")
                else
                        local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.30) / 1 + 0) / 4 + 1) * 4")
                fi
        fi

        # stage: create blank image
        display_alert "Creating blank image for rootfs" "$sdsize MiB" "info"
        if [[ $FAST_CREATE_IMAGE == yes ]]; then
                truncate --size=${sdsize}M ${SDCARD}.raw # sometimes results in fs corruption, revert to previous know to work solution
                sync
        else
                dd if=/dev/zero bs=1M status=none count=$sdsize | pv -p -b -r -s $(($sdsize * 1024 * 1024)) -N "[ .... ] dd" | dd status=none of=${SDCARD}.raw
        fi

        # stage: create partition table
        display_alert "Creating partitions" "${bootfs:+/boot: $bootfs }root: $ROOTFS_TYPE" "info"
        if [[ "${USE_HOOK_FOR_PARTITION}" == "yes" ]]; then
                {
                        [[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&
                                echo "label: dos" ||
                                echo "label: $IMAGE_PARTITION_TABLE"
                } | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||
                        exit_with_error "Create partition table fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"

                call_extension_method "create_partition_table" <<- 'CREATE_PARTITION_TABLE'
                        *only called when USE_HOOK_FOR_PARTITION=yes to create the complete partition table*
                        Finally, we can get our own partition table. You have to partition ${SDCARD}.raw
                        yourself. Good luck.
                CREATE_PARTITION_TABLE
        else
                {
                        [[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&
                                echo "label: dos" ||
                                echo "label: $IMAGE_PARTITION_TABLE"

                        local next=$OFFSET
                        if [[ -n "$biospart" ]]; then
                                # gpt: BIOS boot
                                local type="21686148-6449-6E6F-744E-656564454649"
                                echo "$biospart : name=\"bios\", start=${next}MiB, size=${BIOSSIZE}MiB, type=${type}"
                                local next=$(($next + $BIOSSIZE))
                        fi
                        if [[ -n "$uefipart" ]]; then
                                # dos: EFI (FAT-12/16/32)
                                # gpt: EFI System
                                [[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
                                        local type="ef" ||
                                        local type="C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
                                echo "$uefipart : name=\"efi\", start=${next}MiB, size=${UEFISIZE}MiB, type=${type}"
                                local next=$(($next + $UEFISIZE))
                        fi
                        if [[ -n "$bootpart" ]]; then
                                # Linux extended boot
                                [[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
                                        local type="ea" ||
                                        local type="BC13C2FF-59E6-4262-A352-B275FD6F7172"
                                if [[ -n "$rootpart" ]]; then
                                        echo "$bootpart : name=\"bootfs\", start=${next}MiB, size=${BOOTSIZE}MiB, type=${type}"
                                        local next=$(($next + $BOOTSIZE))
                                else
                                        # no `size` argument mean "as much as possible"
                                        echo "$bootpart : name=\"bootfs\", start=${next}MiB, type=${type}"
                                fi
                        fi
                        if [[ -n "$rootpart" ]]; then
                                # dos: Linux
                                # gpt: Linux filesystem
                                [[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
                                        local type="83" ||
                                        local type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
                                # no `size` argument mean "as much as possible"
                                echo "$rootpart : name=\"rootfs\", start=${next}MiB, type=${type}"
                        fi
                } | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||
                        exit_with_error "Partition fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"
        fi

        call_extension_method "post_create_partitions" <<- 'POST_CREATE_PARTITIONS'
                *called after all partitions are created, but not yet formatted*
        POST_CREATE_PARTITIONS

        # stage: mount image
        # lock access to loop devices
        exec {FD}> /var/lock/orangepi-debootstrap-losetup
        flock -x $FD

        LOOP=$(losetup -f)
        [[ -z $LOOP ]] && exit_with_error "Unable to find free loop device"

        check_loop_device "$LOOP"

        losetup $LOOP ${SDCARD}.raw

        # loop device was grabbed here, unlock
        flock -u $FD

        partprobe $LOOP

        # stage: create fs, mount partitions, create fstab
        rm -f $SDCARD/etc/fstab
        if [[ -n $rootpart ]]; then
                local rootdevice="${LOOP}p${rootpart}"

                if [[ $CRYPTROOT_ENABLE == yes ]]; then
                        display_alert "Encrypting root partition with LUKS..." "cryptsetup luksFormat $rootdevice" ""
                        echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksFormat $CRYPTROOT_PARAMETERS $rootdevice -
                        echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksOpen $rootdevice $ROOT_MAPPER -
                        display_alert "Root partition encryption complete." "" "ext"
                        # TODO: pass /dev/mapper to Docker
                        rootdevice=/dev/mapper/$ROOT_MAPPER # used by `mkfs` and `mount` commands
                fi

                check_loop_device "$rootdevice"
                display_alert "Creating rootfs" "$ROOTFS_TYPE on $rootdevice"
                mkfs.${mkfs[$ROOTFS_TYPE]} ${mkopts[$ROOTFS_TYPE]} ${mkopts_label[$ROOTFS_TYPE]:+${mkopts_label[$ROOTFS_TYPE]}"$ROOT_FS_LABEL"} $rootdevice >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                [[ $ROOTFS_TYPE == ext4 ]] && tune2fs -o journal_data_writeback $rootdevice > /dev/null
                if [[ $ROOTFS_TYPE == btrfs && $BTRFS_COMPRESSION != none ]]; then
                        local fscreateopt="-o compress-force=${BTRFS_COMPRESSION}"
                fi
                mount ${fscreateopt} $rootdevice $MOUNT/
                # create fstab (and crypttab) entry
                if [[ $CRYPTROOT_ENABLE == yes ]]; then
                        # map the LUKS container partition via its UUID to be the 'cryptroot' device
                        echo "$ROOT_MAPPER UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}) none luks" >> $SDCARD/etc/crypttab
                        local rootfs=$rootdevice # used in fstab
                else
                        local rootfs="UUID=$(blkid -s UUID -o value $rootdevice)"
                fi
                echo "$rootfs / ${mkfs[$ROOTFS_TYPE]} defaults,noatime${mountopts[$ROOTFS_TYPE]} 0 1" >> $SDCARD/etc/fstab
        else
                # update_initramfs will fail if /lib/modules/ doesn't exist
                mount --bind --make-private $SDCARD $MOUNT/
                echo "/dev/nfs / nfs defaults 0 0" >> $SDCARD/etc/fstab
        fi
        if [[ -n $bootpart ]]; then
                display_alert "Creating /boot" "$bootfs on ${LOOP}p${bootpart}"
                check_loop_device "${LOOP}p${bootpart}"
                mkfs.${mkfs[$bootfs]} ${mkopts[$bootfs]} ${mkopts_label[$bootfs]:+${mkopts_label[$bootfs]}"$BOOT_FS_LABEL"} ${LOOP}p${bootpart} >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
                mkdir -p $MOUNT/boot/
                mount ${LOOP}p${bootpart} $MOUNT/boot/
                echo "UUID=$(blkid -s UUID -o value ${LOOP}p${bootpart}) /boot ${mkfs[$bootfs]} defaults${mountopts[$bootfs]} 0 2" >> $SDCARD/etc/fstab
        fi
        if [[ -n $uefipart ]]; then
                display_alert "Creating EFI partition" "FAT32 ${UEFI_MOUNT_POINT} on ${LOOP}p${uefipart} label ${UEFI_FS_LABEL}"
                check_loop_device "${LOOP}p${uefipart}"
                mkfs.fat -F32 -n "${UEFI_FS_LABEL}" ${LOOP}p${uefipart} >> "${DEST}"/debug/install.log 2>&1
                mkdir -p "${MOUNT}${UEFI_MOUNT_POINT}"
                mount ${LOOP}p${uefipart} "${MOUNT}${UEFI_MOUNT_POINT}"
                echo "UUID=$(blkid -s UUID -o value ${LOOP}p${uefipart}) ${UEFI_MOUNT_POINT} vfat defaults 0 2" >> $SDCARD/etc/fstab
        fi
        echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab

        call_extension_method "format_partitions" <<- 'FORMAT_PARTITIONS'
                *if you created your own partitions, this would be a good time to format them*
                The loop device is mounted, so ${LOOP}p1 is it's first partition etc.
        FORMAT_PARTITIONS

        # stage: adjust boot script or boot environment
        if [[ -f $SDCARD/boot/orangepiEnv.txt ]]; then
                if [[ $CRYPTROOT_ENABLE == yes ]]; then
                        echo "rootdev=$rootdevice cryptdevice=UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}):$ROOT_MAPPER" >> $SDCARD/boot/orangepiEnv.txt
                else
                        echo "rootdev=$rootfs" >> $SDCARD/boot/orangepiEnv.txt
                fi
                echo "rootfstype=$ROOTFS_TYPE" >> $SDCARD/boot/orangepiEnv.txt
        elif [[ $rootpart != 1 ]] && [[ $SRC_EXTLINUX != yes ]]; then
                local bootscript_dst=${BOOTSCRIPT##*:}
                sed -i 's/mmcblk0p1/mmcblk0p2/' $SDCARD/boot/$bootscript_dst
                sed -i -e "s/rootfstype=ext4/rootfstype=$ROOTFS_TYPE/" \
                        -e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/$bootscript_dst
        fi

        # if we have boot.ini = remove orangepiEnv.txt and add UUID there if enabled
        if [[ -f $SDCARD/boot/boot.ini ]]; then
                sed -i -e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/boot.ini
                if [[ $CRYPTROOT_ENABLE == yes ]]; then
                        local rootpart="UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart})"
                        sed -i 's/^setenv rootdev .*/setenv rootdev "\/dev\/mapper\/'$ROOT_MAPPER' cryptdevice='$rootpart':'$ROOT_MAPPER'"/' $SDCARD/boot/boot.ini
                else
                        sed -i 's/^setenv rootdev .*/setenv rootdev "'$rootfs'"/' $SDCARD/boot/boot.ini
                fi
                if [[ $LINUXFAMILY != meson64 ]]; then
                        [[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt
                fi
        fi

        # if we have a headless device, set console to DEFAULT_CONSOLE
        if [[ -n $DEFAULT_CONSOLE && -f $SDCARD/boot/orangepiEnv.txt ]]; then
                if grep -lq "^console=" $SDCARD/boot/orangepiEnv.txt; then
                        sed -i "s/^console=.*/console=$DEFAULT_CONSOLE/" $SDCARD/boot/orangepiEnv.txt
                else
                        echo "console=$DEFAULT_CONSOLE" >> $SDCARD/boot/orangepiEnv.txt
                fi
        fi

        # recompile .cmd to .scr if boot.cmd exists

        if [[ -f $SDCARD/boot/boot.cmd ]]; then
                if [ -z $BOOTSCRIPT_OUTPUT ]; then BOOTSCRIPT_OUTPUT=boot.scr; fi
                mkimage -C none -A arm -T script -d $SDCARD/boot/boot.cmd $SDCARD/boot/$BOOTSCRIPT_OUTPUT > /dev/null 2>&1
        fi

        # create extlinux config
        if [[ -f $SDCARD/boot/extlinux/extlinux.conf ]]; then
                echo "  append root=$rootfs $SRC_CMDLINE $MAIN_CMDLINE" >> $SDCARD/boot/extlinux/extlinux.conf
                [[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt
        fi

}
6.3.1 准备工作

首先就是定义了一堆变量:

  • parttypemap类型,定义一个关于文件系统类型和分区类型的映射关系;
  • mkoptsmap类型,定义一个关于文件系统类型和mkfs.xxx命令选项关系;
  • mkopts_labelmap类型,定义了用于创建文件系统时的标签参数;
  • mountoptsmap类型,为每种文件系统指定了挂载时使用的选项和参数;
  • 接着就是一些默认值设置:比如UEFISIZEBIOSSIZE等;

源码如下:

declare -A parttype mkopts mkopts_label mkfs mountopts

parttype[ext4]=ext4
parttype[ext2]=ext2
parttype[fat]=fat16
parttype[f2fs]=ext4 # not a copy-paste error
parttype[btrfs]=btrfs
parttype[xfs]=xfs
# parttype[nfs] is empty

# metadata_csum and 64bit may need to be disabled explicitly when migrating to newer supported host OS releases
if [[ $HOSTRELEASE =~ buster|bullseye|bookworm|bionic|focal|jammy|kinetic|sid ]]; then
		mkopts[ext4]="-q -m 2 -O ^64bit,^metadata_csum"
fi
# mkopts[fat] is empty
mkopts[ext2]='-q'
# mkopts[f2fs] is empty
mkopts[btrfs]='-m dup'
# mkopts[xfs] is empty
# mkopts[nfs] is empty

mkopts_label[ext4]='-L '

mkopts_label[ext2]='-L '
mkopts_label[fat]='-n '
mkopts_label[f2fs]='-l '
mkopts_label[btrfs]='-L '
mkopts_label[xfs]='-L '
# mkopts_label[nfs] is empty

mkfs[ext4]=ext4
mkfs[ext2]=ext2
mkfs[fat]=vfat
mkfs[f2fs]=f2fs
mkfs[btrfs]=btrfs
mkfs[xfs]=xfs
# mkfs[nfs] is empty

mountopts[ext4]=',commit=600,errors=remount-ro'
# mountopts[ext2] is empty
# mountopts[fat] is empty
# mountopts[f2fs] is empty
mountopts[btrfs]=',commit=600'
# mountopts[xfs] is empty
# mountopts[nfs] is empty

# default BOOTSIZE to use if not specified
DEFAULT_BOOTSIZE=1024 # MiB
# size of UEFI partition. 0 for no UEFI. Don't mix UEFISIZE>0 and BOOTSIZE>0
UEFISIZE=${UEFISIZE:-0}
BIOSSIZE=${BIOSSIZE:-0}
UEFI_MOUNT_POINT=${UEFI_MOUNT_POINT:-/boot/efi}
UEFI_FS_LABEL="${UEFI_FS_LABEL:-opi_efi}"
ROOT_FS_LABEL="${ROOT_FS_LABEL:-opi_root}"
BOOT_FS_LABEL="${BOOT_FS_LABEL:-opi_boot}"
6.3.2 初始化变量

接着就是初始化一些变量;

  • 设置bootpart=1bootfs=fatBOOTSIZE=${DEFAULT_BOOTSIZE}:即第一个分区为bootfs分区,文件系统类型为vfat,大小为1024MB
  • 设置rootpart=2rootfs_size=$SDCARD目录大小;即第二个分区为rootfs分区,文件系统类型为ext4,大小为$SDCARD目录实际大小;
  • 计算镜像文件大小:imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE)),其中:
    • rootfs_size rootfs分区大小;
    • OFFSET :默认为30
    • BOOTSIZE boot分区大小;
    • UEFISIZE :默认为0;
    • EXTRA_ROOTFS_MIB_SIZE:默认为0;
  • 设置sdsize大小,要比imagesize略大一些;

脚本源码如下:

# stage: determine partition configuration
local next=1
# Check if we need UEFI partition,跳过
if [[ $UEFISIZE -gt 0 ]]; then
		.......
fi
# Check if we need boot partition,BOOTFS_TYPE="fat",因此进入
if [[ -n $BOOTFS_TYPE || $ROOTFS_TYPE != ext4 || $CRYPTROOT_ENABLE == yes ]]; then
		local bootpart=$((next++))
		local bootfs=${BOOTFS_TYPE:-ext4}		
		[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=${DEFAULT_BOOTSIZE}
else
		BOOTSIZE=0
fi

# Check if we need root partition
[[ $ROOTFS_TYPE != nfs ]] &&
		local rootpart=$((next++))

# stage: calculate rootfs size
export rootfs_size=$(du -sm $SDCARD/ | cut -f1) # MiB
display_alert "Current rootfs size" "$rootfs_size MiB" "info"

call_extension_method "prepare_image_size" "config_prepare_image_size" << 'PREPARE_IMAGE_SIZE'
*allow dynamically determining the size based on the $rootfs_size*
Called after `${rootfs_size}` is known, but before `${FIXED_IMAGE_SIZE}` is taken into account.
A good spot to determine `FIXED_IMAGE_SIZE` based on `rootfs_size`.
UEFISIZE can be set to 0 for no UEFI partition, or to a size in MiB to include one.
Last chance to set `USE_HOOK_FOR_PARTITION`=yes and then implement create_partition_table hook_point.
PREPARE_IMAGE_SIZE

if [[ -n $FIXED_IMAGE_SIZE && $FIXED_IMAGE_SIZE =~ ^[0-9]+$ ]]; then
		......
else
		local imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE)) # MiB
		# Hardcoded overhead +25% is needed for desktop images,
		# for CLI it could be lower. Align the size up to 4MiB
		if [[ $BUILD_DESKTOP == yes ]]; then
				local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.35) / 1 + 0) / 4 + 1) * 4")
		else
				local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.30) / 1 + 0) / 4 + 1) * 4")
		fi
fi
6.3.3 创建空白镜像

接着就是使用dd命令从 /dev/zero 读取零来创建一个空白的镜像文件,镜像文件名为${SDCARD}.raw,大小为$sdsize MB

# stage: create blank image
display_alert "Creating blank image for rootfs" "$sdsize MiB" "info"
if [[ $FAST_CREATE_IMAGE == yes ]]; then
		truncate --size=${sdsize}M ${SDCARD}.raw # sometimes results in fs corruption, revert to previous know to work solution
		sync
else
		dd if=/dev/zero bs=1M status=none count=$sdsize | pv -p -b -r -s $(($sdsize * 1024 * 1024)) -N "[ .... ] dd" | dd status=none of=${SDCARD}.raw
fi
6.3.4 创建分区表

接着使用sfdisk命令为${SDCARD}.raw镜像创建分区表,写入bootfsrootfs分区信息;

label: gpt
1 : name="bootfs", start=30MiB, size=1024MiB, type="BC13C2FF-59E6-4262-A352-B275FD6F7172"
2 : name="rootfs", start=1054MiB, type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"

脚本如下:

# stage: create partition table
display_alert "Creating partitions" "${bootfs:+/boot: $bootfs }root: $ROOTFS_TYPE" "info"
# 不会进入
if [[ "${USE_HOOK_FOR_PARTITION}" == "yes" ]]; then
		.......
else
		{
				# IMAGE_PARTITION_TABLE=gpt, 输出:label: gpt
				[[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&
						echo "label: dos" ||
						echo "label: $IMAGE_PARTITION_TABLE"

				local next=$OFFSET
				if [[ -n "$biospart" ]]; then
					   ......
				fi
				if [[ -n "$uefipart" ]]; then
						......
				fi
				# 进入
				if [[ -n "$bootpart" ]]; then
						# Linux extended boot,IMAGE_PARTITION_TABLE=gpt, type被设置为BC13C2FF-59E6-4262-A352-B275FD6F7172
						[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
								local type="ea" ||
								local type="BC13C2FF-59E6-4262-A352-B275FD6F7172"
						# 进入
						if [[ -n "$rootpart" ]]; then
								echo "$bootpart : name=\"bootfs\", start=${next}MiB, size=${BOOTSIZE}MiB, type=${type}"
								local next=$(($next + $BOOTSIZE))
						else
								# no `size` argument mean "as much as possible"
								echo "$bootpart : name=\"bootfs\", start=${next}MiB, type=${type}"
						fi
				fi
				# 进入
				if [[ -n "$rootpart" ]]; then
						# dos: Linux
						# gpt: Linux filesystem,IMAGE_PARTITION_TABLE=gpt, type被设置为0FC63DAF-8483-4772-8E79-3D69D8477DE4
						[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
								local type="83" ||
								local type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
						# no `size` argument mean "as much as possible"
						echo "$rootpart : name=\"rootfs\", start=${next}MiB, type=${type}"
				fi
		} | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||
				exit_with_error "Partition fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"
fi

call_extension_method "post_create_partitions" <<- 'POST_CREATE_PARTITIONS'
		*called after all partitions are created, but not yet formatted*
POST_CREATE_PARTITIONS

分区表的创建总结起来就是:

{
    echo “label: gpt”
    echo "1 : name=\"bootfs\", start=30MiB, size=1024MiB, type=\"BC13C2FF-59E6-4262-A352-B275FD6F7172\""
    echo "2 : name=\"rootfs\", start=1054MiB, type=\"0FC63DAF-8483-4772-8E79-3D69D8477DE4\"" 
} | sfdisk ${SDCARD}.raw
6.3.5 格式化分区&挂载镜像

接着就是处理镜像文件${SDCARD}.raw,并将其与loop设备(比如/dev/loop18)关联,然后格式化分区设备以创建文件系统:

  • bootfs分区(比如:/dev/loop18p1):被格式化为vfat文件系统,用做引导文件系统,并被挂载到$MOUNT/boot
  • rootfs分区(比如:/dev/loop18p2):被格式化为ext4文件系统,用做根文件系统 ,并被挂载到$MOUNT/

最后,脚本会将一些挂载信息写入${SDCARD}/etc/fstab 文件中,以便系统引导时正确挂载这些文件系统。

其中SDCARD<SDK>/.tmp/rootfs-$(uuidgen)MOUNT<SDK>/.tmp/mount-$(uuidgen)

脚本如下:

# stage: mount image
# lock access to loop devices,上锁,防止并发
exec {FD}> /var/lock/orangepi-debootstrap-losetup
flock -x $FD

# 查找当前未被使用的空闲环回设备,并输出该设备的路径 比如/dev/loop18
LOOP=$(losetup -f)
[[ -z $LOOP ]] && exit_with_error "Unable to find free loop device"

check_loop_device "$LOOP"

# 将其与镜像文件${SDCARD}.raw关联起来
losetup $LOOP ${SDCARD}.raw

# loop device was grabbed here, unlock, 释放锁
flock -u $FD

# 通知操作系统重新扫描指定的设备/dev/loop18,以便更新系统中的分区表信息。
partprobe $LOOP

# stage: create fs, mount partitions, create fstab
rm -f $SDCARD/etc/fstab

# 在${SDCARD}.raw镜像rootfs分区创建ext4文件系统
if [[ -n $rootpart ]]; then
		# /dev/loop18p2
		local rootdevice="${LOOP}p${rootpart}"
		
		if [[ $CRYPTROOT_ENABLE == yes ]]; then
				......
		fi

		check_loop_device "$rootdevice"
		display_alert "Creating rootfs" "$ROOTFS_TYPE on $rootdevice"
		# mkfs.ext4 -L opi_root /dev/loop18p2, 在指定的环回设备分区上创建一个ext4文件系统,并为该文件系统设置标签为opi_root
		mkfs.${mkfs[$ROOTFS_TYPE]} ${mkopts[$ROOTFS_TYPE]} ${mkopts_label[$ROOTFS_TYPE]:+${mkopts_label[$ROOTFS_TYPE]}"$ROOT_FS_LABEL"} $rootdevice >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
		# 调整ext4文件系统的的日志模式设置为写回模式
		[[ $ROOTFS_TYPE == ext4 ]] && tune2fs -o journal_data_writeback $rootdevice > /dev/null
		if [[ $ROOTFS_TYPE == btrfs && $BTRFS_COMPRESSION != none ]]; then
				local fscreateopt="-o compress-force=${BTRFS_COMPRESSION}"
		fi
		
		# mount /dev/loop18p2 <SDK>/.tmp/mount-$(uuidgen),将/dev/loop18p2设备挂载到$MOUNT目录中
		mount ${fscreateopt} $rootdevice $MOUNT/
		# create fstab (and crypttab) entry
		if [[ $CRYPTROOT_ENABLE == yes ]]; then
				......
		else
				# 从/dev/loop18p2设备中提取UUID
				local rootfs="UUID=$(blkid -s UUID -o value $rootdevice)"
		fi
		# 将一个新的文件系统条目追加到<SDK>/.tmp/rootfs-$(uuidgen)目录下的/etc/fstab文件中
		echo "$rootfs / ${mkfs[$ROOTFS_TYPE]} defaults,noatime${mountopts[$ROOTFS_TYPE]} 0 1" >> $SDCARD/etc/fstab
else
		......
fi

# 在${SDCARD}.raw镜像bootfs分区创建vfat文件系统
if [[ -n $bootpart ]]; then
		display_alert "Creating /boot" "$bootfs on ${LOOP}p${bootpart}"
		check_loop_device "${LOOP}p${bootpart}"
		
		# mkfs.vfat -n opi_boot /dev/loop18p1, 在指定的环回设备分区上创建一VFAT 文件系统,并为该文件系统设置标签为opi_boot
		mkfs.${mkfs[$bootfs]} ${mkopts[$bootfs]} ${mkopts_label[$bootfs]:+${mkopts_label[$bootfs]}"$BOOT_FS_LABEL"} ${LOOP}p${bootpart} >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
		# 在<SDK>/.tmp/mount-$(uuidgen)下创建boot目录
		mkdir -p $MOUNT/boot/
		# 将/dev/loop18p1设备挂载到$MOUNT/boot目录中
		mount ${LOOP}p${bootpart} $MOUNT/boot/
		# 将一个新的文件系统条目追加到<SDK>/.tmp/rootfs-$(uuidgen)目录下的/etc/fstab文件中
		echo "UUID=$(blkid -s UUID -o value ${LOOP}p${bootpart}) /boot ${mkfs[$bootfs]} defaults${mountopts[$bootfs]} 0 2" >> $SDCARD/etc/fstab
fi
if [[ -n $uefipart ]]; then
		......
fi
# 写入tmpfs文件系统挂载信息
echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab

call_extension_method "format_partitions" <<- 'FORMAT_PARTITIONS'
		*if you created your own partitions, this would be a good time to format them*
		The loop device is mounted, so ${LOOP}p1 is it's first partition etc.
FORMAT_PARTITIONS

以上脚本简化下来就是:

LOOP=$(losetup -f)
losetup $LOOP ${SDCARD}.raw
partprobe $LOOP

# rootfs分区创建ext4文件系统
local rootdevice="${LOOP}p2"
mkfs.ext4 -L opi_root /dev/${LOOP}p2
mount $rootdevice $MOUNT/
echo "UUID=eb5b9342-7a1c-4994-91ea-648cf9059720 / ext4 defaults,noatime,commit=600,errors=remount-ro 0 1" >> $SDCARD/etc/fstab

# bootfs分区创建vfat文件系统
mkfs.vfat -n opi_boot /dev/${LOOP}p1
mkdir -p $MOUNT/boot/
mount ${LOOP}p1 $MOUNT/boot
echo "UUID=24B4-43E5 /boot vfat defaults 0 2" >> $SDCARD/etc/fstab

echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab

.......

# 卸载loop设备
losetup -d ${LOOP}
6.3.6 调整启动脚本/环境

函数最后就是调整boot/orangepiEnv.txt文件以及生成boot script文件boot.scr

这里我们移除一些无效脚本,简化后如下:

# stage: adjust boot script or boot environment
echo "rootdev=$rootfs" >> $SDCARD/boot/orangepiEnv.txt
echo "rootfstype=$ROOTFS_TYPE" >> $SDCARD/boot/orangepiEnv.txt


# if we have a headless device, set console to DEFAULT_CONSOLE,设置默认控制台,这里并不会进入
if [[ -n $DEFAULT_CONSOLE && -f $SDCARD/boot/orangepiEnv.txt ]]; then
		if grep -lq "^console=" $SDCARD/boot/orangepiEnv.txt; then
				sed -i "s/^console=.*/console=$DEFAULT_CONSOLE/" $SDCARD/boot/orangepiEnv.txt
		else
				echo "console=$DEFAULT_CONSOLE" >> $SDCARD/boot/orangepiEnv.txt
		fi
fi

# recompile .cmd to .scr if boot.cmd exists,编译boot.cmd -> boot.scr 制作的镜像采用的就是这种启动方式
if [[ -f $SDCARD/boot/boot.cmd ]]; then
		if [ -z $BOOTSCRIPT_OUTPUT ]; then BOOTSCRIPT_OUTPUT=boot.scr; fi
		mkimage -C none -A arm -T script -d $SDCARD/boot/boot.cmd $SDCARD/boot/$BOOTSCRIPT_OUTPUT > /dev/null 2>&1
fi

# create extlinux config, 制作的镜像没有采用这种启动方式
if [[ -f $SDCARD/boot/extlinux/extlinux.conf ]]; then
		echo "  append root=$rootfs $SRC_CMDLINE $MAIN_CMDLINE" >> $SDCARD/boot/extlinux/extlinux.conf
		[[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt
fi

6.4 create_image

prepare_partitions函数主要就是进行一些初始化工作,比如创建空白镜像,写入分区表、格式化分区设备以创建文件系统,最后会挂载分区设备到$MOUNT目录。

create_image就可以看做是往各个分区挂载目录写入数据的过程,完整脚本如下:

点击查看代码
# create_image
#
# finishes creation of image from cached rootfs
#
create_image()
{
        # stage: create file name
        if [[ $SELECTED_CONFIGURATION == "cli_standard" ]]; then
                IMAGE_TYPE=server
        elif [[ $SELECTED_CONFIGURATION == "cli_minimal" ]]; then
                IMAGE_TYPE=minimal
        else
                IMAGE_TYPE=desktop
        fi

        if [[ ${MEM_TYPE} == "1500MB" ]]; then
                local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")_1.5gb"
        else
                local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"
        fi

        if [[ ${RELEASE} == "raspi" ]]; then
                local version="${BOARD^}_${REVISION}_raspios_bullseye_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"
        fi

        [[ $ROOTFS_TYPE == nfs ]] && version=${version}_nfsboot

        destimg=$DEST/images/${version}
        rm -rf $destimg
        mkdir -p $destimg

        if [[ $ROOTFS_TYPE != nfs ]]; then
                display_alert "Copying files to" "/"
                echo -e "\nCopying files to [/]" >>"${DEST}"/${LOG_SUBPATH}/install.log
                rsync -aHWXh \
                          --exclude="/boot/*" \
                          --exclude="/dev/*" \
                          --exclude="/proc/*" \
                          --exclude="/run/*" \
                          --exclude="/tmp/*" \
                          --exclude="/sys/*" \
                          --info=progress0,stats1 $SDCARD/ $MOUNT/ >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        else
                display_alert "Creating rootfs archive" "rootfs.tgz" "info"
                tar cp --xattrs --directory=$SDCARD/ --exclude='./boot/*' --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
                        --exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "rootfs.tgz" | gzip -c > $destimg/${version}-rootfs.tgz
        fi

        # stage: rsync /boot
        display_alert "Copying files to" "/boot"
        echo -e "\nCopying files to [/boot]" >>"${DEST}"/${LOG_SUBPATH}/install.log
        if [[ $(findmnt --target $MOUNT/boot -o FSTYPE -n) == vfat ]]; then
                # fat32
                rsync -rLtWh \
                          --info=progress0,stats1 \
                          --log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        else
                # ext4
                rsync -aHWXh \
                          --info=progress0,stats1 \
                          --log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
        fi

        call_extension_method "pre_update_initramfs" "config_pre_update_initramfs" << 'PRE_UPDATE_INITRAMFS'
*allow config to hack into the initramfs create process*
Called after rsync has synced both `/root` and `/root` on the target, but before calling `update_initramfs`.
PRE_UPDATE_INITRAMFS

        # stage: create final initramfs
        [[ -n $KERNELSOURCE ]] && {
                update_initramfs $MOUNT
        }

        # DEBUG: print free space
        local freespace=$(LC_ALL=C df -h)
        echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log
        display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"
        display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"

        # stage: write u-boot
        write_uboot $LOOP

        # fix wrong / permissions
        chmod 755 $MOUNT

        call_extension_method "pre_umount_final_image" "config_pre_umount_final_image" << 'PRE_UMOUNT_FINAL_IMAGE'
*allow config to hack into the image before the unmount*
Called before unmounting both `/root` and `/boot`.
PRE_UMOUNT_FINAL_IMAGE

        # unmount /boot/efi first, then /boot, rootfs third, image file last
        sync
        [[ $UEFISIZE != 0 ]] && umount -l "${MOUNT}${UEFI_MOUNT_POINT}"
        [[ $BOOTSIZE != 0 ]] && umount -l $MOUNT/boot
        [[ $ROOTFS_TYPE != nfs ]] && umount -l $MOUNT
        [[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose $ROOT_MAPPER

        call_extension_method "post_umount_final_image" "config_post_umount_final_image" << 'POST_UMOUNT_FINAL_IMAGE'
*allow config to hack into the image after the unmount*
Called after unmounting both `/root` and `/boot`.
POST_UMOUNT_FINAL_IMAGE

        # to make sure its unmounted
        while grep -Eq '(${MOUNT}|${DESTIMG})' /proc/mounts
        do
                display_alert "Wait for unmount" "${MOUNT}" "info"
                sleep 5
        done

        losetup -d $LOOP
        rm -rf --one-file-system $DESTIMG $MOUNT

        mkdir -p $DESTIMG
        mv ${SDCARD}.raw $DESTIMG/${version}.img

        FINALDEST=${destimg}

        # custom post_build_image_modify hook to run before fingerprinting and compression
        [[ $(type -t post_build_image_modify) == function ]] && display_alert "Custom Hook Detected" "post_build_image_modify" "info" && post_build_image_modify "${DESTIMG}/${version}.img"

        if [[ $BUILD_ALL != yes ]]; then

                if [[ $COMPRESS_OUTPUTIMAGE == "" || $COMPRESS_OUTPUTIMAGE == no ]]; then
                        COMPRESS_OUTPUTIMAGE="sha,gpg,img"
                elif [[ $COMPRESS_OUTPUTIMAGE == yes ]]; then
                        COMPRESS_OUTPUTIMAGE="sha,gpg,7z"
                fi

                if [[ $COMPRESS_OUTPUTIMAGE == *gz* ]]; then
                        display_alert "Compressing" "${DESTIMG}/${version}.img.gz" "info"
                        pigz -3 < $DESTIMG/${version}.img > $DESTIMG/${version}.img.gz
                        compression_type=".gz"
                fi

                if [[ $COMPRESS_OUTPUTIMAGE == *xz* ]]; then
                        display_alert "Compressing" "${DESTIMG}/${version}.img.xz" "info"
                        # compressing consumes a lot of memory we don't have. Waiting for previous packing job to finish helps to run a lot more builds in parallel
                        available_cpu=$(grep -c 'processor' /proc/cpuinfo)
                        [[ ${BUILD_ALL} == yes ]] && available_cpu=$(( $available_cpu * 30 / 100 )) # lets use 20% of resources in case of build-all
                        [[ ${available_cpu} -gt 8 ]] && available_cpu=8 # using more cpu cores for compressing is pointless
                        available_mem=$(LC_ALL=c free | grep Mem | awk '{print $4/$2 * 100.0}' | awk '{print int($1)}') # in percentage
                        # build optimisations when memory drops below 5%
                        if [[ ${BUILD_ALL} == yes && ( ${available_mem} -lt 15 || $(ps -uax | grep "pixz" | wc -l) -gt 4 )]]; then
                                while [[ $(ps -uax | grep "pixz" | wc -l) -gt 2 ]]
                                        do echo -en "#"
                                        sleep 20
                                done
                        fi
                        pixz -7 -p ${available_cpu} -f $(expr ${available_cpu} + 2) < $DESTIMG/${version}.img > ${DESTIMG}/${version}.img.xz
                        compression_type=".xz"
                fi

                if [[ $COMPRESS_OUTPUTIMAGE == *img* || $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
#                       mv $DESTIMG/${version}.img ${FINALDEST}/${version}.img || exit 1
                        compression_type=""
                fi

                if [[ $COMPRESS_OUTPUTIMAGE == *sha* ]]; then
                        cd ${DESTIMG}
                        display_alert "SHA256 calculating" "${version}.img${compression_type}" "info"
                        sha256sum -b ${version}.img${compression_type} > ${version}.img${compression_type}.sha
                fi

                if [[ $COMPRESS_OUTPUTIMAGE == *gpg* ]]; then
                        cd ${DESTIMG}
                        if [[ -n $GPG_PASS ]]; then
                                display_alert "GPG signing" "${version}.img${compression_type}" "info"
                                [[ -n ${SUDO_USER} ]] && sudo chown -R ${SUDO_USER}:${SUDO_USER} "${DESTIMG}"/
                                echo "${GPG_PASS}" | sudo -H -u ${SUDO_USER} bash -c "gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${DESTIMG}/${version}.img${compression_type}" || exit 1
                        #else
                        #       display_alert "GPG signing skipped - no GPG_PASS" "${version}.img" "wrn"
                        fi
                fi

                #fingerprint_image "${DESTIMG}/${version}.img${compression_type}.txt" "${version}"

                if [[ $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
                        display_alert "Compressing" "${DESTIMG}/${version}.7z" "info"
                        7za a -t7z -bd -m0=lzma2 -mx=3 -mfb=64 -md=32m -ms=on \
                        ${DESTIMG}/${version}.7z ${version}.key ${version}.img* >/dev/null 2>&1
                        find ${DESTIMG}/ -type \
                        f \( -name "${version}.img" -o -name "${version}.img.asc" -o -name "${version}.img.txt" -o -name "${version}.img.sha" \) -print0 \
                        >/dev/null 2>&1
                fi

        fi
        #display_alert "Done building" "${DESTIMG}/${version}.img" "info"
        display_alert "Done building" "${FINALDEST}/${version}.img" "info"

        # call custom post build hook
        [[ $(type -t post_build_image) == function ]] && post_build_image "${DESTIMG}/${version}.img"

        # move artefacts from temporally directory to its final destination
        [[ -n $compression_type ]] && rm $DESTIMG/${version}.img
        mv $DESTIMG/${version}* ${FINALDEST}
        rm -rf $DESTIMG

        # write image to SD card
        if [[ $(lsblk "$CARD_DEVICE" 2>/dev/null) && -f ${FINALDEST}/${version}.img ]]; then

                # make sha256sum if it does not exists. we need it for comparisson
                if [[ -f "${FINALDEST}/${version}".img.sha ]]; then
                        local ifsha=$(cat ${FINALDEST}/${version}.img.sha | awk '{print $1}')
                else
                        local ifsha=$(sha256sum -b "${FINALDEST}/${version}".img | awk '{print $1}')
                fi

                display_alert "Writing image" "$CARD_DEVICE ${readsha}" "info"

                # write to SD card
                pv -p -b -r -c -N "[ .... ] dd" ${FINALDEST}/${version}.img | dd of=$CARD_DEVICE bs=1M iflag=fullblock oflag=direct status=none

                call_extension_method "post_write_sdcard"  <<- 'POST_BUILD_IMAGE'
                *run after writing img to sdcard*
                After the image is written to `$CARD_DEVICE`, but before verifying it.
                You can still set SKIP_VERIFY=yes to skip verification.
                POST_BUILD_IMAGE

                if [[ "${SKIP_VERIFY}" != "yes" ]]; then
                        # read and compare
                        display_alert "Verifying. Please wait!"
                        local ofsha=$(dd if=$CARD_DEVICE count=$(du -b ${FINALDEST}/${version}.img | cut -f1) status=none iflag=count_bytes oflag=direct | sha256sum | awk '{print $1}')
                        if [[ $ifsha == $ofsha ]]; then
                                display_alert "Writing verified" "${version}.img" "info"
                        else
                                display_alert "Writing failed" "${version}.img" "err"
                        fi
                fi
        elif [[ `systemd-detect-virt` == 'docker' && -n $CARD_DEVICE ]]; then
                # display warning when we want to write sd card under Docker
                display_alert "Can't write to $CARD_DEVICE" "Enable docker privileged mode in config-docker.conf" "wrn"
        fi

} #############################################################################
6.4.1 准备工作

首先就是定义了一堆变量:

  • 设置IMAGE_TYPE=server
  • 设置version=Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
  • 设置destimg=<SDK>/output/images/Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5

这里我们移除一些无效脚本,简化后如下:

# stage: create file name
if [[ $SELECTED_CONFIGURATION == "cli_standard" ]]; then  # 对于我制作的镜像,走这里
		IMAGE_TYPE=server
elif [[ $SELECTED_CONFIGURATION == "cli_minimal" ]]; then
		IMAGE_TYPE=minimal
else
		IMAGE_TYPE=desktop
fi

# Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"

# 创建存放linux镜像的目录
destimg=$DEST/images/${version}
rm -rf $destimg
mkdir -p $destimg
6.4.2 写入bootfs/rootfs分区数据

接着是向rootfsbootfs分区写入数据;

  • rootfs分区(挂载点为$MOUNT):从 $SDCARD/复制文件到$MOUNT/,排除系统目录(/boot/dev/proc/run/tmp/sys);
  • bootfs分区(挂载点为$MOUNT/boot):从 $SDCARD/boot复制文件到$MOUNT目录;

这里我们移除一些无效脚本,简化后如下:

# 向rootfs分区写入数据
display_alert "Copying files to" "/"
echo -e "\nCopying files to [/]" >>"${DEST}"/${LOG_SUBPATH}/install.log
rsync -aHWXh \
		  --exclude="/boot/*" \
		  --exclude="/dev/*" \
		  --exclude="/proc/*" \
		  --exclude="/run/*" \
		  --exclude="/tmp/*" \
		  --exclude="/sys/*" \
		  --info=progress0,stats1 $SDCARD/ $MOUNT/ >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

# bootfs分区写入数据
display_alert "Copying files to" "/boot"
echo -e "\nCopying files to [/boot]" >>"${DEST}"/${LOG_SUBPATH}/install.log
# fat32
rsync -rLtWh \
		  --info=progress0,stats1 \
		  --log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 

这里我们说一下rsync命令的参数:

  • -a, --archive:归档模式,表示以递归方式传输文件,并保持所有文件属性,等于-rlptgoD

  • -H, --hard-links: 保留硬链接;

  • -l, --links:保留软链接;

  • -L, --copy-links :向对待常规文件一样处理软链接;

  • -W, --whole-file:拷贝文件,不进行增量检测;

  • -t, --times:保持文件时间信息;

  • -X,--xattrspreserve extended attributes

  • -h,--human-readableoutput numbers in a human-readable format

  • --log-file=FILElog what we're doing to the specified FILE

  • --info=FLAGS fine-grained informational verbosity

  • --exclude=PATTERN:指定排除不需要传输的文件模式;

6.4.3 update_initramfs

接着就是更新initramfs

call_extension_method "pre_update_initramfs" "config_pre_update_initramfs" << 'PRE_UPDATE_INITRAMFS'
*allow config to hack into the initramfs create process*
Called after rsync has synced both `/root` and `/root` on the target, but before calling `update_initramfs`.
PRE_UPDATE_INITRAMFS

# stage: create final initramfs
[[ -n $KERNELSOURCE ]] && {
		update_initramfs $MOUNT
}

# DEBUG: print free space
local freespace=$(LC_ALL=C df -h)
echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log
display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"
display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"
6.4.4 写入idbloader.img/u-boot.itb

接着通过write_uboot向镜像文件指定扇区写入idbloader.imgu-boot.itb

  • 解压<SDK>/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb
  • 通过dd命令将idbloader.img写入到0x40扇区处;
  • 通过dd命令将u-boot.itb写入到0x4000扇区处。

脚本如下:

# stage: write u-boot
write_uboot $LOOP

write_uboot 定义在scripts/image-helpers.sh

write_uboot()
{
        local loop=$1 revision
        display_alert "Writing U-boot bootloader" "$loop" "info"
        TEMP_DIR=$(mktemp -d || exit 1)
        chmod 700 ${TEMP_DIR}
        revision=${REVISION}
        if [[ -n $UPSTREM_VER ]]; then
                revision=${UPSTREM_VER}
                dpkg -x "${DEB_STORAGE}/u-boot/linux-u-boot-${BOARD}-${BRANCH}_${revision}_${ARCH}.deb" ${TEMP_DIR}/
        else
        		# 解压<SDK>/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb到临时目录
                dpkg -x "${DEB_STORAGE}/u-boot/${CHOSEN_UBOOT}_${revision}_${ARCH}.deb" ${TEMP_DIR}/
        fi

        # source platform install to read $DIR,执行platform_install.sh通过dd命令实现u-boot的安装功能
        source ${TEMP_DIR}/usr/lib/u-boot/platform_install.sh
        write_uboot_platform "${TEMP_DIR}${DIR}" "$loop"   # DIR=/usr/lib/linux-u-boot-current-orangepi3b_1.0.6_arm64
        [[ $? -ne 0 ]] && exit_with_error "U-boot bootloader failed to install" "@host"
        rm -rf ${TEMP_DIR}
}
6.4.5 卸载即清理工作

接着执行如下操作:

  • 修正权限:将$MOUNT目录的权限设置为755
  • 执行卸载操作操作:sync确保所有待处理的数据写入到对应分区,然后卸载卸载$MOUNT/boot$MOUNT
  • 确保卸载完成:循环检查/proc/mounts文件,查看$MOUNT$DESTIMG是否仍在挂载中;
  • 清理操作: 解除循环设备$LOOP的绑定, 递归删除$DESTIMG$MOUNT目录;
  • 生成Linux镜像文件,位于目录$DESTIMGDESTIMG被设置为<SDK>/.tmp/image-${MOUNT_UUID},镜像文件名为Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5.img

移除一些无效代码简化如下:

# fix wrong / permissions
chmod 755 $MOUNT

# unmount /boot, rootfs third, image file last
sync
[[ $BOOTSIZE != 0 ]] && umount -l $MOUNT/boot
[[ $ROOTFS_TYPE != nfs ]] && umount -l $MOUNT

# to make sure its unmounted
while grep -Eq '(${MOUNT}|${DESTIMG})' /proc/mounts
do
		display_alert "Wait for unmount" "${MOUNT}" "info"
		sleep 5
done

losetup -d $LOOP
rm -rf --one-file-system $DESTIMG $MOUNT

mkdir -p $DESTIMG
mv ${SDCARD}.raw $DESTIMG/${version}.img

# FINALDEST=<SDK>/output/images/Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
FINALDEST=${destimg}
6.4.6 post_build_image_modify

post_build_image_modify相当于给用户开放了一个钩子,使得用户可以修改linux镜像文件:

# custom post_build_image_modify hook to run before fingerprinting and compression
[[ $(type -t post_build_image_modify) == function ]] && display_alert "Custom Hook Detected" "post_build_image_modify" "info" && post_build_image_modify "${DESTIMG}/${version}.img"

我们可以创建<SDK>/userpatches/customize-image-host.sh文件,并提供自定义的post_build_image_modify函数来修改linux镜像文件。

6.4.7 生成Linux镜像文件

接着对<SDK>/.tmp/image-${MOUNT_UUID}目录下的Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5.img文件进行校验,并将镜像文件拷贝到<SDK>/output/images/Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5

if [[ $BUILD_ALL != yes ]]; then
		COMPRESS_OUTPUTIMAGE="sha,gpg,img"
		if [[ $COMPRESS_OUTPUTIMAGE == *img* || $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
				compression_type=""
		fi
		if [[ $COMPRESS_OUTPUTIMAGE == *sha* ]]; then
				cd ${DESTIMG}
				display_alert "SHA256 calculating" "${version}.img${compression_type}" "info"
				sha256sum -b ${version}.img${compression_type} > ${version}.img${compression_type}.sha
		fi
fi

display_alert "Done building" "${FINALDEST}/${version}.img" "info"

# call custom post build hook
[[ $(type -t post_build_image) == function ]] && post_build_image "${DESTIMG}/${version}.img"

# move artefacts from temporally directory to its final destination,将Linux镜像拷贝到目标路径${FINALDEST}
[[ -n $compression_type ]] && rm $DESTIMG/${version}.img
mv $DESTIMG/${version}* ${FINALDEST}
rm -rf $DESTIMG

这里有一处值得留意,就是post_build_image,其目的和post_build_image_modify一样。同样我们可以创建<SDK>/userpatches/customize-image-host.sh文件,并提供自定义的post_build_image函数来修改linux镜像文件。

七、配置文件

在编译过程中使用到了若干配置文件,这些配置文件决定了编译的走向,因此来说他们是至关重要的。

7.1 config-default.conf

位于<SDK>/userpatches目录,config-default.conf是一个比较重要的文件,保存用户配置,可以用来配置开发板型号、内核版本、Linux发行版类型等。

这里面定义的变量会被编译脚本使用到,从而决定了编译的流程;

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"

比如:

  • IGNORE_UPDATES:是否关闭源码同步功能,即在编译的过程中不会将github服务器的kernel源码同步到本地;
  • BOARD:可以用来配置选择的开发板型号,比如orangepi3b
  • BRANCH:可以用来配置选择的内核版本,比如current
  • RELEASE:可以用来配置选择Linux发行版的类型,比如bullseye
  • BUILD_KSRC:可以用来配置是否关闭内核构建。

7.2 orangepi3b.conf

<SDK>/external/config/boards目录下存放存放各种型号开发板所使用的配置。

当我们配置了BOARD=orangepi3b,就会使用orangepi3b.conf配置文件;

# Rockchip RK3566 hexa core 4GB RAM SoC GBE eMMC USB3 USB-C WiFi/BT
BOARD_NAME="OPI 3B"
BOARDFAMILY="rockchip-rk356x"
BOOTCONFIG="orangepi-3b-rk3566_defconfig"
KERNEL_TARGET="legacy,current"
BOOT_LOGO="desktop"
BOOT_SUPPORT_SPI="yes"
DISTRIB_TYPE_LEGACY="focal jammy bullseye bookworm raspi"
BOOTFS_TYPE="fat"
IMAGE_PARTITION_TABLE="gpt"
REVISION="1.0.6"

定义了BOARD_NAMEBOARDFAMILYBOOTCONFIGKERNEL_TARGETBOOTFS_TYPEIMAGE_PARTITION_TABLE等;

7.3 configuration.sh

位于scripts目录下,main.sh执行过程中会使用source命令执行configuration.sh脚本;

#shellcheck source=configuration.sh
source "${SRC}"/scripts/configuration.sh

这个脚本中同样定义了一大堆的配置项,同时在这个文件中使用source命令执行若干脚本;

  • ${EXTER}/config/sources/families/${LINUXFAMILY}.conf:即<SDK>/external/config/sources/families/rockchip-rk356x.conf,该文件配置了rk356x型号SoC所使用的配置;
  • ${EXTER}/config/sources/${ARCH}.conf:即<SDK>/external/config/sources/arm64.conf,该文件配置了ARM64架构使用的配置;

具体如下;

点击查看代码
#!/bin/bash
#
# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.


[[ -z $VENDOR ]] && VENDOR="Orange Pi"
[[ -z $ROOTPWD ]] && ROOTPWD="orangepi" # Must be changed @first login
[[ -z $OPI_USERNAME ]] && OPI_USERNAME="orangepi"
[[ -z $OPI_PWD ]] && OPI_PWD="orangepi"
[[ -z $MAINTAINER ]] && MAINTAINER="Orange Pi" # deb signature
[[ -z $MAINTAINERMAIL ]] && MAINTAINERMAIL="leeboby@aliyun.com" # deb signature
[[ -z $DEB_COMPRESS ]] && DEB_COMPRESS="xz" # compress .debs with XZ by default. Use 'none' for faster/larger builds
TZDATA=$(cat /etc/timezone) # Timezone for target is taken from host or defined here.
USEALLCORES=yes # Use all CPU cores for compiling
HOSTRELEASE=$(cat /etc/os-release | grep VERSION_CODENAME | cut -d"=" -f2)
[[ -z $HOSTRELEASE ]] && HOSTRELEASE=$(cut -d'/' -f1 /etc/debian_version)
[[ -z $EXIT_PATCHING_ERROR ]] && EXIT_PATCHING_ERROR="" # exit patching if failed
[[ -z $HOST ]] && HOST="$BOARD" # set hostname to the board
[[ -z $CHINA_DOWNLOAD_MIRROR ]] && CHINA_DOWNLOAD_MIRROR=huawei
cd "${SRC}" || exit
[[ -z "${ROOTFSCACHE_VERSION}" ]] && ROOTFSCACHE_VERSION=11
[[ -z "${CHROOT_CACHE_VERSION}" ]] && CHROOT_CACHE_VERSION=7
[[ -z $PLYMOUTH ]] && PLYMOUTH="yes"

cd ${SRC}/scripts
BUILD_REPOSITORY_URL=$(improved_git remote get-url $(improved_git remote 2>/dev/null | grep origin) 2>/dev/null)
BUILD_REPOSITORY_COMMIT=$(improved_git describe --match=d_e_a_d_b_e_e_f --always --dirty 2>/dev/null)
ROOTFS_CACHE_MAX=200 # max number of rootfs cache, older ones will be cleaned up

DEB_STORAGE=$DEST/debs
DEB_ORANGEPI=$EXTER/cache/debs

# TODO: fixed name can't be used for parallel image building
ROOT_MAPPER="orangepi-root"
[[ -z $ROOTFS_TYPE ]] && ROOTFS_TYPE=ext4 # default rootfs type is ext4
[[ "ext4 f2fs btrfs xfs nfs fel" != *$ROOTFS_TYPE* ]] && exit_with_error "Unknown rootfs type" "$ROOTFS_TYPE"

[[ -z $BTRFS_COMPRESSION ]] && BTRFS_COMPRESSION=zlib # default btrfs filesystem compression method is zlib
[[ ! $BTRFS_COMPRESSION =~ zlib|lzo|zstd|none ]] && exit_with_error "Unknown btrfs compression method" "$BTRFS_COMPRESSION"

# Fixed image size is in 1M dd blocks (MiB)
# to get size of block device /dev/sdX execute as root:
# echo $(( $(blockdev --getsize64 /dev/sdX) / 1024 / 1024 ))
[[ "f2fs" == *$ROOTFS_TYPE* && -z $FIXED_IMAGE_SIZE ]] && exit_with_error "Please define FIXED_IMAGE_SIZE"

# a passphrase is mandatory if rootfs encryption is enabled
if [[ $CRYPTROOT_ENABLE == yes && -z $CRYPTROOT_PASSPHRASE ]]; then
        exit_with_error "Root encryption is enabled but CRYPTROOT_PASSPHRASE is not set"
fi

# small SD card with kernel, boot script and .dtb/.bin files
[[ $ROOTFS_TYPE == nfs ]] && FIXED_IMAGE_SIZE=64

# Since we are having too many options for mirror management,
# then here is yet another mirror related option.
# Respecting user's override in case a mirror is unreachable.
case $REGIONAL_MIRROR in
        china)
                [[ -z $USE_MAINLINE_GOOGLE_MIRROR ]] && [[ -z $MAINLINE_MIRROR ]] && MAINLINE_MIRROR=tuna
                [[ -z $USE_GITHUB_UBOOT_MIRROR ]] && [[ -z $UBOOT_MIRROR ]] && UBOOT_MIRROR=gitee
                [[ -z $GITHUB_MIRROR ]] && GITHUB_MIRROR=gitclone
                [[ -z $DOWNLOAD_MIRROR ]] && DOWNLOAD_MIRROR=china
                ;;
        *)
                ;;
esac

# used by multiple sources - reduce code duplication
[[ $USE_MAINLINE_GOOGLE_MIRROR == yes ]] && MAINLINE_MIRROR=google

case $MAINLINE_MIRROR in
        google)
                MAINLINE_KERNEL_SOURCE='https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable'
                MAINLINE_FIRMWARE_SOURCE='https://kernel.googlesource.com/pub/scm/linux/kernel/git/firmware/linux-firmware.git'
                ;;
        tuna)
                MAINLINE_KERNEL_SOURCE='https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git'
                MAINLINE_FIRMWARE_SOURCE='https://mirrors.tuna.tsinghua.edu.cn/git/linux-firmware.git'
                ;;
        bfsu)
                MAINLINE_KERNEL_SOURCE='https://mirrors.bfsu.edu.cn/git/linux-stable.git'
                MAINLINE_FIRMWARE_SOURCE='https://mirrors.bfsu.edu.cn/git/linux-firmware.git'
                ;;
        *)
                MAINLINE_KERNEL_SOURCE='git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git'
                MAINLINE_FIRMWARE_SOURCE='git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git'
                ;;
esac

MAINLINE_KERNEL_DIR="$SRC/kernel"

[[ $USE_GITHUB_UBOOT_MIRROR == yes ]] && UBOOT_MIRROR=github

case $UBOOT_MIRROR in
        gitee)
                MAINLINE_UBOOT_SOURCE='https://github.com/orangepi-xunlong/u-boot-orangepi.git'
                ;;
        github)
                MAINLINE_UBOOT_SOURCE='https://github.com/orangepi-xunlong/u-boot-orangepi.git'
                ;;
        *)
                MAINLINE_UBOOT_SOURCE='https://source.denx.de/u-boot/u-boot.git'
                ;;
esac

MAINLINE_UBOOT_DIR="$SRC/u-boot"

case $GITHUB_MIRROR in
        fastgit)
                GITHUB_SOURCE='https://hub.fastgit.xyz'
                ;;
        gitclone)
                GITHUB_SOURCE='https://gitclone.com/github.com'
                ;;
        *)
                GITHUB_SOURCE='https://github.com'
                ;;
esac

# Let's set default data if not defined in board configuration above
[[ -z $OFFSET ]] && OFFSET=4 # offset to 1st partition (we use 4MiB boundaries by default)
ARCH=armhf
KERNEL_IMAGE_TYPE=zImage
CAN_BUILD_STRETCH=yes
ATF_COMPILE=yes
[[ -z $CRYPTROOT_SSH_UNLOCK ]] && CRYPTROOT_SSH_UNLOCK=yes
[[ -z $CRYPTROOT_SSH_UNLOCK_PORT ]] && CRYPTROOT_SSH_UNLOCK_PORT=2022
# Default to pdkdf2, this used to be the default with cryptroot <= 2.0, however
# cryptroot 2.1 changed that to Argon2i. Argon2i is a memory intensive
# algorithm which doesn't play well with SBCs (need 1GiB RAM by default !)
# https://gitlab.com/cryptsetup/cryptsetup/-/issues/372
[[ -z $CRYPTROOT_PARAMETERS ]] && CRYPTROOT_PARAMETERS="--pbkdf pbkdf2"
[[ -z $WIREGUARD ]] && WIREGUARD="no"
[[ -z $EXTRAWIFI ]] && EXTRAWIFI="yes"
[[ -z $SKIP_BOOTSPLASH ]] && SKIP_BOOTSPLASH="no"
[[ -z $AUFS ]] && AUFS="yes"
[[ -z $IMAGE_PARTITION_TABLE ]] && IMAGE_PARTITION_TABLE="msdos"
[[ -z $EXTRA_BSP_NAME ]] && EXTRA_BSP_NAME=""
[[ -z $EXTRA_ROOTFS_MIB_SIZE ]] && EXTRA_ROOTFS_MIB_SIZE=0
[[ -z $BUILD_KSRC ]] && BUILD_KSRC="no"

# single ext4 partition is the default and preferred configuration
#BOOTFS_TYPE=''
[[ ! -f ${EXTER}/config/sources/families/$LINUXFAMILY.conf ]] && \
        exit_with_error "Sources configuration not found" "$LINUXFAMILY"

source "${EXTER}/config/sources/families/${LINUXFAMILY}.conf"

if [[ -f $USERPATCHES_PATH/sources/families/$LINUXFAMILY.conf ]]; then
        display_alert "Adding user provided $LINUXFAMILY overrides"
        source "$USERPATCHES_PATH/sources/families/${LINUXFAMILY}.conf"
fi

# load architecture defaults
source "${EXTER}/config/sources/${ARCH}.conf"

## Extensions: at this point we've sourced all the config files that will be used,
##             and (hopefully) not yet invoked any extension methods. So this is the perfect
##             place to initialize the extension manager. It will create functions
##             like the 'post_family_config' that is invoked below.
initialize_extension_manager

call_extension_method "post_family_config" "config_tweaks_post_family_config" << 'POST_FAMILY_CONFIG'
*give the config a chance to override the family/arch defaults*
This hook is called after the family configuration (`sources/families/xxx.conf`) is sourced.
Since the family can override values from the user configuration and the board configuration,
it is often used to in turn override those.
POST_FAMILY_CONFIG

# Myy : Menu configuration for choosing desktop configurations

show_menu() {
        provided_title=$1
        provided_backtitle=$2
        provided_menuname=$3
        # Myy : I don't know why there's a TTY_Y - 8...
        #echo "Provided title : $provided_title"
        #echo "Provided backtitle : $provided_backtitle"
        #echo "Provided menuname : $provided_menuname"
        #echo "Provided options : " "${@:4}"
        #echo "TTY X: $TTY_X Y: $TTY_Y"
        whiptail --title "${provided_title}" --backtitle "${provided_backtitle}" --notags \
                          --menu "${provided_menuname}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          "${@:4}" \
                          3>&1 1>&2 2>&3
}

# Myy : FIXME Factorize
show_select_menu() {
        provided_title=$1
        provided_backtitle=$2
        provided_menuname=$3
        #dialog --stdout --title "${provided_title}" --backtitle "${provided_backtitle}" \
        #--checklist "${provided_menuname}" $TTY_Y $TTY_X $((TTY_Y - 8)) "${@:4}"

        #whiptail --separate-output --title "${provided_title}" --backtitle "${provided_backtitle}" \
        #                  --checklist "${provided_menuname}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
        #                 "${@:4}" \
        #                 3>&1 1>&2 2>&3

        whiptail --title "${provided_title}" --backtitle "${provided_backtitle}" \
                          --checklist "${provided_menuname}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8))  \
                          "${@:4}" \
                          3>&1 1>&2 2>&3
}

# Myy : Once we got a list of selected groups, parse the PACKAGE_LIST inside configuration.sh

DESKTOP_ELEMENTS_DIR="${EXTER}/config/desktop/${RELEASE}"
DESKTOP_CONFIGS_DIR="${DESKTOP_ELEMENTS_DIR}/environments"
DESKTOP_CONFIG_PREFIX="config_"
DESKTOP_APPGROUPS_DIR="${DESKTOP_ELEMENTS_DIR}/appgroups"

desktop_element_available_for_arch() {
        local desktop_element_path="${1}"
        local targeted_arch="${2}"

        local arch_limitation_file="${1}/only_for"

        echo "Checking if ${desktop_element_path} is available for ${targeted_arch} in ${arch_limitation_file}" >> "${DEST}"/${LOG_SUBPATH}/output.log
        if [[ -f "${arch_limitation_file}" ]]; then
                grep -- "${targeted_arch}" "${arch_limitation_file}" > /dev/null
                return $?
        else
                return 0
        fi
}

desktop_element_supported() {

        local desktop_element_path="${1}"

        local support_level_filepath="${desktop_element_path}/support"
        if [[ -f "${support_level_filepath}" ]]; then
                local support_level="$(cat "${support_level_filepath}")"
                if [[ "${support_level}" != "supported" && "${EXPERT}" != "yes" ]]; then
                        return 65
                fi

                desktop_element_available_for_arch "${desktop_element_path}" "${ARCH}"
                if [[ $? -ne 0 ]]; then
                        return 66
                fi
        else
                return 64
        fi

        return 0

}

if [[ $BUILD_DESKTOP == "yes" && -z $DESKTOP_ENVIRONMENT ]]; then

        desktop_environments_prepare_menu() {
                for desktop_env_dir in "${DESKTOP_CONFIGS_DIR}/"*; do
                        local desktop_env_name=$(basename ${desktop_env_dir})
                        local expert_infos=""
                        [[ "${EXPERT}" == "yes" ]] && expert_infos="[$(cat "${desktop_env_dir}/support" 2> /dev/null)]"
                        desktop_element_supported "${desktop_env_dir}" "${ARCH}" && options+=("${desktop_env_name}" "${desktop_env_name^} desktop environment ${expert_infos}")
                done
        }

        options=()
        desktop_environments_prepare_menu

        if [[ "${options[0]}" == "" ]]; then
                exit_with_error "No desktop environment seems to be available for your board ${BOARD} (ARCH : ${ARCH} - EXPERT : ${EXPERT})"
        fi

        DESKTOP_ENVIRONMENT=$(show_menu "Choose a desktop environment" "$backtitle" "Select the default desktop environment to bundle with this image" "${options[@]}")

        unset options

        if [[ -z "${DESKTOP_ENVIRONMENT}" ]]; then
                exit_with_error "No desktop environment selected..."
        fi

fi

if [[ $BUILD_DESKTOP == "yes" ]]; then
        # Expected environment variables :
        # - options
        # - ARCH

        desktop_environment_check_if_valid() {

                local error_msg=""
                desktop_element_supported "${DESKTOP_ENVIRONMENT_DIRPATH}" "${ARCH}"
                local retval=$?

                if [[ ${retval} == 0 ]]; then
                        return
                elif [[ ${retval} == 64 ]]; then
                        error_msg+="Either the desktop environment ${DESKTOP_ENVIRONMENT} does not exist "
                        error_msg+="or the file ${DESKTOP_ENVIRONMENT_DIRPATH}/support is missing"
                elif [[ ${retval} == 65 ]]; then
                        error_msg+="Only experts can build an image with the desktop environment \"${DESKTOP_ENVIRONMENT}\", since the Armbian team won't offer any support for it (EXPERT=${EXPERT})"
                elif [[ ${retval} == 66 ]]; then
                        error_msg+="The desktop environment \"${DESKTOP_ENVIRONMENT}\" has no packages for your targeted board architecture (BOARD=${BOARD} ARCH=${ARCH}). "
                        error_msg+="The supported boards architectures are : "
                        error_msg+="$(cat "${DESKTOP_ENVIRONMENT_DIRPATH}/only_for")"
                fi

                # supress error when cache is rebuilding
                [[ -n "$ROOT_FS_CREATE_ONLY" ]] && exit 0

                exit_with_error "${error_msg}"
        }

        DESKTOP_ENVIRONMENT_DIRPATH="${DESKTOP_CONFIGS_DIR}/${DESKTOP_ENVIRONMENT}"

        desktop_environment_check_if_valid
fi

if [[ $BUILD_DESKTOP == "yes" && -z $DESKTOP_ENVIRONMENT_CONFIG_NAME ]]; then
        # FIXME Check for empty folders, just in case the current maintainer
        # messed up
        # Note, we could also ignore it and don't show anything in the previous
        # menu, but that hides information and make debugging harder, which I
        # don't like. Adding desktop environments as a maintainer is not a
        # trivial nor common task.

        options=()
        for configuration in "${DESKTOP_ENVIRONMENT_DIRPATH}/${DESKTOP_CONFIG_PREFIX}"*; do
                config_filename=$(basename ${configuration})
                config_name=${config_filename#"${DESKTOP_CONFIG_PREFIX}"}
                options+=("${config_filename}" "${config_name} configuration")
        done

        DESKTOP_ENVIRONMENT_CONFIG_NAME=$(show_menu "Choose the desktop environment config" "$backtitle" "Select the configuration for this environment.\nThese are sourced from ${desktop_environment_config_dir}" "${options[@]}")
        unset options

        if [[ -z $DESKTOP_ENVIRONMENT_CONFIG_NAME ]]; then
                exit_with_error "No desktop configuration selected... Do you really want a desktop environment ?"
        fi
fi

if [[ $BUILD_DESKTOP == "yes" ]]; then
        DESKTOP_ENVIRONMENT_PACKAGE_LIST_DIRPATH="${DESKTOP_ENVIRONMENT_DIRPATH}/${DESKTOP_ENVIRONMENT_CONFIG_NAME}"
        DESKTOP_ENVIRONMENT_PACKAGE_LIST_FILEPATH="${DESKTOP_ENVIRONMENT_PACKAGE_LIST_DIRPATH}/packages"
fi

# "-z ${VAR+x}" allows to check for unset variable
# Technically, someone might want to build a desktop with no additional
# appgroups.
if [[ $BUILD_DESKTOP == "yes" && -z ${DESKTOP_APPGROUPS_SELECTED+x} && ${RELEASE} != "raspi" ]]; then

        options=()
        for appgroup_path in "${DESKTOP_APPGROUPS_DIR}/"*; do
                appgroup="$(basename "${appgroup_path}")"
                options+=("${appgroup}" "${appgroup^}" off)
        done

        DESKTOP_APPGROUPS_SELECTED=$(\
                show_select_menu \
                "Choose desktop softwares to add" \
                "$backtitle" \
                "Select which kind of softwares you'd like to add to your build" \
                "${options[@]}")

        DESKTOP_APPGROUPS_SELECTED=${DESKTOP_APPGROUPS_SELECTED//\"/}

        unset options
fi

#exit_with_error 'Testing'

# Expected variables
# - aggregated_content
# - potential_paths
# - separator
# Write to variables :
# - aggregated_content
aggregate_content() {
        LOG_OUTPUT_FILE="${SRC}/output/${LOG_SUBPATH}/potential-paths.log"
        echo -e "Potential paths :" >> "${LOG_OUTPUT_FILE}"
        show_checklist_variables potential_paths
        for filepath in ${potential_paths}; do
                if [[ -f "${filepath}" ]]; then
                        echo -e "${filepath/"$EXTER"\//} yes" >> "${LOG_OUTPUT_FILE}"
                        aggregated_content+=$(cat "${filepath}")
                        aggregated_content+="${separator}"
#               else
#                       echo -e "${filepath/"$EXTER"\//} no\n" >> "${LOG_OUTPUT_FILE}"
                fi

        done
        echo "" >> "${LOG_OUTPUT_FILE}"
        unset LOG_OUTPUT_FILE
}

# set unique mounting directory
MOUNT_UUID=$(uuidgen)
SDCARD="${SRC}/.tmp/rootfs-${MOUNT_UUID}"
MOUNT="${SRC}/.tmp/mount-${MOUNT_UUID}"
DESTIMG="${SRC}/.tmp/image-${MOUNT_UUID}"

# dropbear needs to be configured differently
[[ $CRYPTROOT_ENABLE == yes && $RELEASE == xenial ]] && exit_with_error "Encrypted rootfs is not supported in Xenial"
[[ $RELEASE == stretch && $CAN_BUILD_STRETCH != yes ]] && exit_with_error "Building Debian Stretch images with selected kernel is not supported"
[[ $RELEASE == bionic && $CAN_BUILD_STRETCH != yes ]] && exit_with_error "Building Ubuntu Bionic images with selected kernel is not supported"
[[ $RELEASE == hirsute && $HOSTRELEASE == focal ]] && exit_with_error "Building Ubuntu Hirsute images requires Hirsute build host. Please upgrade your host or select a different target OS"

[[ -n $ATFSOURCE && -z $ATF_USE_GCC ]] && exit_with_error "Error in configuration: ATF_USE_GCC is unset"
[[ -z $UBOOT_USE_GCC ]] && exit_with_error "Error in configuration: UBOOT_USE_GCC is unset"
[[ -z $KERNEL_USE_GCC ]] && exit_with_error "Error in configuration: KERNEL_USE_GCC is unset"

BOOTCONFIG_VAR_NAME=BOOTCONFIG_${BRANCH^^}
[[ -n ${!BOOTCONFIG_VAR_NAME} ]] && BOOTCONFIG=${!BOOTCONFIG_VAR_NAME}
[[ -z $LINUXCONFIG ]] && LINUXCONFIG="linux-${LINUXFAMILY}-${BRANCH}"
[[ -z $BOOTPATCHDIR ]] && BOOTPATCHDIR="u-boot-$LINUXFAMILY"
[[ -z $ATFPATCHDIR ]] && ATFPATCHDIR="atf-$LINUXFAMILY"
[[ -z $KERNELPATCHDIR ]] && KERNELPATCHDIR="$LINUXFAMILY-$BRANCH"

if [[ "$RELEASE" =~ ^(xenial|bionic|focal|hirsute|impish|jammy)$ ]]; then
                DISTRIBUTION="Ubuntu"
elif [[ "$RELEASE" == raspi ]]; then
                DISTRIBUTION="Bullseye"
        else
                DISTRIBUTION="Debian"
fi

CLI_CONFIG_PATH="${EXTER}/config/cli/${RELEASE}"
DEBOOTSTRAP_CONFIG_PATH="${CLI_CONFIG_PATH}/debootstrap"

if [[ $? != 0 ]]; then
        exit_with_error "The desktop environment ${DESKTOP_ENVIRONMENT} is not available for your architecture ${ARCH}"
fi

AGGREGATION_SEARCH_ROOT_ABSOLUTE_DIRS="
${EXTER}/config
${EXTER}/config/optional/_any_board/_config
${EXTER}/config/optional/architectures/${ARCH}/_config
${EXTER}/config/optional/families/${LINUXFAMILY}/_config
${EXTER}/config/optional/boards/${BOARD}/_config
${USERPATCHES_PATH}
"

DEBOOTSTRAP_SEARCH_RELATIVE_DIRS="
cli/_all_distributions/debootstrap
cli/${RELEASE}/debootstrap
"

CLI_SEARCH_RELATIVE_DIRS="
cli/_all_distributions/main
cli/${RELEASE}/main
"

PACKAGES_SEARCH_ROOT_ABSOLUTE_DIRS="
${EXTER}/packages
${EXTER}/config/optional/_any_board/_packages
${EXTER}/config/optional/architectures/${ARCH}/_packages
${EXTER}/config/optional/families/${LINUXFAMILY}/_packages
${EXTER}/config/optional/boards/${BOARD}/_packages
"

DESKTOP_ENVIRONMENTS_SEARCH_RELATIVE_DIRS="
desktop/_all_distributions/environments/_all_environments
desktop/_all_distributions/environments/${DESKTOP_ENVIRONMENT}
desktop/_all_distributions/environments/${DESKTOP_ENVIRONMENT}/${DESKTOP_ENVIRONMENT_CONFIG_NAME}
desktop/${RELEASE}/environments/_all_environments
desktop/${RELEASE}/environments/${DESKTOP_ENVIRONMENT}
desktop/${RELEASE}/environments/${DESKTOP_ENVIRONMENT}/${DESKTOP_ENVIRONMENT_CONFIG_NAME}
"

DESKTOP_APPGROUPS_SEARCH_RELATIVE_DIRS="
desktop/_all_distributions/appgroups
desktop/_all_distributions/environments/${DESKTOP_ENVIRONMENT}/appgroups
desktop/${RELEASE}/appgroups
desktop/${RELEASE}/environments/${DESKTOP_ENVIRONMENT}/appgroups
"

get_all_potential_paths() {
        local root_dirs="${AGGREGATION_SEARCH_ROOT_ABSOLUTE_DIRS}"
        local rel_dirs="${1}"
        local sub_dirs="${2}"
        local looked_up_subpath="${3}"
        for root_dir in ${root_dirs}; do
                for rel_dir in ${rel_dirs}; do
                        for sub_dir in ${sub_dirs}; do
                                potential_paths+="${root_dir}/${rel_dir}/${sub_dir}/${looked_up_subpath} "
                        done
                done
        done
        # for ppath in ${potential_paths}; do
        #       echo "Checking for ${ppath}"
        #       if [[ -f "${ppath}" ]]; then
        #               echo "OK !|"
        #       else
        #               echo "Nope|"
        #       fi
        # done
}

# Environment variables expected :
# - aggregated_content
# Arguments :
# 1. File to look up in each directory
# 2. The separator to add between each concatenated file
# 3. Relative directories paths added to ${3}
# 2. Relative directories paths added to ${4}
#
# The function will basically generate a list of potential paths by
# generating all the potential paths combinations leading to the
# looked up file
# ${AGGREGATION_SEARCH_ROOT_ABSOLUTE_DIRS}/${3}/${4}/${1}
# Then it will concatenate the content of all the available files
# into ${aggregated_content}
#
# TODO :
# ${4} could be removed by just adding the appropriate paths to ${3}
# dynamically for each case
# (debootstrap, cli, desktop environments, desktop appgroups, ...)

aggregate_all_root_rel_sub() {
        local separator="${2}"

        local potential_paths=""
        get_all_potential_paths "${3}" "${4}" "${1}"

        aggregate_content
}

aggregate_all_debootstrap() {
        local sub_dirs_to_check=". "
        if [[ ! -z "${SELECTED_CONFIGURATION+x}" ]]; then
                sub_dirs_to_check+="config_${SELECTED_CONFIGURATION}"
        fi
        aggregate_all_root_rel_sub "${1}" "${2}" "${DEBOOTSTRAP_SEARCH_RELATIVE_DIRS}" "${sub_dirs_to_check}"
}

aggregate_all_cli() {
        local sub_dirs_to_check=". "
        if [[ ! -z "${SELECTED_CONFIGURATION+x}" ]]; then
                sub_dirs_to_check+="config_${SELECTED_CONFIGURATION}"
        fi
        aggregate_all_root_rel_sub "${1}" "${2}" "${CLI_SEARCH_RELATIVE_DIRS}" "${sub_dirs_to_check}"
}

aggregate_all_desktop() {
        aggregate_all_root_rel_sub "${1}" "${2}" "${DESKTOP_ENVIRONMENTS_SEARCH_RELATIVE_DIRS}" "."
        aggregate_all_root_rel_sub "${1}" "${2}" "${DESKTOP_APPGROUPS_SEARCH_RELATIVE_DIRS}" "${DESKTOP_APPGROUPS_SELECTED}"
}

one_line() {
        local aggregate_func_name="${1}"
        local aggregated_content=""
        shift 1
        $aggregate_func_name "${@}"
        cleanup_list aggregated_content
}

DEBOOTSTRAP_LIST="$(one_line aggregate_all_debootstrap "packages" " ")"
DEBOOTSTRAP_COMPONENTS="$(one_line aggregate_all_debootstrap "components" " ")"
DEBOOTSTRAP_COMPONENTS="${DEBOOTSTRAP_COMPONENTS// /,}"
PACKAGE_LIST="$(one_line aggregate_all_cli "packages" " ")"
PACKAGE_LIST_ADDITIONAL="$(one_line aggregate_all_cli "packages.additional" " ")"

LOG_OUTPUT_FILE="$SRC/output/${LOG_SUBPATH}/debootstrap-list.log"
show_checklist_variables "DEBOOTSTRAP_LIST DEBOOTSTRAP_COMPONENTS PACKAGE_LIST PACKAGE_LIST_ADDITIONAL PACKAGE_LIST_UNINSTALL"

# Dependent desktop packages
# Myy : Sources packages from file here

# Myy : FIXME Rename aggregate_all to aggregate_all_desktop
if [[ $BUILD_DESKTOP == "yes" ]]; then
        PACKAGE_LIST_DESKTOP+="$(one_line aggregate_all_desktop "packages" " ")"
        echo -e "\nGroups selected ${DESKTOP_APPGROUPS_SELECTED} -> PACKAGES :" >> "${LOG_OUTPUT_FILE}"
        show_checklist_variables PACKAGE_LIST_DESKTOP
fi
unset LOG_OUTPUT_FILE

DEBIAN_MIRROR='deb.debian.org/debian'
DEBIAN_SECURTY='security.debian.org/'
UBUNTU_MIRROR='ports.ubuntu.com/'
RASPI_MIRROR='archive.raspberrypi.org/debian/'

if [[ $DOWNLOAD_MIRROR == "china" ]] ; then

        if [[ ${CHINA_DOWNLOAD_MIRROR} == tsinghua ]]; then
                DEBIAN_MIRROR='mirrors.tuna.tsinghua.edu.cn/debian'
                DEBIAN_SECURTY='mirrors.tuna.tsinghua.edu.cn/debian-security'
                UBUNTU_MIRROR='mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/'
        fi

        if [[ ${CHINA_DOWNLOAD_MIRROR} == huawei ]]; then
                DEBIAN_MIRROR='repo.huaweicloud.com/debian'
                DEBIAN_SECURTY='repo.huaweicloud.com/debian-security'
                UBUNTU_MIRROR='repo.huaweicloud.com/ubuntu-ports/'
        fi

        RASPI_MIRROR='mirrors.ustc.edu.cn/archive.raspberrypi.org/debian/'

fi

if [[ $DOWNLOAD_MIRROR == "bfsu" ]] ; then
        DEBIAN_MIRROR='mirrors.bfsu.edu.cn/debian'
        DEBIAN_SECURTY='mirrors.bfsu.edu.cn/debian-security'
        UBUNTU_MIRROR='mirrors.bfsu.edu.cn/ubuntu-ports/'
fi

if [[ "${ARCH}" == "amd64" ]]; then
        UBUNTU_MIRROR='archive.ubuntu.com/ubuntu' # ports are only for non-amd64, of course.

                if [[ -n ${CUSTOM_UBUNTU_MIRROR} ]]; then # ubuntu redirector doesn't work well on amd64
                        UBUNTU_MIRROR="${CUSTOM_UBUNTU_MIRROR}"
                fi
fi

# don't use mirrors that throws garbage on 404
if [[ -z ${ARMBIAN_MIRROR} ]]; then
        while true; do

                ARMBIAN_MIRROR=$(wget -SO- -T 1 -t 1 https://redirect.armbian.com 2>&1 | egrep -i "Location" | awk '{print $2}' | head -1)
                [[ ${ARMBIAN_MIRROR} != *armbian.hosthatch* ]] && break

        done
fi

# For (late) user override.
# Notice: it is too late to define hook functions or add extensions in lib.config, since the extension initialization already ran by now.
#         in case the user tries to use them in lib.config, hopefully they'll be detected as "wishful hooking" and the user will be wrn'ed.
if [[ -f $USERPATCHES_PATH/lib.config ]]; then
        display_alert "Using user configuration override" "$USERPATCHES_PATH/lib.config" "info"
        source "$USERPATCHES_PATH"/lib.config
fi

call_extension_method "user_config" << 'USER_CONFIG'
*Invoke function with user override*
Allows for overriding configuration values set anywhere else.
It is called after sourcing the `lib.config` file if it exists,
but before assembling any package lists.
USER_CONFIG

call_extension_method "extension_prepare_config" << 'EXTENSION_PREPARE_CONFIG'
*allow extensions to prepare their own config, after user config is done*
Implementors should preserve variable values pre-set, but can default values an/or validate them.
This runs *after* user_config. Don't change anything not coming from other variables or meant to be configured by the user.
EXTENSION_PREPARE_CONFIG

# apt-cacher-ng mirror configurarion
if [[ $DISTRIBUTION == Ubuntu ]]; then
        APT_MIRROR=$UBUNTU_MIRROR
else
        APT_MIRROR=$DEBIAN_MIRROR
fi

[[ -n $APT_PROXY_ADDR ]] && display_alert "Using custom apt-cacher-ng address" "$APT_PROXY_ADDR" "info"

# Build final package list after possible override
PACKAGE_LIST="$PACKAGE_LIST $PACKAGE_LIST_RELEASE $PACKAGE_LIST_ADDITIONAL"
PACKAGE_MAIN_LIST="$(cleanup_list PACKAGE_LIST)"

[[ $BUILD_DESKTOP == yes ]] && PACKAGE_LIST="$PACKAGE_LIST $PACKAGE_LIST_DESKTOP"
PACKAGE_LIST="$(cleanup_list PACKAGE_LIST)"

# remove any packages defined in PACKAGE_LIST_RM in lib.config
aggregated_content="${PACKAGE_LIST_RM} "
aggregate_all_cli "packages.remove" " "
aggregate_all_desktop "packages.remove" " "
PACKAGE_LIST_RM="$(cleanup_list aggregated_content)"
unset aggregated_content

aggregated_content=""
aggregate_all_cli "packages.uninstall" " "
aggregate_all_desktop "packages.uninstall" " "
PACKAGE_LIST_UNINSTALL="$(cleanup_list aggregated_content)"
unset aggregated_content


if [[ -n $PACKAGE_LIST_RM ]]; then
        display_alert "Package remove list ${PACKAGE_LIST_RM}"
        # Turns out that \b can be tricked by dashes.
        # So if you remove mesa-utils but still want to install "mesa-utils-extra"
        # a "\b(mesa-utils)\b" filter will convert "mesa-utils-extra" to "-extra".
        # \W is not tricked by this but consumes the surrounding spaces, so we
        # replace the occurence by one space, to avoid sticking the next word to
        # the previous one after consuming the spaces.
        DEBOOTSTRAP_LIST=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${DEBOOTSTRAP_LIST} ")
        PACKAGE_LIST=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${PACKAGE_LIST} ")
        PACKAGE_MAIN_LIST=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${PACKAGE_MAIN_LIST} ")
        if [[ $BUILD_DESKTOP == "yes" ]]; then
                PACKAGE_LIST_DESKTOP=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${PACKAGE_LIST_DESKTOP} ")
                # Removing double spaces... AGAIN, since we might have used a sed on them
                # Do not quote the variables. This would defeat the trick.
                PACKAGE_LIST_DESKTOP="$(echo ${PACKAGE_LIST_DESKTOP})"
        fi

        # Removing double spaces... AGAIN, since we might have used a sed on them
        # Do not quote the variables. This would defeat the trick.
        DEBOOTSTRAP_LIST="$(echo ${DEBOOTSTRAP_LIST})"
        PACKAGE_LIST="$(echo ${PACKAGE_LIST})"
        PACKAGE_MAIN_LIST="$(echo ${PACKAGE_MAIN_LIST})"
fi


LOG_OUTPUT_FILE="$SRC/output/${LOG_SUBPATH}/debootstrap-list.log"
echo -e "\nVariables after manual configuration" >>$LOG_OUTPUT_FILE
show_checklist_variables "DEBOOTSTRAP_COMPONENTS DEBOOTSTRAP_LIST PACKAGE_LIST PACKAGE_MAIN_LIST"
unset LOG_OUTPUT_FILE

# Give the option to configure DNS server used in the chroot during the build process
[[ -z $NAMESERVER ]] && NAMESERVER="1.0.0.1" # default is cloudflare alternate

call_extension_method "post_aggregate_packages" "user_config_post_aggregate_packages" << 'POST_AGGREGATE_PACKAGES'
*For final user override, using a function, after all aggregations are done*
Called after aggregating all package lists, before the end of `compilation.sh`.
Packages will still be installed after this is called, so it is the last chance
to confirm or change any packages.
POST_AGGREGATE_PACKAGES

# debug
cat <<-EOF >> "${DEST}"/${LOG_SUBPATH}/output.log

## BUILD SCRIPT ENVIRONMENT

Repository: $REPOSITORY_URL
Version: $REPOSITORY_COMMIT

Host OS: $HOSTRELEASE
Host arch: $(dpkg --print-architecture)
Host system: $(uname -a)
Virtualization type: $(systemd-detect-virt)

## Build script directories
Build directory is located on:
$(findmnt -o TARGET,SOURCE,FSTYPE,AVAIL -T "${SRC}")

Build directory permissions:
$(getfacl -p "${SRC}")

Temp directory permissions:
$(getfacl -p "${SRC}"/.tmp 2> /dev/null)

## BUILD CONFIGURATION

Build target:
Board: $BOARD
Branch: $BRANCH
Minimal: $BUILD_MINIMAL
Desktop: $BUILD_DESKTOP
Desktop Environment: $DESKTOP_ENVIRONMENT
Software groups: $DESKTOP_APPGROUPS_SELECTED

Kernel configuration:
Repository: $KERNELSOURCE
Branch: $KERNELBRANCH
Config file: $LINUXCONFIG

U-boot configuration:
Repository: $BOOTSOURCE
Branch: $BOOTBRANCH
Config file: $BOOTCONFIG

Partitioning configuration: $IMAGE_PARTITION_TABLE offset: $OFFSET
Boot partition type: ${BOOTFS_TYPE:-(none)} ${BOOTSIZE:+"(${BOOTSIZE} MB)"}
Root partition type: $ROOTFS_TYPE ${FIXED_IMAGE_SIZE:+"(${FIXED_IMAGE_SIZE} MB)"}

CPU configuration: $CPUMIN - $CPUMAX with $GOVERNOR
EOF
7.3.1 rockchip-rk356x.conf

<SDK>/external/config/sources/families目录下存放rockchip某一型号SoC所使用的配置文件。

当我们配置了BOARDFAMILY="rockchip-rk356x",就会使用rockchip-rk356x.conf配置文件;

source "${BASH_SOURCE%/*}/include/rockchip64_common.inc"

BOOTBRANCH='branch:v2017.09-rk3588'
OVERLAY_PREFIX="rk356x"

CPUMIN=408000
CPUMAX=1800000

case $BRANCH in

        legacy)
                KERNELBRANCH='branch:orange-pi-5.10-rk35xx'
                LINUXCONFIG='linux-rockchip-rk356x-legacy'
                ;;
        current)
                KERNELBRANCH='branch:orange-pi-6.6-rk35xx'
                LINUXCONFIG='linux-rockchip-rk356x-current'
                ;;
esac

# 进入u-boot配置初始化
prepare_boot_configuration

install_balenaEtcher(){

        if [[ $BUILD_DESKTOP == yes && $install_balena_etcher == yes ]]; then

                balena_etcher_deb=balena-etcher-electron_1.7.9+5945ab1f_arm64.deb
                balena_etcher_url="https://github.com/Itai-Nelken/BalenaEtcher-arm/releases/download/v1.7.9/${balena_etcher_deb}"
                balena_etcher=${EXTER}/cache/debs/arm64/${balena_etcher_deb}

                if [[ ! -f ${balena_etcher} ]]; then
                        wget -P ${EXTER}/cache/debs/arm64 ${balena_etcher_url}
                fi

                install_deb_chroot ${balena_etcher}
        fi
}

family_tweaks_bsp()
{
        install -m 755 $EXTER/packages/bsp/adb/adbd-${ARCH} ${destination}/usr/bin/adbd
}

family_tweaks_s() {

        rsync -a --chown=root:root "${EXTER}"/packages/bsp/rk356x/* ${SDCARD}/

        chroot $SDCARD /bin/bash -c "apt-get -y -qq install dnsmasq v4l-utils cheese swig python3-dev python3-setuptools bluez libncurses-dev" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1

        [[ ${RELEASE} == jammy ]] && rm ${SDCARD}/etc/profile.d/orangepi-ssh-title.sh # for adbd
        chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable usbdevice.service >/dev/null 2>&1"

        if [[ -f "${SDCARD}"/etc/profile.d/im-config_wayland.sh && ${DESKTOP_ENVIRONMENT} =~ xfce|kde-plasma ]]; then
                chroot $SDCARD /bin/bash -c "rm /etc/profile.d/im-config_wayland.sh" # for adbd
        fi

        install_docker
        install_balenaEtcher
        install_wiringop
        if [[ -f ${SDCARD}/etc/pulse/default.pa && ${SELECTED_CONFIGURATION} == desktop && ${BOARD} =~ orangepicm4|orangepi3b ]]; then
                sed -i "s/auto-profiles = yes/auto-profiles = no/" ${SDCARD}/usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf

                echo "load-module module-alsa-sink device=hw:0,0 sink_name=AudioCodec-Playback sink_properties=\"device.description='Headphone'\"" >> ${SDCARD}/etc/pulse/default.pa
                echo "load-module module-alsa-sink device=hw:1,0 sink_name=HDMI-Playback sink_properties=\"device.description='HDMI Audio'\"" >> ${SDCARD}/etc/pulse/default.pa
        fi
}

定义了BOOTBRANCHOVERLAY_PREFIXKERNELBRANCHLINUXCONFIG等;

此外会调用prepare_boot_configuration函数进行u-boot配置初始化工作。

7.3.2 rockchip64_common.inc

位于<SDK>/external/config/sources/families/include目录,保存rockchip系列SoC所使用的通用配置;

点击查看代码
source "${BASH_SOURCE%/*}/rk3399_gpu_vpu.inc"
source "${BASH_SOURCE%/*}/rk35xx_gpu_vpu.inc"
source "${BASH_SOURCE%/*}/rk356x_gpu_vpu.inc"

enable_extension "rkbin-tools"
ARCH=arm64
KERNEL_IMAGE_TYPE=Image
OFFSET=30
BOOTSCRIPT='boot-rockchip64.cmd:boot.cmd'
BOOTENV_FILE='rockchip.txt'
UBOOT_TARGET_MAP=";;idbloader.bin uboot.img trust.bin"
BOOTDELAY=0
OVERLAY_PREFIX='rockchip'
SERIALCON=${SERIALCON:=$([[ $BRANCH == "legacy" || $BOARDFAMILY == "rockchip-rk3588" && $BRANCH == "current" ]] && echo "ttyFIQ0:1500000" || echo "ttyS2:1500000")}
GOVERNOR="ondemand"
BOOTBRANCH="branch:v2020.10-rockchip64"
PACKAGE_LIST_FAMILY="ethtool can-utils"

if [[ $RELEASE == "jammy" ]]; then
        PACKAGE_LIST_DESKTOP_FAMILY="fcitx5 fonts-wqy-zenhei xfce4-volumed"
else
        PACKAGE_LIST_DESKTOP_FAMILY="fcitx fonts-wqy-zenhei"
fi

[[ $BRANCH != "next" ]] && PACKAGE_LIST_FAMILY_REMOVE="mpv"

RKBIN_DIR="$EXTER/cache/sources/rkbin-tools"
if [ "$(uname -m)" = "aarch64" ]; then
        case "$(lsb_release -sc)" in
        "bookworm"|"bullseye"|"focal"|"jammy")
                PKG_PREFIX="qemu-x86_64-static -L /usr/x86_64-linux-gnu "
                ;;
        *)
                PKG_PREFIX="qemu-x86_64 -L /usr/x86_64-linux-gnu "
                ;;
        esac
else
        PKG_PREFIX=""
fi

# BOOTCONFIG="orangepi-3b-rk3566_defconfig",因此进入
if [[ $BOOTCONFIG =~ *3399*|*3566* ]]; then

        CPUMIN=${CPUMIN:="408000"}
        CPUMAX=${CPUMAX:="1800000"}

else # rk3328

        CPUMIN=${CPUMIN:="408000"}
        CPUMAX=${CPUMAX:="1296000"}

fi

if [[ $BOARD =~ orangepir1plus|orangepir1plus-lts ]]; then

        BOOT_USE_BLOBS=yes
        BOOT_SOC=rk3328
        DDR_BLOB='rk33/rk3328_ddr_333MHz_v1.16.bin'
        MINILOADER_BLOB='rk33/rk322xh_miniloader_v2.50.bin'
        BL31_BLOB='rk33/rk322xh_bl31_v1.44.elf'

elif [[ $BOARDFAMILY == "rockchip-rk3588" ]]; then

        BOOT_SCENARIO="${BOOT_SCENARIO:=spl-blobs}"

        if [[ "${BOARD}x" =~ orangepi5maxx|orangepi5prox|orangepi5plusx|orangepi5x ]]; then
                DDR_BLOB="${DDR_BLOB:=rk35/rk3588_ddr_lp4_2112MHz_lp5_2400MHz_v1.16.bin}"
                BL31_BLOB='rk35/rk3588_bl31_v1.45_20240422.elf'
        else
                DDR_BLOB="${DDR_BLOB:=rk35/rk3588_ddr_lp4_2112MHz_lp5_2736MHz_v1.11.bin}"
                BL31_BLOB='rk35/rk3588_bl31_v1.38.elf'
        fi
# BOARD="orangepi3b",因此进入
elif [[ $BOARD =~ orangepicm4|orangepi3b ]]; then

        BOOT_SCENARIO="${BOOT_SCENARIO:=spl-blobs}"
        BOOT_SOC=rk3568
        DDR_BLOB="${DDR_BLOB:=rk35/rk3566_ddr_1056MHz_v1.10.bin}"
        BL31_BLOB='rk35/rk3568_bl31_v1.28.elf'

else

        ATFPATCHDIR='atf-rk3399'

fi

prepare_boot_configuration()
{

        if [[ $BOOT_USE_MAINLINE_ATF == yes ]]; then

                UBOOT_TARGET_MAP="BL31=bl31.bin idbloader.img u-boot.itb;;idbloader.img u-boot.itb"
                ATFSOURCE='https://github.com/ARM-software/arm-trusted-firmware'
                ATF_COMPILER='aarch64-linux-gnu-'
                ATFDIR='arm-trusted-firmware'
                ATFBRANCH='tag:v2.2'
                ATF_USE_GCC='> 6.3'
                ATF_TARGET_MAP="M0_CROSS_COMPILE=arm-linux-gnueabi- PLAT=$BOOT_SOC bl31;;build/$BOOT_SOC/release/bl31/bl31.elf:bl31.bin"
                ATF_TOOLCHAIN2="arm-linux-gnueabi-:> 5.0"

        elif [[ $BOOT_USE_TPL_SPL_BLOB == yes ]]; then

                UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB idbloader.img u-boot.itb;;idbloader.img u-boot.itb"
                ATFSOURCE=''
                ATF_COMPILE='no'
                
		# 进入该分支	
        elif [[ $BOOT_SCENARIO == "spl-blobs" ]]; then

                UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb;;idbloader.img u-boot.itb"
                ATFSOURCE=''
                ATF_COMPILE='no'

        elif [[ $BOOT_USE_BLOBS == yes ]]; then

                UBOOT_TARGET_MAP="u-boot-dtb.bin;;idbloader.bin uboot.img trust.bin"
                ATFSOURCE=''
                ATF_COMPILE='no'
        fi

        if [[ $BOOT_SUPPORT_SPI == yes ]]; then

                UBOOT_TARGET_MAP="${UBOOT_TARGET_MAP} rkspi_loader.img"

        fi
}

uboot_custom_postprocess()
{
		# BOOT_SUPPORT_SPI="yes",进入
        if [[ $BOOT_SUPPORT_SPI == yes ]]; then

                if [[ $BOARDFAMILY == "rockchip-rk3588" ]]; then

                        tools/mkimage -n rk3588 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                        dd if=/dev/zero of=rkspi_loader.img bs=1M count=0 seek=4
                        /sbin/parted -s rkspi_loader.img mklabel gpt
                        /sbin/parted -s rkspi_loader.img unit s mkpart idbloader 64 1023
                        /sbin/parted -s rkspi_loader.img unit s mkpart uboot 1024 7167
                        #/sbin/parted -s rkspi_loader.img unit s mkpart vnvm 7168 7679
                        #/sbin/parted -s rkspi_loader.img unit s mkpart reserved_space 7680 8063
                        #/sbin/parted -s rkspi_loader.img unit s mkpart reserved1 8064 8127
                        #/sbin/parted -s rkspi_loader.img unit s mkpart uboot_env 8128 8191
                        #/sbin/parted -s rkspi_loader.img unit s mkpart reserved2 8192 16383
                        dd if=idbloader.img of=rkspi_loader.img seek=64 conv=notrunc
                        dd if=u-boot.itb of=rkspi_loader.img seek=1024 conv=notrunc
                        #dd if=$EXTER/packages/blobs/splash/logo.bmp of=rkspi_loader.img seek=8192 conv=notrunc
				# BOARDFAMILY="rockchip-rk356x",进入
                elif [[ $BOARDFAMILY == "rockchip-rk356x" ]]; then

                        tools/mkimage -n rk3568 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                        dd if=/dev/zero of=rkspi_loader.img bs=1M count=0 seek=4
                        /sbin/parted -s rkspi_loader.img mklabel gpt
                        /sbin/parted -s rkspi_loader.img unit s mkpart idbloader 64 1023
                        /sbin/parted -s rkspi_loader.img unit s mkpart uboot 1024 7167
                        dd if=idbloader.img of=rkspi_loader.img seek=64 conv=notrunc
                        dd if=u-boot.itb of=rkspi_loader.img seek=1024 conv=notrunc

                fi
        fi

        if [[ $BOOT_USE_MAINLINE_ATF == yes || $BOOT_USE_TPL_SPL_BLOB == yes ]]; then

                if [[ $BOOT_SUPPORT_SPI == yes ]]; then
                        tools/mkimage -n rk3399 -T rkspi -d tpl/u-boot-tpl.bin:spl/u-boot-spl.bin rkspi_tpl_spl.img
                        dd if=/dev/zero of=rkspi_loader.img count=8128 status=none
                        dd if=rkspi_tpl_spl.img of=rkspi_loader.img conv=notrunc status=none
                        dd if=u-boot.itb of=rkspi_loader.img seek=768 conv=notrunc status=none
                fi

        elif [[ $BOOT_USE_BLOBS == yes ]]; then

                local tempfile=$(mktemp)
                tools/mkimage -n $BOOT_SOC -T rksd -d $RKBIN_DIR/$DDR_BLOB idbloader.bin
                cat $RKBIN_DIR/$MINILOADER_BLOB >> idbloader.bin
                loaderimage --pack --uboot ./u-boot-dtb.bin uboot.img 0x200000
                trust_merger --replace bl31.elf $RKBIN_DIR/$BL31_BLOB trust.ini

		# BOOT_SCENARIO="spl-blobs",进入
        elif [[ $BOOT_SCENARIO == "spl-blobs" || $BOOT_SCENARIO == "tpl-blob-atf-mainline" ]]; then
        		# BOARD="orangepi3b",进入
                if [[ $BOARD =~ orangepicm4|orangepi3b ]]; then
                        tools/mkimage -n rk3568 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                elif [[ $BOARD =~ orangepi4-lts|orangepi800 ]]; then
                        tools/mkimage -n rk3399 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
                fi

                :

        else
                echo "[uboot_custom_postprocess]: Unsupported u-boot processing configuration!"
                exit 1

        fi

		# MERGE_UBOOT未定义不会进入
        if [[ ${MERGE_UBOOT} ]]; then

                display_alert "Merge u-boot" "u-boot-${BOARD}-merged.bin" "info"

                dd if=/dev/zero of=u-boot-${BOARD}-merged.bin bs=1M count=32 > /dev/null 2>&1
                dd if=idbloader.bin of=u-boot-${BOARD}-merged.bin seek=64 conv=notrunc status=none
                dd if=uboot.img of=u-boot-${BOARD}-merged.bin seek=16384 conv=notrunc status=none
                dd if=trust.bin of=u-boot-${BOARD}-merged.bin seek=24576 conv=notrunc status=none
                mv u-boot-${BOARD}-merged.bin ${DEB_STORAGE}/u-boot/
        fi
}

write_uboot_platform()
{

        if [[ -f $1/rksd_loader.img ]]; then # legacy rk3399 loader

                dd if=$1/rksd_loader.img of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1

        elif [[ -f $1/u-boot.itb ]]; then # $BOOT_USE_MAINLINE_ATF == yes || $BOOT_USE_TPL_SPL_BLOB == yes

                dd if=$1/idbloader.img of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1
                dd if=$1/u-boot.itb of=$2 seek=16384 conv=notrunc status=none >/dev/null 2>&1

        elif [[ -f $1/uboot.img ]]; then # $BOOT_USE_BLOBS == yes

                dd if=$1/idbloader.bin of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1
                dd if=$1/uboot.img of=$2 seek=16384 conv=notrunc status=none >/dev/null 2>&1
                dd if=$1/trust.bin of=$2 seek=24576 conv=notrunc status=none >/dev/null 2>&1

        else
                echo "[write_uboot_platform]: Unsupported u-boot processing configuration!"
                exit 1

        fi
}

write_uboot_platform_mtd()
{

        if [[ -f $1/rkspi_loader.img ]]; then

                dd if=$1/rkspi_loader.img of=$2 conv=notrunc status=none >/dev/null 2>&1

        else

                echo "SPI u-boot image not found!"
                exit 1

        fi
}

setup_write_uboot_platform()
{

        if grep -q "ubootpart" /proc/cmdline; then

                local tmp=$(cat /proc/cmdline)
                tmp="${tmp##*ubootpart=}"
                tmp="${tmp%% *}"
                [[ -n $tmp ]] && local part=$(findfs PARTUUID=$tmp 2>/dev/null)
                [[ -n $part ]] && local dev=$(lsblk -n -o PKNAME $part 2>/dev/null)
                [[ -n $dev ]] && DEVICE="/dev/$dev"

        fi
}

atf_custom_postprocess()
{

        # remove bl31.bin which can be 4+GiB in size and thus may fill the tmpfs mount
        rm -f build/rk322xh/debug/bl31.bin
        # ATF
        trust_merger trust.ini
}

family_tweaks()
{

        # execute specific tweaks function if present
        [[ $(type -t family_tweaks_s) == function ]] && family_tweaks_s

        [[ "$BRANCH" =~ legacy ]] && rk3399_gpu_vpu_tweaks
        [[ "$BRANCH" =~ current && $BOARDFAMILY == "rk3399" ]] && rk35xx_gpu_vpu_tweaks
        [[ "$BRANCH" =~ legacy|current && $BOARDFAMILY == "rockchip-rk3588" ]] && rk35xx_gpu_vpu_tweaks
        [[ "$BRANCH" == legacy && $BOARDFAMILY == "rockchip-rk356x" ]] && rk35xx_gpu_vpu_tweaks

        #if [[ ${BOARD} == orangepi800 && ${SELECTED_CONFIGURATION} == desktop && ${RELEASE} =~ bullseye|bookworm ]]; then

        #       install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/xfce4-zorinmenulite-plugin_1.1.2_arm64.deb"
        #       install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx-common_1.0~beta+git20210222-1~ppa1_all.deb"
        #       install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx-dockx_1.0~beta+git20210222-1~ppa1_all.deb"
        #       install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx_1.0~beta+git20210222-1~ppa1_all.deb"
        #       install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/xfce4-dockbarx-plugin_0.6+git20210221-1~ppa2~20.10_arm64.deb"
        #       install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx-theme-zorin_1.1.0_all.deb"

        #       ## Orange Pi Desktop layout switcher
        #       install_deb_chroot "$EXTER/cache/debs/arm64/layout_switcher/layoutswitcher_0.2-0_all.deb"
        #       #install_deb_chroot "$EXTER/cache/debs/arm64/layout_switcher/switcher_1.0-1_arm64.deb"
        #fi

        if [[ $BOARD =~ orangepir1plus|orangepir1plus-lts ]]; then

                # rename USB based network to lan0
                mkdir -p $SDCARD/etc/udev/rules.d/
                echo 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="r8152", KERNEL=="eth1", NAME="lan0"' > $SDCARD/etc/udev/rules.d/70-rename-lan.rules

                # workaround - create old school initial network config since network manager have issues
                #echo "auto eth0" >> $SDCARD/etc/network/interfaces
                #echo " iface eth0 inet dhcp" >> $SDCARD/etc/network/interfaces
                #echo "auto lan0" >> $SDCARD/etc/network/interfaces
                #echo " iface lan0 inet dhcp" >> $SDCARD/etc/network/interfaces
        fi

        if [[ -f $SDCARD/lib/systemd/system/rk3399-bluetooth.service ]]; then

                # install and enable Bluetooth
                chroot $SDCARD /bin/bash -c "apt-get -y -qq install rfkill bluetooth bluez bluez-tools >/dev/null 2>&1"
                chroot $SDCARD /bin/bash -c "systemctl --no-reload enable rk3399-bluetooth.service >/dev/null 2>&1"
        fi

        if [[ $RELEASE == jammy && $BUILD_DESKTOP == yes && $install_chromium == yes ]]; then

                chromium_name="chromium-browser_107.0.5304.62-0ubuntu1~ppa1~22.04.1_arm64.deb"
                chromium_extra_name="chromium-codecs-ffmpeg-extra_107.0.5304.62-0ubuntu1~ppa1~22.04.1_arm64.deb"
                chromium_browser_l10n_name="chromium-browser-l10n_107.0.5304.62-0ubuntu1~ppa1~22.04.1_all.deb"
                chromium_url="http://ppa.launchpadcontent.net/saiarcot895/chromium-beta/ubuntu/pool/main/c/chromium-browser"
                chromium=${EXTER}/cache/debs/arm64/${chromium_name}
                chromium_extra=${EXTER}/cache/debs/arm64/${chromium_extra_name}
                chromium_browser_l10n=${EXTER}/cache/debs/arm64/${chromium_browser_l10n_name}

                if [[ ! -f $chromium_extra ]]; then
                        wget -P ${EXTER}/cache/debs/arm64 ${chromium_url}/${chromium_name}
                        wget -P ${EXTER}/cache/debs/arm64 ${chromium_url}/${chromium_extra_name}
                        wget -P ${EXTER}/cache/debs/arm64 ${chromium_url}/${chromium_browser_l10n_name}
                fi

                install_deb_chroot $chromium_extra
                install_deb_chroot $chromium
                install_deb_chroot $chromium_browser_l10n

        fi

        if [[ $RELEASE == jammy && $BUILD_DESKTOP == yes && $install_firefox == yes ]]; then

                firefox_name="firefox-esr_102.6.0esr+build1-0ubuntu0.22.04.1_arm64.deb"
                firefox_url="https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu/pool/main/f/firefox-esr/"
                firefox=${EXTER}/cache/debs/arm64/${firefox_name}

                if [[ ! -f $firefox ]]; then
                        wget -P ${EXTER}/cache/debs/arm64 ${firefox_url}/${firefox_name}
                fi

                install_deb_chroot $firefox

        fi

        rm $SDCARD/root/*.deb >/dev/null 2>&1
}


family_tweaks_bsp()
{

        if [[ $BOOTCONFIG == *3328* ]] && [[ $BRANCH != legacy ]]; then
                mkdir -p "$destination"/etc/X11/xorg.conf.d
                cat <<-EOF > "$destination"/etc/X11/xorg.conf.d/02-driver.conf
                # set fbdev as default driver.
                Section "Device"
                 Identifier "NOGPU"
                 Driver "fbdev"
                EndSection
                EOF
        fi

        # Graphics and media
        mkdir -p $destination/etc/udev/rules.d
        cp $EXTER/packages/bsp/rk3399/50-mali.rules $destination/etc/udev/rules.d/
        cp $EXTER/packages/bsp/rk3399/50-rk3399-vpu.rules $destination/etc/udev/rules.d/
        mkdir -p $destination/etc/sysfs.d
        cp $EXTER/packages/bsp/rk3399/20-gpu-governor.conf $destination/etc/sysfs.d/
}

定义了ARCHKERNEL_IMAGE_TYPEBOOTSCRIPTBOOTENV_FILEUBOOT_TARGET_MAPBOOTDELAY等;

7.3.3 arm64.conf

arm64.conf文件位于<SDK>/external/config/sources

#!/bin/bash
#
# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
#
# This file is a part of the Armbian build script
# https://github.com/armbian/build/
QEMU_BINARY="qemu-aarch64-static"
ARCHITECTURE=arm64
ARCH=arm64
KERNEL_IMAGE_TYPE=Image
CAN_BUILD_STRETCH=yes
GIT_SERVER="https://github.com/orangepi-xunlong"

[[ -z $INITRD_ARCH ]] && INITRD_ARCH=arm64

if [[ $BOARD =~ orangepipch5|orangepioneh5 ]]; then
        ARCH=armhf
        QEMU_BINARY="qemu-arm-static"
fi

if [ "$(uname -m)" = "aarch64" ]; then
        [[ $ATF_COMPILE != "no" && -z $ATF_COMPILER ]] && ATF_COMPILER="aarch64-linux-gnu-"
        [[ -z $UBOOT_COMPILER ]] && UBOOT_COMPILER="aarch64-linux-gnu-"
        [[ -z $KERNEL_COMPILER ]] && KERNEL_COMPILER="aarch64-linux-gnu-"
else
        [[ $ATF_COMPILE != "no" && -z $ATF_COMPILER ]] && ATF_COMPILER="aarch64-none-linux-gnu-"
        [[ -z $UBOOT_COMPILER ]] && UBOOT_COMPILER="aarch64-none-linux-gnu-"
        [[ -z $KERNEL_COMPILER ]] && KERNEL_COMPILER="aarch64-none-linux-gnu-"
fi

[[ $ATF_COMPILE != "no" && -z $ATFSOURCE ]] && ATFSOURCE='https://github.com/ARM-software/arm-trusted-firmware'
[[ $ATF_COMPILE != "no" && -z $ATFDIR ]] && ATFDIR='arm-trusted-firmware-sunxi-mainline'
[[ $ATF_COMPILE != "no" && -z $ATFBRANCH ]] && ATFBRANCH='branch:master'
[[ $ATF_COMPILE != "no" && -z $ATF_USE_GCC ]] && ATF_USE_GCC='> 8.0'

[[ -z $UBOOT_USE_GCC ]]         && UBOOT_USE_GCC='> 8.0'
[[ -z $BOOTSOURCE ]]            && BOOTSOURCE="${GIT_SERVER}/u-boot-orangepi.git"
[[ -z $BOOTDIR ]]               && BOOTDIR="${SRC}/u-boot"
[[ -z $BOOTBRANCH ]]            && BOOTBRANCH='branch:v2020.04'

[[ -z $KERNEL_USE_GCC ]]        && KERNEL_USE_GCC='> 8.0'
[[ -z $KERNELDIR ]]             && KERNELDIR="${SRC}/kernel"
[[ -z $KERNELSOURCE ]]          && KERNELSOURCE="${GIT_SERVER}/linux-orangepi.git"
[[ -z $KERNELBRANCH ]]          && KERNELBRANCH='branch:orange-pi-5.4'

7.4 变量值

Linux SDK构建过程中会多次使用一些变量,其中一些变量的值如下;

DEST=<SDK>/output
DEB_STORAGE=$DEST/debs
CCACHE=""
BUILD_OPT="image"
KERNEL_CONFIGURE="no"
BOARD="orangepi3b"
BRANCH="current"
RELEASE="bullseye"
BUILD_DESKTOP="no"
BUILD_MINIMAL="no"
COMPRESS_OUTPUTIMAGE="sha,gpg,img"
SELECTED_CONFIGURATION="cli_standard"
BOOT_SCENARIO="spl-blobs"
BOOT_SOC=rk3568
DDR_BLOB="rk35/rk3566_ddr_1056MHz_v1.10.bin"
BL31_BLOB='rk35/rk3568_bl31_v1.28.elf'
RKBIN_DIR="external/cache/sources/rkbin-tools"
UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb;;idbloader.img u-boot.itb"
ATFSOURCE=''
ATF_COMPILE='no'
posted @ 2024-07-10 21:34  大奥特曼打小怪兽  阅读(166)  评论(2编辑  收藏  举报
如果有任何技术小问题,欢迎大家交流沟通,共同进步