程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

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),内容如下:

View Code
# 获取当前脚本所在的目录路径
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),内容如下:

View Code
#!/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创建子目录;

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

View Code
# 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软件包。由于脚本内容较多,以下脚本会提出一些不会执行的代码,具体如下:

View Code
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软件包。由于脚本内容较多,以下脚本会提出一些不会执行的代码,具体如下:

View Code
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

View Code
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镜像文件。

View Code
#!/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包解压,包含如下文件;

View Code
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

View Code
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

View Code
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
} #############################################################################

亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。

日期姓名金额
2023-09-06*源19
2023-09-11*朝科88
2023-09-21*号5
2023-09-16*真60
2023-10-26*通9.9
2023-11-04*慎0.66
2023-11-24*恩0.01
2023-12-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
2024-09-06*强8.8
2024-09-09*波1
2024-09-10*口1
2024-09-10*波1
2024-09-12*波10
2024-09-18*明1.68
2024-09-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(792)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示