Rockchip RK3566 - orangepi-build脚本分析
----------------------------------------------------------------------------------------------------------------------------
开发板 :Orange Pi 3B
开发板
eMMC
:32GB
LPDDR4
:2GB
显示屏 :15.6
英寸HDMI
接口显示屏
u-boot
:2017.09
linux
:6.6
----------------------------------------------------------------------------------------------------------------------------
在《Rockchip RK3566 - orangepi-build
编译》我们介绍了SDK
的编译流程,本节将会对编译脚本进行深入的分析。
一、build.sh
分析
orangepi-build
编译命令是由build.sh
脚本实现的,其脚本相对来说比较长,这里我们去掉一些非重点代码(比如docker
),内容如下:
点击查看代码
# 获取当前脚本所在的目录路径
SRC="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
# check for whitespace in ${SRC} and exit for safety reasons 空字符串检验
grep -q "[[:space:]]" <<<"${SRC}" && { echo "\"${SRC}\" contains whitespace. Not supported. Aborting." >&2 ; exit 1 ; }
cd "${SRC}" || exit
# 启用调用跟踪
if [[ "${ORANGEPI_ENABLE_CALL_TRACING}" == "yes" ]]; then
set -T # inherit return/debug traps
mkdir -p "${SRC}"/output/debug
echo -n "" > "${SRC}"/output/debug/calls.txt
trap 'echo "${BASH_LINENO[@]}|${BASH_SOURCE[@]}|${FUNCNAME[@]}" >> ${SRC}/output/debug/calls.txt ;' RETURN
fi
# 执行./script/general.sh脚本
if [[ -f "${SRC}"/scripts/general.sh ]]; then
# shellcheck source=scripts/general.sh
source "${SRC}"/scripts/general.sh
else
echo "Error: missing build directory structure"
echo "Please clone the full repository by https://github.com/orangepi-xunlong/orangepi-build"
exit 255
fi
# 校验第一个参数
if [[ "${EUID}" == "0" ]] || [[ "${1}" == "vagrant" ]]; then
:
elif [[ "${1}" == docker || "${1}" == dockerpurge || "${1}" == docker-shell ]] && grep -q "$(whoami)" <(getent group docker); then
:
else
# 以root身份执行脚本
display_alert "This script requires root privileges, trying to use sudo" "" "wrn"
sudo "${SRC}/build.sh" "$@"
exit $?
fi
# 走else分支,为宿主机ubuntu 22.04系统安装基础包,比如dialog、uuid、uuid-runtime等
if [ "$OFFLINE_WORK" == "yes" ]; then
echo -e "\n"
display_alert "* " "You are working offline."
display_alert "* " "Sources, time and host will not be checked"
echo -e "\n"
sleep 3s
else
# check and install the basic utilities here
prepare_host_basic
fi
EXTER="${SRC}/external"
# Create userpatches directory if not exists
mkdir -p "${SRC}"/userpatches
# Create example configs if none found in userpatches
if ! ls "${SRC}"/userpatches/{config-example.conf,config-docker.conf,config-vagrant.conf} 1> /dev/null 2>&1; then
# Migrate old configs
if ls "${SRC}"/*.conf 1> /dev/null 2>&1; then
display_alert "Migrate config files to userpatches directory" "all *.conf" "info"
cp "${SRC}"/*.conf "${SRC}"/userpatches || exit 1
rm "${SRC}"/*.conf
[[ ! -L "${SRC}"/userpatches/config-example.conf ]] && ln -fs config-example.conf "${SRC}"/userpatches/config-default.conf || exit 1
fi
display_alert "Create example config file using template" "config-default.conf" "info"
# Create example config
if [[ ! -f "${SRC}"/userpatches/config-example.conf ]]; then
cp "${EXTER}"/config/templates/config-example.conf "${SRC}"/userpatches/config-example.conf || exit 1
ln -fs config-example.conf "${SRC}"/userpatches/config-default.conf || exit 1
fi
# Create Docker config
if [[ ! -f "${SRC}"/userpatches/config-docker.conf ]]; then
cp "${EXTER}"/config/templates/config-docker.conf "${SRC}"/userpatches/config-docker.conf || exit 1
fi
# Create Docker file
if [[ ! -f "${SRC}"/userpatches/Dockerfile ]]; then
cp "${EXTER}"/config/templates/Dockerfile "${SRC}"/userpatches/Dockerfile || exit 1
fi
# Create Vagrant config
if [[ ! -f "${SRC}"/userpatches/config-vagrant.conf ]]; then
cp "${EXTER}"/config/templates/config-vagrant.conf "${SRC}"/userpatches/config-vagrant.conf || exit 1
fi
# Create Vagrant file
if [[ ! -f "${SRC}"/userpatches/Vagrantfile ]]; then
cp "${EXTER}"/config/templates/Vagrantfile "${SRC}"/userpatches/Vagrantfile || exit 1
fi
fi
# 不会进入
if [[ -z "${CONFIG}" && -n "$1" && -f "${SRC}/userpatches/config-$1.conf" ]]; then
CONFIG="userpatches/config-$1.conf"
shift
fi
# usind default if custom not found
if [[ -z "${CONFIG}" && -f "${SRC}/userpatches/config-default.conf" ]]; then
CONFIG="userpatches/config-default.conf"
fi
# source build configuration file
CONFIG_FILE="$(realpath "${CONFIG}")"
if [[ ! -f "${CONFIG_FILE}" ]]; then
display_alert "Config file does not exist" "${CONFIG}" "error"
exit 254
fi
CONFIG_PATH=$(dirname "${CONFIG_FILE}")
# Source the extensions manager library at this point, before sourcing the config.
# This allows early calls to enable_extension(), but initialization proper is done later.
# shellcheck source=scripts/extensions.sh
source "${SRC}"/scripts/extensions.sh
display_alert "Using config file" "${CONFIG_FILE}" "info"
pushd "${CONFIG_PATH}" > /dev/null || exit
# shellcheck source=/dev/null
source "${CONFIG_FILE}"
popd > /dev/null || exit
[[ -z "${USERPATCHES_PATH}" ]] && USERPATCHES_PATH="${CONFIG_PATH}"
# Script parameters handling
while [[ "${1}" == *=* ]]; do
parameter=${1%%=*}
value=${1##*=}
shift
display_alert "Command line: setting $parameter to" "${value:-(empty)}" "info"
eval "$parameter=\"$value\""
done
if [[ "${BUILD_ALL}" == "yes" || "${BUILD_ALL}" == "demo" ]]; then
# shellcheck source=scripts/build-all-ng.sh
source "${SRC}"/scripts/build-all-ng.sh
else
# shellcheck source=scripts/main.sh
source "${SRC}"/scripts/main.sh
fi
接下来我们针对该脚本内容从上往下依次分析。
1.1 开启调用追踪
如果我们需要启动调用跟踪,在执行命令时设置ORANGEPI_ENABLE_CALL_TRACING
即可,比如:
ORANGEPI_ENABLE_CALL_TRACING=yes ./build.sh
如果环境变量 ORANGEPI_ENABLE_CALL_TRACING
设置为 "yes"
,将启用函数调用跟踪,并将日志记录到./output/debug/calls.txt
;
if [[ "${ORANGEPI_ENABLE_CALL_TRACING}" == "yes" ]]; then
set -T # inherit return/debug traps
# 创建调试目录
mkdir -p "${SRC}"/output/debug
# 初始化一个空的calls.txt文件,用于存储调试信息
echo -n "" > "${SRC}"/output/debug/calls.txt
# 设置一个陷阱,在脚本退出时记录函数调用详情
trap 'echo "${BASH_LINENO[@]}|${BASH_SOURCE[@]}|${FUNCNAME[@]}" >> ${SRC}/output/debug/calls.txt ;' RETURN
fi
1.2 执行general.sh
脚本
接着是使用source
命令执行general.sh
脚本,该脚本位于<SDK>/scripts
目录下;
if [[ -f "${SRC}"/scripts/general.sh ]]; then
source "${SRC}"/scripts/general.sh
else
echo "错误:缺少构建目录结构"
echo "请通过 https://github.com/orangepi-xunlong/orangepi-build 克隆完整的存储库"
exit 255
fi
使用source
命令执行脚本的一些注意事项:
- 环境变量和函数的影响:被执行的脚本可以修改当前
shell
的环境变量和定义的函数,这些修改将持续影响到当前shell
的会话,直到会话结束或者重新定义了这些变量和函数。 - 退出状态:被执行的脚本的退出状态(即最后一个命令的退出状态)会影响到当前
shell
。可以通过$?
变量来获取最近一次执行命令的退出状态; - 交互性:与直接执行脚本不同,使用
source
执行脚本时,不会创建新的shell
环境,因此不会有新的子shell
进程。这使得它适合于需要脚本和当前shell
环境之间相互影响的场景,例如定义函数或设置环境变量。
1.3 执行prepare_host_basic
脚本
prepare_host_basic
脚本是在general.sh
中定义的,为宿主机ubuntu 22.04
系统安装基础包,比如dialog
、uuid
、uuid-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.conf
和Dockerfile
到./userpatches/
目录; - 创建
Vagrant
相关文件:如果./userpatches/config-vagrant.conf
和./userpatches/Vagrantfile
不存在,则分别从./external/config/templates/
目录复制config-vagrant.conf
和Vagrantfile
到${SRC}/userpatches/
目录。
因此执行完成后会在userpatches
目录下创建config-default.conf
、config-docker.conf
、config-example.conf
、config-vagrant.conf
、Vagrantfile
文件;
root@ubuntu:/work/sambashare/rk3566/orangepi-build$ ll userpatches/
lrwxrwxrwx 1 root root 19 7月 10 14:20 config-default.conf -> config-example.conf
-rw-r--r-- 1 root root 5846 7月 10 14:20 config-docker.conf
-rw-r--r-- 1 root root 1274 7月 10 17:57 config-example.conf
-rw-r--r-- 1 root root 715 7月 10 14:20 config-vagrant.conf
-rw-r--r-- 1 root root 3111 7月 10 14:20 Dockerfile
-rw-r--r-- 1 root root 1715 7月 10 14:20 Vagrantfile
1.4.2 使用config-default.conf
确定要使用的配置文件路径,并确保该配置文件存在。如果未找到任何自定义配置文件 ($1
),则将使用默认配置文件 (config-default.conf
);
# 检查自定义配置文件的存在,由于参数1未指定因此不会进入
if [[ -z "${CONFIG}" && -n "$1" && -f "${SRC}/userpatches/config-$1.conf" ]]; then
CONFIG="userpatches/config-$1.conf"
shift
fi
# usind default if custom not found 进入
if [[ -z "${CONFIG}" && -f "${SRC}/userpatches/config-default.conf" ]]; then
CONFIG="userpatches/config-default.conf"
fi
# source build configuration file, 获取配置文件绝对路径,由于config-default.conf链接到了config-example.conf,因此此处值为<SDK>/userpatches/config-example.conf
CONFIG_FILE="$(realpath "${CONFIG}")"
# 检查配置文件的实际存在性,由于文件的确存在因此不会进入
if [[ ! -f "${CONFIG_FILE}" ]]; then
display_alert "Config file does not exist" "${CONFIG}" "error"
exit 254
fi
# 获取配置文件所在目录,<SDK>/userpatches
CONFIG_PATH=$(dirname "${CONFIG_FILE}")
1.5 执行extensions.sh
脚本
接着是使用source
命令执行extensions.sh
脚本,该脚本位于<SDK>/scripts
目录下;
# Source the extensions manager library at this point, before sourcing the config.
# This allows early calls to enable_extension(), but initialization proper is done later.
# shellcheck source=scripts/extensions.sh
source "${SRC}"/scripts/extensions.sh
1.6 加载配置文件
接着是输出当前使用的配置文件信息,然后切换工作目录并加载配置文件:
display_alert "Using config file" "${CONFIG_FILE}" "info"
# 将当前工作目录切换到 ${CONFIG_PATH}
pushd "${CONFIG_PATH}" > /dev/null || exit
# shellcheck source=/dev/null,加载${CONFIG_FILE}中的shell脚本
source "${CONFIG_FILE}"
# 恢复之前的工作目录
popd > /dev/null || exit
# 设置USERPATCHES_PATH=${CONFIG_PATH}
[[ -z "${USERPATCHES_PATH}" ]] && USERPATCHES_PATH="${CONFIG_PATH}"
# Script parameters handling,由于未指定参数1因此不会进入
while [[ "${1}" == *=* ]]; do
parameter=${1%%=*}
value=${1##*=}
shift
display_alert "Command line: setting $parameter to" "${value:-(empty)}" "info"
eval "$parameter=\"$value\""
done
CONFIG_FILE
被定义为了<SDK>/userpatches/config-example.conf
,该脚本中定义的一些变量将会被加载到当前shell
中。
1.7 进入main.sh
脚本的最后使用source
命令执行main.sh
脚本;
if [[ "${BUILD_ALL}" == "yes" || "${BUILD_ALL}" == "demo" ]]; then
# shellcheck source=scripts/build-all-ng.sh
source "${SRC}"/scripts/build-all-ng.sh
else
# shellcheck source=scripts/main.sh
source "${SRC}"/scripts/main.sh
fi
二、main.sh
分析
main.sh
脚本位于<SDK>/scripts
目录下,该脚本比较重要,可以认为就是编译的主程序。
这里我们去掉一些非重点代码(比如docker
),内容如下:
点击查看代码
#!/bin/bash
#
# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
#
# Main program
#
cleanup_list() {
local varname="${1}"
local list_to_clean="${!varname}"
list_to_clean="${list_to_clean#"${list_to_clean%%[![:space:]]*}"}"
list_to_clean="${list_to_clean%"${list_to_clean##*[![:space:]]}"}"
echo ${list_to_clean}
}
# default umask for root is 022 so parent directories won't be group writeable without this
# this is used instead of making the chmod in prepare_host() recursive
# 设置文件创建的默认权限
umask 002
# destination 确定目标路径
if [ -d "$CONFIG_PATH/output" ]; then
DEST="${CONFIG_PATH}"/output
else
DEST="${SRC}"/output
fi
# 进入
[[ -z $REVISION ]] && REVISION="3.0.8"
# 进入
[[ $DOWNLOAD_MIRROR == "china" ]] && NTP_SERVER="cn.pool.ntp.org"
# 进入,根据终端的当前尺寸设置 TTY_X(宽度)和 TTY_Y(高度)
if [[ $BUILD_ALL != "yes" ]]; then
# override stty size
[[ -n $COLUMNS ]] && stty cols $COLUMNS
[[ -n $LINES ]] && stty rows $LINES
TTY_X=$(($(stty size | awk '{print $2}')-6)) # determine terminal width
TTY_Y=$(($(stty size | awk '{print $1}')-6)) # determine terminal height
fi
# We'll use this title on all menus 设置标题和菜单
backtitle="Orange Pi building script, http://www.orangepi.org"
titlestr="Choose an option"
# Warnings mitigation 设置语言和终端的字符集编码
[[ -z $LANGUAGE ]] && export LANGUAGE="en_US:en" # set to english if not set
[[ -z $CONSOLE_CHAR ]] && export CONSOLE_CHAR="UTF-8" # set console to UTF-8 if not set
# Libraries include
# shellcheck source=debootstrap.sh
source "${SRC}"/scripts/debootstrap.sh # system specific install
# shellcheck source=image-helpers.sh
source "${SRC}"/scripts/image-helpers.sh # helpers for OS image building
# shellcheck source=distributions.sh
source "${SRC}"/scripts/distributions.sh # system specific install
# shellcheck source=desktop.sh
source "${SRC}"/scripts/desktop.sh # desktop specific install
# shellcheck source=compilation.sh
source "${SRC}"/scripts/compilation.sh # patching and compilation of kernel, uboot, ATF
# shellcheck source=compilation-prepare.sh
#source "${SRC}"/scripts/compilation-prepare.sh # drivers that are not upstreamed
# shellcheck source=makeboarddeb.sh
source "${SRC}"/scripts/makeboarddeb.sh # board support package
# shellcheck source=general.sh
source "${SRC}"/scripts/general.sh # general functions
# shellcheck source=chroot-buildpackages.sh
source "${SRC}"/scripts/chroot-buildpackages.sh # chroot packages building
# shellcheck source=pack.sh
source "${SRC}"/scripts/pack-uboot.sh
# set log path 设置输出日志路径
LOG_SUBPATH=${LOG_SUBPATH:=debug}
# compress and remove old logs
mkdir -p "${DEST}"/${LOG_SUBPATH}
(cd "${DEST}"/${LOG_SUBPATH} && tar -czf logs-"$(<timestamp)".tgz ./*.log) > /dev/null 2>&1
rm -f "${DEST}"/${LOG_SUBPATH}/*.log > /dev/null 2>&1
date +"%d_%m_%Y-%H_%M_%S" > "${DEST}"/${LOG_SUBPATH}/timestamp
# delete compressed logs older than 7 days
(cd "${DEST}"/${LOG_SUBPATH} && find . -name '*.tgz' -mtime +7 -delete) > /dev/null
# 设置缓存目录
SHOW_WARNING=yes
if [[ $USE_CCACHE != no ]]; then
CCACHE=ccache
export PATH="/usr/lib/ccache:$PATH"
# private ccache directory to avoid permission issues when using build script with "sudo"
# see https://ccache.samba.org/manual.html#_sharing_a_cache for alternative solution
[[ $PRIVATE_CCACHE == yes ]] && export CCACHE_DIR=$EXTER/cache/ccache
else
CCACHE=""
fi
# if BUILD_OPT, KERNEL_CONFIGURE, BOARD, BRANCH or RELEASE are not set, display selection menu
# 创建了一个用户界面,用户可以从菜单中选择构建选项(u-boot、kernel、rootfs、image)
if [[ -z $BUILD_OPT ]]; then
options+=("u-boot" "U-boot package")
options+=("kernel" "Kernel package")
options+=("rootfs" "Rootfs and all deb packages")
options+=("image" "Full OS image for flashing")
menustr="Compile image | rootfs | kernel | u-boot"
BUILD_OPT=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" --notags \
--menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
unset options
[[ -z $BUILD_OPT ]] && exit_with_error "No option selected"
[[ $BUILD_OPT == rootfs ]] && ROOT_FS_CREATE_ONLY="yes"
fi
# 如果选择了kernel或image则进入,选择内核配置
if [[ ${BUILD_OPT} =~ kernel|image ]]; then
if [[ -z $KERNEL_CONFIGURE ]]; then
options+=("no" "Do not change the kernel configuration")
options+=("yes" "Show a kernel configuration menu before compilation")
menustr="Select the kernel configuration."
KERNEL_CONFIGURE=$(whiptail --title "${titlestr}" --backtitle "$backtitle" --notags \
--menu "${menustr}" $TTY_Y $TTY_X $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
unset options
[[ -z $KERNEL_CONFIGURE ]] && exit_with_error "No option selected"
fi
fi
# 进入,选择开发板型号
if [[ -z $BOARD ]]; then
#options+=("orangepir1" "Allwinner H2+ quad core 256MB RAM WiFi SPI 2xETH")
#options+=("orangepizero" "Allwinner H2+ quad core 256MB/512MB RAM WiFi SPI")
#options+=("orangepipc" "Allwinner H3 quad core 1GB RAM")
#options+=("orangepipcplus" "Allwinner H3 quad core 1GB RAM WiFi eMMC")
#options+=("orangepione" "Allwinner H3 quad core 512MB RAM")
#options+=("orangepilite" "Allwinner H3 quad core 512MB RAM WiFi")
#options+=("orangepiplus" "Allwinner H3 quad core 1GB/2GB RAM WiFi GBE eMMC")
#options+=("orangepiplus2e" "Allwinner H3 quad core 2GB RAM WiFi GBE eMMC")
#options+=("orangepizeroplus2h3" "Allwinner H3 quad core 512MB RAM WiFi/BT eMMC")
#options+=("orangepipch5" "Allwinner H5 quad core 1GB RAM")
#options+=("orangepipc2" "Allwinner H5 quad core 1GB RAM GBE SPI")
#options+=("orangepioneh5" "Allwinner H5 quad core 512MB/1GB RAM")
#options+=("orangepiprime" "Allwinner H5 quad core 2GB RAM GBE WiFi/BT")
#options+=("orangepizeroplus" "Allwinner H5 quad core 512MB RAM GBE WiFi SPI")
#options+=("orangepizeroplus2h5" "Allwinner H5 quad core 512MB RAM WiFi/BT eMMC")
options+=("orangepi3" "Allwinner H6 quad core 1GB/2GB RAM GBE WiFi/BT eMMC USB3")
options+=("orangepi3-lts" "Allwinner H6 quad core 2GB RAM GBE WiFi/BT-AW859A eMMC USB3")
#options+=("orangepilite2" "Allwinner H6 quad core 1GB RAM WiFi/BT USB3")
#options+=("orangepioneplus" "Allwinner H6 quad core 1GB RAM GBE")
options+=("orangepizero2" "Allwinner H616 quad core 512MB/1GB RAM WiFi/BT GBE SPI")
#options+=("orangepizero2-b" "Allwinner H616 quad core 512MB/1GB RAM WiFi/BT GBE SPI")
#options+=("orangepizero2-lts" "Allwinner H616 quad core 1.5GB RAM WiFi/BT GBE SPI")
options+=("orangepizero3" "Allwinner H618 quad core 1GB/1.5GB/2GB/4GB RAM WiFi/BT GBE SPI")
options+=("orangepizero2w" "Allwinner H618 quad core 1GB/1.5GB/2GB/4GB RAM WiFi/BT SPI")
#options+=("orangepir1b" "Allwinner H618 quad core 1.5GB/2GB/4GB RAM WiFi/BT GBE SPI")
#options+=("orangepi400" "Allwinner H616 quad core 4GB RAM WiFi/BT GBE eMMC VGA")
options+=("orangepi4" "Rockchip RK3399 hexa core 4GB RAM GBE eMMC USB3 USB-C WiFi/BT")
options+=("orangepi4-lts" "Rockchip RK3399 hexa core 4GB RAM GBE eMMC USB3 USB-C WiFi/BT")
options+=("orangepi800" "Rockchip RK3399 hexa core 4GB RAM GBE eMMC USB3 USB-C WiFi/BT VGA")
options+=("orangepi5" "Rockchip RK3588S octa core 4-16GB RAM GBE USB3 USB-C NVMe")
options+=("orangepicm5" "Rockchip RK3588S octa core 4-16GB RAM GBE USB3 USB-C")
options+=("orangepicm5-tablet" "Rockchip RK3588S octa core 4-16GB RAM USB3 USB-C WiFi/BT")
options+=("orangepi5b" "Rockchip RK3588S octa core 4-16GB RAM GBE USB3 USB-C WiFi/BT eMMC")
#options+=("orangepitab" "Rockchip RK3588S octa core 4-16GB RAM USB-C WiFi/BT NVMe")
#options+=("orangepi900" "Rockchip RK3588 octa core 4-16GB RAM 2.5GBE USB3 USB-C WiFi/BT NVMe")
options+=("orangepi5pro" "Rockchip RK3588S octa core 4-16GB RAM GBE USB3 WiFi/BT NVMe eMMC")
options+=("orangepi5max" "Rockchip RK3588 octa core 4-16GB RAM 2.5GBE USB3 WiFi/BT NVMe eMMC")
options+=("orangepi5plus" "Rockchip RK3588 octa core 4-32GB RAM 2.5GBE USB3 USB-C WiFi/BT NVMe eMMC")
options+=("orangepicm4" "Rockchip RK3566 quad core 2-8GB RAM GBE eMMC USB3 NvMe WiFi/BT")
options+=("orangepi3b" "Rockchip RK3566 quad core 2-8GB RAM GBE eMMC USB3 NvMe WiFi/BT")
#options+=("orangepir1plus" "Rockchip RK3328 quad core 1GB RAM 2xGBE USB2 SPI")
#options+=("orangepi3plus" "Amlogic S905D3 quad core 2/4GB RAM SoC eMMC GBE USB3 SPI WiFi/BT")
menustr="Please choose a Board."
BOARD=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" \
--menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
unset options
[[ -z $BOARD ]] && exit_with_error "No option selected"
fi
# 加载板载配置文件external/config/boards/orangepi3b.conf
BOARD_TYPE="conf"
# shellcheck source=/dev/null
source "${EXTER}/config/boards/${BOARD}.${BOARD_TYPE}"
LINUXFAMILY="${BOARDFAMILY}"
[[ -z $KERNEL_TARGET ]] && exit_with_error "Board configuration does not define valid kernel config"
# 进入,选择内核版本
if [[ -z $BRANCH ]]; then
options=()
[[ $KERNEL_TARGET == *current* ]] && options+=("current" "Recommended. Come with best support")
[[ $KERNEL_TARGET == *legacy* ]] && options+=("legacy" "Old stable / Legacy")
[[ $KERNEL_TARGET == *next* ]] && options+=("next" "Use the latest kernel")
menustr="Select the target kernel branch\nExact kernel versions depend on selected board"
# do not display selection dialog if only one kernel branch is available
if [[ "${#options[@]}" == 2 ]]; then
BRANCH="${options[0]}"
else
BRANCH=$(whiptail --title "${titlestr}" --backtitle "${backtitle}" \
--menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
fi
unset options
[[ -z $BRANCH ]] && exit_with_error "No kernel branch selected"
[[ $BRANCH == dev && $SHOW_WARNING == yes ]] && show_developer_warning
fi
# 如果选择了kernel或image,并且未设置RELEASE则进入选择Linux发行版的类型
if [[ $BUILD_OPT =~ rootfs|image && -z $RELEASE ]]; then
options=()
distros_options
menustr="Select the target OS release package base"
RELEASE=$(whiptail --title "Choose a release package base" --backtitle "${backtitle}" \
--menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
#echo "options : ${options}"
[[ -z $RELEASE ]] && exit_with_error "No release selected"
unset options
fi
# don't show desktop option if we choose minimal build,如果设置了Minimal版本版本
[[ $BUILD_MINIMAL == yes ]] && BUILD_DESKTOP=no
# 选择镜像的类型,有桌面和无桌面
if [[ $BUILD_OPT =~ rootfs|image && -z $BUILD_DESKTOP ]]; then
# read distribution support status which is written to the orangepi-release file
set_distribution_status
options=()
options+=("no" "Image with console interface (server)")
options+=("yes" "Image with desktop environment")
menustr="Select the target image type"
BUILD_DESKTOP=$(whiptail --title "Choose image type" --backtitle "${backtitle}" \
--menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
unset options
[[ -z $BUILD_DESKTOP ]] && exit_with_error "No option selected"
if [[ ${BUILD_DESKTOP} == "yes" ]]; then
BUILD_MINIMAL=no
SELECTED_CONFIGURATION="desktop"
fi
fi
# 对于无桌面的镜像,选择Standard版本或者Minimal版本
if [[ $BUILD_OPT =~ rootfs|image && $BUILD_DESKTOP == no && -z $BUILD_MINIMAL ]]; then
options=()
options+=("no" "Standard image with console interface")
options+=("yes" "Minimal image with console interface")
menustr="Select the target image type"
BUILD_MINIMAL=$(whiptail --title "Choose image type" --backtitle "${backtitle}" \
--menu "${menustr}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
--cancel-button Exit --ok-button Select "${options[@]}" \
3>&1 1>&2 2>&3)
unset options
[[ -z $BUILD_MINIMAL ]] && exit_with_error "No option selected"
if [[ $BUILD_MINIMAL == "yes" ]]; then
SELECTED_CONFIGURATION="cli_minimal"
else
SELECTED_CONFIGURATION="cli_standard"
fi
fi
#prevent conflicting setup 处理冲突配置
if [[ $BUILD_DESKTOP == "yes" ]]; then
BUILD_MINIMAL=no
SELECTED_CONFIGURATION="desktop"
elif [[ $BUILD_MINIMAL != "yes" || -z "${BUILD_MINIMAL}" ]]; then
BUILD_MINIMAL=no # Just in case BUILD_MINIMAL is not defined
BUILD_DESKTOP=no
SELECTED_CONFIGURATION="cli_standard"
elif [[ $BUILD_MINIMAL == "yes" ]]; then
BUILD_DESKTOP=no
SELECTED_CONFIGURATION="cli_minimal"
fi
#shellcheck source=configuration.sh
source "${SRC}"/scripts/configuration.sh
# optimize build time with 100% CPU usage 定系统中的CPU核心数,并根据USEALLCORES的设置动态调整并行编译线程数
CPUS=$(grep -c 'processor' /proc/cpuinfo)
if [[ $USEALLCORES != no ]]; then
CTHREADS="-j$((CPUS + CPUS/2))"
else
CTHREADS="-j1"
fi
call_extension_method "post_determine_cthreads" "config_post_determine_cthreads" << 'POST_DETERMINE_CTHREADS'
*give config a chance modify CTHREADS programatically. A build server may work better with hyperthreads-1 for example.*
Called early, before any compilation work starts.
POST_DETERMINE_CTHREADS
if [[ $BETA == yes ]]; then
IMAGE_TYPE=nightly
elif [[ $BETA != "yes" && $BUILD_ALL == yes && -n $GPG_PASS ]]; then
IMAGE_TYPE=stable
else
IMAGE_TYPE=user-built # 走这里
fi
branch2dir() {
[[ "${1}" == "head" ]] && echo "HEAD" || echo "${1##*:}"
}
BOOTSOURCEDIR="${BOOTDIR}/$(branch2dir "${BOOTBRANCH}")"
LINUXSOURCEDIR="${KERNELDIR}/$(branch2dir "${KERNELBRANCH}")"
[[ -n $ATFSOURCE ]] && ATFSOURCEDIR="${ATFDIR}/$(branch2dir "${ATFBRANCH}")"
BSP_CLI_PACKAGE_NAME="orangepi-bsp-cli-${BOARD}"
BSP_CLI_PACKAGE_FULLNAME="${BSP_CLI_PACKAGE_NAME}_${REVISION}_${ARCH}"
BSP_DESKTOP_PACKAGE_NAME="orangepi-bsp-desktop-${BOARD}"
BSP_DESKTOP_PACKAGE_FULLNAME="${BSP_DESKTOP_PACKAGE_NAME}_${REVISION}_${ARCH}"
CHOSEN_UBOOT=linux-u-boot-${BRANCH}-${BOARD}
CHOSEN_KERNEL=linux-image-${BRANCH}-${LINUXFAMILY}
CHOSEN_ROOTFS=${BSP_CLI_PACKAGE_NAME}
CHOSEN_DESKTOP=orangepi-${RELEASE}-desktop-${DESKTOP_ENVIRONMENT}
CHOSEN_KSRC=linux-source-${BRANCH}-${LINUXFAMILY}
do_default() {
start=$(date +%s)
# Check and install dependencies, directory structure and settings
# The OFFLINE_WORK variable inside the function
prepare_host
[[ "${JUST_INIT}" == "yes" ]] && exit 0
[[ $CLEAN_LEVEL == *sources* ]] && cleaning "sources"
# fetch_from_repo <url> <dir> <ref> <subdir_flag>
# ignore updates help on building all images - for internal purposes
if [[ ${IGNORE_UPDATES} != yes ]]; then
display_alert "Downloading sources" "" "info"
[[ $BUILD_OPT =~ u-boot|image ]] && fetch_from_repo "$BOOTSOURCE" "$BOOTDIR" "$BOOTBRANCH" "yes"
[[ $BUILD_OPT =~ kernel|image ]] && fetch_from_repo "$KERNELSOURCE" "$KERNELDIR" "$KERNELBRANCH" "yes"
if [[ -n ${ATFSOURCE} ]]; then
[[ ${BUILD_OPT} =~ u-boot|image ]] && fetch_from_repo "$ATFSOURCE" "${EXTER}/cache/sources/$ATFDIR" "$ATFBRANCH" "yes"
fi
if [[ ${BOARDFAMILY} == "rockchip-rk356x" && $RELEASE =~ bullseye|focal|jammy|raspi ]]; then
[[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/rk35xx_packages" "branch:rk35xx_packages"
fi
if [[ ${BOARD} =~ orangepi3|orangepi3-lts && $RELEASE =~ bullseye && $BRANCH == current ]]; then
[[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/ffmpeg_kodi_${RELEASE}" "branch:ffmpeg_kodi_${RELEASE}"
fi
call_extension_method "fetch_sources_tools" <<- 'FETCH_SOURCES_TOOLS'
*fetch host-side sources needed for tools and build*
Run early to fetch_from_repo or otherwise obtain sources for needed tools.
FETCH_SOURCES_TOOLS
call_extension_method "build_host_tools" <<- 'BUILD_HOST_TOOLS'
*build needed tools for the build, host-side*
After sources are fetched, build host-side tools needed for the build.
BUILD_HOST_TOOLS
if [[ ${BOARDFAMILY} == "rockchip-rk3588" ]]; then
local rkbin_url="https://github.com/orangepi-xunlong/rk-rootfs-build/raw/rkbin/rk35"
wget -nc -P ${EXTER}/cache/sources/rkbin-tools/rk35/ ${rkbin_url}/rk3588_bl31_v1.45_20240422.elf
fi
fi
for option in $(tr ',' ' ' <<< "$CLEAN_LEVEL"); do
[[ $option != sources ]] && cleaning "$option"
done
# Compile u-boot if packed .deb does not exist or use the one from Orange Pi
if [[ $BUILD_OPT == u-boot || $BUILD_OPT == image ]]; then
if [[ ! -f "${DEB_STORAGE}"/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb ]]; then
[[ -n "${ATFSOURCE}" && "${REPOSITORY_INSTALL}" != *u-boot* ]] && compile_atf
[[ ${REPOSITORY_INSTALL} != *u-boot* ]] && compile_uboot
fi
if [[ $BUILD_OPT == "u-boot" ]]; then
unset BUILD_MINIMAL BUILD_DESKTOP COMPRESS_OUTPUTIMAGE
display_alert "U-boot build done" "@host" "info"
display_alert "Target directory" "${DEB_STORAGE}/u-boot" "info"
display_alert "File name" "${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" "info"
fi
fi
# Compile kernel if packed .deb does not exist or use the one from Orange Pi
if [[ $BUILD_OPT == kernel || $BUILD_OPT == image ]]; then
if [[ ! -f ${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb ]]; then
KDEB_CHANGELOG_DIST=$RELEASE
[[ "${REPOSITORY_INSTALL}" != *kernel* ]] && compile_kernel
fi
if [[ $BUILD_OPT == "kernel" ]]; then
unset BUILD_MINIMAL BUILD_DESKTOP COMPRESS_OUTPUTIMAGE
display_alert "Kernel build done" "@host" "info"
display_alert "Target directory" "${DEB_STORAGE}/" "info"
display_alert "File name" "${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" "info"
fi
fi
if [[ $BUILD_OPT == rootfs || $BUILD_OPT == image ]]; then
# Compile orangepi-config if packed .deb does not exist or use the one from Orange Pi
if [[ ! -f ${DEB_STORAGE}/orangepi-config_${REVISION}_all.deb ]]; then
[[ "${REPOSITORY_INSTALL}" != *orangepi-config* ]] && compile_orangepi-config
fi
# Compile orangepi-zsh if packed .deb does not exist or use the one from repository
if [[ ! -f ${DEB_STORAGE}/orangepi-zsh_${REVISION}_all.deb ]]; then
[[ "${REPOSITORY_INSTALL}" != *orangepi-zsh* ]] && compile_orangepi-zsh
fi
# Compile plymouth-theme-orangepi if packed .deb does not exist or use the one from repository
if [[ ! -f ${DEB_STORAGE}/plymouth-theme-orangepi_${REVISION}_all.deb ]]; then
[[ "${REPOSITORY_INSTALL}" != *plymouth-theme-orangepi* ]] && compile_plymouth-theme-orangepi
fi
# Compile orangepi-firmware if packed .deb does not exist or use the one from repository
if [[ "${REPOSITORY_INSTALL}" != *orangepi-firmware* ]]; then
if ! ls "${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb" 1> /dev/null 2>&1; then
FULL=""
REPLACE="-full"
compile_firmware
fi
fi
overlayfs_wrapper "cleanup"
# create board support package
[[ -n $RELEASE && ! -f ${DEB_STORAGE}/$RELEASE/${BSP_CLI_PACKAGE_FULLNAME}.deb ]] && create_board_package
# create desktop package
[[ -n $RELEASE && $DESKTOP_ENVIRONMENT ]] && create_desktop_package
[[ -n $RELEASE && $DESKTOP_ENVIRONMENT ]] && create_bsp_desktop_package
# build additional packages
[[ $EXTERNAL_NEW == compile ]] && chroot_build_packages
[[ $BSP_BUILD != yes ]] && debootstrap_ng
fi
# hook for function to run after build, i.e. to change owner of $SRC
# NOTE: this will run only if there were no errors during build process
[[ $(type -t run_after_build) == function ]] && run_after_build || true
end=$(date +%s)
runtime=$(((end-start)/60))
display_alert "Runtime" "$runtime min" "info"
# Make it easy to repeat build by displaying build options used
[ "$(systemd-detect-virt)" == 'docker' ] && BUILD_CONFIG='docker'
display_alert "Repeat Build Options" "sudo ./build.sh ${BUILD_CONFIG} BOARD=${BOARD} BRANCH=${BRANCH} \
$([[ -n $BUILD_OPT ]] && echo "BUILD_OPT=${BUILD_OPT} ")\
$([[ -n $RELEASE ]] && echo "RELEASE=${RELEASE} ")\
$([[ -n $BUILD_MINIMAL ]] && echo "BUILD_MINIMAL=${BUILD_MINIMAL} ")\
$([[ -n $BUILD_DESKTOP ]] && echo "BUILD_DESKTOP=${BUILD_DESKTOP} ")\
$([[ -n $KERNEL_CONFIGURE ]] && echo "KERNEL_CONFIGURE=${KERNEL_CONFIGURE} ")\
$([[ -n $DESKTOP_ENVIRONMENT ]] && echo "DESKTOP_ENVIRONMENT=${DESKTOP_ENVIRONMENT} ")\
$([[ -n $DESKTOP_ENVIRONMENT_CONFIG_NAME ]] && echo "DESKTOP_ENVIRONMENT_CONFIG_NAME=${DESKTOP_ENVIRONMENT_CONFIG_NAME} ")\
$([[ -n $DESKTOP_APPGROUPS_SELECTED ]] && echo "DESKTOP_APPGROUPS_SELECTED=\"${DESKTOP_APPGROUPS_SELECTED}\" ")\
$([[ -n $DESKTOP_APT_FLAGS_SELECTED ]] && echo "DESKTOP_APT_FLAGS_SELECTED=\"${DESKTOP_APT_FLAGS_SELECTED}\" ")\
$([[ -n $COMPRESS_OUTPUTIMAGE ]] && echo "COMPRESS_OUTPUTIMAGE=${COMPRESS_OUTPUTIMAGE} ")\
" "ext"
} # end of do_default()
if [[ -z $1 ]]; then
do_default
else
eval "$@"
f
接下来我们针对该脚本内容从上往下依次分析。
2.1 准备工作
main.sh
脚本开始是一系列的准备工作,具体如下。
2.1.1 确定目标路径
确定编译输出目标路径,设置为<SDK>/output
;
# destination 确定目标路径
if [ -d "$CONFIG_PATH/output" ]; then
DEST="${CONFIG_PATH}"/output
else
DEST="${SRC}"/output # 走这里
fi
2.1.2 加载若干脚本函数
接着是使用source
命令执行若干*.sh
脚本,脚本均位于<SDK>/scripts
目录下;
# shellcheck source=debootstrap.sh
source "${SRC}"/scripts/debootstrap.sh # system specific install
# shellcheck source=image-helpers.sh
source "${SRC}"/scripts/image-helpers.sh # helpers for OS image building
# shellcheck source=distributions.sh
source "${SRC}"/scripts/distributions.sh # system specific install
# shellcheck source=desktop.sh
source "${SRC}"/scripts/desktop.sh # desktop specific install
# shellcheck source=compilation.sh
source "${SRC}"/scripts/compilation.sh # patching and compilation of kernel, uboot, ATF
# shellcheck source=compilation-prepare.sh
#source "${SRC}"/scripts/compilation-prepare.sh # drivers that are not upstreamed
# shellcheck source=makeboarddeb.sh
source "${SRC}"/scripts/makeboarddeb.sh # board support package
# shellcheck source=general.sh
source "${SRC}"/scripts/general.sh # general functions
# shellcheck source=chroot-buildpackages.sh
source "${SRC}"/scripts/chroot-buildpackages.sh # chroot packages building
# shellcheck source=pack.sh
source "${SRC}"/scripts/pack-uboot.sh
这些脚本都是定义了若干个函数,这些脚本中定义的函数将会被加载到当前shell
中。
2.1.3 设置日志路径
接着设置日志输出路径:
# set log path
LOG_SUBPATH=${LOG_SUBPATH:=debug}
# compress and remove old logs 创建目录
mkdir -p "${DEST}"/${LOG_SUBPATH}
# 压缩并删除旧的日志文件,并将其压缩后的文件存档
(cd "${DEST}"/${LOG_SUBPATH} && tar -czf logs-"$(<timestamp)".tgz ./*.log) > /dev/null 2>&1
rm -f "${DEST}"/${LOG_SUBPATH}/*.log > /dev/null 2>&1
date +"%d_%m_%Y-%H_%M_%S" > "${DEST}"/${LOG_SUBPATH}/timestamp
# delete compressed logs older than 7 days 删除超过一周的旧日志压缩文件
(cd "${DEST}"/${LOG_SUBPATH} && find . -name '*.tgz' -mtime +7 -delete) > /dev/null
日志输出路径被设置为debug
,位于<SDK>/output
目录下;
root@ubuntu:/work/sambashare/rk3566/orangepi-build$ ll output/debug/
-rw-rw-r-- 1 root sudo 6100 7月 10 15:24 logs-10_07_2024-14_20_17.tgz
-rw-rw-r-- 1 root sudo 246 7月 10 15:26 logs-10_07_2024-15_24_55.tgz
-rw-rw-r-- 1 root sudo 254 7月 10 15:30 logs-10_07_2024-15_26_21.tgz
-rw-rw-r-- 1 root sudo 2253 7月 10 15:42 logs-10_07_2024-15_30_06.tgz
-rw-rw-r-- 1 root sudo 6296 7月 10 16:53 logs-10_07_2024-15_42_43.tgz
-rw-rw-r-- 1 root sudo 246 7月 10 16:55 logs-10_07_2024-16_53_05.tgz
-rw-rw-r-- 1 root sudo 4630 7月 10 17:40 logs-10_07_2024-16_55_36.tgz
-rw-rw-r-- 1 root sudo 3659 7月 10 17:45 logs-10_07_2024-17_40_27.tgz
-rw-rw-r-- 1 root sudo 3714 7月 10 17:57 logs-10_07_2024-17_45_03.tgz
-rw-rw-r-- 1 root sudo 30427 7月 10 20:20 logs-10_07_2024-17_57_14.tgz
-rw-rw-r-- 1 root root 45 7月 10 14:20 logs-.tgz
-rw-rw-r-- 1 root sudo 198 7月 10 20:20 output.log
-rw-rw-r-- 1 root root 20 7月 10 20:20 timestamp
在该路径下存放在一周内的编译日志。
2.1.4 设置缓存路径
这段脚本片段的作用是根据条件设置ccache
的相关环境变量和路径:
SHOW_WARNING=yes
if [[ $USE_CCACHE != no ]]; then
CCACHE=ccache
export PATH="/usr/lib/ccache:$PATH"
# private ccache directory to avoid permission issues when using build script with "sudo"
# see https://ccache.samba.org/manual.html#_sharing_a_cache for alternative solution
[[ $PRIVATE_CCACHE == yes ]] && export CCACHE_DIR=$EXTER/cache/ccache
else
CCACHE=""
fi
由于未设置USE_CCACHE
,因此CCACHE=""
。
2.2 支持用户选择
接下来就是可视化交互页面,允许用户选择编译u-boot
、kernel
、rootfs
、image
以及版本等信息。
2.2.1 编译选项
接着创建了一个用户界面,用户可以从菜单中选择构建选项(u-boot
、kernel
、rootfs
、image
);
# 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
;
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
如果不需要修改内核配置,则选择第一个即可,如果需要修改内核配置,则选择第二个;
假如我们选择了第一个,那么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"
,因此支持的选项如下;
假如我们选择了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
发行版的类型;
假如我们选择了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
;
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 interface
,BUILD_MINIMAL=no
、SELECTED_CONFIGURATION=cli_standard
;
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"
BOOTSOURCE
、BOOTDIR
、BOOTBRANCH
定义在<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"
KERNELSOURCE
、KERNELDIR
、KERNELBRANCH
定义在<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
函数,入参依次外debs
、oldcache
,该函数定义在scripts/general.sh
。
如果我们没有对u-boot
、kernel
进行修改,我们就可以不清理u-boot
、kernel
编译生成的deb
包,即修改:
CLEAN_LEVEL="oldcache"
这样就不会重新编译u-boot
、kernel
。
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 构建rootfs
和Linux
镜像
接着是进行Linux
镜像的构建,这里会判断如下包是否存在,不存在则会编译生成;
-
<SDK>/output/debs/orangepi-config_1.0.6_all.deb
:compile_orangepi
编译生成; -
<SDK>/output/debs/orangepi-zsh_1.0.6_all.deb
:compile_orangepi
编译生成; -
<SDK>/output/debs/orangepi-plymouth-theme_1.0.6_all.deb
:compile_plymouth-theme-orangepi
编译生成; -
<SDK>/output/debs/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
:create_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
目录下,脚本提供了通用功能;cleaning
、exit_with_error
、get_package_list_hash
、create_sources_list
、clean_up_git
、waiter_local_git
、fetch_from_repo
、improved_git
、distro_menu
、addtorepo
、repo-remove-old-packages
、wait_for_package_manager
、install_pkg_deb
、prepare_host_basic
、prepare_host
、webseed
、download_and_verify
、show_developer_warning
、show_checklist_variables
。
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
系统安装基础包,比如dialog
、uuid
、uuid-runtime
等;
# prepare_host_basic
#
# * installs only basic packages
#
prepare_host_basic()
{
# command:package1 package2 ...
# list of commands that are neeeded:packages where this command is
local check_pack install_pack
local checklist=(
"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=bullseye
、distro_fullname=Debian 11 Bullseye
;
所以当BRANCH=current
时,设置DISTRIB_TYPE=bullseye bookworm focal jammy
;
DISTRIB_TYPE="${DISTRIB_TYPE_CURRENT}"
[[ -z "${DISTRIB_TYPE_CURRENT}" ]] && DISTRIB_TYPE="bullseye bookworm focal jammy"
函数最后设置:
options+=("${distro_codename}" "${distro_fullname} ${expert_infos}")
即:
bullseye Debian 11 Bullseye
3.4 fetch_from_repo
fetch_from_repo
用于源码下载,接收三个参数:
url
:源码下载地址directory
:源码下载下来的存放目录;ref
:下载的branch/tag
名称;ref_subdir
:是否为该branch/tag
创建子目录;
由于源码较多,就不一一分析了;
点击查看代码
# fetch_from_repo <url> <directory> <ref> <ref_subdir>
# <url>: remote repository URL
# <directory>: local directory; subdir for branch/tag will be created
# <ref>:
# branch:name
# tag:name
# head(*)
# commit:hash
#
# *: Implies ref_subdir=no
#
# <ref_subdir>: "yes" to create subdirectory for tag or branch name
#
fetch_from_repo()
{
local url=$1
local dir=$2
local ref=$3
local ref_subdir=$4
# Set GitHub mirror before anything else touches $url
url=${url//'https://github.com/'/$GITHUB_SOURCE'/'}
# The 'offline' variable must always be set to 'true' or 'false'
if [ "$OFFLINE_WORK" == "yes" ]; then
local offline=true
else
local offline=false
fi
[[ -z $ref || ( $ref != tag:* && $ref != branch:* && $ref != head && $ref != commit:* ) ]] && exit_with_error "Error in configuration"
local ref_type=${ref%%:*}
if [[ $ref_type == head ]]; then
local ref_name=HEAD
else
local ref_name=${ref##*:}
fi
display_alert "Checking git sources" "$dir $ref_name" "info"
# get default remote branch name without cloning
# local ref_name=$(git ls-remote --symref $url HEAD | grep -o 'refs/heads/\S*' | sed 's%refs/heads/%%')
# for git:// protocol comparing hashes of "git ls-remote -h $url" and "git ls-remote --symref $url HEAD" is needed
if [[ $ref_subdir == yes ]]; then
local workdir=$dir/$ref_name
else
local workdir=$dir
fi
mkdir -p "${workdir}" 2>/dev/null || \
exit_with_error "No path or no write permission" "${workdir}"
cd "${workdir}" || exit
# check if existing remote URL for the repo or branch does not match current one
# may not be supported by older git versions
# Check the folder as a git repository.
# Then the target URL matches the local URL.
if [[ "$(git rev-parse --git-dir 2>/dev/null)" == ".git" && \
"$url" != *"$(git remote get-url origin | sed 's/^.*@//' | sed 's/^.*\/\///' 2>/dev/null)" ]]; then
display_alert "Remote URL does not match, removing existing local copy"
rm -rf .git ./*
fi
if [[ "$(git rev-parse --git-dir 2>/dev/null)" != ".git" ]]; then
display_alert "Creating local copy"
git init -q .
git remote add origin "${url}"
# Here you need to upload from a new address
offline=false
fi
local changed=false
# when we work offline we simply return the sources to their original state
if ! $offline; then
local local_hash
local_hash=$(git rev-parse @ 2>/dev/null)
case $ref_type in
branch)
# TODO: grep refs/heads/$name
local remote_hash
remote_hash=$(improved_git ls-remote -h "${url}" "$ref_name" | head -1 | cut -f1)
[[ -z $local_hash || "${local_hash}" != "${remote_hash}" ]] && changed=true
;;
tag)
local remote_hash
remote_hash=$(improved_git ls-remote -t "${url}" "$ref_name" | cut -f1)
if [[ -z $local_hash || "${local_hash}" != "${remote_hash}" ]]; then
remote_hash=$(improved_git ls-remote -t "${url}" "$ref_name^{}" | cut -f1)
[[ -z $remote_hash || "${local_hash}" != "${remote_hash}" ]] && changed=true
fi
;;
head)
local remote_hash
remote_hash=$(improved_git ls-remote "${url}" HEAD | cut -f1)
[[ -z $local_hash || "${local_hash}" != "${remote_hash}" ]] && changed=true
;;
commit)
[[ -z $local_hash || $local_hash == "@" ]] && changed=true
;;
esac
fi # offline
if [[ $changed == true ]]; then
# remote was updated, fetch and check out updates
display_alert "Fetching updates"
case $ref_type in
branch) improved_git fetch --depth 200 origin "${ref_name}" ;;
tag) improved_git fetch --depth 200 origin tags/"${ref_name}" ;;
head) improved_git fetch --depth 200 origin HEAD ;;
esac
# commit type needs support for older git servers that doesn't support fetching id directly
if [[ $ref_type == commit ]]; then
improved_git fetch --depth 200 origin "${ref_name}"
# cover old type
if [[ $? -ne 0 ]]; then
display_alert "Commit checkout not supported on this repository. Doing full clone." "" "wrn"
improved_git pull
git checkout -fq "${ref_name}"
display_alert "Checkout out to" "$(git --no-pager log -2 --pretty=format:"$ad%s [%an]" | head -1)" "info"
else
display_alert "Checking out"
git checkout -f -q FETCH_HEAD
git clean -qdf
fi
else
display_alert "Checking out"
git checkout -f -q FETCH_HEAD
git clean -qdf
fi
elif [[ -n $(git status -uno --porcelain --ignore-submodules=all) ]]; then
# working directory is not clean
display_alert " Cleaning .... " "$(git status -s | wc -l) files"
# Return the files that are tracked by git to the initial state.
git checkout -f -q HEAD
# Files that are not tracked by git and were added
# when the patch was applied must be removed.
git clean -qdf
else
# working directory is clean, nothing to do
display_alert "Up to date"
fi
if [[ -f .gitmodules ]]; then
display_alert "Updating submodules" "" "ext"
# FML: http://stackoverflow.com/a/17692710
for i in $(git config -f .gitmodules --get-regexp path | awk '{ print $2 }'); do
cd "${workdir}" || exit
local surl sref
surl=$(git config -f .gitmodules --get "submodule.$i.url")
sref=$(git config -f .gitmodules --get "submodule.$i.branch")
if [[ -n $sref ]]; then
sref="branch:$sref"
else
sref="head"
fi
fetch_from_repo "$surl" "$workdir/$i" "$sref"
done
fi
} #############################################################################
3.5 cleaning
cleaning
执行一些清理工作,支持的选项有:
-
debs
:删除u-boot
、kernel
、bsp
等deb
包,这些包均位于<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_atf
、compile_uboot
、compile_kernel
、compile_firmware
、compile_orangepi-config
、compile_sunxi_tools
、install_rkbin_tools
、grab_version
、find_toolchain
、advanced_patch
、process_patch_file
、userpatch_create
、overlayfs_wrapper
。
4.1 compile_uboot
compile_uboot
函数实现了u-boot
源码的编译并生成deb
软件包。由于脚本内容较多,以下脚本会提出一些不会执行的代码,具体如下:
点击查看代码
compile_uboot()
{
if [[ ${BOARDFAMILY} == "sun50iw9" && ${BRANCH} =~ legacy|current && $(dpkg --print-architecture) == arm64 ]]; then
.......
else
# not optimal, but extra cleaning before overlayfs_wrapper should keep sources directory clean
if [[ $CLEAN_LEVEL == *make* ]]; then
display_alert "Cleaning" "$BOOTSOURCEDIR" "info"
(cd $BOOTSOURCEDIR; make clean > /dev/null 2>&1)
fi
if [[ $USE_OVERLAYFS == yes ]]; then
local ubootdir
ubootdir=$(overlayfs_wrapper "wrap" "$BOOTSOURCEDIR" "u-boot_${LINUXFAMILY}_${BRANCH}")
else
local ubootdir="$BOOTSOURCEDIR"
fi
cd "${ubootdir}" || exit
# read uboot version
local version hash
version=$(grab_version "$ubootdir")
hash=$(improved_git --git-dir="$ubootdir"/.git rev-parse HEAD)
display_alert "Compiling u-boot" "v$version" "info"
# build aarch64
if [[ $(dpkg --print-architecture) == amd64 ]]; then
local toolchain
# 查找交叉编译工具链,UBOOT_COMPILER=aarch64-none-linux-gnu- UBOOT_USE_GCC='> 8.0'
toolchain=$(find_toolchain "$UBOOT_COMPILER" "$UBOOT_USE_GCC")
[[ -z $toolchain ]] && exit_with_error "Could not find required toolchain" "${UBOOT_COMPILER}gcc $UBOOT_USE_GCC"
# 未定义
if [[ -n $UBOOT_TOOLCHAIN2 ]]; then
......
fi
# build aarch64
fi
display_alert "Compiler version" "${UBOOT_COMPILER}gcc $(eval env PATH="${toolchain}:${toolchain2}:${PATH}" "${UBOOT_COMPILER}gcc" -dumpversion)" "info"
# 未定义
[[ -n $toolchain2 ]] && display_alert "Additional compiler version" "${toolchain2_type}gcc $(eval env PATH="${toolchain}:${toolchain2}:${PATH}" "${toolchain2_type}gcc" -dumpversion)" "info"
# create directory structure for the .deb package 创建临时目录
uboottempdir=$(mktemp -d)
chmod 700 ${uboottempdir}
trap "ret=\$?; rm -rf \"${uboottempdir}\" ; exit \$ret" 0 1 2 3 15
# linux-u-boot-current-orangepi3b_1.0.6_arm64
local uboot_name=${CHOSEN_UBOOT}_${REVISION}_${ARCH}
rm -rf $uboottempdir/$uboot_name
# 创建/tmp/xxxx/linux-u-boot-current-orangepi3b_1.0.6_arm64/usr/lib/u-boot、tmp/xxxx/linux-u-boot-current-orangepi3b_1.0.6_arm64/usr/lib/linux-u-boot-current-orangepi3b_1.0.6_arm64等目录
mkdir -p $uboottempdir/$uboot_name/usr/lib/{u-boot,$uboot_name} $uboottempdir/$uboot_name/DEBIAN
# process compilation for one or multiple targets,读取UBOOT_TARGET_MAP变量值,一次读取一行
while read -r target; do
local target_make target_patchdir target_files
# BL31=external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb
target_make=$(cut -d';' -f1 <<< "${target}")
# target_patchdir=
target_patchdir=$(cut -d';' -f2 <<< "${target}")
# idbloader.img u-boot.itb
target_files=$(cut -d';' -f3 <<< "${target}")
# needed for multiple targets and for calling compile_uboot directly
#display_alert "Checking out to clean sources"
#improved_git checkout -f -q HEAD
if [[ $CLEAN_LEVEL == *make* ]]; then
display_alert "Cleaning" "$BOOTSOURCEDIR" "info"
(cd "${BOOTSOURCEDIR}"; make clean > /dev/null 2>&1)
fi
advanced_patch "u-boot" "$BOOTPATCHDIR" "$BOARD" "$target_patchdir" "$BRANCH" "${LINUXFAMILY}-${BOARD}-${BRANCH}"
# create patch for manual source changes
[[ $CREATE_PATCHES == yes ]] && userpatch_create "u-boot"
if [[ -n $ATFSOURCE ]]; then
cp -Rv "${atftempdir}"/*.bin .
rm -rf "${atftempdir}"
fi
# == u-boot make orangepi-3b-rk3566_defconfig ==
echo -e "\n\t== u-boot make $BOOTCONFIG ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
## 先将交叉编译工具添加到环境变量PATH,然后执行make -j4 orangepi-3b-rk3566_defconfig CROSS_COMPILE=aarch64-none-linux-gnu-
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \
'make $CTHREADS $BOOTCONFIG \
CROSS_COMPILE="$CCACHE $UBOOT_COMPILER"' 2>> "${DEST}"/${LOG_SUBPATH}/compilation.log \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
# 修改.config配置文件,比如配置BOOTDELAY
if [[ "${version}" != 2014.07 ]]; then
# orangepi specifics u-boot settings
[[ -f .config ]] && sed -i 's/CONFIG_LOCALVERSION=""/CONFIG_LOCALVERSION="-orangepi"/g' .config
[[ -f .config ]] && sed -i 's/CONFIG_LOCALVERSION_AUTO=.*/# CONFIG_LOCALVERSION_AUTO is not set/g' .config
# for modern kernel and non spi targets
if [[ ${BOOTBRANCH} =~ ^tag:v201[8-9](.*) && ${target} != "spi" && -f .config ]]; then
......
fi
if [[ ${BOARDFAMILY} == "sun50iw9" && ${BRANCH} == "next" ]]; then
......
fi
[[ -f tools/logos/udoo.bmp ]] && cp "${EXTER}"/packages/blobs/splash/udoo.bmp tools/logos/udoo.bmp
touch .scmversion
# $BOOTDELAY can be set in board family config, ensure autoboot can be stopped even if set to 0
[[ $BOOTDELAY == 0 ]] && echo -e "CONFIG_ZERO_BOOTDELAY_CHECK=y" >> .config
[[ -n $BOOTDELAY ]] && sed -i "s/^CONFIG_BOOTDELAY=.*/CONFIG_BOOTDELAY=${BOOTDELAY}/" .config || [[ -f .config ]] && echo "CONFIG_BOOTDELAY=${BOOTDELAY}" >> .config
fi
# workaround when two compilers are needed
cross_compile="CROSS_COMPILE=$CCACHE $UBOOT_COMPILER";
[[ -n $UBOOT_TOOLCHAIN2 ]] && cross_compile="ORANGEPI=foe"; # empty parameter is not allowed
# == u-boot make BL31=/work/sambashare/rk3566/orangepi-build/external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb ==
echo -e "\n\t== u-boot make $target_make ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
## 先将交叉编译工具添加到环境变量PATH,然后执行make BL31=/work/sambashare/rk3566/orangepi-build/external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb -j4 CROSS_COMPILE=aarch64-none-linux-gnu-
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \
'make $target_make $CTHREADS \
"${cross_compile}"' 2>>"${DEST}"/${LOG_SUBPATH}/compilation.log \
${PROGRESS_LOG_TO_FILE:+' | tee -a "${DEST}"/${LOG_SUBPATH}/compilation.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Compiling u-boot..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "U-boot compilation failed"
# 执行uboot_custom_postprocess函数
[[ $(type -t uboot_custom_postprocess) == function ]] && uboot_custom_postprocess
# copy files to build directory,拷贝编译出来的文件到构建目录
for f in $target_files; do
local f_src
f_src=$(cut -d':' -f1 <<< "${f}")
if [[ $f == *:* ]]; then
local f_dst
f_dst=$(cut -d':' -f2 <<< "${f}")
else
local f_dst
f_dst=$(basename "${f_src}")
fi
[[ ! -f $f_src ]] && exit_with_error "U-boot file not found" "$(basename "${f_src}")"
if [[ "${version}" =~ 2014.07|2011.09 ]]; then
cp "${f_src}" "$uboottempdir/packout/${f_dst}"
else
cp "${f_src}" "$uboottempdir/${uboot_name}/usr/lib/${uboot_name}/${f_dst}"
fi
done
done <<< "$UBOOT_TARGET_MAP"
# 未定义
if [[ $PACK_UBOOT == "yes" ]];then
if [[ $BOARDFAMILY =~ sun50iw1 ]]; then
......
else
pack_uboot
cp $uboottempdir/packout/{boot0_sdcard.fex,boot_package.fex} "${SRC}/.tmp/${uboot_name}/usr/lib/${uboot_name}/"
cp $uboottempdir/packout/dts/${BOARD}-u-boot.dts "${SRC}/.tmp/${uboot_name}/usr/lib/u-boot/"
fi
cd "${ubootdir}" || exit
fi
# declare -f on non-defined function does not do anything
cat <<-EOF > "$uboottempdir/${uboot_name}/usr/lib/u-boot/platform_install.sh"
DIR=/usr/lib/$uboot_name
$(declare -f write_uboot_platform)
$(declare -f write_uboot_platform_mtd)
$(declare -f setup_write_uboot_platform)
EOF
# set up control file
cat <<-EOF > "$uboottempdir/${uboot_name}/DEBIAN/control"
Package: linux-u-boot-${BOARD}-${BRANCH}
Version: $REVISION
Architecture: $ARCH
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Installed-Size: 1
Section: kernel
Priority: optional
Provides: orangepi-u-boot
Replaces: orangepi-u-boot
Conflicts: orangepi-u-boot, u-boot-sunxi
Description: Uboot loader $version
EOF
# copy config file to the package
# useful for FEL boot with overlayfs_wrapper
[[ -f .config && -n $BOOTCONFIG ]] && cp .config "$uboottempdir/${uboot_name}/usr/lib/u-boot/${BOOTCONFIG}"
# copy license files from typical locations
[[ -f COPYING ]] && cp COPYING "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
[[ -f Licenses/README ]] && cp Licenses/README "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
[[ -n $atftempdir && -f $atftempdir/license.md ]] && cp "${atftempdir}/license.md" "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE.atf"
display_alert "Building deb" "${uboot_name}.deb" "info"
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "$uboottempdir/${uboot_name}" "$uboottempdir/${uboot_name}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
rm -rf "$uboottempdir/${uboot_name}"
[[ -n $atftempdir ]] && rm -rf "${atftempdir}"
[[ ! -f $uboottempdir/${uboot_name}.deb ]] && exit_with_error "Building u-boot package failed"
rsync --remove-source-files -rq "$uboottempdir/${uboot_name}.deb" "${DEB_STORAGE}/u-boot/"
rm -rf "$uboottempdir"
fi
}
u-boot
编译包含两部分:配置和编译。
4.1.1 配置
先将交叉编译工具添加到环境变量PATH
,然后执行make -j4 orangepi-3b-rk3566_defconfig CROSS_COMPILE=aarch64-none-linux-gnu-
;
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \
'make $CTHREADS $BOOTCONFIG \
CROSS_COMPILE="$CCACHE $UBOOT_COMPILER"' 2>> "${DEST}"/${LOG_SUBPATH}/compilation.log \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
eval
命令在shell
中用于执行参数中的命令,并将结果作为一个命令来执行。主要功能包括:
- 执行字符串:
eval
命令会将其参数作为shell
命令执行,这在需要动态生成命令或者执行复杂的命令字符串时非常有用; - 变量展开:如果参数中包含变量,
eval
命令会先展开这些变量再执行命令。这使得可以在运行时动态地构造命令; - 处理引号:
eval
可以处理包括引号在内的特殊字符,使得shell
可以正确解析参数中的命令和变量。
env
命令主要用于设置和显示环境变量。它的主要功能包括:
- 设置环境变量:可以在命令执行前设置一个或多个环境变量;
- 显示当前环境变量:如果不带参数,
env
命令会显示当前shell
的所有环境变量及其值; - 运行命令时设置环境变量:可以在命令执行时暂时设置环境变量,而不是永久更改
shell
环境。
4.1.2 编译
先将交叉编译工具添加到环境变量PATH
,然后执行make BL31=/work/sambashare/rk3566/orangepi-build/external/cache/sources/rkbin-tools/rk35/rk3568_bl31_v1.28.elf spl/u-boot-spl.bin u-boot.dtb u-boot.itb -j4 CROSS_COMPILE=aarch64-none-linux-gnu-
;
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${toolchain2}:${PATH}" \
'make $target_make $CTHREADS \
"${cross_compile}"' 2>>"${DEST}"/${LOG_SUBPATH}/compilation.log \
${PROGRESS_LOG_TO_FILE:+' | tee -a "${DEST}"/${LOG_SUBPATH}/compilation.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Compiling u-boot..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
4.1.3 uboot_custom_postprocess
首先执行uboot_custom_postprocess
,该函数会在u-boot
编译完成后执行,主要做一些后置处理;
[[ $(type -t uboot_custom_postprocess) == function ]] && uboot_custom_postprocess
# copy files to build directory
# target_files=idbloader.img u-boot.itb
for f in $target_files; do
local f_src
f_src=$(cut -d':' -f1 <<< "${f}")
if [[ $f == *:* ]]; then
local f_dst
f_dst=$(cut -d':' -f2 <<< "${f}")
else
local f_dst
f_dst=$(basename "${f_src}")
fi
[[ ! -f $f_src ]] && exit_with_error "U-boot file not found" "$(basename "${f_src}")"
if [[ "${version}" =~ 2014.07|2011.09 ]]; then
cp "${f_src}" "$uboottempdir/packout/${f_dst}"
else
cp "${f_src}" "$uboottempdir/${uboot_name}/usr/lib/${uboot_name}/${f_dst}"
fi
done
接着从<SDK>/u-boot/v2017.09-rk3588
拷贝idbloader.img
、 u-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.bin
和spl/u-boot-spl.bin
文件,如果配置了BOOT_SUPPORT_SPI
:
-
那么
uboot_custom_postprocess
调用mkimage
根据external/cache/sources/rkbin-tools/rk35/rk3566_ddr_1056MHz_v1.10.bin
和spl/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_platform
、write_uboot_platform_mtd
、setup_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_arm64
的Debian
软件包,并将其复制到指定的目标存储位置 <SDK>/output/debs/u-boot/
。
# copy config file to the package
# useful for FEL boot with overlayfs_wrapper, 将.config 复制到 ${uboottempdir}/${uboot_name}/usr/lib/u-boot/orangepi-3b-rk3566_defconfig
[[ -f .config && -n $BOOTCONFIG ]] && cp .config "$uboottempdir/${uboot_name}/usr/lib/u-boot/${BOOTCONFIG}"
# copy license files from typical locations,将COPYING复制${uboot_name}/usr/lib/u-boot/LICENSE
[[ -f COPYING ]] && cp COPYING "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
# 无
[[ -f Licenses/README ]] && cp Licenses/README "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE"
# 无
[[ -n $atftempdir && -f $atftempdir/license.md ]] && cp "${atftempdir}/license.md" "$uboottempdir/${uboot_name}/usr/lib/u-boot/LICENSE.atf"
# 使用fakeroot命令打包${uboottempdir}/${uboot_name}目录为${uboottempdir}/${uboot_name}.deb 文件
display_alert "Building deb" "${uboot_name}.deb" "info"
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "$uboottempdir/${uboot_name}" "$uboottempdir/${uboot_name}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
# 清理临时目录
rm -rf "$uboottempdir/${uboot_name}"
[[ -n $atftempdir ]] && rm -rf "${atftempdir}"
[[ ! -f $uboottempdir/${uboot_name}.deb ]] && exit_with_error "Building u-boot package failed"
# 将构建好的软件包复制到目标存储位置
rsync --remove-source-files -rq "$uboottempdir/${uboot_name}.deb" "${DEB_STORAGE}/u-boot/"
# 最终清理临时目录
rm -rf "$uboottempdir"
经过上面一系列的操作,会在临时目录$uboottempdir
下创建一个linux-u-boot-current-orangepi3b_1.0.6_arm64
文件夹,内部包含如下文件;
usr/
└── lib
├── linux-u-boot-current-orangepi3b_1.0.6_arm64
│ ├── idbloader.img
│ ├── rkspi_loader.img
│ └── u-boot.itb
└── u-boot
├── LICENSE
├── orangepi-3b-rk3566_defconfig
└── platform_install.sh
最终会通过fakeroot
工具将linux-u-boot-current-orangepi3b_1.0.6_arm64
目录打包成linux-u-boot-current-orangepi3b_1.0.6_arm64.deb
,并拷贝到<SDK>/output/debs/u-boot/
。
4.2 compile_kernel
compile_kernel
函数实现了kernel
源码的编译并生成deb
软件包。由于脚本内容较多,以下脚本会提出一些不会执行的代码,具体如下:
点击查看代码
compile_kernel()
{
if [[ $CLEAN_LEVEL == *make* ]]; then
display_alert "Cleaning" "$LINUXSOURCEDIR" "info"
(cd ${LINUXSOURCEDIR}; make ARCH="${ARCHITECTURE}" clean >/dev/null 2>&1)
fi
if [[ $USE_OVERLAYFS == yes ]]; then
local kerneldir
kerneldir=$(overlayfs_wrapper "wrap" "$LINUXSOURCEDIR" "kernel_${LINUXFAMILY}_${BRANCH}")
else
local kerneldir="$LINUXSOURCEDIR"
fi
cd "${kerneldir}" || exit
rm -f localversion
# read kernel version
local version hash
version=$(grab_version "$kerneldir")
# read kernel git hash
hash=$(improved_git --git-dir="$kerneldir"/.git rev-parse HEAD)
# Apply a series of patches if a series file exists
if test -f "${EXTER}"/patch/kernel/${KERNELPATCHDIR}/series.conf; then
display_alert "series.conf file visible. Apply"
series_conf="${SRC}"/patch/kernel/${KERNELPATCHDIR}/series.conf
# apply_patch_series <target dir> <full path to series file>
apply_patch_series "${kerneldir}" "$series_conf"
fi
# build 3rd party drivers
# compilation_prepare
advanced_patch "kernel" "$KERNELPATCHDIR" "$BOARD" "" "$BRANCH" "$LINUXFAMILY-$BRANCH"
# create patch for manual source changes in debug mode
[[ $CREATE_PATCHES == yes ]] && userpatch_create "kernel"
# re-read kernel version after patching
local version
version=$(grab_version "$kerneldir")
display_alert "Compiling $BRANCH kernel" "$version" "info"
# compare with the architecture of the current Debian node
# if it matches we use the system compiler
if $(dpkg-architecture -e "${ARCH}"); then
display_alert "Native compilation"
elif [[ $(dpkg --print-architecture) == amd64 ]]; then
local toolchain
toolchain=$(find_toolchain "$KERNEL_COMPILER" "$KERNEL_USE_GCC")
[[ -z $toolchain ]] && exit_with_error "Could not find required toolchain" "${KERNEL_COMPILER}gcc $KERNEL_USE_GCC"
else
exit_with_error "Architecture [$ARCH] is not supported"
fi
display_alert "Compiler version" "${KERNEL_COMPILER}gcc $(eval env PATH="${toolchain}:${PATH}" "${KERNEL_COMPILER}gcc" -dumpversion)" "info"
# copy kernel config
if [[ $KERNEL_KEEP_CONFIG == yes && -f "${DEST}"/config/$LINUXCONFIG.config ]]; then
display_alert "Using previous kernel config" "${DEST}/config/$LINUXCONFIG.config" "info"
cp -p "${DEST}/config/${LINUXCONFIG}.config" .config
else
if [[ -f $USERPATCHES_PATH/$LINUXCONFIG.config ]]; then
display_alert "Using kernel config provided by user" "userpatches/$LINUXCONFIG.config" "info"
cp -p "${USERPATCHES_PATH}/${LINUXCONFIG}.config" .config
else
display_alert "Using kernel config file" "${EXTER}/config/kernel/$LINUXCONFIG.config" "info"
cp -p "${EXTER}/config/kernel/${LINUXCONFIG}.config" .config
fi
fi
call_extension_method "custom_kernel_config" << 'CUSTOM_KERNEL_CONFIG'
*Kernel .config is in place, still clean from git version*
Called after ${LINUXCONFIG}.config is put in place (.config).
Before any olddefconfig any Kconfig make is called.
A good place to customize the .config directly.
CUSTOM_KERNEL_CONFIG
# hack for deb builder. To pack what's missing in headers pack.
cp "$EXTER"/patch/misc/headers-debian-byteshift.patch /tmp
if [[ $KERNEL_CONFIGURE != yes ]]; then
if [[ $BRANCH == legacy && ! $BOARDFAMILY =~ "rockchip-rk3588"|"rockchip-rk356x" ]]; then
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" silentoldconfig'
else
# TODO: check if required
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" olddefconfig'
fi
else
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make $CTHREADS ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" oldconfig'
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make $CTHREADS ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" ${KERNEL_MENUCONFIG:-menuconfig}'
[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Error kernel menuconfig failed"
# store kernel config in easily reachable place
display_alert "Exporting new kernel config" "$DEST/config/$LINUXCONFIG.config" "info"
cp .config "${DEST}/config/${LINUXCONFIG}.config"
cp .config "${EXTER}/config/kernel/${LINUXCONFIG}.config"
# export defconfig too if requested
if [[ $KERNEL_EXPORT_DEFCONFIG == yes ]]; then
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make ARCH=$ARCHITECTURE CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" savedefconfig'
[[ -f defconfig ]] && cp defconfig "${DEST}/config/${LINUXCONFIG}.defconfig"
fi
fi
# create linux-source package - with already patched sources
# We will build this package first and clear the memory.
if [[ $BUILD_KSRC != no ]]; then
create_linux-source_package
fi
echo -e "\n\t== kernel ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make $CTHREADS ARCH=$ARCHITECTURE \
CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" \
$SRC_LOADADDR \
LOCALVERSION="-$LINUXFAMILY" \
$KERNEL_IMAGE_TYPE ${KERNEL_EXTRA_TARGETS:-modules dtbs} 2>>$DEST/${LOG_SUBPATH}/compilation.log' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" \
--progressbox "Compiling kernel..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
if [[ ${PIPESTATUS[0]} -ne 0 || ! -f arch/$ARCHITECTURE/boot/$KERNEL_IMAGE_TYPE ]]; then
grep -i error $DEST/${LOG_SUBPATH}/compilation.log
exit_with_error "Kernel was not built" "@host"
fi
# different packaging for 4.3+
if linux-version compare "${version}" ge 4.3; then
local kernel_packing="bindeb-pkg"
else
local kernel_packing="deb-pkg"
fi
#if [[ $BRANCH == legacy && $LINUXFAMILY =~ sun50iw2|sun50iw6|sun50iw9 ]]; then
# make -C modules/gpu LICHEE_MOD_DIR=${SRC}/.tmp/gpu_modules_${LINUXFAMILY} LICHEE_KDIR=${kerneldir} CROSS_COMPILE=$toolchain/$KERNEL_COMPILER ARCH=$ARCHITECTURE
#fi
display_alert "Creating packages"
# produce deb packages: image, headers, firmware, dtb
echo -e "\n\t== deb packages: image, headers, firmware, dtb ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
eval CCACHE_BASEDIR="$(pwd)" env PATH="${toolchain}:${PATH}" \
'make $CTHREADS $kernel_packing \
KDEB_PKGVERSION=$REVISION \
KDEB_COMPRESS=${DEB_COMPRESS} \
BRANCH=$BRANCH \
LOCALVERSION="-${LINUXFAMILY}" \
KBUILD_DEBARCH=$ARCH \
ARCH=$ARCHITECTURE \
DEBFULLNAME="$MAINTAINER" \
DEBEMAIL="$MAINTAINERMAIL" \
CROSS_COMPILE="$CCACHE $KERNEL_COMPILER" 2>>$DEST/${LOG_SUBPATH}/compilation.log' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/compilation.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Creating kernel packages..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
cd .. || exit
# remove firmare image packages here - easier than patching ~40 packaging scripts at once
rm -f linux-firmware-image-*.deb
rsync --remove-source-files -rq ./*.deb "${DEB_STORAGE}/" || exit_with_error "Failed moving kernel DEBs"
}
编译完成之后,会生成linux-image-current-rockchip-rk356x_1.0.6_arm64.deb
,该文件是由<SDK>/kernel/orange-pi-6.6-rk35xx/debian/tmp/
打包生成的;
root@ubuntu:/work/sambashare/rk3566/orangepi-build/kernel/orange-pi-6.6-rk35xx/$ ll debian/tmp/
drwxr-xr-x 2 root root 4096 8月 4 09:37 boot/
drwxr-xr-x 2 root root 4096 8月 4 09:37 DEBIAN/
drwxr-xr-x 3 root root 4096 8月 4 09:37 etc/
drwxr-xr-x 3 root root 4096 8月 4 09:37 lib/
drwxr-xr-x 4 root root 4096 8月 4 09:37 usr/=
可以看到这个目录和linux-image-current-rockchip-rk356x_1.0.6_arm64.deb
解压后的内容基本是一致的。有关kernel deb
更多实现细节可以查看./scripts/package/builddeb
脚本。
4.3 compile_firmware
点击查看代码
compile_firmware()
{
display_alert "Merging and packaging linux firmware" "@host" "info"
local firmwaretempdir plugin_dir
firmwaretempdir=$(mktemp -d)
chmod 700 ${firmwaretempdir}
trap "ret=\$?; rm -rf \"${firmwaretempdir}\" ; exit \$ret" 0 1 2 3 15
plugin_dir="orangepi-firmware${FULL}"
mkdir -p "${firmwaretempdir}/${plugin_dir}/lib/firmware"
[[ $IGNORE_UPDATES != yes ]] && fetch_from_repo "https://github.com/orangepi-xunlong/firmware" "${EXTER}/cache/sources/orangepi-firmware-git" "branch:master"
if [[ -n $FULL ]]; then
[[ $IGNORE_UPDATES != yes ]] && fetch_from_repo "$MAINLINE_FIRMWARE_SOURCE" "${EXTER}/cache/sources/linux-firmware-git" "branch:master"
# cp : create hardlinks
cp -af --reflink=auto "${EXTER}"/cache/sources/linux-firmware-git/* "${firmwaretempdir}/${plugin_dir}/lib/firmware/"
fi
# overlay our firmware
# cp : create hardlinks
cp -af --reflink=auto "${EXTER}"/cache/sources/orangepi-firmware-git/* "${firmwaretempdir}/${plugin_dir}/lib/firmware/"
# cleanup what's not needed for sure
rm -rf "${firmwaretempdir}/${plugin_dir}"/lib/firmware/{amdgpu,amd-ucode,radeon,nvidia,matrox,.git}
cd "${firmwaretempdir}/${plugin_dir}" || exit
# set up control file
mkdir -p DEBIAN
cat <<-END > DEBIAN/control
Package: orangepi-firmware${FULL}
Version: $REVISION
Architecture: all
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Installed-Size: 1
Replaces: linux-firmware, firmware-brcm80211, firmware-ralink, firmware-samsung, firmware-realtek, orangepi-firmware${REPLACE}
Section: kernel
Priority: optional
Description: Linux firmware${FULL}
END
cd "${firmwaretempdir}" || exit
# pack
mv "orangepi-firmware${FULL}" "orangepi-firmware${FULL}_${REVISION}_all"
display_alert "Building firmware package" "orangepi-firmware${FULL}_${REVISION}_all" "info"
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "orangepi-firmware${FULL}_${REVISION}_all" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
mv "orangepi-firmware${FULL}_${REVISION}_all" "orangepi-firmware${FULL}"
rsync -rq "orangepi-firmware${FULL}_${REVISION}_all.deb" "${DEB_STORAGE}/"
# remove temp directory
rm -rf "${firmwaretempdir}"
}
4.4 compile_orangepi-config
compile_orangepi-config
函数是用来编译和打包Orange Pi
的配置工具 orangepi-config
到一个Debian
包 ;
compile_orangepi-config()
{
local tmpdir=${SRC}/.tmp/orangepi-config_${REVISION}_all
display_alert "Building deb" "orangepi-config" "info"
mkdir -p "${tmpdir}"/{DEBIAN,usr/bin/,usr/sbin/,usr/lib/orangepi-config/}
# set up control file
cat <<-END > "${tmpdir}"/DEBIAN/control
Package: orangepi-config
Version: $REVISION
Architecture: all
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Replaces: orangepi-bsp
Depends: bash, iperf3, psmisc, curl, bc, expect, dialog, pv, \
debconf-utils, unzip, build-essential, html2text, apt-transport-https, html2text, dirmngr, software-properties-common
Recommends: orangepi-bsp
Suggests: libpam-google-authenticator, qrencode, network-manager, sunxi-tools
Section: utils
Priority: optional
Description: Orange Pi configuration utility
END
install -m 755 $EXTER/cache/sources/orangepi-config/scripts/tv_grab_file $tmpdir/usr/bin/tv_grab_file
install -m 755 $EXTER/cache/sources/orangepi-config/debian-config $tmpdir/usr/sbin/orangepi-config
install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-jobs $tmpdir/usr/lib/orangepi-config/jobs.sh
install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-submenu $tmpdir/usr/lib/orangepi-config/submenu.sh
install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-functions $tmpdir/usr/lib/orangepi-config/functions.sh
install -m 644 $EXTER/cache/sources/orangepi-config/debian-config-functions-network $tmpdir/usr/lib/orangepi-config/functions-network.sh
install -m 755 $EXTER/cache/sources/orangepi-config/softy $tmpdir/usr/sbin/softy
# fallback to replace orangepi-config in BSP
ln -sf /usr/sbin/orangepi-config $tmpdir/usr/bin/orangepi-config
ln -sf /usr/sbin/softy "${tmpdir}"/usr/bin/softy
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "${tmpdir}" >/dev/null
rsync --remove-source-files -rq "${tmpdir}.deb" "${DEB_STORAGE}/"
rm -rf "${tmpdir}"
}
五、makeboarddeb.sh
makeboarddeb.sh
文件只实现了 create_board_package
函数,用于构建BSP
包orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
,需要注意的是这个包并不是rootfs
镜像文件。
点击查看代码
#!/bin/bash
create_board_package()
{
display_alert "Creating board support package for CLI" "$CHOSEN_ROOTFS" "info"
# 创建临时目录
bsptempdir=$(mktemp -d)
# 修改权限
chmod 700 ${bsptempdir}
# 设置信号处理器,当收到信号0/1/2/3/15 删除临时目录
trap "rm -rf \"${bsptempdir}\" ; exit 0" 0 1 2 3 15
# 设置目标目录{bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
local destination=${bsptempdir}/${RELEASE}/${BSP_CLI_PACKAGE_FULLNAME}
# 创建目录{bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64/DEBIAN
mkdir -p "${destination}"/DEBIAN
cd $destination
# copy general overlay from packages/bsp-cli
copy_all_packages_files_for "bsp-cli"
# install copy of boot script & environment file
if [[ "${BOOTCONFIG}" != "none" ]]; then
# @TODO: add extension method bsp_prepare_bootloader(), refactor into u-boot extension
local bootscript_src=${BOOTSCRIPT%%:*}
local bootscript_dst=${BOOTSCRIPT##*:}
mkdir -p "${destination}"/usr/share/orangepi/
# create extlinux config file
if [[ $SRC_EXTLINUX != yes ]]; then
if [ -f "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" ]; then
cp "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
else
cp "${EXTER}/config/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
fi
[[ -n $BOOTENV_FILE && -f $SRC/config/bootenv/$BOOTENV_FILE ]] && \
cp "${EXTER}/config/bootenv/${BOOTENV_FILE}" "${destination}"/usr/share/orangepi/orangepiEnv.txt
fi
# add configuration for setting uboot environment from userspace with: fw_setenv fw_printenv
if [[ -n $UBOOT_FW_ENV ]]; then
UBOOT_FW_ENV=($(tr ',' ' ' <<< "$UBOOT_FW_ENV"))
mkdir -p "${destination}"/etc
echo "# Device to access offset env size" > "${destination}"/etc/fw_env.config
echo "/dev/mmcblk0 ${UBOOT_FW_ENV[0]} ${UBOOT_FW_ENV[1]}" >> "${destination}"/etc/fw_env.config
fi
fi
# Replaces: base-files is needed to replace /etc/update-motd.d/ files on Xenial
# Replaces: unattended-upgrades may be needed to replace /etc/apt/apt.conf.d/50unattended-upgrades
# (distributions provide good defaults, so this is not needed currently)
# Depends: linux-base is needed for "linux-version" command in initrd cleanup script
# Depends: fping is needed for orangepimonitor to upload orangepi-hardware-monitor.log
cat <<-EOF > "${destination}"/DEBIAN/control
Package: ${BSP_CLI_PACKAGE_NAME}
Version: $REVISION
Architecture: $ARCH
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Installed-Size: 1
Section: kernel
Priority: optional
Depends: bash, linux-base, u-boot-tools, initramfs-tools, lsb-release, fping
Provides: linux-${RELEASE}-root-legacy-$BOARD, linux-${RELEASE}-root-current-$BOARD, linux-${RELEASE}-root-next-$BOARD
Suggests: orangepi-config
Replaces: zram-config, base-files, orangepi-tools-$RELEASE, linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
Breaks: linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
Recommends: bsdutils, parted, util-linux, toilet
Description: OrangePi board support files for $BOARD
EOF
# set up pre install script
cat <<-EOF > "${destination}"/DEBIAN/preinst
#!/bin/sh
# tell people to reboot at next login
[ "\$1" = "upgrade" ] && touch /var/run/.reboot_required
# convert link to file
if [ -L "/etc/network/interfaces" ]; then
cp /etc/network/interfaces /etc/network/interfaces.tmp
rm /etc/network/interfaces
mv /etc/network/interfaces.tmp /etc/network/interfaces
fi
# fixing ramdisk corruption when using lz4 compression method
sed -i "s/^COMPRESS=.*/COMPRESS=gzip/" /etc/initramfs-tools/initramfs.conf
# swap
grep -q vm.swappiness /etc/sysctl.conf
case \$? in
0)
sed -i 's/vm\.swappiness.*/vm.swappiness=100/' /etc/sysctl.conf
;;
*)
echo vm.swappiness=100 >>/etc/sysctl.conf
;;
esac
sysctl -p >/dev/null 2>&1
# disable deprecated services
[ -f "/etc/profile.d/activate_psd_user.sh" ] && rm /etc/profile.d/activate_psd_user.sh
[ -f "/etc/profile.d/check_first_login.sh" ] && rm /etc/profile.d/check_first_login.sh
[ -f "/etc/profile.d/check_first_login_reboot.sh" ] && rm /etc/profile.d/check_first_login_reboot.sh
[ -f "/etc/profile.d/ssh-title.sh" ] && rm /etc/profile.d/ssh-title.sh
[ -f "/etc/update-motd.d/10-header" ] && rm /etc/update-motd.d/10-header
[ -f "/etc/update-motd.d/30-sysinfo" ] && rm /etc/update-motd.d/30-sysinfo
[ -f "/etc/update-motd.d/35-tips" ] && rm /etc/update-motd.d/35-tips
[ -f "/etc/update-motd.d/40-updates" ] && rm /etc/update-motd.d/40-updates
[ -f "/etc/update-motd.d/98-autoreboot-warn" ] && rm /etc/update-motd.d/98-autoreboot-warn
[ -f "/etc/update-motd.d/99-point-to-faq" ] && rm /etc/update-motd.d/99-point-to-faq
[ -f "/etc/update-motd.d/80-esm" ] && rm /etc/update-motd.d/80-esm
[ -f "/etc/update-motd.d/80-livepatch" ] && rm /etc/update-motd.d/80-livepatch
[ -f "/etc/apt/apt.conf.d/02compress-indexes" ] && rm /etc/apt/apt.conf.d/02compress-indexes
[ -f "/etc/apt/apt.conf.d/02periodic" ] && rm /etc/apt/apt.conf.d/02periodic
[ -f "/etc/apt/apt.conf.d/no-languages" ] && rm /etc/apt/apt.conf.d/no-languages
[ -f "/etc/init.d/armhwinfo" ] && rm /etc/init.d/armhwinfo
[ -f "/etc/logrotate.d/armhwinfo" ] && rm /etc/logrotate.d/armhwinfo
[ -f "/etc/init.d/firstrun" ] && rm /etc/init.d/firstrun
[ -f "/etc/init.d/resize2fs" ] && rm /etc/init.d/resize2fs
[ -f "/lib/systemd/system/firstrun-config.service" ] && rm /lib/systemd/system/firstrun-config.service
[ -f "/lib/systemd/system/firstrun.service" ] && rm /lib/systemd/system/firstrun.service
[ -f "/lib/systemd/system/resize2fs.service" ] && rm /lib/systemd/system/resize2fs.service
[ -f "/usr/lib/orangepi/apt-updates" ] && rm /usr/lib/orangepi/apt-updates
[ -f "/usr/lib/orangepi/firstrun-config.sh" ] && rm /usr/lib/orangepi/firstrun-config.sh
# fix for https://bugs.launchpad.net/ubuntu/+source/lightdm-gtk-greeter/+bug/1897491
[ -d "/var/lib/lightdm" ] && (chown -R lightdm:lightdm /var/lib/lightdm ; chmod 0750 /var/lib/lightdm)
exit 0
EOF
chmod 755 "${destination}"/DEBIAN/preinst
# postrm script
cat <<-EOF > "${destination}"/DEBIAN/postrm
#!/bin/sh
if [ remove = "\$1" ] || [ abort-install = "\$1" ]; then
systemctl disable orangepi-hardware-monitor.service orangepi-hardware-optimize.service >/dev/null 2>&1
systemctl disable orangepi-zram-config.service orangepi-ramlog.service >/dev/null 2>&1
fi
exit 0
EOF
chmod 755 "${destination}"/DEBIAN/postrm
# set up post install script
cat <<-EOF > "${destination}"/DEBIAN/postinst
#!/bin/sh
#
# ${BOARD} BSP post installation script
#
[ -f /etc/lib/systemd/system/orangepi-ramlog.service ] && systemctl --no-reload enable orangepi-ramlog.service
# check if it was disabled in config and disable in new service
if [ -n "\$(grep -w '^ENABLED=false' /etc/default/log2ram 2> /dev/null)" ]; then
sed -i "s/^ENABLED=.*/ENABLED=false/" /etc/default/orangepi-ramlog
fi
# fix boot delay "waiting for suspend/resume device"
if [ -f "/etc/initramfs-tools/initramfs.conf" ]; then
if ! grep --quiet "RESUME=none" /etc/initramfs-tools/initramfs.conf; then
echo "RESUME=none" >> /etc/initramfs-tools/initramfs.conf
fi
fi
EOF
# install bootscripts if they are not present. Fix upgrades from old images
if [[ $FORCE_BOOTSCRIPT_UPDATE == yes ]]; then
cat <<-EOF >> "${destination}"/DEBIAN/postinst
if [ true ]; then
# this package recreate boot scripts
EOF
else
cat <<-EOF >> "${destination}"/DEBIAN/postinst
if [ ! -f /boot/$bootscript_dst ]; then
# if boot script does not exits its recreated
EOF
fi
cat <<-EOF >> "${destination}"/DEBIAN/postinst
# move bootscript to /usr/share/orangepi
# create a backup
[ -f /etc/orangepi-release ] && . /etc/orangepi-release
[ -z \${VERSION} ] && VERSION=$(echo \`date +%s\`)
if [ -f /boot/$bootscript_dst ]; then
cp /boot/$bootscript_dst /usr/share/orangepi/${bootscript_dst}-\${VERSION} >/dev/null 2>&1
echo "NOTE: You can find previous bootscript versions in /usr/share/orangepi !"
fi
# cleanup old bootscript backup
ls /usr/share/orangepi/boot.cmd-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --
ls /usr/share/orangepi/boot.ini-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --
echo "Recreating boot script"
cp /usr/share/orangepi/$bootscript_dst /boot >/dev/null 2>&1
rootdev=\$(sed -e 's/^.*root=//' -e 's/ .*\$//' < /proc/cmdline)
rootfstype=\$(sed -e 's/^.*rootfstype=//' -e 's/ .*$//' < /proc/cmdline)
# recreate orangepiEnv.txt if it and extlinux does not exists
if [ ! -f /boot/orangepiEnv.txt ] && [ ! -f /boot/extlinux/extlinux.conf ]; then
cp /usr/share/orangepi/orangepiEnv.txt /boot >/dev/null 2>&1
echo "rootdev="\$rootdev >> /boot/orangepiEnv.txt
echo "rootfstype="\$rootfstype >> /boot/orangepiEnv.txt
fi
[ -f /boot/boot.ini ] && sed -i "s/setenv rootdev.*/setenv rootdev \\"\$rootdev\\"/" /boot/boot.ini
[ -f /boot/boot.ini ] && sed -i "s/setenv rootfstype.*/setenv rootfstype \\"\$rootfstype\\"/" /boot/boot.ini
[ -f /boot/boot.cmd ] && mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr >/dev/null 2>&1
fi
[ ! -f "/etc/network/interfaces" ] && [ -f "/etc/network/interfaces.default" ] && cp /etc/network/interfaces.default /etc/network/interfaces
ln -sf /var/run/motd /etc/motd
rm -f /etc/update-motd.d/00-header /etc/update-motd.d/10-help-text
if [ -f "/boot/bin/$BOARD.bin" ] && [ ! -f "/boot/script.bin" ]; then ln -sf bin/$BOARD.bin /boot/script.bin >/dev/null 2>&1 || cp /boot/bin/$BOARD.bin /boot/script.bin; fi
if [ ! -f "/etc/default/orangepi-motd" ]; then
mv /etc/default/orangepi-motd.dpkg-dist /etc/default/orangepi-motd
fi
if [ ! -f "/etc/default/orangepi-ramlog" ] && [ -f /etc/default/orangepi-ramlog.dpkg-dist ]; then
mv /etc/default/orangepi-ramlog.dpkg-dist /etc/default/orangepi-ramlog
fi
if [ ! -f "/etc/default/orangepi-zram-config" ] && [ -f /etc/default/orangepi-zram-config.dpkg-dist ]; then
mv /etc/default/orangepi-zram-config.dpkg-dist /etc/default/orangepi-zram-config
fi
if [ -L "/usr/lib/chromium-browser/master_preferences.dpkg-dist" ]; then
mv /usr/lib/chromium-browser/master_preferences.dpkg-dist /usr/lib/chromium-browser/master_preferences
fi
# Read release value
if [ -f /etc/lsb-release ]; then
RELEASE=\$(cat /etc/lsb-release | grep CODENAME | cut -d"=" -f2 | sed 's/.*/\u&/')
sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${VENDOR} $REVISION "\${RELEASE}"\"/" /etc/os-release
echo "${VENDOR} ${REVISION} \${RELEASE} \\l \n" > /etc/issue
echo "${VENDOR} ${REVISION} \${RELEASE}" > /etc/issue.net
fi
# Reload services
systemctl --no-reload enable orangepi-hardware-monitor.service orangepi-hardware-optimize.service orangepi-zram-config.service >/dev/null 2>&1
exit 0
EOF
chmod 755 "${destination}"/DEBIAN/postinst
# won't recreate files if they were removed by user
# TODO: Add proper handling for updated conffiles
#cat <<-EOF > "${destination}"/DEBIAN/conffiles
#EOF
# copy common files from a premade directory structure
rsync -a "${EXTER}"/packages/bsp/common/* ${destination}
# trigger uInitrd creation after installation, to apply
# /etc/initramfs/post-update.d/99-uboot
cat <<-EOF > "${destination}"/DEBIAN/triggers
activate update-initramfs
EOF
# copy distribution support status
local releases=($(find ${EXTER}/config/distributions -mindepth 1 -maxdepth 1 -type d))
for i in ${releases[@]}
do
echo "$(echo $i | sed 's/.*\///')=$(cat $i/support)" >> "${destination}"/etc/orangepi-distribution-status
done
# armhwinfo, firstrun, orangepimonitor, etc. config file
cat <<-EOF > "${destination}"/etc/orangepi-release
# PLEASE DO NOT EDIT THIS FILE
BOARD=${BOARD}
BOARD_NAME="$BOARD_NAME"
BOARDFAMILY=${BOARDFAMILY}
BUILD_REPOSITORY_URL=${BUILD_REPOSITORY_URL}
BUILD_REPOSITORY_COMMIT=${BUILD_REPOSITORY_COMMIT}
DISTRIBUTION_CODENAME=${RELEASE}
DISTRIBUTION_STATUS=${DISTRIBUTION_STATUS}
VERSION=${REVISION}
LINUXFAMILY=${LINUXFAMILY}
ARCH=${ARCHITECTURE}
IMAGE_TYPE=$IMAGE_TYPE
BOARD_TYPE=$BOARD_TYPE
INITRD_ARCH=${INITRD_ARCH}
KERNEL_IMAGE_TYPE=${KERNEL_IMAGE_TYPE}
BRANCH=${BRANCH}
EOF
# this is required for NFS boot to prevent deconfiguring the network on shutdown
sed -i 's/#no-auto-down/no-auto-down/g' "${destination}"/etc/network/interfaces.default
if [[ ( $LINUXFAMILY == sun8i ) && $BRANCH == legacy ]]; then
# add mpv config for vdpau_sunxi
mkdir -p "${destination}"/etc/mpv/
cp "${EXTER}"/packages/bsp/mpv/mpv_sunxi.conf "${destination}"/etc/mpv/mpv.conf
echo "export VDPAU_OSD=1" > "${destination}"/etc/profile.d/90-vdpau.sh
chmod 755 "${destination}"/etc/profile.d/90-vdpau.sh
fi
if [[ $LINUXFAMILY == sunxi* ]]; then
# add mpv config for x11 output - slow, but it works compared to no config at all
# TODO: Test which output driver is better with DRM
mkdir -p "${destination}"/etc/mpv/
cp "${EXTER}"/packages/bsp/mpv/mpv_mainline.conf "${destination}"/etc/mpv/mpv.conf
fi
case $RELEASE in
xenial)
if [[ $BRANCH == legacy && $LINUXFAMILY == sun8i ]]; then
# this is required only for old kernels
# not needed for Stretch since there will be no Stretch images with kernels < 4.4
mkdir -p "${destination}"/lib/systemd/system/haveged.service.d/
cp "${EXTER}"/packages/bsp/10-no-new-privileges.conf "${destination}"/lib/systemd/system/haveged.service.d/
fi
;;
esac
# execute $LINUXFAMILY-specific tweaks
[[ $(type -t family_tweaks_bsp) == function ]] && family_tweaks_bsp
call_extension_method "post_family_tweaks_bsp" << 'POST_FAMILY_TWEAKS_BSP'
*family_tweaks_bsp overrrides what is in the config, so give it a chance to override the family tweaks*
This should be implemented by the config to tweak the BSP, after the board or family has had the chance to.
POST_FAMILY_TWEAKS_BSP
# add some summary to the image
fingerprint_image "${destination}/etc/orangepi.txt"
# fixing permissions (basic), reference: dh_fixperms
find "${destination}" -print0 2>/dev/null | xargs -0r chown --no-dereference 0:0
find "${destination}" ! -type l -print0 2>/dev/null | xargs -0r chmod 'go=rX,u+rw,a-s'
# create board DEB file
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "${destination}" "${destination}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
mkdir -p "${DEB_STORAGE}/${RELEASE}/"
rsync --remove-source-files -rq "${destination}.deb" "${DEB_STORAGE}/${RELEASE}/"
# cleanup
rm -rf ${bsptempdir}
}
接下来我们对该函数进行一一分析。
5.1 准备工作
最开始是一系列的准备工作,具体如下。
5.1.1 确定目标路径
创建BSP
临时目录,全路径为${bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
;
# 创建临时目录
bsptempdir=$(mktemp -d)
# 修改权限
chmod 700 ${bsptempdir}
# 设置信号处理器,当收到信号0/1/2/3/15 删除临时目录
trap "rm -rf \"${bsptempdir}\" ; exit 0" 0 1 2 3 15
# 设置目标目录${bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
local destination=${bsptempdir}/${RELEASE}/${BSP_CLI_PACKAGE_FULLNAME}
# 创建目录{bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64/DEBIAN
mkdir -p "${destination}"/DEBIAN
cd $destination
注意:后文我们就把$destination
这个目录称为bsp
目录。
5.1.2 拷贝bsp-cli
包
接着是将若干个目录复制到bsp
目录中;
# copy general overlay from packages/bsp-cli
copy_all_packages_files_for "bsp-cli"
copy_all_packages_files_for
函数定义在scripts/image-helpers.sh
;
# copy_all_packages_files_for <folder> to package
#
copy_all_packages_files_for()
{
# bsp-cli
local package_name="${1}"
# 遍历以下目录
# <SDK>/external/packages => 不存在子目录bsp-cli
# <SDK>/external/config/optional/_any_board/_packages => 不存在子目录bsp-cli
# <SDK>/external/config/optional/architectures/arm64/_packages => 不存在子目录bsp-cli
# <SDK>/external/config/optional/families/rockchip-rk356x/_packages
# <SDK>/external/config/optional/boards/orangepi3b/_packages => 不存在子目录bsp-cli
for package_src_dir in ${PACKAGES_SEARCH_ROOT_ABSOLUTE_DIRS};
do
# ..../bsp-cli
local package_dirpath="${package_src_dir}/${package_name}"
# 如果目录存在,则拷贝到${bsptempdir}/bullseye/orangepi-bsp-cli-orangepi3b_1.0.6_arm64
if [ -d "${package_dirpath}" ];
then
cp -r "${package_dirpath}/"* "${destination}/" 2> /dev/null
display_alert "Adding files from" "${package_dirpath}"
fi
done
}
这里只有external/config/optional/boards/orangepi3b/_packages/bsp-cli/
目录是存在的;
root@ubuntu:/work/sambashare/rk3566/orangepi-build# tree external/config/optional/boards/orangepi3b/_packages/bsp-cli/
external/config/optional/boards/orangepi3b/_packages/bsp-cli/
└── usr
└── bin
└── hciattach_opi
5.1.3 拷贝boot
脚本/环境遍历文件
接着是拷贝boot
脚本以及环境变量文件到bsp
目录;
# install copy of boot script & environment file, BOOTCONFIG="orangepi-3b-rk3566_defconfig"
if [[ "${BOOTCONFIG}" != "none" ]]; then
# @TODO: add extension method bsp_prepare_bootloader(), refactor into u-boot extension
# 提取冒号前的部分boot-rockchip64.cmd
local bootscript_src=${BOOTSCRIPT%%:*}
# 提取冒号后的部分boot.cmd
local bootscript_dst=${BOOTSCRIPT##*:}
# 在rootfs创建/usr/share/orangepi/目录
mkdir -p "${destination}"/usr/share/orangepi/
# create extlinux config file
if [[ $SRC_EXTLINUX != yes ]]; then
# 不会进入
if [ -f "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" ]; then
cp "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
else
# 进入
cp "${EXTER}/config/bootscripts/${bootscript_src}" "${destination}/usr/share/orangepi/${bootscript_dst}"
fi
# BOOTENV_FILE='rockchip.txt',$SRC/config/bootenv/$BOOTENV_FILE文件不存在,不会进入
[[ -n $BOOTENV_FILE && -f $SRC/config/bootenv/$BOOTENV_FILE ]] && \
cp "${EXTER}/config/bootenv/${BOOTENV_FILE}" "${destination}"/usr/share/orangepi/orangepiEnv.txt
fi
# add configuration for setting uboot environment from userspace with: fw_setenv fw_printenv,不会进入
if [[ -n $UBOOT_FW_ENV ]]; then
UBOOT_FW_ENV=($(tr ',' ' ' <<< "$UBOOT_FW_ENV"))
mkdir -p "${destination}"/etc
echo "# Device to access offset env size" > "${destination}"/etc/fw_env.config
echo "/dev/mmcblk0 ${UBOOT_FW_ENV[0]} ${UBOOT_FW_ENV[1]}" >> "${destination}"/etc/fw_env.config
fi
fi
该段脚本会就是复制<SDK>/external/config/bootscripts/boot-rockchip64.cmd
到根文件系统/usr/share/orangepi/boot.cmd
。
5.2 DEBIAN
目录
5.2.1 创建/DEBIAN/control
接着生成一个Debian
包的控制文件 (control
),包含了必要的元数据、依赖关系和建议项,确保正确的安装和管理;
# Replaces: base-files is needed to replace /etc/update-motd.d/ files on Xenial
# Replaces: unattended-upgrades may be needed to replace /etc/apt/apt.conf.d/50unattended-upgrades
# (distributions provide good defaults, so this is not needed currently)
# Depends: linux-base is needed for "linux-version" command in initrd cleanup script
# Depends: fping is needed for orangepimonitor to upload orangepi-hardware-monitor.log
cat <<-EOF > "${destination}"/DEBIAN/control
Package: ${BSP_CLI_PACKAGE_NAME}
Version: $REVISION
Architecture: $ARCH
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Installed-Size: 1
Section: kernel
Priority: optional
Depends: bash, linux-base, u-boot-tools, initramfs-tools, lsb-release, fping
Provides: linux-${RELEASE}-root-legacy-$BOARD, linux-${RELEASE}-root-current-$BOARD, linux-${RELEASE}-root-next-$BOARD
Suggests: orangepi-config
Replaces: zram-config, base-files, orangepi-tools-$RELEASE, linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
Breaks: linux-${RELEASE}-root-legacy-$BOARD (<< $REVISION~), linux-${RELEASE}-root-current-$BOARD (<< $REVISION~), linux-${RELEASE}-root-next-$BOARD (<< $REVISION~)
Recommends: bsdutils, parted, util-linux, toilet
Description: OrangePi board support files for $BOARD
EOF
5.2.1 创建/DEBIAN/preinst
接着是生成一个Debian
软件包的preinst
脚本,用于在软件包安装之前执行一些操作;
# set up pre install script
cat <<-EOF > "${destination}"/DEBIAN/preinst
#!/bin/sh
# tell people to reboot at next login
[ "\$1" = "upgrade" ] && touch /var/run/.reboot_required
# convert link to file
if [ -L "/etc/network/interfaces" ]; then
cp /etc/network/interfaces /etc/network/interfaces.tmp
rm /etc/network/interfaces
mv /etc/network/interfaces.tmp /etc/network/interfaces
fi
# fixing ramdisk corruption when using lz4 compression method
sed -i "s/^COMPRESS=.*/COMPRESS=gzip/" /etc/initramfs-tools/initramfs.conf
# swap
grep -q vm.swappiness /etc/sysctl.conf
case \$? in
0)
sed -i 's/vm\.swappiness.*/vm.swappiness=100/' /etc/sysctl.conf
;;
*)
echo vm.swappiness=100 >>/etc/sysctl.conf
;;
esac
sysctl -p >/dev/null 2>&1
# disable deprecated services
[ -f "/etc/profile.d/activate_psd_user.sh" ] && rm /etc/profile.d/activate_psd_user.sh
[ -f "/etc/profile.d/check_first_login.sh" ] && rm /etc/profile.d/check_first_login.sh
[ -f "/etc/profile.d/check_first_login_reboot.sh" ] && rm /etc/profile.d/check_first_login_reboot.sh
[ -f "/etc/profile.d/ssh-title.sh" ] && rm /etc/profile.d/ssh-title.sh
[ -f "/etc/update-motd.d/10-header" ] && rm /etc/update-motd.d/10-header
[ -f "/etc/update-motd.d/30-sysinfo" ] && rm /etc/update-motd.d/30-sysinfo
[ -f "/etc/update-motd.d/35-tips" ] && rm /etc/update-motd.d/35-tips
[ -f "/etc/update-motd.d/40-updates" ] && rm /etc/update-motd.d/40-updates
[ -f "/etc/update-motd.d/98-autoreboot-warn" ] && rm /etc/update-motd.d/98-autoreboot-warn
[ -f "/etc/update-motd.d/99-point-to-faq" ] && rm /etc/update-motd.d/99-point-to-faq
[ -f "/etc/update-motd.d/80-esm" ] && rm /etc/update-motd.d/80-esm
[ -f "/etc/update-motd.d/80-livepatch" ] && rm /etc/update-motd.d/80-livepatch
[ -f "/etc/apt/apt.conf.d/02compress-indexes" ] && rm /etc/apt/apt.conf.d/02compress-indexes
[ -f "/etc/apt/apt.conf.d/02periodic" ] && rm /etc/apt/apt.conf.d/02periodic
[ -f "/etc/apt/apt.conf.d/no-languages" ] && rm /etc/apt/apt.conf.d/no-languages
[ -f "/etc/init.d/armhwinfo" ] && rm /etc/init.d/armhwinfo
[ -f "/etc/logrotate.d/armhwinfo" ] && rm /etc/logrotate.d/armhwinfo
[ -f "/etc/init.d/firstrun" ] && rm /etc/init.d/firstrun
[ -f "/etc/init.d/resize2fs" ] && rm /etc/init.d/resize2fs
[ -f "/lib/systemd/system/firstrun-config.service" ] && rm /lib/systemd/system/firstrun-config.service
[ -f "/lib/systemd/system/firstrun.service" ] && rm /lib/systemd/system/firstrun.service
[ -f "/lib/systemd/system/resize2fs.service" ] && rm /lib/systemd/system/resize2fs.service
[ -f "/usr/lib/orangepi/apt-updates" ] && rm /usr/lib/orangepi/apt-updates
[ -f "/usr/lib/orangepi/firstrun-config.sh" ] && rm /usr/lib/orangepi/firstrun-config.sh
# fix for https://bugs.launchpad.net/ubuntu/+source/lightdm-gtk-greeter/+bug/1897491
[ -d "/var/lib/lightdm" ] && (chown -R lightdm:lightdm /var/lib/lightdm ; chmod 0750 /var/lib/lightdm)
exit 0
EOF
chmod 755 "${destination}"/DEBIAN/preinst
5.2.3 创建/DEBIAN/postinst
接着是生成一个Debian
软件包的postinst
脚本,用于在软件包安装之后执行一些操作;
# 第一段
# set up post install script
cat <<-EOF > "${destination}"/DEBIAN/postinst
#!/bin/sh
#
# ${BOARD} BSP post installation script
#
[ -f /etc/lib/systemd/system/orangepi-ramlog.service ] && systemctl --no-reload enable orangepi-ramlog.service
# check if it was disabled in config and disable in new service
if [ -n "\$(grep -w '^ENABLED=false' /etc/default/log2ram 2> /dev/null)" ]; then
sed -i "s/^ENABLED=.*/ENABLED=false/" /etc/default/orangepi-ramlog
fi
# fix boot delay "waiting for suspend/resume device"
if [ -f "/etc/initramfs-tools/initramfs.conf" ]; then
if ! grep --quiet "RESUME=none" /etc/initramfs-tools/initramfs.conf; then
echo "RESUME=none" >> /etc/initramfs-tools/initramfs.conf
fi
fi
EOF
# install bootscripts if they are not present. Fix upgrades from old images
if [[ $FORCE_BOOTSCRIPT_UPDATE == yes ]]; then
# 第二段
cat <<-EOF >> "${destination}"/DEBIAN/postinst
if [ true ]; then
# this package recreate boot scripts
EOF
else
cat <<-EOF >> "${destination}"/DEBIAN/postinst
if [ ! -f /boot/$bootscript_dst ]; then
# if boot script does not exits its recreated
EOF
fi
# 第二段
cat <<-EOF >> "${destination}"/DEBIAN/postinst
# move bootscript to /usr/share/orangepi
# create a backup
[ -f /etc/orangepi-release ] && . /etc/orangepi-release
[ -z \${VERSION} ] && VERSION=$(echo \`date +%s\`)
if [ -f /boot/$bootscript_dst ]; then
cp /boot/$bootscript_dst /usr/share/orangepi/${bootscript_dst}-\${VERSION} >/dev/null 2>&1
echo "NOTE: You can find previous bootscript versions in /usr/share/orangepi !"
fi
# cleanup old bootscript backup
ls /usr/share/orangepi/boot.cmd-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --
ls /usr/share/orangepi/boot.ini-* >/dev/null 2>&1 | head -n -5 | xargs rm -f --
echo "Recreating boot script"
cp /usr/share/orangepi/$bootscript_dst /boot >/dev/null 2>&1
rootdev=\$(sed -e 's/^.*root=//' -e 's/ .*\$//' < /proc/cmdline)
rootfstype=\$(sed -e 's/^.*rootfstype=//' -e 's/ .*$//' < /proc/cmdline)
# recreate orangepiEnv.txt if it and extlinux does not exists
if [ ! -f /boot/orangepiEnv.txt ] && [ ! -f /boot/extlinux/extlinux.conf ]; then
cp /usr/share/orangepi/orangepiEnv.txt /boot >/dev/null 2>&1
echo "rootdev="\$rootdev >> /boot/orangepiEnv.txt
echo "rootfstype="\$rootfstype >> /boot/orangepiEnv.txt
fi
[ -f /boot/boot.ini ] && sed -i "s/setenv rootdev.*/setenv rootdev \\"\$rootdev\\"/" /boot/boot.ini
[ -f /boot/boot.ini ] && sed -i "s/setenv rootfstype.*/setenv rootfstype \\"\$rootfstype\\"/" /boot/boot.ini
[ -f /boot/boot.cmd ] && mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr >/dev/null 2>&1
fi
[ ! -f "/etc/network/interfaces" ] && [ -f "/etc/network/interfaces.default" ] && cp /etc/network/interfaces.default /etc/network/interfaces
ln -sf /var/run/motd /etc/motd
rm -f /etc/update-motd.d/00-header /etc/update-motd.d/10-help-text
if [ -f "/boot/bin/$BOARD.bin" ] && [ ! -f "/boot/script.bin" ]; then ln -sf bin/$BOARD.bin /boot/script.bin >/dev/null 2>&1 || cp /boot/bin/$BOARD.bin /boot/script.bin; fi
if [ ! -f "/etc/default/orangepi-motd" ]; then
mv /etc/default/orangepi-motd.dpkg-dist /etc/default/orangepi-motd
fi
if [ ! -f "/etc/default/orangepi-ramlog" ] && [ -f /etc/default/orangepi-ramlog.dpkg-dist ]; then
mv /etc/default/orangepi-ramlog.dpkg-dist /etc/default/orangepi-ramlog
fi
if [ ! -f "/etc/default/orangepi-zram-config" ] && [ -f /etc/default/orangepi-zram-config.dpkg-dist ]; then
mv /etc/default/orangepi-zram-config.dpkg-dist /etc/default/orangepi-zram-config
fi
if [ -L "/usr/lib/chromium-browser/master_preferences.dpkg-dist" ]; then
mv /usr/lib/chromium-browser/master_preferences.dpkg-dist /usr/lib/chromium-browser/master_preferences
fi
# Read release value
if [ -f /etc/lsb-release ]; then
RELEASE=\$(cat /etc/lsb-release | grep CODENAME | cut -d"=" -f2 | sed 's/.*/\u&/')
sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${VENDOR} $REVISION "\${RELEASE}"\"/" /etc/os-release
echo "${VENDOR} ${REVISION} \${RELEASE} \\l \n" > /etc/issue
echo "${VENDOR} ${REVISION} \${RELEASE}" > /etc/issue.net
fi
# Reload services
systemctl --no-reload enable orangepi-hardware-monitor.service orangepi-hardware-optimize.service orangepi-zram-config.service >/dev/null 2>&1
exit 0
EOF
5.3 其它
5.3.1 orangepi-distribution-status
接着是将这段脚本片段的作用是将<external/config/distributions/>
路径下的各个发行版支持状态信息复制到/etc/orangepi-distribution-status
;
# copy distribution support status
local releases=($(find ${EXTER}/config/distributions -mindepth 1 -maxdepth 1 -type d))
for i in ${releases[@]}
do
# 读取每个发行版目录中的support文件的内容,然后将发行版名称和其支持状态写入到orangepi-distribution-status
echo "$(echo $i | sed 's/.*\///')=$(cat $i/support)" >> "${destination}"/etc/orangepi-distribution-status
done
最终得到的orangepi-distribution-status
文件内容;
sid=csc
jammy=supported
focal=supported
bullseye=supported
bionic=supported
stretch=eos
xenial=eos
buster=supported
raspi=supported
bookworm=supported
5.3.2 orangepi-release
接着向/etc/orangepi-release
写入一些配置信息;
# armhwinfo, firstrun, orangepimonitor, etc. config file
cat <<-EOF > "${destination}"/etc/orangepi-release
# PLEASE DO NOT EDIT THIS FILE
BOARD=${BOARD}
BOARD_NAME="$BOARD_NAME"
BOARDFAMILY=${BOARDFAMILY}
BUILD_REPOSITORY_URL=${BUILD_REPOSITORY_URL}
BUILD_REPOSITORY_COMMIT=${BUILD_REPOSITORY_COMMIT}
DISTRIBUTION_CODENAME=${RELEASE}
DISTRIBUTION_STATUS=${DISTRIBUTION_STATUS}
VERSION=${REVISION}
LINUXFAMILY=${LINUXFAMILY}
ARCH=${ARCHITECTURE}
IMAGE_TYPE=$IMAGE_TYPE
BOARD_TYPE=$BOARD_TYPE
INITRD_ARCH=${INITRD_ARCH}
KERNEL_IMAGE_TYPE=${KERNEL_IMAGE_TYPE}
BRANCH=${BRANCH}
EOF
最终得到的orangepi-release
文件内容;
# PLEASE DO NOT EDIT THIS FILE
BOARD=orangepi3b
BOARD_NAME="OPI 3B"
BOARDFAMILY=rockchip-rk356x
BUILD_REPOSITORY_URL=https://kkgithub.com/orangepi-xunlong/orangepi-build.git
BUILD_REPOSITORY_COMMIT=5262437
DISTRIBUTION_CODENAME=bullseye
DISTRIBUTION_STATUS=supported
VERSION=1.0.6
LINUXFAMILY=rockchip-rk356x
ARCH=arm64
IMAGE_TYPE=user-built
BOARD_TYPE=conf
INITRD_ARCH=arm64
KERNEL_IMAGE_TYPE=Image
BRANCH=current
5.3.3 生成deb
包
函数最后先是将/etc/network/interfaces.default
文件中的#no-auto-down
替换为no-auto-down
;
接着执行了family_tweaks_bsp
函数,其定义在<SDK>/external/config/sources/families/rockchip-rk356x.conf
;
然后设置bsp
目录下的所有文件和目录的所有者、组以及权限;
最后就是使用fakeroot
将bsp
目录打包成deb
包,包名为orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
,并拷贝到<SDK>/output/debs/bullseye
目录;
# this is required for NFS boot to prevent deconfiguring the network on shutdown
sed -i 's/#no-auto-down/no-auto-down/g' "${destination}"/etc/network/interfaces.default
# execute $LINUXFAMILY-specific tweaks
[[ $(type -t family_tweaks_bsp) == function ]] && family_tweaks_bsp
call_extension_method "post_family_tweaks_bsp" << 'POST_FAMILY_TWEAKS_BSP'
*family_tweaks_bsp overrrides what is in the config, so give it a chance to override the family tweaks*
This should be implemented by the config to tweak the BSP, after the board or family has had the chance to.
POST_FAMILY_TWEAKS_BSP
# add some summary to the image
fingerprint_image "${destination}/etc/orangepi.txt"
# fixing permissions (basic), reference: dh_fixperms,将根文件系统下的所有文件和目录的所有者和组设置为root
find "${destination}" -print0 2>/dev/null | xargs -0r chown --no-dereference 0:0
# 将不是符号链接的文件和目录,权限设置为为755(目录)和644(文件)
find "${destination}" ! -type l -print0 2>/dev/null | xargs -0r chmod 'go=rX,u+rw,a-s'
# create board DEB file
fakeroot dpkg-deb -b -Z${DEB_COMPRESS} "${destination}" "${destination}.deb" >> "${DEST}"/${LOG_SUBPATH}/output.log 2>&1
mkdir -p "${DEB_STORAGE}/${RELEASE}/"
rsync --remove-source-files -rq "${destination}.deb" "${DEB_STORAGE}/${RELEASE}/"
# cleanup
rm -rf ${bsptempdir}
将orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
包解压,包含如下文件;
点击查看代码
root@ubuntu:/work/sambashare/rk3566/orangepi-build/output/debs/bullseye$ sudo dpkg -x orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb ./
root@ubuntu:/work/sambashare/rk3566/orangepi-build/output/debs/bullseye$ ll
drwxr-xr-x 17 root root 4096 7月 10 18:01 etc/
drwxr-xr-x 4 root root 4096 7月 10 13:40 lib/
-rw-r--r-- 1 root sudo 781316 7月 10 18:01 orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
drwxr-xr-x 8 root root 4096 7月 10 13:40 usr/
drwxr-xr-x 3 root root 4096 7月 10 13:40 var/
root@ubuntu:/work/sambashare/rk3566/orangepi-build/output/debs/bullseye# tree
.
├── etc
│ ├── apt
│ │ └── apt.conf.d
│ │ ├── 02-orangepi-compress-indexes
│ │ ├── 02-orangepi-periodic
│ │ ├── 71-orangepi-no-recommends
│ │ └── 81-orangepi-no-languages
│ ├── cron.d
│ │ ├── orangepi-truncate-logs
│ │ └── orangepi-updates
│ ├── cron.daily
│ │ └── orangepi-ram-logging
│ ├── default
│ │ ├── orangepi-motd.dpkg-dist
│ │ ├── orangepi-ramlog.dpkg-dist
│ │ └── orangepi-zram-config.dpkg-dist
│ ├── initramfs
│ │ └── post-update.d
│ │ └── 99-uboot
│ ├── initramfs-tools
│ │ └── hooks
│ │ └── bootsplash.sh
│ ├── kernel
│ │ ├── postinst.d
│ │ │ └── xx-initramfs-cleanup
│ │ └── postrm.d
│ │ └── xx-initramfs-cleanup
│ ├── modprobe.d
│ │ ├── 8189fs.conf
│ │ └── r8723bs.conf
│ ├── network
│ │ └── interfaces.default
│ ├── NetworkManager
│ │ └── conf.d
│ │ ├── 10-override-wifi-random-mac-disable.conf
│ │ └── 20-override-wifi-powersave-disable.conf
│ ├── orangepi-distribution-status
│ ├── orangepi-release
│ ├── orangepi.txt
│ ├── profile.d
│ │ ├── orangepi-activate-profile-sync-daemon.sh
│ │ ├── orangepi-check-first-login-reboot.sh
│ │ ├── orangepi-check-first-login.sh
│ │ ├── orangepi-lang.sh
│ │ └── orangepi-ssh-title.sh
│ ├── skel
│ ├── systemd
│ │ └── system
│ │ └── logrotate.service
│ ├── udev
│ │ └── rules.d
│ │ ├── 10-wifi-disable-powermanagement.rules
│ │ └── 50-usb-realtek-net.rules
│ └── update-motd.d
│ ├── 10-orangepi-header
│ ├── 30-orangepi-sysinfo
│ ├── 35-orangepi-tips
│ ├── 40-orangepi-updates
│ ├── 41-orangepi-config
│ └── 98-orangepi-autoreboot-warn
├── lib
│ ├── systemd
│ │ └── system
│ │ ├── bootsplash-ask-password-console.path
│ │ ├── bootsplash-ask-password-console.service
│ │ ├── bootsplash-hide-when-booted.service
│ │ ├── bootsplash-show-on-shutdown.service
│ │ ├── getty@tty1.service.d
│ │ │ └── 10-noclear.conf
│ │ ├── orangepi-disable-autologin.service
│ │ ├── orangepi-disable-autologin.timer
│ │ ├── orangepi-firstrun-config.service
│ │ ├── orangepi-firstrun.service
│ │ ├── orangepi-hardware-monitor.service
│ │ ├── orangepi-hardware-optimize.service
│ │ ├── orangepi-ramlog.service
│ │ ├── orangepi-resize-filesystem.service
│ │ ├── orangepi-zram-config.service
│ │ ├── serial-getty@.service.d
│ │ │ └── 10-term.conf
│ │ ├── systemd-journald.service.d
│ │ │ └── override.conf
│ │ └── systemd-modules-load.service.d
│ │ └── 10-timeout.conf
│ └── udev
│ └── rules.d
│ └── 71-axp-power-button.rules
├── orangepi-bsp-cli-orangepi3b_1.0.6_arm64.deb
├── usr
│ ├── bin
│ │ ├── adbd
│ │ ├── hciattach_opi
│ │ ├── memtester.sh
│ │ └── orangepimonitor
│ ├── lib
│ │ ├── chromium-browser
│ │ │ └── master_preferences.dpkg-dist -> ../../share/chromium/master_preferences
│ │ ├── nand-sata-install
│ │ │ └── exclude.txt
│ │ └── orangepi
│ │ ├── orangepi-apt-updates
│ │ ├── orangepi-audio-config
│ │ ├── orangepi-common
│ │ ├── orangepi-firstlogin
│ │ ├── orangepi-firstrun
│ │ ├── orangepi-firstrun-config
│ │ ├── orangepi-hardware-monitor
│ │ ├── orangepi-hardware-optimization
│ │ ├── orangepi-ramlog
│ │ ├── orangepi-resize-filesystem
│ │ ├── orangepi-truncate-logs
│ │ └── orangepi-zram-config
│ ├── local
│ │ └── bin
│ │ └── test_pwm.sh
│ ├── sbin
│ │ ├── blink_all_gpio
│ │ ├── burn_to_emmc
│ │ ├── nand-sata-install
│ │ └── orangepi-add-overlay
│ ├── share
│ │ ├── initramfs-tools
│ │ │ ├── conf-hooks.d
│ │ │ │ └── orangepi-plymouth
│ │ │ └── hooks
│ │ │ └── usb_modeswitch
│ │ └── orangepi
│ │ └── boot.cmd
│ └── src
│ └── hello
│ ├── hello.c
│ └── Makefile
└── var
└── lib
└── polkit-1
└── localauthority
└── 50-local.d
├── backlight.pkla
├── networkmanager.pkla
├── plugdev.pkla
├── power.pkla
└── printing.pkla
54 directories, 88 files
上面我们看到了一大堆文件,但是在create_board_package
中并没有创建那么多文件,那么其它一些文件哪里来的呢?这个我们后续会介绍到。
六 debootstrap.sh
debootstrap.sh
脚本文件实现了rootfs
文件系统以及Linux
镜像文件(统一固件)的制作,该文件主要提供了debootstrap_ng
、create_rootfs_cache
、prepare_partitions
、update_initramfs
、create_image
函数。
6.1 debootstrap_ng
点击查看代码
debootstrap_ng()
{
display_alert "Starting rootfs and image building process for" "${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED:-null} ${DESKTOP_ENVIRONMENT:-null} ${BUILD_MINIMAL}" "info"
# ROOTFS_TYPE默认为ext4
[[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming $BOARD $BRANCH kernel supports $ROOTFS_TYPE" "" "wrn"
# trap to unmount stuff in case of error/manual interruption,
trap unmount_on_exit INT TERM EXIT
# stage: clean and create directories
rm -rf $SDCARD $MOUNT
mkdir -p $SDCARD $MOUNT $DEST/images $EXTER/cache/rootfs
# stage: verify tmpfs configuration and mount
# CLI needs ~1.5GiB, desktop - ~3.5GiB
# calculate and set tmpfs mount to use 9/10 of available RAM+SWAP
local phymem=$(( (($(awk '/MemTotal/ {print $2}' /proc/meminfo) + $(awk '/SwapTotal/ {print $2}' /proc/meminfo))) / 1024 * 9 / 10 )) # MiB
if [[ $BUILD_DESKTOP == yes ]]; then local tmpfs_max_size=3500; else local tmpfs_max_size=1500; fi # MiB
if [[ $FORCE_USE_RAMDISK == no ]]; then local use_tmpfs=no
elif [[ $FORCE_USE_RAMDISK == yes || $phymem -gt $tmpfs_max_size ]]; then
local use_tmpfs=yes
fi
[[ -n $FORCE_TMPFS_SIZE ]] && phymem=$FORCE_TMPFS_SIZE
[[ $use_tmpfs == yes ]] && mount -t tmpfs -o size=${phymem}M tmpfs $SDCARD
# stage: prepare basic rootfs: unpack cache or create from scratch
create_rootfs_cache
call_extension_method "pre_install_distribution_specific" "config_pre_install_distribution_specific" << 'PRE_INSTALL_DISTRIBUTION_SPECIFIC'
*give config a chance to act before install_distribution_specific*
Called after `create_rootfs_cache` (_prepare basic rootfs: unpack cache or create from scratch_) but before `install_distribution_specific` (_install distribution and board specific applications_).
PRE_INSTALL_DISTRIBUTION_SPECIFIC
# stage: install kernel and u-boot packages
# install distribution and board specific applications
if [[ ${RELEASE} == "raspi" ]]; then
install_opi_specific
else
install_distribution_specific
install_common
# install locally built packages or install pre-built packages from orangepi
[[ $EXTERNAL_NEW == compile || $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages_local
#[[ $EXTERNAL_NEW == prebuilt ]] && chroot_installpackages "yes"
# stage: user customization script
# NOTE: installing too many packages may fill tmpfs mount
customize_image
# remove packages that are no longer needed. Since we have intrudoced uninstall feature, we might want to clean things that are no longer needed
display_alert "No longer needed packages" "purge" "info"
chroot $SDCARD /bin/bash -c "apt-get autoremove -y" >/dev/null 2>&1
# create list of installed packages for debug purposes
chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > $DEST/${LOG_SUBPATH}/installed-packages-${RELEASE}$([[ ${BUILD_MINIMAL} == yes ]] && echo "-minimal")$([[ ${BUILD_DESKTOP} == yes ]] && echo "-desktop").list 2>&1
fi
# clean up / prepare for making the image
umount_chroot "$SDCARD"
post_debootstrap_tweaks
if [[ $ROOTFS_TYPE == fel ]]; then
FEL_ROOTFS=$SDCARD/
display_alert "Starting FEL boot" "$BOARD" "info"
source $SRC/scripts/fel-load.sh
else
prepare_partitions
create_image
fi
# stage: unmount tmpfs
umount $SDCARD 2>&1
if [[ $use_tmpfs = yes ]]; then
while grep -qs "$SDCARD" /proc/mounts
do
umount $SDCARD
sleep 5
done
fi
rm -rf $SDCARD
# remove exit trap
trap - INT TERM EXIT
} #############################################################################
6.1.1 准备工作
函数最开始是一些准备工作,比如:
- 输出构建
rootfs
开始的日志信息; - 设置陷阱,当接收到
INT/TERM/EXIT
信号时执行unmount_on_exit
; - 删除
$SDCARD
目录,SDCARD
被设置为<SDK>/.tmp/rootfs-$(uuidgen)
:用于挂载tmpfs
文件系统,存放根文件系统文件(对于tmpfs
文件系统,数据是放在内存的); - 删除
$MOUNT
目录,MOUNT
被设置为<SDK>/.tmp/mount-$(uuidgen)
:创建根文件系统镜像<SDK>/.tmp/rootfs-$(uuidgen).raw
时的挂载点; - 创建临时目录:
$SDCARD
、$MOUNT
、<SDK>/output/images
、<SDK>/external/cache/rootfs
; - 设置
phymem
=RAM+SWAP
内存空间的9/10
,比如我使用的宿主机这个大小为8949MB
; - 设置
tmpfs_max_size=1500
(根文件系统在临时文件系统占用的最大空间)、use_tmpfs=yes
; - 最后将一个大小为
phymem
的tmpfs
文件系统挂载到$SDCARD
目录;tmpfs
是一种存在于内存中的临时文件系统,用于存储临时数据,因此要求phymem
>tmpfs_max_size
;
比如我们在编译过程中可以查看<SDK>/.tmp
目录;
root@ubuntu:/work/sambashare/rk3566/orangepi-build/.tmp$ ll
-rw-rw-r-- 1 root root 219 8月 4 11:31 extension_function_cleanup.sh
drwxrwxr-x 3 root root 4096 8月 4 11:31 .extensions/
drwxrwxr-x 2 root root 4096 8月 4 11:31 mount-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/
drwxrwxrwt 18 root root 420 8月 4 11:31 rootfs-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/
root@ubuntu:/work/sambashare/rk3566/orangepi-build/.tmp$ ls rootfs-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/
bin boot dev etc home lib media mnt opt proc root run sbin selinux srv sys tmp usr var
root@ubuntu:/work/sambashare/rk3566/orangepi-build/.tmp$ df -hT
文件系统 类型 大小 已用 可用 已用% 挂载点
......
tmpfs tmpfs 8.8G 1.2G 7.6G 14% /work/sambashare/rk3566/orangepi-build/.tmp/rootfs-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1
/dev/loop18p2 ext4 2.8G 2.0G 776M 73% /work/sambashare/rk3566/orangepi-build/.tmp/mount-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1
/dev/loop18p1 vfat 1022M 67M 956M 7% /work/sambashare/rk3566/orangepi-build/.tmp/mount-1b5ba10e-5b78-4dbc-b6d5-01dd1f829aa1/boot
相关脚本如下:
display_alert "Starting rootfs and image building process for" "${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED:-null} ${DESKTOP_ENVIRONMENT:-null} ${BUILD_MINIMAL}" "info"
[[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming $BOARD $BRANCH kernel supports $ROOTFS_TYPE" "" "wrn"
# trap to unmount stuff in case of error/manual interruption,设置信号处理器,当收到信号INT/TERM/EXIT取消挂载
trap unmount_on_exit INT TERM EXIT
# stage: clean and create directories
rm -rf $SDCARD $MOUNT
mkdir -p $SDCARD $MOUNT $DEST/images $EXTER/cache/rootfs
# stage: verify tmpfs configuration and mount
# CLI needs ~1.5GiB, desktop - ~3.5GiB
# calculate and set tmpfs mount to use 9/10 of available RAM+SWAP
local phymem=$(( (($(awk '/MemTotal/ {print $2}' /proc/meminfo) + $(awk '/SwapTotal/ {print $2}' /proc/meminfo))) / 1024 * 9 / 10 )) # MiB
# 无桌面环境,因此设置tmpfs_max_size=1500
if [[ $BUILD_DESKTOP == yes ]]; then local tmpfs_max_size=3500; else local tmpfs_max_size=1500; fi # MiB
# 判断是否使用tmpfs文件系统
if [[ $FORCE_USE_RAMDISK == no ]]; then local use_tmpfs=no
elif [[ $FORCE_USE_RAMDISK == yes || $phymem -gt $tmpfs_max_size ]]; then
local use_tmpfs=yes
fi
[[ -n $FORCE_TMPFS_SIZE ]] && phymem=$FORCE_TMPFS_SIZE
# 将一个大小为phymem的tmpfs文件系统挂载到$SDCARD目录
[[ $use_tmpfs == yes ]] && mount -t tmpfs -o size=${phymem}M tmpfs $SDCARD
unmount_on_exit
定义在scripts/image-helpers.sh
;
umount_chroot()
{
local target=$1
display_alert "Unmounting" "$target" "info"
# 检查并卸载chroot环境中的${target}/dev、${target}/proc和${target}/sys目录下的挂载点
while grep -Eq "${target}.*(dev|proc|sys)" /proc/mounts
do
umount -l --recursive "${target}"/dev >/dev/null 2>&1
umount -l "${target}"/proc >/dev/null 2>&1
umount -l "${target}"/sys >/dev/null 2>&1
sleep 5
done
}
unmount_on_exit()
{
trap - INT TERM EXIT
local stacktrace="$(get_extension_hook_stracktrace "${BASH_SOURCE[*]}" "${BASH_LINENO[*]}")"
display_alert "unmount_on_exit() called!" "$stacktrace" "err"
if [[ "${ERROR_DEBUG_SHELL}" == "yes" ]]; then
ERROR_DEBUG_SHELL=no # dont do it twice
display_alert "MOUNT" "${MOUNT}" "err"
display_alert "SDCARD" "${SDCARD}" "err"
display_alert "ERROR_DEBUG_SHELL=yes, starting a shell." "ERROR_DEBUG_SHELL" "err"
bash < /dev/tty || true
fi
# 卸载chroot环境中的挂载点
umount_chroot "${SDCARD}/"
umount -l "${SDCARD}"/tmp >/dev/null 2>&1
umount -l "${SDCARD}" >/dev/null 2>&1
umount -l "${MOUNT}"/boot >/dev/null 2>&1
umount -l "${MOUNT}" >/dev/null 2>&1
[[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose "${ROOT_MAPPER}"
losetup -d "${LOOP}" >/dev/null 2>&1
rm -rf --one-file-system "${SDCARD}"
exit_with_error "debootstrap-ng was interrupted" || true # don't trigger again
}
6.1.2 create_rootfs_cache
接着调用create_rootfs_cache
准备基本的根文件系统:解压缓存或从头开始创建。
6.1.3 install_common
install_common
定义在scripts/distributions.sh
;
点击查看代码
install_common()
{
display_alert "Applying common tweaks" "" "info"
# install rootfs encryption related packages separate to not break packages cache
if [[ $CRYPTROOT_ENABLE == yes ]]; then
display_alert "Installing rootfs encryption related packages" "cryptsetup" "info"
chroot "${SDCARD}" /bin/bash -c "apt-get -y -qq --no-install-recommends install cryptsetup" \
>> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
if [[ $CRYPTROOT_SSH_UNLOCK == yes ]]; then
display_alert "Installing rootfs encryption related packages" "dropbear-initramfs" "info"
chroot "${SDCARD}" /bin/bash -c "apt-get -y -qq --no-install-recommends install dropbear-initramfs cryptsetup-initramfs" \
>> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
fi
fi
# add dummy fstab entry to make mkinitramfs happy
echo "/dev/mmcblk0p1 / $ROOTFS_TYPE defaults 0 1" >> "${SDCARD}"/etc/fstab
# required for initramfs-tools-core on Stretch since it ignores the / fstab entry
echo "/dev/mmcblk0p2 /usr $ROOTFS_TYPE defaults 0 2" >> "${SDCARD}"/etc/fstab
# adjust initramfs dropbear configuration
# needs to be done before kernel installation, else it won't be in the initrd image
if [[ $CRYPTROOT_ENABLE == yes && $CRYPTROOT_SSH_UNLOCK == yes ]]; then
# Set the port of the dropbear ssh daemon in the initramfs to a different one if configured
# this avoids the typical 'host key changed warning' - `WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!`
[[ -f "${SDCARD}"/etc/dropbear-initramfs/config ]] && \
sed -i 's/^#DROPBEAR_OPTIONS=/DROPBEAR_OPTIONS="-p '"${CRYPTROOT_SSH_UNLOCK_PORT}"'"/' \
"${SDCARD}"/etc/dropbear-initramfs/config
# setup dropbear authorized_keys, either provided by userpatches or generated
if [[ -f $USERPATCHES_PATH/dropbear_authorized_keys ]]; then
cp "$USERPATCHES_PATH"/dropbear_authorized_keys "${SDCARD}"/etc/dropbear-initramfs/authorized_keys
else
# generate a default ssh key for login on dropbear in initramfs
# this key should be changed by the user on first login
display_alert "Generating a new SSH key pair for dropbear (initramfs)" "" ""
ssh-keygen -t ecdsa -f "${SDCARD}"/etc/dropbear-initramfs/id_ecdsa \
-N '' -O force-command=cryptroot-unlock -C 'AUTOGENERATED_BY_ARMBIAN_BUILD' >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
# /usr/share/initramfs-tools/hooks/dropbear will automatically add 'id_ecdsa.pub' to authorized_keys file
# during mkinitramfs of update-initramfs
#cat "${SDCARD}"/etc/dropbear-initramfs/id_ecdsa.pub > "${SDCARD}"/etc/dropbear-initramfs/authorized_keys
# The version of the Linux kernel
CRYPTROOT_SSH_UNLOCK_KEY_NAME="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${SELECTED_CONFIGURATION}_linux"$(grab_version "$LINUXSOURCEDIR")"".key
# copy dropbear ssh key to image output dir for convenience
cp "${SDCARD}"/etc/dropbear-initramfs/id_ecdsa "${DEST}/images/${CRYPTROOT_SSH_UNLOCK_KEY_NAME}"
display_alert "SSH private key for dropbear (initramfs) has been copied to:" \
"$DEST/images/$CRYPTROOT_SSH_UNLOCK_KEY_NAME" "info"
fi
fi
# create modules file
local modules=MODULES_${BRANCH^^}
if [[ -n "${!modules}" ]]; then
tr ' ' '\n' <<< "${!modules}" > "${SDCARD}"/etc/modules
elif [[ -n "${MODULES}" ]]; then
tr ' ' '\n' <<< "${MODULES}" > "${SDCARD}"/etc/modules
fi
# create blacklist files
local blacklist=MODULES_BLACKLIST_${BRANCH^^}
if [[ -n "${!blacklist}" ]]; then
tr ' ' '\n' <<< "${!blacklist}" | sed -e 's/^/blacklist /' > "${SDCARD}/etc/modprobe.d/blacklist-${BOARD}.conf"
elif [[ -n "${MODULES_BLACKLIST}" ]]; then
tr ' ' '\n' <<< "${MODULES_BLACKLIST}" | sed -e 's/^/blacklist /' > "${SDCARD}/etc/modprobe.d/blacklist-${BOARD}.conf"
fi
# configure MIN / MAX speed for cpufrequtils
cat <<-EOF > "${SDCARD}"/etc/default/cpufrequtils
ENABLE=true
MIN_SPEED=$CPUMIN
MAX_SPEED=$CPUMAX
GOVERNOR=$GOVERNOR
EOF
# remove default interfaces file if present
# before installing board support package
rm -f "${SDCARD}"/etc/network/interfaces
# disable selinux by default
mkdir -p "${SDCARD}"/selinux
[[ -f "${SDCARD}"/etc/selinux/config ]] && sed "s/^SELINUX=.*/SELINUX=disabled/" -i "${SDCARD}"/etc/selinux/config
# remove Ubuntu's legal text
[[ -f "${SDCARD}"/etc/legal ]] && rm "${SDCARD}"/etc/legal
# Prevent loading paralel printer port drivers which we don't need here.
# Suppress boot error if kernel modules are absent
if [[ -f "${SDCARD}"/etc/modules-load.d/cups-filters.conf ]]; then
sed "s/^lp/#lp/" -i "${SDCARD}"/etc/modules-load.d/cups-filters.conf
sed "s/^ppdev/#ppdev/" -i "${SDCARD}"/etc/modules-load.d/cups-filters.conf
sed "s/^parport_pc/#parport_pc/" -i "${SDCARD}"/etc/modules-load.d/cups-filters.conf
fi
# console fix due to Debian bug
sed -e 's/CHARMAP=".*"/CHARMAP="'$CONSOLE_CHAR'"/g' -i "${SDCARD}"/etc/default/console-setup
# add the /dev/urandom path to the rng config file
echo "HRNGDEVICE=/dev/urandom" >> "${SDCARD}"/etc/default/rng-tools
# ping needs privileged action to be able to create raw network socket
# this is working properly but not with (at least) Debian Buster
chroot "${SDCARD}" /bin/bash -c "chmod u+s /bin/ping"
# change time zone data
echo "${TZDATA}" > "${SDCARD}"/etc/timezone
chroot "${SDCARD}" /bin/bash -c "dpkg-reconfigure -f noninteractive tzdata >/dev/null 2>&1"
# set root password
chroot "${SDCARD}" /bin/bash -c "(echo $ROOTPWD;echo $ROOTPWD;) | passwd root >/dev/null 2>&1"
# enable automated login to console(s)
#mkdir -p "${SDCARD}"/etc/systemd/system/getty@.service.d/
#mkdir -p "${SDCARD}"/etc/systemd/system/serial-getty@.service.d/
#cat <<-EOF > "${SDCARD}"/etc/systemd/system/serial-getty@.service.d/override.conf
#[Service]
#ExecStartPre=/bin/sh -c 'exec /bin/sleep 10'
#ExecStart=
#ExecStart=-/sbin/agetty --noissue --autologin root %I \$TERM
#Type=idle
#EOF
#cp "${SDCARD}"/etc/systemd/system/serial-getty@.service.d/override.conf "${SDCARD}"/etc/systemd/system/getty@.service.d/override.conf
# force change root password at first login
#chroot "${SDCARD}" /bin/bash -c "chage -d 0 root"
# change console welcome text
echo -e "${VENDOR} ${REVISION} ${RELEASE^} \\l \n" > "${SDCARD}"/etc/issue
echo "${VENDOR} ${REVISION} ${RELEASE^}" > "${SDCARD}"/etc/issue.net
sed -i "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${VENDOR} $REVISION "${RELEASE^}"\"/" "${SDCARD}"/etc/os-release
# enable few bash aliases enabled in Ubuntu by default to make it even
sed "s/#alias ll='ls -l'/alias ll='ls -l'/" -i "${SDCARD}"/etc/skel/.bashrc
sed "s/#alias la='ls -A'/alias la='ls -A'/" -i "${SDCARD}"/etc/skel/.bashrc
sed "s/#alias l='ls -CF'/alias l='ls -CF'/" -i "${SDCARD}"/etc/skel/.bashrc
# root user is already there. Copy bashrc there as well
cp "${SDCARD}"/etc/skel/.bashrc "${SDCARD}"/root
# display welcome message at first root login
touch "${SDCARD}"/root/.not_logged_in_yet
if [[ ${DESKTOP_AUTOLOGIN} != no ]]; then
# set desktop autologin
touch "${SDCARD}"/root/.desktop_autologin
fi
# NOTE: this needs to be executed before family_tweaks
local bootscript_src=${BOOTSCRIPT%%:*}
local bootscript_dst=${BOOTSCRIPT##*:}
# create extlinux config file
if [[ $SRC_EXTLINUX == yes ]]; then
mkdir -p $SDCARD/boot/extlinux
cat <<-EOF > "$SDCARD/boot/extlinux/extlinux.conf"
label ${VENDOR}
kernel /boot/$NAME_KERNEL
initrd /boot/$NAME_INITRD
EOF
if [[ -n $BOOT_FDT_FILE ]]; then
if [[ $BOOT_FDT_FILE != "none" ]]; then
echo " fdt /boot/dtb/$BOOT_FDT_FILE" >> "$SDCARD/boot/extlinux/extlinux.conf"
fi
else
echo " fdtdir /boot/dtb/" >> "$SDCARD/boot/extlinux/extlinux.conf"
fi
else
if [[ "${BOOTCONFIG}" != "none" ]]; then
if [ -f "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" ]; then
cp "${USERPATCHES_PATH}/bootscripts/${bootscript_src}" "${SDCARD}/boot/${bootscript_dst}"
else
cp "${EXTER}/config/bootscripts/${bootscript_src}" "${SDCARD}/boot/${bootscript_dst}"
fi
fi
if [[ -n $BOOTENV_FILE ]]; then
if [[ -f $USERPATCHES_PATH/bootenv/$BOOTENV_FILE ]]; then
cp "$USERPATCHES_PATH/bootenv/${BOOTENV_FILE}" "${SDCARD}"/boot/orangepiEnv.txt
elif [[ -f $EXTER/config/bootenv/$BOOTENV_FILE ]]; then
cp "${EXTER}/config/bootenv/${BOOTENV_FILE}" "${SDCARD}"/boot/orangepiEnv.txt
fi
fi
# TODO: modify $bootscript_dst or orangepiEnv.txt to make NFS boot universal
# instead of copying sunxi-specific template
if [[ $ROOTFS_TYPE == nfs ]]; then
display_alert "Copying NFS boot script template"
if [[ -f $USERPATCHES_PATH/nfs-boot.cmd ]]; then
cp "$USERPATCHES_PATH"/nfs-boot.cmd "${SDCARD}"/boot/boot.cmd
else
cp "${EXTER}"/config/templates/nfs-boot.cmd.template "${SDCARD}"/boot/boot.cmd
fi
fi
[[ -n $OVERLAY_PREFIX && -f "${SDCARD}"/boot/orangepiEnv.txt && ($BRANCH =~ current|next || $BOARDFAMILY =~ "rockchip-rk3588"|"rockchip-rk356x") ]] && \
echo "overlay_prefix=$OVERLAY_PREFIX" >> "${SDCARD}"/boot/orangepiEnv.txt
[[ -n $DEFAULT_OVERLAYS && -f "${SDCARD}"/boot/orangepiEnv.txt && ($BRANCH =~ current|next || $BOARDFAMILY =~ "rockchip-rk3588"|"rockchip-rk356x") ]] && \
echo "overlays=${DEFAULT_OVERLAYS//,/ }" >> "${SDCARD}"/boot/orangepiEnv.txt
[[ -n $BOOT_FDT_FILE && -f "${SDCARD}"/boot/orangepiEnv.txt ]] && \
echo "fdtfile=${BOOT_FDT_FILE}" >> "${SDCARD}/boot/orangepiEnv.txt"
fi
# initial date for fake-hwclock
date -u '+%Y-%m-%d %H:%M:%S' > "${SDCARD}"/etc/fake-hwclock.data
echo "${HOST}" > "${SDCARD}"/etc/hostname
# set hostname in hosts file
cat <<-EOF > "${SDCARD}"/etc/hosts
127.0.0.1 localhost
127.0.1.1 $HOST
::1 localhost $HOST ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
EOF
cd $SRC
# Prepare and export caching-related params common to all apt calls below, to maximize apt-cacher-ng usage
export APT_EXTRA_DIST_PARAMS=""
[[ $NO_APT_CACHER != yes ]] && APT_EXTRA_DIST_PARAMS="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\" -o Acquire::http::Proxy::localhost=\"DIRECT\""
display_alert "Cleaning" "package lists"
chroot "${SDCARD}" /bin/bash -c "apt-get clean"
display_alert "Updating" "package lists"
chroot "${SDCARD}" /bin/bash -c "apt-get ${APT_EXTRA_DIST_PARAMS} update" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
display_alert "Temporarily disabling" "initramfs-tools hook for kernel"
chroot "${SDCARD}" /bin/bash -c "chmod -v -x /etc/kernel/postinst.d/initramfs-tools" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
# install family packages
if [[ -n ${PACKAGE_LIST_FAMILY} ]]; then
display_alert "Installing PACKAGE_LIST_FAMILY packages" "${PACKAGE_LIST_FAMILY}"
chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt-get ${APT_EXTRA_DIST_PARAMS} -yqq --no-install-recommends install $PACKAGE_LIST_FAMILY" >> "${DEST}"/${LOG_SUBPATH}/install.log
fi
# install board packages
if [[ -n ${PACKAGE_LIST_BOARD} ]]; then
display_alert "Installing PACKAGE_LIST_BOARD packages" "${PACKAGE_LIST_BOARD}"
chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt-get ${APT_EXTRA_DIST_PARAMS} -yqq --no-install-recommends install $PACKAGE_LIST_BOARD" >> "${DEST}"/${LOG_SUBPATH}/install.log || { display_alert "Failed to install PACKAGE_LIST_BOARD" "${PACKAGE_LIST_BOARD}" "err"; exit 2; }
fi
# remove family packages
if [[ -n ${PACKAGE_LIST_FAMILY_REMOVE} ]]; then
display_alert "Removing PACKAGE_LIST_FAMILY_REMOVE packages" "${PACKAGE_LIST_FAMILY_REMOVE}"
chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt-get ${APT_EXTRA_DIST_PARAMS} -yqq remove --auto-remove $PACKAGE_LIST_FAMILY_REMOVE" >> "${DEST}"/${LOG_SUBPATH}/install.log
fi
# remove board packages
if [[ -n ${PACKAGE_LIST_BOARD_REMOVE} ]]; then
display_alert "Removing PACKAGE_LIST_BOARD_REMOVE packages" "${PACKAGE_LIST_BOARD_REMOVE}"
for PKG_REMOVE in ${PACKAGE_LIST_BOARD_REMOVE}; do
chroot "${SDCARD}" /bin/bash -c "DEBIAN_FRONTEND=noninteractive apt-get ${APT_EXTRA_DIST_PARAMS} -yqq remove --auto-remove ${PKG_REMOVE}" >> "${DEST}"/${LOG_SUBPATH}/install.log
done
fi
# install u-boot
# @TODO: add install_bootloader() extension method, refactor into u-boot extension
[[ "${BOOTCONFIG}" != "none" ]] && {
if [[ "${REPOSITORY_INSTALL}" != *u-boot* ]]; then
UBOOT_VER=$(dpkg --info "${DEB_STORAGE}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" | grep Descr | awk '{print $(NF)}')
install_deb_chroot "${DEB_STORAGE}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb"
else
UBOOT_VER=$(dpkg --info "${DEB_ORANGEPI}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" | grep Descr | awk '{print $(NF)}')
install_deb_chroot "${DEB_ORANGEPI}/u-boot/${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb" "orangepi"
fi
}
call_extension_method "pre_install_kernel_debs" << 'PRE_INSTALL_KERNEL_DEBS'
*called before installing the Armbian-built kernel deb packages*
It is not too late to `unset KERNELSOURCE` here and avoid kernel install.
PRE_INSTALL_KERNEL_DEBS
# install kernel
[[ -n $KERNELSOURCE ]] && {
if [[ "${REPOSITORY_INSTALL}" != *kernel* ]]; then
VER=$(dpkg --info "${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" | awk -F"-" '/Source:/{print $2}')
install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb"
if [[ -f ${DEB_STORAGE}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb ]]; then
install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb"
fi
if [[ $INSTALL_HEADERS == yes ]]; then
install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KERNEL/image/headers}_${REVISION}_${ARCH}.deb"
else
cp "${DEB_STORAGE}/${CHOSEN_KERNEL/image/headers}_${REVISION}_${ARCH}.deb" "${SDCARD}"/opt/
fi
else
VER=$(dpkg --info "${DEB_ORANGEPI}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" | grep Descr | awk '{print $(NF)}')
VER="${VER/-$LINUXFAMILY/}"
install_deb_chroot "${DEB_ORANGEPI}/${CHOSEN_KERNEL}_${REVISION}_${ARCH}.deb" "orangepi"
if [[ -f ${DEB_ORANGEPI}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb ]]; then
install_deb_chroot "${DEB_ORANGEPI}/${CHOSEN_KERNEL/image/dtb}_${REVISION}_${ARCH}.deb" "orangepi"
fi
if [[ $INSTALL_HEADERS == yes ]]; then
install_deb_chroot "${DEB_ORANGEPI}/${CHOSEN_KERNEL/image/headers}_${REVISION}_${ARCH}.deb" "orangepi"
fi
fi
}
call_extension_method "post_install_kernel_debs" << 'POST_INSTALL_KERNEL_DEBS'
*allow config to do more with the installed kernel/headers*
Called after packages, u-boot, kernel and headers installed in the chroot, but before the BSP is installed.
If `KERNELSOURCE` is (still?) unset after this, Armbian-built firmware will not be installed.
POST_INSTALL_KERNEL_DEBS
# install board support packages
if [[ "${REPOSITORY_INSTALL}" != *bsp* ]]; then
install_deb_chroot "${DEB_STORAGE}/$RELEASE/${BSP_CLI_PACKAGE_FULLNAME}.deb"
else
install_deb_chroot "${DEB_ORANGEPI}/$RELEASE/${CHOSEN_ROOTFS}_${BSP_CLI_PACKAGE_FULLNAME}.deb" "orangepi"
fi
# install orangepi-desktop
if [[ "${REPOSITORY_INSTALL}" != *orangepi-desktop* ]]; then
if [[ $BUILD_DESKTOP == yes ]]; then
install_deb_chroot "${DEB_STORAGE}/${RELEASE}/${CHOSEN_DESKTOP}_${REVISION}_all.deb"
install_deb_chroot "${DEB_STORAGE}/${RELEASE}/${BSP_DESKTOP_PACKAGE_FULLNAME}.deb"
# install display manager and PACKAGE_LIST_DESKTOP_FULL packages if enabled per board
desktop_postinstall
fi
else
if [[ $BUILD_DESKTOP == yes ]]; then
install_deb_chroot "${CHOSEN_DESKTOP}" "orangepi"
# install display manager and PACKAGE_LIST_DESKTOP_FULL packages if enabled per board
desktop_postinstall
fi
fi
# install orangepi-firmware
if [[ "${REPOSITORY_INSTALL}" != *orangepi-firmware* ]]; then
if [[ -f ${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb ]]; then
install_deb_chroot "${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb"
fi
else
if [[ -f ${DEB_STORAGE}/orangepi-firmware_${REVISION}_all.deb ]]; then
install_deb_chroot "${DEB_ORANGEPI}/orangepi-firmware_${REVISION}_all.deb" "orangepi"
fi
fi
# install orangepi-config
if [[ "${PACKAGE_LIST_RM}" != *orangepi-config* ]]; then
if [[ "${REPOSITORY_INSTALL}" != *orangepi-config* ]]; then
if [[ $BUILD_MINIMAL != yes ]]; then
install_deb_chroot "${DEB_STORAGE}/orangepi-config_${REVISION}_all.deb"
fi
else
if [[ $BUILD_MINIMAL != yes ]]; then
install_deb_chroot "${DEB_ORANGEPI}/orangepi-config_${REVISION}_all.deb" "orangepi"
fi
fi
fi
# install orangepi-zsh
if [[ "${PACKAGE_LIST_RM}" != *orangepi-zsh* ]]; then
if [[ "${REPOSITORY_INSTALL}" != *orangepi-zsh* ]]; then
if [[ $BUILD_MINIMAL != yes ]]; then
install_deb_chroot "${DEB_STORAGE}/orangepi-zsh_${REVISION}_all.deb"
fi
else
if [[ $BUILD_MINIMAL != yes ]]; then
install_deb_chroot "orangepi-zsh" "remote"
fi
fi
fi
# install plymouth-theme-orangepi
if [[ $PLYMOUTH == yes && $BUILD_DESKTOP == yes && $RELEASE != buster ]]; then
if [[ "${REPOSITORY_INSTALL}" != *plymouth-theme-orangepi* ]]; then
install_deb_chroot "${DEB_STORAGE}/orangepi-plymouth-theme_${REVISION}_all.deb"
else
install_deb_chroot "orangepi-plymouth-theme" "remote"
fi
fi
# install kernel sources
if [[ -f ${DEB_STORAGE}/${CHOSEN_KSRC}_${REVISION}_all.deb && $INSTALL_KSRC == yes ]]; then
install_deb_chroot "${DEB_STORAGE}/${CHOSEN_KSRC}_${REVISION}_all.deb"
elif [[ $INSTALL_KSRC == yes ]]; then
display_alert "Please set BUILD_KSRC=yes to generate the kernel source package" "" "wrn"
fi
# install wireguard tools
if [[ $WIREGUARD == yes ]]; then
chroot "${SDCARD}" /bin/bash -c "apt-get -y -qq install wireguard-tools --no-install-recommends" >> "${DEST}"/debug/install.log 2>&1
fi
# freeze orangepi packages
if [[ $BSPFREEZE == yes ]]; then
display_alert "Freezing Orange Pi packages" "$BOARD" "info"
chroot "${SDCARD}" /bin/bash -c "apt-mark hold ${CHOSEN_KERNEL} ${CHOSEN_KERNEL/image/headers} \
linux-u-boot-${BOARD}-${BRANCH} ${CHOSEN_KERNEL/image/dtb}" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
fi
# add orangepi user
chroot "${SDCARD}" /bin/bash -c "adduser --quiet --disabled-password --shell /bin/bash --home /home/${OPI_USERNAME} --gecos ${OPI_USERNAME} ${OPI_USERNAME}"
chroot "${SDCARD}" /bin/bash -c "(echo ${OPI_PWD};echo ${OPI_PWD};) | passwd "${OPI_USERNAME}" >/dev/null 2>&1"
for additionalgroup in sudo netdev audio video disk tty users games dialout plugdev input bluetooth systemd-journal ssh; do
chroot "${SDCARD}" /bin/bash -c "usermod -aG ${additionalgroup} ${OPI_USERNAME} 2>/dev/null"
done
# fix for gksu in Xenial
touch ${SDCARD}/home/${OPI_USERNAME}/.Xauthority
chroot "${SDCARD}" /bin/bash -c "chown ${OPI_USERNAME}:${OPI_USERNAME} /home/${OPI_USERNAME}/.Xauthority"
# set up profile sync daemon on desktop systems
chroot "${SDCARD}" /bin/bash -c "which psd >/dev/null 2>&1"
if [ $? -eq 0 ]; then
echo -e "${OPI_USERNAME} ALL=(ALL) NOPASSWD: /usr/bin/psd-overlay-helper" >> ${SDCARD}/etc/sudoers
touch ${SDCARD}/home/${OPI_USERNAME}/.activate_psd
chroot "${SDCARD}" /bin/bash -c "chown $OPI_USERNAME:$OPI_USERNAME /home/${OPI_USERNAME}/.activate_psd"
fi
# remove deb files
rm -f "${SDCARD}"/root/*.deb
# copy boot splash images
cp "${EXTER}"/packages/blobs/splash/orangepi-u-boot.bmp "${SDCARD}"/boot/boot.bmp
cp "${EXTER}"/packages/blobs/splash/logo.bmp "${SDCARD}"/boot/logo.bmp
# copy audio.wav and mute.wav
cp "${EXTER}"/packages/blobs/audio_wav/audio.wav "${SDCARD}"/usr/share/sounds/alsa/
cp "${EXTER}"/packages/blobs/audio_wav/mute.wav "${SDCARD}"/usr/share/sounds/alsa/
cp "${EXTER}"/packages/blobs/test.mp4 "${SDCARD}"/usr/local/
# copy watchdog test programm
cp "${EXTER}"/packages/blobs/watchdog/watchdog_test_${ARCH} "${SDCARD}"/usr/local/bin/watchdog_test
[[ -f "${SDCARD}"/usr/bin/gnome-session ]] && sed -i "s/user-session.*/user-session=ubuntu-wayland/" ${SDCARD}/etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf
[[ -f "${SDCARD}"/usr/bin/startplasma-x11 ]] && sed -i "s/user-session.*/user-session=plasma-x11/" ${SDCARD}/etc/lightdm/lightdm.conf.d/22-orangepi-autologin.conf
# execute $LINUXFAMILY-specific tweaks
[[ $(type -t family_tweaks) == function ]] && family_tweaks
call_extension_method "post_family_tweaks" << 'FAMILY_TWEAKS'
*customize the tweaks made by $LINUXFAMILY-specific family_tweaks*
It is run after packages are installed in the rootfs, but before enabling additional services.
It allows implementors access to the rootfs (`${SDCARD}`) in its pristine state after packages are installed.
FAMILY_TWEAKS
# enable additional services
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-firstrun.service >/dev/null 2>&1"
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-firstrun-config.service >/dev/null 2>&1"
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-zram-config.service >/dev/null 2>&1"
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-hardware-optimize.service >/dev/null 2>&1"
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-ramlog.service >/dev/null 2>&1"
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-resize-filesystem.service >/dev/null 2>&1"
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable orangepi-hardware-monitor.service >/dev/null 2>&1"
# copy "first run automated config, optional user configured"
cp ${EXTER}/packages/bsp/orangepi_first_run.txt.template "${SDCARD}"/boot/orangepi_first_run.txt.template
## switch to beta repository at this stage if building nightly images
#[[ $IMAGE_TYPE == nightly ]] \
#&& echo "deb http://beta.orangepi.com $RELEASE main ${RELEASE}-utils ${RELEASE}-desktop" \
#> "${SDCARD}"/etc/apt/sources.list.d/orangepi.list
# Cosmetic fix [FAILED] Failed to start Set console font and keymap at first boot
[[ -f "${SDCARD}"/etc/console-setup/cached_setup_font.sh ]] \
&& sed -i "s/^printf '.*/printf '\\\033\%\%G'/g" "${SDCARD}"/etc/console-setup/cached_setup_font.sh
[[ -f "${SDCARD}"/etc/console-setup/cached_setup_terminal.sh ]] \
&& sed -i "s/^printf '.*/printf '\\\033\%\%G'/g" "${SDCARD}"/etc/console-setup/cached_setup_terminal.sh
[[ -f "${SDCARD}"/etc/console-setup/cached_setup_keyboard.sh ]] \
&& sed -i "s/-u/-x'/g" "${SDCARD}"/etc/console-setup/cached_setup_keyboard.sh
# fix for https://bugs.launchpad.net/ubuntu/+source/blueman/+bug/1542723
chroot "${SDCARD}" /bin/bash -c "chown root:messagebus /usr/lib/dbus-1.0/dbus-daemon-launch-helper"
chroot "${SDCARD}" /bin/bash -c "chmod u+s /usr/lib/dbus-1.0/dbus-daemon-launch-helper"
# disable samba NetBIOS over IP name service requests since it hangs when no network is present at boot
chroot "${SDCARD}" /bin/bash -c "systemctl --quiet disable nmbd 2> /dev/null"
# disable low-level kernel messages for non betas
if [[ -z $BETA ]]; then
sed -i "s/^#kernel.printk*/kernel.printk/" "${SDCARD}"/etc/sysctl.conf
fi
# disable repeated messages due to xconsole not being installed.
[[ -f "${SDCARD}"/etc/rsyslog.d/50-default.conf ]] && \
sed '/daemon\.\*\;mail.*/,/xconsole/ s/.*/#&/' -i "${SDCARD}"/etc/rsyslog.d/50-default.conf
# disable deprecated parameter
sed '/.*$KLogPermitNonKernelFacility.*/,// s/.*/#&/' -i "${SDCARD}"/etc/rsyslog.conf
# enable getty on multiple serial consoles
# and adjust the speed if it is defined and different than 115200
#
# example: SERIALCON="ttyS0:15000000,ttyGS1"
#
ifs=$IFS
for i in $(echo "${SERIALCON:-'ttyS0'}" | sed "s/,/ /g")
do
IFS=':' read -r -a array <<< "$i"
[[ "${array[0]}" == "tty1" ]] && continue # Don't enable tty1 as serial console.
display_alert "Enabling serial console" "${array[0]}" "info"
# add serial console to secure tty list
[ -z "$(grep -w '^${array[0]}' "${SDCARD}"/etc/securetty 2> /dev/null)" ] && \
echo "${array[0]}" >> "${SDCARD}"/etc/securetty
if [[ ${array[1]} != "115200" && -n ${array[1]} ]]; then
# make a copy, fix speed and enable
cp "${SDCARD}"/lib/systemd/system/serial-getty@.service \
"${SDCARD}/lib/systemd/system/serial-getty@${array[0]}.service"
sed -i "s/--keep-baud 115200/--keep-baud ${array[1]},115200/" \
"${SDCARD}/lib/systemd/system/serial-getty@${array[0]}.service"
fi
chroot "${SDCARD}" /bin/bash -c "systemctl daemon-reload" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable serial-getty@${array[0]}.service" \
>> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
if [[ "${array[0]}" == "ttyGS0" && $LINUXFAMILY == sun8i && $BRANCH == legacy ]]; then
mkdir -p "${SDCARD}"/etc/systemd/system/serial-getty@ttyGS0.service.d
cat <<-EOF > "${SDCARD}"/etc/systemd/system/serial-getty@ttyGS0.service.d/10-switch-role.conf
[Service]
ExecStartPre=-/bin/sh -c "echo 2 > /sys/bus/platform/devices/sunxi_usb_udc/otg_role"
EOF
fi
done
IFS=$ifs
[[ $LINUXFAMILY == sun*i ]] && mkdir -p "${SDCARD}"/boot/overlay-user
# to prevent creating swap file on NFS (needs specific kernel options)
# and f2fs/btrfs (not recommended or needs specific kernel options)
[[ $ROOTFS_TYPE != ext4 ]] && touch "${SDCARD}"/var/swap
# install initial asound.state if defined
mkdir -p "${SDCARD}"/var/lib/alsa/
[[ -n $ASOUND_STATE ]] && cp "${EXTER}/packages/blobs/asound.state/${ASOUND_STATE}" "${SDCARD}"/var/lib/alsa/asound.state
# save initial orangepi-release state
cp "${SDCARD}"/etc/orangepi-release "${SDCARD}"/etc/orangepi-image-release
# DNS fix. package resolvconf is not available everywhere
if [ -d /etc/resolvconf/resolv.conf.d ] && [ -n "$NAMESERVER" ]; then
echo "nameserver $NAMESERVER" > "${SDCARD}"/etc/resolvconf/resolv.conf.d/head
fi
# permit root login via SSH for the first boot
sed -i 's/#\?PermitRootLogin .*/PermitRootLogin yes/' "${SDCARD}"/etc/ssh/sshd_config
# enable PubkeyAuthentication
sed -i 's/#\?PubkeyAuthentication .*/PubkeyAuthentication yes/' "${SDCARD}"/etc/ssh/sshd_config
if [ -f "${SDCARD}"/etc/NetworkManager/NetworkManager.conf ]; then
# configure network manager
sed "s/managed=\(.*\)/managed=true/g" -i "${SDCARD}"/etc/NetworkManager/NetworkManager.conf
# remove network manager defaults to handle eth by default
rm -f "${SDCARD}"/usr/lib/NetworkManager/conf.d/10-globally-managed-devices.conf
# most likely we don't need to wait for nm to get online
chroot "${SDCARD}" /bin/bash -c "systemctl disable NetworkManager-wait-online.service" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
# Just regular DNS and maintain /etc/resolv.conf as a file
sed "/dns/d" -i "${SDCARD}"/etc/NetworkManager/NetworkManager.conf
sed "s/\[main\]/\[main\]\ndns=default\nrc-manager=file/g" -i "${SDCARD}"/etc/NetworkManager/NetworkManager.conf
if [[ -n $NM_IGNORE_DEVICES ]]; then
mkdir -p "${SDCARD}"/etc/NetworkManager/conf.d/
cat <<-EOF > "${SDCARD}"/etc/NetworkManager/conf.d/10-ignore-interfaces.conf
[keyfile]
unmanaged-devices=$NM_IGNORE_DEVICES
EOF
fi
elif [ -d "${SDCARD}"/etc/systemd/network ]; then
# configure networkd
rm "${SDCARD}"/etc/resolv.conf
ln -s /run/systemd/resolve/resolv.conf "${SDCARD}"/etc/resolv.conf
# enable services
chroot "${SDCARD}" /bin/bash -c "systemctl enable systemd-networkd.service systemd-resolved.service" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
if [ -e /etc/systemd/timesyncd.conf ]; then
chroot "${SDCARD}" /bin/bash -c "systemctl enable systemd-timesyncd.service" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
fi
umask 022
cat > "${SDCARD}"/etc/systemd/network/eth0.network <<- __EOF__
[Match]
Name=eth0
[Network]
#MACAddress=
DHCP=ipv4
LinkLocalAddressing=ipv4
#Address=192.168.1.100/24
#Gateway=192.168.1.1
#DNS=192.168.1.1
#Domains=example.com
NTP=0.pool.ntp.org 1.pool.ntp.org
__EOF__
fi
# avahi daemon defaults if exists
[[ -f "${SDCARD}"/usr/share/doc/avahi-daemon/examples/sftp-ssh.service ]] && \
cp "${SDCARD}"/usr/share/doc/avahi-daemon/examples/sftp-ssh.service "${SDCARD}"/etc/avahi/services/
[[ -f "${SDCARD}"/usr/share/doc/avahi-daemon/examples/ssh.service ]] && \
cp "${SDCARD}"/usr/share/doc/avahi-daemon/examples/ssh.service "${SDCARD}"/etc/avahi/services/
# nsswitch settings for sane DNS behavior: remove resolve, assure libnss-myhostname support
sed "s/hosts\:.*/hosts: files mymachines dns myhostname/g" -i "${SDCARD}"/etc/nsswitch.conf
# build logo in any case
boot_logo
# disable MOTD for first boot - we want as clean 1st run as possible
chmod -x "${SDCARD}"/etc/update-motd.d/*
}
6.1.4 chroot_installpackages_local
chroot_installpackages_local
定义在scripts/chroot-buildpackages.sh
;
chroot_installpackages_local()
{
local conf=$EXTER/config/aptly-temp.conf
rm -rf /tmp/aptly-temp/
mkdir -p /tmp/aptly-temp/
aptly -config="${conf}" repo create temp >> "${DEST}"/${LOG_SUBPATH}/install.log
# NOTE: this works recursively
if [[ $EXTERNAL_NEW == prebuilt ]]; then
aptly -config="${conf}" repo add temp "${DEB_ORANGEPI}/extra/${RELEASE}-desktop/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
aptly -config="${conf}" repo add temp "${DEB_ORANGEPI}/extra/${RELEASE}-utils/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
else
aptly -config="${conf}" repo add temp "${DEB_STORAGE}/extra/${RELEASE}-desktop/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
aptly -config="${conf}" repo add temp "${DEB_STORAGE}/extra/${RELEASE}-utils/" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
fi
# -gpg-key="925644A6"
[[ ! -d /root/.gnupg ]] && mkdir -p /root/.gnupg
aptly -keyring="$EXTER/packages/extras-buildpkgs/buildpkg-public.gpg" -secret-keyring="$EXTER/packages/extras-buildpkgs/buildpkg.gpg" -batch=true -config="${conf}" \
-gpg-key="925644A6" -passphrase="testkey1234" -component=temp -distribution="${RELEASE}" publish repo temp >> "${DEST}"/${LOG_SUBPATH}/install.log
#aptly -config="${conf}" -listen=":8189" serve &
aptly -config="${conf}" -listen=":8189" serve >> "${DEST}"/debug/install.log 2>&1 &
local aptly_pid=$!
cp $EXTER/packages/extras-buildpkgs/buildpkg.key "${SDCARD}"/tmp/buildpkg.key
cat <<-'EOF' > "${SDCARD}"/etc/apt/preferences.d/90-orangepi-temp.pref
Package: *
Pin: origin "localhost"
Pin-Priority: 550
EOF
cat <<-EOF > "${SDCARD}"/etc/apt/sources.list.d/orangepi-temp.list
deb http://localhost:8189/ $RELEASE temp
EOF
chroot_installpackages
kill "${aptly_pid}" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
} #############################################################################
6.1.5 customize_image
customize_image
定义在scripts/image-helpers.sh
;这里相当于给用户开放了一个钩子,使得用户可以在<SDK>/userpatches
目录下提供若干个hook
文件:
customize-image-host.sh
:在当前环境执行,在该脚本中可以获取到Linux SDK
编译过程中的上下文信息(变量);customize-image.sh
:在chroot
环境(可以理解为运行环境切换到为了根文件系统中)中执行,因此可以在该脚本中配置根文件系统,安装一些软件包等;比如执行cp /tmp/overlay/usr/bin/xx /usr/bin/xx
;overlay
:该目录会挂载到${SDCARD}/tmp/overlay
,因此在customize-image.sh
脚本执行时可以通过/tmp/overlay
路径获取到该目录内的数据;
脚本源码如下:
customize_image()
{
# for users that need to prepare files at host,在当前环境执行customize-image-host.sh
[[ -f $USERPATCHES_PATH/customize-image-host.sh ]] && source "$USERPATCHES_PATH"/customize-image-host.sh
call_extension_method "pre_customize_image" "image_tweaks_pre_customize" << 'PRE_CUSTOMIZE_IMAGE'
*run before customize-image.sh*
This hook is called after `customize-image-host.sh` is called, but before the overlay is mounted.
It thus can be used for the same purposes as `customize-image-host.sh`.
PRE_CUSTOMIZE_IMAGE
cp "$USERPATCHES_PATH"/customize-image.sh "${SDCARD}"/tmp/customize-image.sh
chmod +x "${SDCARD}"/tmp/customize-image.sh
mkdir -p "${SDCARD}"/tmp/overlay
# util-linux >= 2.27 required,挂载overlay
mount -o bind,ro "$USERPATCHES_PATH"/overlay "${SDCARD}"/tmp/overlay
display_alert "Calling image customization script" "customize-image.sh" "info"
# 切换到chroot环境执行customize-image.sh
chroot "${SDCARD}" /bin/bash -c "/tmp/customize-image.sh $RELEASE $LINUXFAMILY $BOARD $BUILD_DESKTOP $ARCH"
CUSTOMIZE_IMAGE_RC=$?
# 卸载挂载overlay
umount -i "${SDCARD}"/tmp/overlay >/dev/null 2>&1
mountpoint -q "${SDCARD}"/tmp/overlay || rm -r "${SDCARD}"/tmp/overlay
if [[ $CUSTOMIZE_IMAGE_RC != 0 ]]; then
exit_with_error "customize-image.sh exited with error (rc: $CUSTOMIZE_IMAGE_RC)"
fi
call_extension_method "post_customize_image" "image_tweaks_post_customize" << 'POST_CUSTOMIZE_IMAGE'
*post customize-image.sh hook*
Run after the customize-image.sh script is run, and the overlay is unmounted.
POST_CUSTOMIZE_IMAGE
}
6.1.6 chroot
环境
# remove packages that are no longer needed. Since we have intrudoced uninstall feature, we might want to clean things that are no longer needed
display_alert "No longer needed packages" "purge" "info"
chroot $SDCARD /bin/bash -c "apt-get autoremove -y" >/dev/null 2>&1
# create list of installed packages for debug purposes
chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > $DEST/${LOG_SUBPATH}/installed-packages-${RELEASE}$([[ ${BUILD_MINIMAL} == yes ]] && echo "-minimal")$([[ ${BUILD_DESKTOP} == yes ]] && echo "-desktop").list 2>&1
这段脚本的功能分为两部分:
- 在
chroot
环境中执行命令:移除不再需要的软件包; - 在
chroot
环境中执行命令:创建已安装软件包列表,将处理后的软件包列表保存到指定路径中;
6.1.7 准备分区/创建镜像
接着执行如下操作:
- 卸载
chroot
环境中的挂载点; - 调用每个
post_debootstrap_tweaks
函数移除一些启动服务; - 创建分区;
- 创建根文件系统镜像;
脚本如下:
# clean up / prepare for making the image, 卸载chroot环境中的挂载点
umount_chroot "$SDCARD"
post_debootstrap_tweaks
if [[ $ROOTFS_TYPE == fel ]]; then
......
else
prepare_partitions
create_image
fi
post_debootstrap_tweaks
函数定义在scripts/distributions.sh
;
post_debootstrap_tweaks()
{
# remove service start blockers and QEMU binary
rm -f "${SDCARD}"/sbin/initctl "${SDCARD}"/sbin/start-stop-daemon
chroot "${SDCARD}" /bin/bash -c "dpkg-divert --quiet --local --rename --remove /sbin/initctl"
chroot "${SDCARD}" /bin/bash -c "dpkg-divert --quiet --local --rename --remove /sbin/start-stop-daemon"
rm -f "${SDCARD}"/usr/sbin/policy-rc.d "${SDCARD}/usr/bin/${QEMU_BINARY}"
call_extension_method "post_post_debootstrap_tweaks" "config_post_debootstrap_tweaks" << 'POST_POST_DEBOOTSTRAP_TWEAKS'
*run after removing diversions and qemu with chroot unmounted*
Last chance to touch the `${SDCARD}` filesystem before it is copied to the final media.
It is too late to run any chrooted commands, since the supporting filesystems are already unmounted.
POST_POST_DEBOOTSTRAP_TWEAKS
}
6.1.8 清理工作
最后执行文件系统的卸载工作,以及临时目录的清理工作;
# stage: unmount tmpfs,卸载指定的文件系统
umount $SDCARD 2>&1
if [[ $use_tmpfs = yes ]]; then
# 查找/proc/mounts是否还存在$SDCARD挂载信息,如果存在尝试卸载
while grep -qs "$SDCARD" /proc/mounts
do
umount $SDCARD
sleep 5
done
fi
# 删除临时目录
rm -rf $SDCARD
# remove exit trap,取消或者清除对这些信号的处理
trap - INT TERM EXIT
6.2 create_rootfs_cache
点击查看代码
# create_rootfs_cache
#
# unpacks cached rootfs for $RELEASE or creates one
#
create_rootfs_cache()
{
local packages_hash=$(get_package_list_hash "$ROOTFSCACHE_VERSION")
local cache_type="cli"
[[ ${BUILD_DESKTOP} == yes ]] && local cache_type="xfce-desktop"
[[ -n ${DESKTOP_ENVIRONMENT} ]] && local cache_type="${DESKTOP_ENVIRONMENT}"
[[ ${BUILD_MINIMAL} == yes ]] && local cache_type="minimal"
local cache_name=${RELEASE}-${cache_type}-${ARCH}.$packages_hash.tar.lz4
local cache_fname=${EXTER}/cache/rootfs/${cache_name}
local display_name=${RELEASE}-${cache_type}-${ARCH}.${packages_hash:0:3}...${packages_hash:29}.tar.lz4
if [[ -f $cache_fname && "$ROOT_FS_CREATE_ONLY" != "force" ]]; then
local date_diff=$(( ($(date +%s) - $(stat -c %Y $cache_fname)) / 86400 ))
display_alert "Extracting $display_name" "$date_diff days old" "info"
pv -p -b -r -c -N "[ .... ] $display_name" "$cache_fname" | lz4 -dc | tar xp --xattrs -C $SDCARD/
[[ $? -ne 0 ]] && rm $cache_fname && exit_with_error "Cache $cache_fname is corrupted and was deleted. Restart."
rm $SDCARD/etc/resolv.conf
echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf
create_sources_list "$RELEASE" "$SDCARD/"
elif [[ $RELEASE == "raspi" ]]; then
display_alert "local not found" "Creating new rootfs cache for $RELEASE" "info"
cd $SDCARD # this will prevent error sh: 0: getcwd() failed
bootstrap bullseye "$SDCARD" "https://mirrors.ustc.edu.cn/debian/"
mount_chroot "$SDCARD"
display_alert "Diverting" "initctl/start-stop-daemon" "info"
# policy-rc.d script prevents starting or reloading services during image creation
printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d
LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl" &> /dev/null
LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon" &> /dev/null
printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon
printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl
chmod 755 $SDCARD/usr/sbin/policy-rc.d
chmod 755 $SDCARD/sbin/initctl
chmod 755 $SDCARD/sbin/start-stop-daemon
install_raspi_specific
umount_chroot "$SDCARD"
tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
--exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -5 -c > $cache_fname
else
display_alert "local not found" "Creating new rootfs cache for $RELEASE" "info"
# stage: debootstrap base system
if [[ $NO_APT_CACHER != yes ]]; then
# apt-cacher-ng apt-get proxy parameter
local apt_extra="-o Acquire::http::Proxy=\"http://${APT_PROXY_ADDR:-localhost:3142}\""
local apt_mirror="http://${APT_PROXY_ADDR:-localhost:3142}/$APT_MIRROR"
else
local apt_mirror="http://$APT_MIRROR"
fi
# fancy progress bars
[[ -z $OUTPUT_DIALOG ]] && local apt_extra_progress="--show-progress -o DPKG::Progress-Fancy=1"
# Ok so for eval+PIPESTATUS.
# Try this on your bash shell:
# ONEVAR="testing" eval 'bash -c "echo value once $ONEVAR && false && echo value twice $ONEVAR"' '| grep value' '| grep value' ; echo ${PIPESTATUS[*]}
# Notice how PIPESTATUS has only one element. and it is always true, although we failed explicitly with false in the middle of the bash.
# That is because eval itself is considered a single command, no matter how many pipes you put in there, you'll get a single value, the return code of the LAST pipe.
# Lets export the value of the pipe inside eval so we know outside what happened:
# ONEVAR="testing" eval 'bash -e -c "echo value once $ONEVAR && false && echo value twice $ONEVAR"' '| grep value' '| grep value' ';EVALPIPE=(${PIPESTATUS[@]})' ; echo ${EVALPIPE[*]}
display_alert "Installing base system" "Stage 1/2" "info"
cd $SDCARD # this will prevent error sh: 0: getcwd() failed
eval 'debootstrap --variant=minbase --include=${DEBOOTSTRAP_LIST// /,} ${PACKAGE_LIST_EXCLUDE:+ --exclude=${PACKAGE_LIST_EXCLUDE// /,}} \
--arch=$ARCH --components=${DEBOOTSTRAP_COMPONENTS} $DEBOOTSTRAP_OPTION --foreign $RELEASE $SDCARD/ $apt_mirror' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 1/2)..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${EVALPIPE[0]} -ne 0 || ! -f $SDCARD/debootstrap/debootstrap ]] && exit_with_error "Debootstrap base system for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} first stage failed"
cp /usr/bin/$QEMU_BINARY $SDCARD/usr/bin/
mkdir -p $SDCARD/usr/share/keyrings/
cp /usr/share/keyrings/*-archive-keyring.gpg $SDCARD/usr/share/keyrings/
display_alert "Installing base system" "Stage 2/2" "info"
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "/debootstrap/debootstrap --second-stage"' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Debootstrap (stage 2/2)..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${EVALPIPE[0]} -ne 0 || ! -f $SDCARD/bin/bash ]] && exit_with_error "Debootstrap base system for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} second stage failed"
mount_chroot "$SDCARD"
display_alert "Diverting" "initctl/start-stop-daemon" "info"
# policy-rc.d script prevents starting or reloading services during image creation
printf '#!/bin/sh\nexit 101' > $SDCARD/usr/sbin/policy-rc.d
LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/initctl" &> /dev/null
LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg-divert --quiet --local --rename --add /sbin/start-stop-daemon" &> /dev/null
printf '#!/bin/sh\necho "Warning: Fake start-stop-daemon called, doing nothing"' > $SDCARD/sbin/start-stop-daemon
printf '#!/bin/sh\necho "Warning: Fake initctl called, doing nothing"' > $SDCARD/sbin/initctl
chmod 755 $SDCARD/usr/sbin/policy-rc.d
chmod 755 $SDCARD/sbin/initctl
chmod 755 $SDCARD/sbin/start-stop-daemon
# stage: configure language and locales
display_alert "Configuring locales" "$DEST_LANG" "info"
[[ -f $SDCARD/etc/locale.gen ]] && sed -i "s/^# $DEST_LANG/$DEST_LANG/" $SDCARD/etc/locale.gen
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "locale-gen $DEST_LANG"' ${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "update-locale LANG=$DEST_LANG LANGUAGE=$DEST_LANG LC_MESSAGES=$DEST_LANG"' \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'}
if [[ -f $SDCARD/etc/default/console-setup ]]; then
sed -e 's/CHARMAP=.*/CHARMAP="UTF-8"/' -e 's/FONTSIZE=.*/FONTSIZE="8x16"/' \
-e 's/CODESET=.*/CODESET="guess"/' -i $SDCARD/etc/default/console-setup
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "setupcon --save --force"'
fi
# stage: create apt-get sources list
create_sources_list "$RELEASE" "$SDCARD/"
# add armhf arhitecture to arm64, unless configured not to do so.
if [[ "a${ARMHF_ARCH}" != "askip" ]]; then
[[ $ARCH == arm64 ]] && eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -c "dpkg --add-architecture armhf"'
fi
# this should fix resolvconf installation failure in some cases
chroot $SDCARD /bin/bash -c 'echo "resolvconf resolvconf/linkify-resolvconf boolean false" | debconf-set-selections'
# stage: update packages list
display_alert "Updating package list" "$RELEASE" "info"
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "apt-get -q -y $apt_extra update"' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Updating package lists..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${EVALPIPE[0]} -ne 0 ]] && display_alert "Updating package lists" "failed" "wrn"
# stage: upgrade base packages from xxx-updates and xxx-backports repository branches
display_alert "Upgrading base packages" "Orange Pi" "info"
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
$apt_extra $apt_extra_progress upgrade"' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Upgrading base packages..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
# Myy: Dividing the desktop packages installation steps into multiple
# ones. We first install the "ADDITIONAL_PACKAGES" in order to get
# access to software-common-properties installation.
# THEN we add the APT sources and install the Desktop packages.
# TODO : Find a way to add APT sources WITHOUT software-common-properties
[[ ${EVALPIPE[0]} -ne 0 ]] && display_alert "Upgrading base packages" "failed" "wrn"
# stage: install additional packages
display_alert "Installing the main packages for" "Orange Pi" "info"
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
$apt_extra $apt_extra_progress --no-install-recommends install $PACKAGE_MAIN_LIST"' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Orange Pi main packages..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi main packages for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} failed"
if [[ $BUILD_DESKTOP == "yes" ]]; then
# FIXME Myy : Are we keeping this only for Desktop users,
# or should we extend this to CLI users too ?
# There might be some clunky boards that require Debian packages from
# specific repos...
display_alert "Adding apt sources for Desktop packages"
add_desktop_package_sources
local apt_desktop_install_flags=""
if [[ ! -z ${DESKTOP_APT_FLAGS_SELECTED+x} ]]; then
for flag in ${DESKTOP_APT_FLAGS_SELECTED}; do
apt_desktop_install_flags+=" --install-${flag}"
done
else
# Myy : Using the previous default option, if the variable isn't defined
# And ONLY if it's not defined !
apt_desktop_install_flags+=" --no-install-recommends"
fi
display_alert "Installing the desktop packages for" "Orange Pi" "info"
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
$apt_extra $apt_extra_progress install ${apt_desktop_install_flags} $PACKAGE_LIST_DESKTOP"' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Installing Orange Pi desktop packages..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${PIPESTATUS[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi desktop packages for ${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL} failed"
fi
# Remove packages from packages.uninstall
display_alert "Uninstall packages" "$PACKAGE_LIST_UNINSTALL" "info"
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq \
$apt_extra $apt_extra_progress purge $PACKAGE_LIST_UNINSTALL"' \
${PROGRESS_LOG_TO_FILE:+' >> $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Removing packages.uninstall packages..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "Installation of Orange Pi packages failed"
# stage: purge residual packages
display_alert "Purging residual packages for" "Orange Pi" "info"
PURGINGPACKAGES=$(chroot $SDCARD /bin/bash -c "dpkg -l | grep \"^rc\" | awk '{print \$2}' | tr \"\n\" \" \"")
eval 'LC_ALL=C LANG=C chroot $SDCARD /bin/bash -e -c "DEBIAN_FRONTEND=noninteractive apt-get -y -q \
$apt_extra $apt_extra_progress remove --purge $PURGINGPACKAGES"' \
${PROGRESS_LOG_TO_FILE:+' | tee -a $DEST/${LOG_SUBPATH}/debootstrap.log'} \
${OUTPUT_DIALOG:+' | dialog --backtitle "$backtitle" --progressbox "Purging residual Orange Pi packages..." $TTY_Y $TTY_X'} \
${OUTPUT_VERYSILENT:+' >/dev/null 2>/dev/null'} ';EVALPIPE=(${PIPESTATUS[@]})'
[[ ${EVALPIPE[0]} -ne 0 ]] && exit_with_error "Purging of residual Orange Pi packages failed"
# stage: remove downloaded packages
chroot $SDCARD /bin/bash -c "apt-get -y autoremove; apt-get clean"
# DEBUG: print free space
local freespace=$(LC_ALL=C df -h)
echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log
display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"
display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"
# create list of installed packages for debug purposes
chroot $SDCARD /bin/bash -c "dpkg --get-selections" | grep -v deinstall | awk '{print $1}' | cut -f1 -d':' > ${cache_fname}.list 2>&1
# creating xapian index that synaptic runs faster
if [[ $BUILD_DESKTOP == yes ]]; then
display_alert "Recreating Synaptic search index" "Please wait" "info"
chroot $SDCARD /bin/bash -c "[[ -f /usr/sbin/update-apt-xapian-index ]] && /usr/sbin/update-apt-xapian-index -u"
fi
# this is needed for the build process later since resolvconf generated file in /run is not saved
rm $SDCARD/etc/resolv.conf
echo "nameserver $NAMESERVER" >> $SDCARD/etc/resolv.conf
# stage: make rootfs cache archive
display_alert "Ending debootstrap process and preparing cache" "$RELEASE" "info"
sync
# the only reason to unmount here is compression progress display
# based on rootfs size calculation
umount_chroot "$SDCARD"
tar cp --xattrs --directory=$SDCARD/ --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
--exclude='./sys/*' --exclude='./home/*' --exclude='./root/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "$display_name" | lz4 -5 -c > $cache_fname
# sign rootfs cache archive that it can be used for web cache once. Internal purposes
if [[ -n "${GPG_PASS}" && "${SUDO_USER}" ]]; then
[[ -n ${SUDO_USER} ]] && sudo chown -R ${SUDO_USER}:${SUDO_USER} "${DEST}"/images/
echo "${GPG_PASS}" | sudo -H -u ${SUDO_USER} bash -c "gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${cache_fname}" || exit 1
fi
# needed for backend to keep current only
touch $cache_fname.current
fi
# used for internal purposes. Faster rootfs cache rebuilding
if [[ -n "$ROOT_FS_CREATE_ONLY" ]]; then
umount --lazy "$SDCARD"
rm -rf $SDCARD
display_alert "Rootfs build done" "@host" "info"
display_alert "Target directory" "${EXTER}/cache/rootfs" "info"
display_alert "File name" "${cache_name}" "info"
# remove exit trap
trap - INT TERM EXIT
exit
fi
mount_chroot "$SDCARD"
} #############################################################################
6.3 prepare_partitions
prepare_partitions
函数中我们主要实现如下功能:
- 创建了空白镜像:
${SDCARD}.raw
,即<SDK>/.tmp/rootfs-$(uuidgen).raw
; - 为镜像创建分区表,分区表定义了镜像每个分区的位置和大小;这里分区格式设置为
gpt
,并写入了bootfs
、rootfs
分区信息; - 为镜像写入分区表后,需要格式化分区设备以创建文件系统;
- 这里将
bootfs
分区格式化为vfat
文件系统,并将其挂载到了$MOUNT/boot
;其中MOUNT
为<SDK>/.tmp/mount-$(uuidgen)
; - 这里将
rootfs
分区格式化为ext4
文件系统,并将其挂载到了$MOUNT
;
- 这里将
- 最后调整
boot/orangepiEnv.txt
文件以及生成boot script
文件boot.scr
;
完整脚本如下:
点击查看代码
# prepare_partitions
#
# creates image file, partitions and fs
# and mounts it to local dir
# FS-dependent stuff (boot and root fs partition types) happens here
#
prepare_partitions() {
display_alert "Preparing image file for rootfs" "$BOARD $RELEASE" "info"
# possible partition combinations
# /boot: none, ext4, ext2, fat (BOOTFS_TYPE)
# root: ext4, btrfs, f2fs, nfs (ROOTFS_TYPE)
# declare makes local variables by default if used inside a function
# NOTE: mountopts string should always start with comma if not empty
# array copying in old bash versions is tricky, so having filesystems as arrays
# with attributes as keys is not a good idea
declare -A parttype mkopts mkopts_label mkfs mountopts
parttype[ext4]=ext4
parttype[ext2]=ext2
parttype[fat]=fat16
parttype[f2fs]=ext4 # not a copy-paste error
parttype[btrfs]=btrfs
parttype[xfs]=xfs
# parttype[nfs] is empty
# metadata_csum and 64bit may need to be disabled explicitly when migrating to newer supported host OS releases
if [[ $HOSTRELEASE =~ buster|bullseye|bookworm|bionic|focal|jammy|kinetic|sid ]]; then
mkopts[ext4]="-q -m 2 -O ^64bit,^metadata_csum"
fi
# mkopts[fat] is empty
mkopts[ext2]='-q'
# mkopts[f2fs] is empty
mkopts[btrfs]='-m dup'
# mkopts[xfs] is empty
# mkopts[nfs] is empty
mkopts_label[ext4]='-L '
mkopts_label[ext2]='-L '
mkopts_label[fat]='-n '
mkopts_label[f2fs]='-l '
mkopts_label[btrfs]='-L '
mkopts_label[xfs]='-L '
# mkopts_label[nfs] is empty
mkfs[ext4]=ext4
mkfs[ext2]=ext2
mkfs[fat]=vfat
mkfs[f2fs]=f2fs
mkfs[btrfs]=btrfs
mkfs[xfs]=xfs
# mkfs[nfs] is empty
mountopts[ext4]=',commit=600,errors=remount-ro'
# mountopts[ext2] is empty
# mountopts[fat] is empty
# mountopts[f2fs] is empty
mountopts[btrfs]=',commit=600'
# mountopts[xfs] is empty
# mountopts[nfs] is empty
# default BOOTSIZE to use if not specified
DEFAULT_BOOTSIZE=1024 # MiB
# size of UEFI partition. 0 for no UEFI. Don't mix UEFISIZE>0 and BOOTSIZE>0
UEFISIZE=${UEFISIZE:-0}
BIOSSIZE=${BIOSSIZE:-0}
UEFI_MOUNT_POINT=${UEFI_MOUNT_POINT:-/boot/efi}
UEFI_FS_LABEL="${UEFI_FS_LABEL:-opi_efi}"
ROOT_FS_LABEL="${ROOT_FS_LABEL:-opi_root}"
BOOT_FS_LABEL="${BOOT_FS_LABEL:-opi_boot}"
call_extension_method "pre_prepare_partitions" "prepare_partitions_custom" << 'PRE_PREPARE_PARTITIONS'
*allow custom options for mkfs*
Good time to change stuff like mkfs opts, types etc.
PRE_PREPARE_PARTITIONS
# stage: determine partition configuration
local next=1
# Check if we need UEFI partition
if [[ $UEFISIZE -gt 0 ]]; then
# Check if we need BIOS partition
[[ $BIOSSIZE -gt 0 ]] && local biospart=$((next++))
local uefipart=$((next++))
fi
# Check if we need boot partition
if [[ -n $BOOTFS_TYPE || $ROOTFS_TYPE != ext4 || $CRYPTROOT_ENABLE == yes ]]; then
local bootpart=$((next++))
local bootfs=${BOOTFS_TYPE:-ext4}
[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=${DEFAULT_BOOTSIZE}
else
BOOTSIZE=0
fi
# Check if we need root partition
[[ $ROOTFS_TYPE != nfs ]] &&
local rootpart=$((next++))
# stage: calculate rootfs size
export rootfs_size=$(du -sm $SDCARD/ | cut -f1) # MiB
display_alert "Current rootfs size" "$rootfs_size MiB" "info"
call_extension_method "prepare_image_size" "config_prepare_image_size" << 'PREPARE_IMAGE_SIZE'
*allow dynamically determining the size based on the $rootfs_size*
Called after `${rootfs_size}` is known, but before `${FIXED_IMAGE_SIZE}` is taken into account.
A good spot to determine `FIXED_IMAGE_SIZE` based on `rootfs_size`.
UEFISIZE can be set to 0 for no UEFI partition, or to a size in MiB to include one.
Last chance to set `USE_HOOK_FOR_PARTITION`=yes and then implement create_partition_table hook_point.
PREPARE_IMAGE_SIZE
if [[ -n $FIXED_IMAGE_SIZE && $FIXED_IMAGE_SIZE =~ ^[0-9]+$ ]]; then
display_alert "Using user-defined image size" "$FIXED_IMAGE_SIZE MiB" "info"
local sdsize=$FIXED_IMAGE_SIZE
# basic sanity check
if [[ $ROOTFS_TYPE != nfs && $sdsize -lt $rootfs_size ]]; then
exit_with_error "User defined image size is too small" "$sdsize <= $rootfs_size"
fi
else
local imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE)) # MiB
# Hardcoded overhead +25% is needed for desktop images,
# for CLI it could be lower. Align the size up to 4MiB
if [[ $BUILD_DESKTOP == yes ]]; then
local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.35) / 1 + 0) / 4 + 1) * 4")
else
local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.30) / 1 + 0) / 4 + 1) * 4")
fi
fi
# stage: create blank image
display_alert "Creating blank image for rootfs" "$sdsize MiB" "info"
if [[ $FAST_CREATE_IMAGE == yes ]]; then
truncate --size=${sdsize}M ${SDCARD}.raw # sometimes results in fs corruption, revert to previous know to work solution
sync
else
dd if=/dev/zero bs=1M status=none count=$sdsize | pv -p -b -r -s $(($sdsize * 1024 * 1024)) -N "[ .... ] dd" | dd status=none of=${SDCARD}.raw
fi
# stage: create partition table
display_alert "Creating partitions" "${bootfs:+/boot: $bootfs }root: $ROOTFS_TYPE" "info"
if [[ "${USE_HOOK_FOR_PARTITION}" == "yes" ]]; then
{
[[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&
echo "label: dos" ||
echo "label: $IMAGE_PARTITION_TABLE"
} | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||
exit_with_error "Create partition table fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"
call_extension_method "create_partition_table" <<- 'CREATE_PARTITION_TABLE'
*only called when USE_HOOK_FOR_PARTITION=yes to create the complete partition table*
Finally, we can get our own partition table. You have to partition ${SDCARD}.raw
yourself. Good luck.
CREATE_PARTITION_TABLE
else
{
[[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&
echo "label: dos" ||
echo "label: $IMAGE_PARTITION_TABLE"
local next=$OFFSET
if [[ -n "$biospart" ]]; then
# gpt: BIOS boot
local type="21686148-6449-6E6F-744E-656564454649"
echo "$biospart : name=\"bios\", start=${next}MiB, size=${BIOSSIZE}MiB, type=${type}"
local next=$(($next + $BIOSSIZE))
fi
if [[ -n "$uefipart" ]]; then
# dos: EFI (FAT-12/16/32)
# gpt: EFI System
[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
local type="ef" ||
local type="C12A7328-F81F-11D2-BA4B-00A0C93EC93B"
echo "$uefipart : name=\"efi\", start=${next}MiB, size=${UEFISIZE}MiB, type=${type}"
local next=$(($next + $UEFISIZE))
fi
if [[ -n "$bootpart" ]]; then
# Linux extended boot
[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
local type="ea" ||
local type="BC13C2FF-59E6-4262-A352-B275FD6F7172"
if [[ -n "$rootpart" ]]; then
echo "$bootpart : name=\"bootfs\", start=${next}MiB, size=${BOOTSIZE}MiB, type=${type}"
local next=$(($next + $BOOTSIZE))
else
# no `size` argument mean "as much as possible"
echo "$bootpart : name=\"bootfs\", start=${next}MiB, type=${type}"
fi
fi
if [[ -n "$rootpart" ]]; then
# dos: Linux
# gpt: Linux filesystem
[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
local type="83" ||
local type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
# no `size` argument mean "as much as possible"
echo "$rootpart : name=\"rootfs\", start=${next}MiB, type=${type}"
fi
} | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||
exit_with_error "Partition fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"
fi
call_extension_method "post_create_partitions" <<- 'POST_CREATE_PARTITIONS'
*called after all partitions are created, but not yet formatted*
POST_CREATE_PARTITIONS
# stage: mount image
# lock access to loop devices
exec {FD}> /var/lock/orangepi-debootstrap-losetup
flock -x $FD
LOOP=$(losetup -f)
[[ -z $LOOP ]] && exit_with_error "Unable to find free loop device"
check_loop_device "$LOOP"
losetup $LOOP ${SDCARD}.raw
# loop device was grabbed here, unlock
flock -u $FD
partprobe $LOOP
# stage: create fs, mount partitions, create fstab
rm -f $SDCARD/etc/fstab
if [[ -n $rootpart ]]; then
local rootdevice="${LOOP}p${rootpart}"
if [[ $CRYPTROOT_ENABLE == yes ]]; then
display_alert "Encrypting root partition with LUKS..." "cryptsetup luksFormat $rootdevice" ""
echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksFormat $CRYPTROOT_PARAMETERS $rootdevice -
echo -n $CRYPTROOT_PASSPHRASE | cryptsetup luksOpen $rootdevice $ROOT_MAPPER -
display_alert "Root partition encryption complete." "" "ext"
# TODO: pass /dev/mapper to Docker
rootdevice=/dev/mapper/$ROOT_MAPPER # used by `mkfs` and `mount` commands
fi
check_loop_device "$rootdevice"
display_alert "Creating rootfs" "$ROOTFS_TYPE on $rootdevice"
mkfs.${mkfs[$ROOTFS_TYPE]} ${mkopts[$ROOTFS_TYPE]} ${mkopts_label[$ROOTFS_TYPE]:+${mkopts_label[$ROOTFS_TYPE]}"$ROOT_FS_LABEL"} $rootdevice >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
[[ $ROOTFS_TYPE == ext4 ]] && tune2fs -o journal_data_writeback $rootdevice > /dev/null
if [[ $ROOTFS_TYPE == btrfs && $BTRFS_COMPRESSION != none ]]; then
local fscreateopt="-o compress-force=${BTRFS_COMPRESSION}"
fi
mount ${fscreateopt} $rootdevice $MOUNT/
# create fstab (and crypttab) entry
if [[ $CRYPTROOT_ENABLE == yes ]]; then
# map the LUKS container partition via its UUID to be the 'cryptroot' device
echo "$ROOT_MAPPER UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}) none luks" >> $SDCARD/etc/crypttab
local rootfs=$rootdevice # used in fstab
else
local rootfs="UUID=$(blkid -s UUID -o value $rootdevice)"
fi
echo "$rootfs / ${mkfs[$ROOTFS_TYPE]} defaults,noatime${mountopts[$ROOTFS_TYPE]} 0 1" >> $SDCARD/etc/fstab
else
# update_initramfs will fail if /lib/modules/ doesn't exist
mount --bind --make-private $SDCARD $MOUNT/
echo "/dev/nfs / nfs defaults 0 0" >> $SDCARD/etc/fstab
fi
if [[ -n $bootpart ]]; then
display_alert "Creating /boot" "$bootfs on ${LOOP}p${bootpart}"
check_loop_device "${LOOP}p${bootpart}"
mkfs.${mkfs[$bootfs]} ${mkopts[$bootfs]} ${mkopts_label[$bootfs]:+${mkopts_label[$bootfs]}"$BOOT_FS_LABEL"} ${LOOP}p${bootpart} >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
mkdir -p $MOUNT/boot/
mount ${LOOP}p${bootpart} $MOUNT/boot/
echo "UUID=$(blkid -s UUID -o value ${LOOP}p${bootpart}) /boot ${mkfs[$bootfs]} defaults${mountopts[$bootfs]} 0 2" >> $SDCARD/etc/fstab
fi
if [[ -n $uefipart ]]; then
display_alert "Creating EFI partition" "FAT32 ${UEFI_MOUNT_POINT} on ${LOOP}p${uefipart} label ${UEFI_FS_LABEL}"
check_loop_device "${LOOP}p${uefipart}"
mkfs.fat -F32 -n "${UEFI_FS_LABEL}" ${LOOP}p${uefipart} >> "${DEST}"/debug/install.log 2>&1
mkdir -p "${MOUNT}${UEFI_MOUNT_POINT}"
mount ${LOOP}p${uefipart} "${MOUNT}${UEFI_MOUNT_POINT}"
echo "UUID=$(blkid -s UUID -o value ${LOOP}p${uefipart}) ${UEFI_MOUNT_POINT} vfat defaults 0 2" >> $SDCARD/etc/fstab
fi
echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab
call_extension_method "format_partitions" <<- 'FORMAT_PARTITIONS'
*if you created your own partitions, this would be a good time to format them*
The loop device is mounted, so ${LOOP}p1 is it's first partition etc.
FORMAT_PARTITIONS
# stage: adjust boot script or boot environment
if [[ -f $SDCARD/boot/orangepiEnv.txt ]]; then
if [[ $CRYPTROOT_ENABLE == yes ]]; then
echo "rootdev=$rootdevice cryptdevice=UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart}):$ROOT_MAPPER" >> $SDCARD/boot/orangepiEnv.txt
else
echo "rootdev=$rootfs" >> $SDCARD/boot/orangepiEnv.txt
fi
echo "rootfstype=$ROOTFS_TYPE" >> $SDCARD/boot/orangepiEnv.txt
elif [[ $rootpart != 1 ]] && [[ $SRC_EXTLINUX != yes ]]; then
local bootscript_dst=${BOOTSCRIPT##*:}
sed -i 's/mmcblk0p1/mmcblk0p2/' $SDCARD/boot/$bootscript_dst
sed -i -e "s/rootfstype=ext4/rootfstype=$ROOTFS_TYPE/" \
-e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/$bootscript_dst
fi
# if we have boot.ini = remove orangepiEnv.txt and add UUID there if enabled
if [[ -f $SDCARD/boot/boot.ini ]]; then
sed -i -e "s/rootfstype \"ext4\"/rootfstype \"$ROOTFS_TYPE\"/" $SDCARD/boot/boot.ini
if [[ $CRYPTROOT_ENABLE == yes ]]; then
local rootpart="UUID=$(blkid -s UUID -o value ${LOOP}p${rootpart})"
sed -i 's/^setenv rootdev .*/setenv rootdev "\/dev\/mapper\/'$ROOT_MAPPER' cryptdevice='$rootpart':'$ROOT_MAPPER'"/' $SDCARD/boot/boot.ini
else
sed -i 's/^setenv rootdev .*/setenv rootdev "'$rootfs'"/' $SDCARD/boot/boot.ini
fi
if [[ $LINUXFAMILY != meson64 ]]; then
[[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt
fi
fi
# if we have a headless device, set console to DEFAULT_CONSOLE
if [[ -n $DEFAULT_CONSOLE && -f $SDCARD/boot/orangepiEnv.txt ]]; then
if grep -lq "^console=" $SDCARD/boot/orangepiEnv.txt; then
sed -i "s/^console=.*/console=$DEFAULT_CONSOLE/" $SDCARD/boot/orangepiEnv.txt
else
echo "console=$DEFAULT_CONSOLE" >> $SDCARD/boot/orangepiEnv.txt
fi
fi
# recompile .cmd to .scr if boot.cmd exists
if [[ -f $SDCARD/boot/boot.cmd ]]; then
if [ -z $BOOTSCRIPT_OUTPUT ]; then BOOTSCRIPT_OUTPUT=boot.scr; fi
mkimage -C none -A arm -T script -d $SDCARD/boot/boot.cmd $SDCARD/boot/$BOOTSCRIPT_OUTPUT > /dev/null 2>&1
fi
# create extlinux config
if [[ -f $SDCARD/boot/extlinux/extlinux.conf ]]; then
echo " append root=$rootfs $SRC_CMDLINE $MAIN_CMDLINE" >> $SDCARD/boot/extlinux/extlinux.conf
[[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt
fi
}
6.3.1 准备工作
首先就是定义了一堆变量:
parttype
:map
类型,定义一个关于文件系统类型和分区类型的映射关系;mkopts
:map
类型,定义一个关于文件系统类型和mkfs.xxx
命令选项关系;mkopts_label
:map
类型,定义了用于创建文件系统时的标签参数;mountopts
:map
类型,为每种文件系统指定了挂载时使用的选项和参数;- 接着就是一些默认值设置:比如
UEFISIZE
,BIOSSIZE
等;
源码如下:
declare -A parttype mkopts mkopts_label mkfs mountopts
parttype[ext4]=ext4
parttype[ext2]=ext2
parttype[fat]=fat16
parttype[f2fs]=ext4 # not a copy-paste error
parttype[btrfs]=btrfs
parttype[xfs]=xfs
# parttype[nfs] is empty
# metadata_csum and 64bit may need to be disabled explicitly when migrating to newer supported host OS releases
if [[ $HOSTRELEASE =~ buster|bullseye|bookworm|bionic|focal|jammy|kinetic|sid ]]; then
mkopts[ext4]="-q -m 2 -O ^64bit,^metadata_csum"
fi
# mkopts[fat] is empty
mkopts[ext2]='-q'
# mkopts[f2fs] is empty
mkopts[btrfs]='-m dup'
# mkopts[xfs] is empty
# mkopts[nfs] is empty
mkopts_label[ext4]='-L '
mkopts_label[ext2]='-L '
mkopts_label[fat]='-n '
mkopts_label[f2fs]='-l '
mkopts_label[btrfs]='-L '
mkopts_label[xfs]='-L '
# mkopts_label[nfs] is empty
mkfs[ext4]=ext4
mkfs[ext2]=ext2
mkfs[fat]=vfat
mkfs[f2fs]=f2fs
mkfs[btrfs]=btrfs
mkfs[xfs]=xfs
# mkfs[nfs] is empty
mountopts[ext4]=',commit=600,errors=remount-ro'
# mountopts[ext2] is empty
# mountopts[fat] is empty
# mountopts[f2fs] is empty
mountopts[btrfs]=',commit=600'
# mountopts[xfs] is empty
# mountopts[nfs] is empty
# default BOOTSIZE to use if not specified
DEFAULT_BOOTSIZE=1024 # MiB
# size of UEFI partition. 0 for no UEFI. Don't mix UEFISIZE>0 and BOOTSIZE>0
UEFISIZE=${UEFISIZE:-0}
BIOSSIZE=${BIOSSIZE:-0}
UEFI_MOUNT_POINT=${UEFI_MOUNT_POINT:-/boot/efi}
UEFI_FS_LABEL="${UEFI_FS_LABEL:-opi_efi}"
ROOT_FS_LABEL="${ROOT_FS_LABEL:-opi_root}"
BOOT_FS_LABEL="${BOOT_FS_LABEL:-opi_boot}"
6.3.2 初始化变量
接着就是初始化一些变量;
- 设置
bootpart=1
,bootfs
=fat
,BOOTSIZE=${DEFAULT_BOOTSIZE}
:即第一个分区为bootfs
分区,文件系统类型为vfat
,大小为1024MB
; - 设置
rootpart=2
,rootfs_size=$SDCARD
目录大小;即第二个分区为rootfs
分区,文件系统类型为ext4
,大小为$SDCARD
目录实际大小; - 计算镜像文件大小:
imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE))
,其中:rootfs_size
:rootfs
分区大小;OFFSET
:默认为30
;BOOTSIZE
:boot
分区大小;UEFISIZE
:默认为0;EXTRA_ROOTFS_MIB_SIZE
:默认为0;
- 设置
sdsize
大小,要比imagesize
略大一些;
脚本源码如下:
# stage: determine partition configuration
local next=1
# Check if we need UEFI partition,跳过
if [[ $UEFISIZE -gt 0 ]]; then
.......
fi
# Check if we need boot partition,BOOTFS_TYPE="fat",因此进入
if [[ -n $BOOTFS_TYPE || $ROOTFS_TYPE != ext4 || $CRYPTROOT_ENABLE == yes ]]; then
local bootpart=$((next++))
local bootfs=${BOOTFS_TYPE:-ext4}
[[ -z $BOOTSIZE || $BOOTSIZE -le 8 ]] && BOOTSIZE=${DEFAULT_BOOTSIZE}
else
BOOTSIZE=0
fi
# Check if we need root partition
[[ $ROOTFS_TYPE != nfs ]] &&
local rootpart=$((next++))
# stage: calculate rootfs size
export rootfs_size=$(du -sm $SDCARD/ | cut -f1) # MiB
display_alert "Current rootfs size" "$rootfs_size MiB" "info"
call_extension_method "prepare_image_size" "config_prepare_image_size" << 'PREPARE_IMAGE_SIZE'
*allow dynamically determining the size based on the $rootfs_size*
Called after `${rootfs_size}` is known, but before `${FIXED_IMAGE_SIZE}` is taken into account.
A good spot to determine `FIXED_IMAGE_SIZE` based on `rootfs_size`.
UEFISIZE can be set to 0 for no UEFI partition, or to a size in MiB to include one.
Last chance to set `USE_HOOK_FOR_PARTITION`=yes and then implement create_partition_table hook_point.
PREPARE_IMAGE_SIZE
if [[ -n $FIXED_IMAGE_SIZE && $FIXED_IMAGE_SIZE =~ ^[0-9]+$ ]]; then
......
else
local imagesize=$(($rootfs_size + $OFFSET + $BOOTSIZE + $UEFISIZE + $EXTRA_ROOTFS_MIB_SIZE)) # MiB
# Hardcoded overhead +25% is needed for desktop images,
# for CLI it could be lower. Align the size up to 4MiB
if [[ $BUILD_DESKTOP == yes ]]; then
local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.35) / 1 + 0) / 4 + 1) * 4")
else
local sdsize=$(bc -l <<< "scale=0; ((($imagesize * 1.30) / 1 + 0) / 4 + 1) * 4")
fi
fi
6.3.3 创建空白镜像
接着就是使用dd
命令从 /dev/zero
读取零来创建一个空白的镜像文件,镜像文件名为${SDCARD}.raw
,大小为$sdsize MB
;
# stage: create blank image
display_alert "Creating blank image for rootfs" "$sdsize MiB" "info"
if [[ $FAST_CREATE_IMAGE == yes ]]; then
truncate --size=${sdsize}M ${SDCARD}.raw # sometimes results in fs corruption, revert to previous know to work solution
sync
else
dd if=/dev/zero bs=1M status=none count=$sdsize | pv -p -b -r -s $(($sdsize * 1024 * 1024)) -N "[ .... ] dd" | dd status=none of=${SDCARD}.raw
fi
6.3.4 创建分区表
接着使用sfdisk
命令为${SDCARD}.raw
镜像创建分区表,写入bootfs
、rootfs
分区信息;
label: gpt
1 : name="bootfs", start=30MiB, size=1024MiB, type="BC13C2FF-59E6-4262-A352-B275FD6F7172"
2 : name="rootfs", start=1054MiB, type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
脚本如下:
# stage: create partition table
display_alert "Creating partitions" "${bootfs:+/boot: $bootfs }root: $ROOTFS_TYPE" "info"
# 不会进入
if [[ "${USE_HOOK_FOR_PARTITION}" == "yes" ]]; then
.......
else
{
# IMAGE_PARTITION_TABLE=gpt, 输出:label: gpt
[[ "$IMAGE_PARTITION_TABLE" == "msdos" ]] &&
echo "label: dos" ||
echo "label: $IMAGE_PARTITION_TABLE"
local next=$OFFSET
if [[ -n "$biospart" ]]; then
......
fi
if [[ -n "$uefipart" ]]; then
......
fi
# 进入
if [[ -n "$bootpart" ]]; then
# Linux extended boot,IMAGE_PARTITION_TABLE=gpt, type被设置为BC13C2FF-59E6-4262-A352-B275FD6F7172
[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
local type="ea" ||
local type="BC13C2FF-59E6-4262-A352-B275FD6F7172"
# 进入
if [[ -n "$rootpart" ]]; then
echo "$bootpart : name=\"bootfs\", start=${next}MiB, size=${BOOTSIZE}MiB, type=${type}"
local next=$(($next + $BOOTSIZE))
else
# no `size` argument mean "as much as possible"
echo "$bootpart : name=\"bootfs\", start=${next}MiB, type=${type}"
fi
fi
# 进入
if [[ -n "$rootpart" ]]; then
# dos: Linux
# gpt: Linux filesystem,IMAGE_PARTITION_TABLE=gpt, type被设置为0FC63DAF-8483-4772-8E79-3D69D8477DE4
[[ "$IMAGE_PARTITION_TABLE" != "gpt" ]] &&
local type="83" ||
local type="0FC63DAF-8483-4772-8E79-3D69D8477DE4"
# no `size` argument mean "as much as possible"
echo "$rootpart : name=\"rootfs\", start=${next}MiB, type=${type}"
fi
} | sfdisk ${SDCARD}.raw >> "${DEST}/${LOG_SUBPATH}/install.log" 2>&1 ||
exit_with_error "Partition fail. Please check" "${DEST}/${LOG_SUBPATH}/install.log"
fi
call_extension_method "post_create_partitions" <<- 'POST_CREATE_PARTITIONS'
*called after all partitions are created, but not yet formatted*
POST_CREATE_PARTITIONS
分区表的创建总结起来就是:
{
echo “label: gpt”
echo "1 : name=\"bootfs\", start=30MiB, size=1024MiB, type=\"BC13C2FF-59E6-4262-A352-B275FD6F7172\""
echo "2 : name=\"rootfs\", start=1054MiB, type=\"0FC63DAF-8483-4772-8E79-3D69D8477DE4\""
} | sfdisk ${SDCARD}.raw
6.3.5 格式化分区&挂载镜像
接着就是处理镜像文件${SDCARD}.raw
,并将其与loop
设备(比如/dev/loop18
)关联,然后格式化分区设备以创建文件系统:
bootfs
分区(比如:/dev/loop18p1
):被格式化为vfat
文件系统,用做引导文件系统,并被挂载到$MOUNT/boot
;rootfs
分区(比如:/dev/loop18p2
):被格式化为ext4
文件系统,用做根文件系统 ,并被挂载到$MOUNT/
;
最后,脚本会将一些挂载信息写入${SDCARD}/etc/fstab
文件中,以便系统引导时正确挂载这些文件系统。
其中SDCARD
为<SDK>/.tmp/rootfs-$(uuidgen)
、MOUNT
为<SDK>/.tmp/mount-$(uuidgen)
。
脚本如下:
# stage: mount image
# lock access to loop devices,上锁,防止并发
exec {FD}> /var/lock/orangepi-debootstrap-losetup
flock -x $FD
# 查找当前未被使用的空闲环回设备,并输出该设备的路径 比如/dev/loop18
LOOP=$(losetup -f)
[[ -z $LOOP ]] && exit_with_error "Unable to find free loop device"
check_loop_device "$LOOP"
# 将其与镜像文件${SDCARD}.raw关联起来
losetup $LOOP ${SDCARD}.raw
# loop device was grabbed here, unlock, 释放锁
flock -u $FD
# 通知操作系统重新扫描指定的设备/dev/loop18,以便更新系统中的分区表信息。
partprobe $LOOP
# stage: create fs, mount partitions, create fstab
rm -f $SDCARD/etc/fstab
# 在${SDCARD}.raw镜像rootfs分区创建ext4文件系统
if [[ -n $rootpart ]]; then
# /dev/loop18p2
local rootdevice="${LOOP}p${rootpart}"
if [[ $CRYPTROOT_ENABLE == yes ]]; then
......
fi
check_loop_device "$rootdevice"
display_alert "Creating rootfs" "$ROOTFS_TYPE on $rootdevice"
# mkfs.ext4 -L opi_root /dev/loop18p2, 在指定的环回设备分区上创建一个ext4文件系统,并为该文件系统设置标签为opi_root
mkfs.${mkfs[$ROOTFS_TYPE]} ${mkopts[$ROOTFS_TYPE]} ${mkopts_label[$ROOTFS_TYPE]:+${mkopts_label[$ROOTFS_TYPE]}"$ROOT_FS_LABEL"} $rootdevice >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
# 调整ext4文件系统的的日志模式设置为写回模式
[[ $ROOTFS_TYPE == ext4 ]] && tune2fs -o journal_data_writeback $rootdevice > /dev/null
if [[ $ROOTFS_TYPE == btrfs && $BTRFS_COMPRESSION != none ]]; then
local fscreateopt="-o compress-force=${BTRFS_COMPRESSION}"
fi
# mount /dev/loop18p2 <SDK>/.tmp/mount-$(uuidgen),将/dev/loop18p2设备挂载到$MOUNT目录中
mount ${fscreateopt} $rootdevice $MOUNT/
# create fstab (and crypttab) entry
if [[ $CRYPTROOT_ENABLE == yes ]]; then
......
else
# 从/dev/loop18p2设备中提取UUID
local rootfs="UUID=$(blkid -s UUID -o value $rootdevice)"
fi
# 将一个新的文件系统条目追加到<SDK>/.tmp/rootfs-$(uuidgen)目录下的/etc/fstab文件中
echo "$rootfs / ${mkfs[$ROOTFS_TYPE]} defaults,noatime${mountopts[$ROOTFS_TYPE]} 0 1" >> $SDCARD/etc/fstab
else
......
fi
# 在${SDCARD}.raw镜像bootfs分区创建vfat文件系统
if [[ -n $bootpart ]]; then
display_alert "Creating /boot" "$bootfs on ${LOOP}p${bootpart}"
check_loop_device "${LOOP}p${bootpart}"
# mkfs.vfat -n opi_boot /dev/loop18p1, 在指定的环回设备分区上创建一VFAT 文件系统,并为该文件系统设置标签为opi_boot
mkfs.${mkfs[$bootfs]} ${mkopts[$bootfs]} ${mkopts_label[$bootfs]:+${mkopts_label[$bootfs]}"$BOOT_FS_LABEL"} ${LOOP}p${bootpart} >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
# 在<SDK>/.tmp/mount-$(uuidgen)下创建boot目录
mkdir -p $MOUNT/boot/
# 将/dev/loop18p1设备挂载到$MOUNT/boot目录中
mount ${LOOP}p${bootpart} $MOUNT/boot/
# 将一个新的文件系统条目追加到<SDK>/.tmp/rootfs-$(uuidgen)目录下的/etc/fstab文件中
echo "UUID=$(blkid -s UUID -o value ${LOOP}p${bootpart}) /boot ${mkfs[$bootfs]} defaults${mountopts[$bootfs]} 0 2" >> $SDCARD/etc/fstab
fi
if [[ -n $uefipart ]]; then
......
fi
# 写入tmpfs文件系统挂载信息
echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab
call_extension_method "format_partitions" <<- 'FORMAT_PARTITIONS'
*if you created your own partitions, this would be a good time to format them*
The loop device is mounted, so ${LOOP}p1 is it's first partition etc.
FORMAT_PARTITIONS
以上脚本简化下来就是:
LOOP=$(losetup -f)
losetup $LOOP ${SDCARD}.raw
partprobe $LOOP
# rootfs分区创建ext4文件系统
local rootdevice="${LOOP}p2"
mkfs.ext4 -L opi_root /dev/${LOOP}p2
mount $rootdevice $MOUNT/
echo "UUID=eb5b9342-7a1c-4994-91ea-648cf9059720 / ext4 defaults,noatime,commit=600,errors=remount-ro 0 1" >> $SDCARD/etc/fstab
# bootfs分区创建vfat文件系统
mkfs.vfat -n opi_boot /dev/${LOOP}p1
mkdir -p $MOUNT/boot/
mount ${LOOP}p1 $MOUNT/boot
echo "UUID=24B4-43E5 /boot vfat defaults 0 2" >> $SDCARD/etc/fstab
echo "tmpfs /tmp tmpfs defaults,nosuid 0 0" >> $SDCARD/etc/fstab
.......
# 卸载loop设备
losetup -d ${LOOP}
6.3.6 调整启动脚本/环境
函数最后就是调整boot/orangepiEnv.txt
文件以及生成boot script
文件boot.scr
。
这里我们移除一些无效脚本,简化后如下:
# stage: adjust boot script or boot environment
echo "rootdev=$rootfs" >> $SDCARD/boot/orangepiEnv.txt
echo "rootfstype=$ROOTFS_TYPE" >> $SDCARD/boot/orangepiEnv.txt
# if we have a headless device, set console to DEFAULT_CONSOLE,设置默认控制台,这里并不会进入
if [[ -n $DEFAULT_CONSOLE && -f $SDCARD/boot/orangepiEnv.txt ]]; then
if grep -lq "^console=" $SDCARD/boot/orangepiEnv.txt; then
sed -i "s/^console=.*/console=$DEFAULT_CONSOLE/" $SDCARD/boot/orangepiEnv.txt
else
echo "console=$DEFAULT_CONSOLE" >> $SDCARD/boot/orangepiEnv.txt
fi
fi
# recompile .cmd to .scr if boot.cmd exists,编译boot.cmd -> boot.scr 制作的镜像采用的就是这种启动方式
if [[ -f $SDCARD/boot/boot.cmd ]]; then
if [ -z $BOOTSCRIPT_OUTPUT ]; then BOOTSCRIPT_OUTPUT=boot.scr; fi
mkimage -C none -A arm -T script -d $SDCARD/boot/boot.cmd $SDCARD/boot/$BOOTSCRIPT_OUTPUT > /dev/null 2>&1
fi
# create extlinux config, 制作的镜像没有采用这种启动方式
if [[ -f $SDCARD/boot/extlinux/extlinux.conf ]]; then
echo " append root=$rootfs $SRC_CMDLINE $MAIN_CMDLINE" >> $SDCARD/boot/extlinux/extlinux.conf
[[ -f $SDCARD/boot/orangepiEnv.txt ]] && rm $SDCARD/boot/orangepiEnv.txt
fi
6.4 create_image
prepare_partitions
函数主要就是进行一些初始化工作,比如创建空白镜像,写入分区表、格式化分区设备以创建文件系统,最后会挂载分区设备到$MOUNT
目录。
而create_image
就可以看做是往各个分区挂载目录写入数据的过程,完整脚本如下:
点击查看代码
# create_image
#
# finishes creation of image from cached rootfs
#
create_image()
{
# stage: create file name
if [[ $SELECTED_CONFIGURATION == "cli_standard" ]]; then
IMAGE_TYPE=server
elif [[ $SELECTED_CONFIGURATION == "cli_minimal" ]]; then
IMAGE_TYPE=minimal
else
IMAGE_TYPE=desktop
fi
if [[ ${MEM_TYPE} == "1500MB" ]]; then
local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")_1.5gb"
else
local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"
fi
if [[ ${RELEASE} == "raspi" ]]; then
local version="${BOARD^}_${REVISION}_raspios_bullseye_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"
fi
[[ $ROOTFS_TYPE == nfs ]] && version=${version}_nfsboot
destimg=$DEST/images/${version}
rm -rf $destimg
mkdir -p $destimg
if [[ $ROOTFS_TYPE != nfs ]]; then
display_alert "Copying files to" "/"
echo -e "\nCopying files to [/]" >>"${DEST}"/${LOG_SUBPATH}/install.log
rsync -aHWXh \
--exclude="/boot/*" \
--exclude="/dev/*" \
--exclude="/proc/*" \
--exclude="/run/*" \
--exclude="/tmp/*" \
--exclude="/sys/*" \
--info=progress0,stats1 $SDCARD/ $MOUNT/ >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
else
display_alert "Creating rootfs archive" "rootfs.tgz" "info"
tar cp --xattrs --directory=$SDCARD/ --exclude='./boot/*' --exclude='./dev/*' --exclude='./proc/*' --exclude='./run/*' --exclude='./tmp/*' \
--exclude='./sys/*' . | pv -p -b -r -s $(du -sb $SDCARD/ | cut -f1) -N "rootfs.tgz" | gzip -c > $destimg/${version}-rootfs.tgz
fi
# stage: rsync /boot
display_alert "Copying files to" "/boot"
echo -e "\nCopying files to [/boot]" >>"${DEST}"/${LOG_SUBPATH}/install.log
if [[ $(findmnt --target $MOUNT/boot -o FSTYPE -n) == vfat ]]; then
# fat32
rsync -rLtWh \
--info=progress0,stats1 \
--log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
else
# ext4
rsync -aHWXh \
--info=progress0,stats1 \
--log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
fi
call_extension_method "pre_update_initramfs" "config_pre_update_initramfs" << 'PRE_UPDATE_INITRAMFS'
*allow config to hack into the initramfs create process*
Called after rsync has synced both `/root` and `/root` on the target, but before calling `update_initramfs`.
PRE_UPDATE_INITRAMFS
# stage: create final initramfs
[[ -n $KERNELSOURCE ]] && {
update_initramfs $MOUNT
}
# DEBUG: print free space
local freespace=$(LC_ALL=C df -h)
echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log
display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"
display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"
# stage: write u-boot
write_uboot $LOOP
# fix wrong / permissions
chmod 755 $MOUNT
call_extension_method "pre_umount_final_image" "config_pre_umount_final_image" << 'PRE_UMOUNT_FINAL_IMAGE'
*allow config to hack into the image before the unmount*
Called before unmounting both `/root` and `/boot`.
PRE_UMOUNT_FINAL_IMAGE
# unmount /boot/efi first, then /boot, rootfs third, image file last
sync
[[ $UEFISIZE != 0 ]] && umount -l "${MOUNT}${UEFI_MOUNT_POINT}"
[[ $BOOTSIZE != 0 ]] && umount -l $MOUNT/boot
[[ $ROOTFS_TYPE != nfs ]] && umount -l $MOUNT
[[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose $ROOT_MAPPER
call_extension_method "post_umount_final_image" "config_post_umount_final_image" << 'POST_UMOUNT_FINAL_IMAGE'
*allow config to hack into the image after the unmount*
Called after unmounting both `/root` and `/boot`.
POST_UMOUNT_FINAL_IMAGE
# to make sure its unmounted
while grep -Eq '(${MOUNT}|${DESTIMG})' /proc/mounts
do
display_alert "Wait for unmount" "${MOUNT}" "info"
sleep 5
done
losetup -d $LOOP
rm -rf --one-file-system $DESTIMG $MOUNT
mkdir -p $DESTIMG
mv ${SDCARD}.raw $DESTIMG/${version}.img
FINALDEST=${destimg}
# custom post_build_image_modify hook to run before fingerprinting and compression
[[ $(type -t post_build_image_modify) == function ]] && display_alert "Custom Hook Detected" "post_build_image_modify" "info" && post_build_image_modify "${DESTIMG}/${version}.img"
if [[ $BUILD_ALL != yes ]]; then
if [[ $COMPRESS_OUTPUTIMAGE == "" || $COMPRESS_OUTPUTIMAGE == no ]]; then
COMPRESS_OUTPUTIMAGE="sha,gpg,img"
elif [[ $COMPRESS_OUTPUTIMAGE == yes ]]; then
COMPRESS_OUTPUTIMAGE="sha,gpg,7z"
fi
if [[ $COMPRESS_OUTPUTIMAGE == *gz* ]]; then
display_alert "Compressing" "${DESTIMG}/${version}.img.gz" "info"
pigz -3 < $DESTIMG/${version}.img > $DESTIMG/${version}.img.gz
compression_type=".gz"
fi
if [[ $COMPRESS_OUTPUTIMAGE == *xz* ]]; then
display_alert "Compressing" "${DESTIMG}/${version}.img.xz" "info"
# compressing consumes a lot of memory we don't have. Waiting for previous packing job to finish helps to run a lot more builds in parallel
available_cpu=$(grep -c 'processor' /proc/cpuinfo)
[[ ${BUILD_ALL} == yes ]] && available_cpu=$(( $available_cpu * 30 / 100 )) # lets use 20% of resources in case of build-all
[[ ${available_cpu} -gt 8 ]] && available_cpu=8 # using more cpu cores for compressing is pointless
available_mem=$(LC_ALL=c free | grep Mem | awk '{print $4/$2 * 100.0}' | awk '{print int($1)}') # in percentage
# build optimisations when memory drops below 5%
if [[ ${BUILD_ALL} == yes && ( ${available_mem} -lt 15 || $(ps -uax | grep "pixz" | wc -l) -gt 4 )]]; then
while [[ $(ps -uax | grep "pixz" | wc -l) -gt 2 ]]
do echo -en "#"
sleep 20
done
fi
pixz -7 -p ${available_cpu} -f $(expr ${available_cpu} + 2) < $DESTIMG/${version}.img > ${DESTIMG}/${version}.img.xz
compression_type=".xz"
fi
if [[ $COMPRESS_OUTPUTIMAGE == *img* || $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
# mv $DESTIMG/${version}.img ${FINALDEST}/${version}.img || exit 1
compression_type=""
fi
if [[ $COMPRESS_OUTPUTIMAGE == *sha* ]]; then
cd ${DESTIMG}
display_alert "SHA256 calculating" "${version}.img${compression_type}" "info"
sha256sum -b ${version}.img${compression_type} > ${version}.img${compression_type}.sha
fi
if [[ $COMPRESS_OUTPUTIMAGE == *gpg* ]]; then
cd ${DESTIMG}
if [[ -n $GPG_PASS ]]; then
display_alert "GPG signing" "${version}.img${compression_type}" "info"
[[ -n ${SUDO_USER} ]] && sudo chown -R ${SUDO_USER}:${SUDO_USER} "${DESTIMG}"/
echo "${GPG_PASS}" | sudo -H -u ${SUDO_USER} bash -c "gpg --passphrase-fd 0 --armor --detach-sign --pinentry-mode loopback --batch --yes ${DESTIMG}/${version}.img${compression_type}" || exit 1
#else
# display_alert "GPG signing skipped - no GPG_PASS" "${version}.img" "wrn"
fi
fi
#fingerprint_image "${DESTIMG}/${version}.img${compression_type}.txt" "${version}"
if [[ $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
display_alert "Compressing" "${DESTIMG}/${version}.7z" "info"
7za a -t7z -bd -m0=lzma2 -mx=3 -mfb=64 -md=32m -ms=on \
${DESTIMG}/${version}.7z ${version}.key ${version}.img* >/dev/null 2>&1
find ${DESTIMG}/ -type \
f \( -name "${version}.img" -o -name "${version}.img.asc" -o -name "${version}.img.txt" -o -name "${version}.img.sha" \) -print0 \
>/dev/null 2>&1
fi
fi
#display_alert "Done building" "${DESTIMG}/${version}.img" "info"
display_alert "Done building" "${FINALDEST}/${version}.img" "info"
# call custom post build hook
[[ $(type -t post_build_image) == function ]] && post_build_image "${DESTIMG}/${version}.img"
# move artefacts from temporally directory to its final destination
[[ -n $compression_type ]] && rm $DESTIMG/${version}.img
mv $DESTIMG/${version}* ${FINALDEST}
rm -rf $DESTIMG
# write image to SD card
if [[ $(lsblk "$CARD_DEVICE" 2>/dev/null) && -f ${FINALDEST}/${version}.img ]]; then
# make sha256sum if it does not exists. we need it for comparisson
if [[ -f "${FINALDEST}/${version}".img.sha ]]; then
local ifsha=$(cat ${FINALDEST}/${version}.img.sha | awk '{print $1}')
else
local ifsha=$(sha256sum -b "${FINALDEST}/${version}".img | awk '{print $1}')
fi
display_alert "Writing image" "$CARD_DEVICE ${readsha}" "info"
# write to SD card
pv -p -b -r -c -N "[ .... ] dd" ${FINALDEST}/${version}.img | dd of=$CARD_DEVICE bs=1M iflag=fullblock oflag=direct status=none
call_extension_method "post_write_sdcard" <<- 'POST_BUILD_IMAGE'
*run after writing img to sdcard*
After the image is written to `$CARD_DEVICE`, but before verifying it.
You can still set SKIP_VERIFY=yes to skip verification.
POST_BUILD_IMAGE
if [[ "${SKIP_VERIFY}" != "yes" ]]; then
# read and compare
display_alert "Verifying. Please wait!"
local ofsha=$(dd if=$CARD_DEVICE count=$(du -b ${FINALDEST}/${version}.img | cut -f1) status=none iflag=count_bytes oflag=direct | sha256sum | awk '{print $1}')
if [[ $ifsha == $ofsha ]]; then
display_alert "Writing verified" "${version}.img" "info"
else
display_alert "Writing failed" "${version}.img" "err"
fi
fi
elif [[ `systemd-detect-virt` == 'docker' && -n $CARD_DEVICE ]]; then
# display warning when we want to write sd card under Docker
display_alert "Can't write to $CARD_DEVICE" "Enable docker privileged mode in config-docker.conf" "wrn"
fi
} #############################################################################
6.4.1 准备工作
首先就是定义了一堆变量:
- 设置
IMAGE_TYPE=server
; - 设置
version=Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
; - 设置
destimg=<SDK>/output/images/Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
;
这里我们移除一些无效脚本,简化后如下:
# stage: create file name
if [[ $SELECTED_CONFIGURATION == "cli_standard" ]]; then # 对于我制作的镜像,走这里
IMAGE_TYPE=server
elif [[ $SELECTED_CONFIGURATION == "cli_minimal" ]]; then
IMAGE_TYPE=minimal
else
IMAGE_TYPE=desktop
fi
# Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
local version="${BOARD^}_${REVISION}_${DISTRIBUTION,}_${RELEASE}_${IMAGE_TYPE}"${DESKTOP_ENVIRONMENT:+_$DESKTOP_ENVIRONMENT}"_linux$(grab_version "$LINUXSOURCEDIR")"
# 创建存放linux镜像的目录
destimg=$DEST/images/${version}
rm -rf $destimg
mkdir -p $destimg
6.4.2 写入bootfs/rootfs
分区数据
接着是向rootfs
、bootfs
分区写入数据;
rootfs
分区(挂载点为$MOUNT
):从$SDCARD/
复制文件到$MOUNT/
,排除系统目录(/boot
、/dev
、/proc
、/run
、/tmp
、/sys
);bootfs
分区(挂载点为$MOUNT/boot
):从$SDCARD/boot
复制文件到$MOUNT
目录;
这里我们移除一些无效脚本,简化后如下:
# 向rootfs分区写入数据
display_alert "Copying files to" "/"
echo -e "\nCopying files to [/]" >>"${DEST}"/${LOG_SUBPATH}/install.log
rsync -aHWXh \
--exclude="/boot/*" \
--exclude="/dev/*" \
--exclude="/proc/*" \
--exclude="/run/*" \
--exclude="/tmp/*" \
--exclude="/sys/*" \
--info=progress0,stats1 $SDCARD/ $MOUNT/ >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
# bootfs分区写入数据
display_alert "Copying files to" "/boot"
echo -e "\nCopying files to [/boot]" >>"${DEST}"/${LOG_SUBPATH}/install.log
# fat32
rsync -rLtWh \
--info=progress0,stats1 \
--log-file="${DEST}"/${LOG_SUBPATH}/install.log $SDCARD/boot $MOUNT >> "${DEST}"/${LOG_SUBPATH}/install.log
这里我们说一下rsync
命令的参数:
-
-a, --archive
:归档模式,表示以递归方式传输文件,并保持所有文件属性,等于-rlptgoD
; -
-H, --hard-links
: 保留硬链接; -
-l, --links
:保留软链接; -
-L, --copy-links
:向对待常规文件一样处理软链接; -
-W, --whole-file
:拷贝文件,不进行增量检测; -
-t, --times
:保持文件时间信息; -
-X,--xattrs
:preserve extended attributes
; -
-h,--human-readable
:output numbers in a human-readable format
; -
--log-file=FILE
:log what we're doing to the specified FILE
; -
--info=FLAGS
:fine-grained informational verbosity
; -
--exclude=PATTERN
:指定排除不需要传输的文件模式;
6.4.3 update_initramfs
接着就是更新initramfs
;
call_extension_method "pre_update_initramfs" "config_pre_update_initramfs" << 'PRE_UPDATE_INITRAMFS'
*allow config to hack into the initramfs create process*
Called after rsync has synced both `/root` and `/root` on the target, but before calling `update_initramfs`.
PRE_UPDATE_INITRAMFS
# stage: create final initramfs
[[ -n $KERNELSOURCE ]] && {
update_initramfs $MOUNT
}
# DEBUG: print free space
local freespace=$(LC_ALL=C df -h)
echo $freespace >> $DEST/${LOG_SUBPATH}/debootstrap.log
display_alert "Free SD cache" "$(echo -e "$freespace" | grep $SDCARD | awk '{print $5}')" "info"
display_alert "Mount point" "$(echo -e "$freespace" | grep $MOUNT | head -1 | awk '{print $5}')" "info"
6.4.4 写入idbloader.img/u-boot.itb
接着通过write_uboot
向镜像文件指定扇区写入idbloader.img
、u-boot.itb
;
- 解压
<SDK>/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb
; - 通过
dd
命令将idbloader.img
写入到0x40
扇区处; - 通过
dd
命令将u-boot.itb
写入到0x4000
扇区处。
脚本如下:
# stage: write u-boot
write_uboot $LOOP
write_uboot
定义在scripts/image-helpers.sh
;
write_uboot()
{
local loop=$1 revision
display_alert "Writing U-boot bootloader" "$loop" "info"
TEMP_DIR=$(mktemp -d || exit 1)
chmod 700 ${TEMP_DIR}
revision=${REVISION}
if [[ -n $UPSTREM_VER ]]; then
revision=${UPSTREM_VER}
dpkg -x "${DEB_STORAGE}/u-boot/linux-u-boot-${BOARD}-${BRANCH}_${revision}_${ARCH}.deb" ${TEMP_DIR}/
else
# 解压<SDK>/debs/u-boot/linux-u-boot-current-orangepi3b_1.0.6_arm64.deb到临时目录
dpkg -x "${DEB_STORAGE}/u-boot/${CHOSEN_UBOOT}_${revision}_${ARCH}.deb" ${TEMP_DIR}/
fi
# source platform install to read $DIR,执行platform_install.sh通过dd命令实现u-boot的安装功能
source ${TEMP_DIR}/usr/lib/u-boot/platform_install.sh
write_uboot_platform "${TEMP_DIR}${DIR}" "$loop" # DIR=/usr/lib/linux-u-boot-current-orangepi3b_1.0.6_arm64
[[ $? -ne 0 ]] && exit_with_error "U-boot bootloader failed to install" "@host"
rm -rf ${TEMP_DIR}
}
6.4.5 卸载即清理工作
接着执行如下操作:
- 修正权限:将
$MOUNT
目录的权限设置为755
; - 执行卸载操作操作:
sync
确保所有待处理的数据写入到对应分区,然后卸载卸载$MOUNT/boot
、$MOUNT
; - 确保卸载完成:循环检查
/proc/mounts
文件,查看$MOUNT
或$DESTIMG
是否仍在挂载中; - 清理操作: 解除循环设备
$LOOP
的绑定, 递归删除$DESTIMG
和$MOUNT
目录; - 生成
Linux
镜像文件,位于目录$DESTIMG
,DESTIMG
被设置为<SDK>/.tmp/image-${MOUNT_UUID}
,镜像文件名为Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5.img
;
移除一些无效代码简化如下:
# fix wrong / permissions
chmod 755 $MOUNT
# unmount /boot, rootfs third, image file last
sync
[[ $BOOTSIZE != 0 ]] && umount -l $MOUNT/boot
[[ $ROOTFS_TYPE != nfs ]] && umount -l $MOUNT
# to make sure its unmounted
while grep -Eq '(${MOUNT}|${DESTIMG})' /proc/mounts
do
display_alert "Wait for unmount" "${MOUNT}" "info"
sleep 5
done
losetup -d $LOOP
rm -rf --one-file-system $DESTIMG $MOUNT
mkdir -p $DESTIMG
mv ${SDCARD}.raw $DESTIMG/${version}.img
# FINALDEST=<SDK>/output/images/Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
FINALDEST=${destimg}
6.4.6 post_build_image_modify
post_build_image_modify
相当于给用户开放了一个钩子,使得用户可以修改linux
镜像文件:
# custom post_build_image_modify hook to run before fingerprinting and compression
[[ $(type -t post_build_image_modify) == function ]] && display_alert "Custom Hook Detected" "post_build_image_modify" "info" && post_build_image_modify "${DESTIMG}/${version}.img"
我们可以创建<SDK>/userpatches/customize-image-host.sh
文件,并提供自定义的post_build_image_modify
函数来修改linux
镜像文件。
6.4.7 生成Linux
镜像文件
接着对<SDK>/.tmp/image-${MOUNT_UUID}
目录下的Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5.img
文件进行校验,并将镜像文件拷贝到<SDK>/output/images/Orangepi3b_1.0.6_debian_bullseye_server_linux6.6.0-rc5
;
if [[ $BUILD_ALL != yes ]]; then
COMPRESS_OUTPUTIMAGE="sha,gpg,img"
if [[ $COMPRESS_OUTPUTIMAGE == *img* || $COMPRESS_OUTPUTIMAGE == *7z* ]]; then
compression_type=""
fi
if [[ $COMPRESS_OUTPUTIMAGE == *sha* ]]; then
cd ${DESTIMG}
display_alert "SHA256 calculating" "${version}.img${compression_type}" "info"
sha256sum -b ${version}.img${compression_type} > ${version}.img${compression_type}.sha
fi
fi
display_alert "Done building" "${FINALDEST}/${version}.img" "info"
# call custom post build hook
[[ $(type -t post_build_image) == function ]] && post_build_image "${DESTIMG}/${version}.img"
# move artefacts from temporally directory to its final destination,将Linux镜像拷贝到目标路径${FINALDEST}
[[ -n $compression_type ]] && rm $DESTIMG/${version}.img
mv $DESTIMG/${version}* ${FINALDEST}
rm -rf $DESTIMG
这里有一处值得留意,就是post_build_image
,其目的和post_build_image_modify
一样。同样我们可以创建<SDK>/userpatches/customize-image-host.sh
文件,并提供自定义的post_build_image
函数来修改linux
镜像文件。
七、配置文件
在编译过程中使用到了若干配置文件,这些配置文件决定了编译的走向,因此来说他们是至关重要的。
7.1 config-default.conf
位于<SDK>/userpatches
目录,config-default.conf
是一个比较重要的文件,保存用户配置,可以用来配置开发板型号、内核版本、Linux
发行版类型等。
这里面定义的变量会被编译脚本使用到,从而决定了编译的流程;
KERNEL_CONFIGURE="" # leave empty to select each time, set to "yes" or "no" to skip dialog prompt
CLEAN_LEVEL="debs,oldcache" # comma-separated list of clean targets: "make" = make clean for selected kernel and u-boot,
# "debs" = delete packages in "./output/debs" for current branch and family,
# "alldebs" = delete all packages in "./output/debs", "images" = delete "./output/images",
# "cache" = delete "./output/cache", "sources" = delete "./sources"
# "oldcache" = remove old cached rootfs except for the newest 8 files
DEST_LANG="en_US.UTF-8" # sl_SI.UTF-8, en_US.UTF-8
# advanced
EXTERNAL_NEW="prebuilt" # compile and install or install prebuilt additional packages
INSTALL_HEADERS="" # install kernel headers package
LIB_TAG="master" # change to "branchname" to use any branch currently available.
USE_TORRENT="yes" # use torrent network for faster toolchain and cache download
DOWNLOAD_MIRROR="china" # set to "china" to use mirrors.tuna.tsinghua.edu.cn
BOARD=""
BRANCH=""
RELEASE=""
WIREGUARD="no"
BUILD_KSRC="no"
INSTALL_KSRC="no"
IGNORE_UPDATES="yes"
COMPRESS_OUTPUTIMAGE="no"
NO_APT_CACHER="yes"
#install_balena_etcher="yes"
#install_zfs="yes"
#install_docker="yes"
#install_chromium="yes"
#install_firefox="yes"
比如:
IGNORE_UPDATES
:是否关闭源码同步功能,即在编译的过程中不会将github
服务器的kernel
源码同步到本地;BOARD
:可以用来配置选择的开发板型号,比如orangepi3b
;BRANCH
:可以用来配置选择的内核版本,比如current
;RELEASE
:可以用来配置选择Linux
发行版的类型,比如bullseye
;BUILD_KSRC
:可以用来配置是否关闭内核构建。
7.2 orangepi3b.conf
<SDK>/external/config/boards
目录下存放存放各种型号开发板所使用的配置。
当我们配置了BOARD=orangepi3b
,就会使用orangepi3b.conf
配置文件;
# Rockchip RK3566 hexa core 4GB RAM SoC GBE eMMC USB3 USB-C WiFi/BT
BOARD_NAME="OPI 3B"
BOARDFAMILY="rockchip-rk356x"
BOOTCONFIG="orangepi-3b-rk3566_defconfig"
KERNEL_TARGET="legacy,current"
BOOT_LOGO="desktop"
BOOT_SUPPORT_SPI="yes"
DISTRIB_TYPE_LEGACY="focal jammy bullseye bookworm raspi"
BOOTFS_TYPE="fat"
IMAGE_PARTITION_TABLE="gpt"
REVISION="1.0.6"
定义了BOARD_NAME
、BOARDFAMILY
、BOOTCONFIG
、KERNEL_TARGET
、BOOTFS_TYPE
、IMAGE_PARTITION_TABLE
等;
7.3 configuration.sh
位于scripts
目录下,main.sh
执行过程中会使用source
命令执行configuration.sh
脚本;
#shellcheck source=configuration.sh
source "${SRC}"/scripts/configuration.sh
这个脚本中同样定义了一大堆的配置项,同时在这个文件中使用source
命令执行若干脚本;
${EXTER}/config/sources/families/${LINUXFAMILY}.conf
:即<SDK>/external/config/sources/families/rockchip-rk356x.conf
,该文件配置了rk356x
型号SoC
所使用的配置;${EXTER}/config/sources/${ARCH}.conf
:即<SDK>/external/config/sources/arm64.conf
,该文件配置了ARM64
架构使用的配置;
具体如下;
点击查看代码
#!/bin/bash
#
# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
[[ -z $VENDOR ]] && VENDOR="Orange Pi"
[[ -z $ROOTPWD ]] && ROOTPWD="orangepi" # Must be changed @first login
[[ -z $OPI_USERNAME ]] && OPI_USERNAME="orangepi"
[[ -z $OPI_PWD ]] && OPI_PWD="orangepi"
[[ -z $MAINTAINER ]] && MAINTAINER="Orange Pi" # deb signature
[[ -z $MAINTAINERMAIL ]] && MAINTAINERMAIL="leeboby@aliyun.com" # deb signature
[[ -z $DEB_COMPRESS ]] && DEB_COMPRESS="xz" # compress .debs with XZ by default. Use 'none' for faster/larger builds
TZDATA=$(cat /etc/timezone) # Timezone for target is taken from host or defined here.
USEALLCORES=yes # Use all CPU cores for compiling
HOSTRELEASE=$(cat /etc/os-release | grep VERSION_CODENAME | cut -d"=" -f2)
[[ -z $HOSTRELEASE ]] && HOSTRELEASE=$(cut -d'/' -f1 /etc/debian_version)
[[ -z $EXIT_PATCHING_ERROR ]] && EXIT_PATCHING_ERROR="" # exit patching if failed
[[ -z $HOST ]] && HOST="$BOARD" # set hostname to the board
[[ -z $CHINA_DOWNLOAD_MIRROR ]] && CHINA_DOWNLOAD_MIRROR=huawei
cd "${SRC}" || exit
[[ -z "${ROOTFSCACHE_VERSION}" ]] && ROOTFSCACHE_VERSION=11
[[ -z "${CHROOT_CACHE_VERSION}" ]] && CHROOT_CACHE_VERSION=7
[[ -z $PLYMOUTH ]] && PLYMOUTH="yes"
cd ${SRC}/scripts
BUILD_REPOSITORY_URL=$(improved_git remote get-url $(improved_git remote 2>/dev/null | grep origin) 2>/dev/null)
BUILD_REPOSITORY_COMMIT=$(improved_git describe --match=d_e_a_d_b_e_e_f --always --dirty 2>/dev/null)
ROOTFS_CACHE_MAX=200 # max number of rootfs cache, older ones will be cleaned up
DEB_STORAGE=$DEST/debs
DEB_ORANGEPI=$EXTER/cache/debs
# TODO: fixed name can't be used for parallel image building
ROOT_MAPPER="orangepi-root"
[[ -z $ROOTFS_TYPE ]] && ROOTFS_TYPE=ext4 # default rootfs type is ext4
[[ "ext4 f2fs btrfs xfs nfs fel" != *$ROOTFS_TYPE* ]] && exit_with_error "Unknown rootfs type" "$ROOTFS_TYPE"
[[ -z $BTRFS_COMPRESSION ]] && BTRFS_COMPRESSION=zlib # default btrfs filesystem compression method is zlib
[[ ! $BTRFS_COMPRESSION =~ zlib|lzo|zstd|none ]] && exit_with_error "Unknown btrfs compression method" "$BTRFS_COMPRESSION"
# Fixed image size is in 1M dd blocks (MiB)
# to get size of block device /dev/sdX execute as root:
# echo $(( $(blockdev --getsize64 /dev/sdX) / 1024 / 1024 ))
[[ "f2fs" == *$ROOTFS_TYPE* && -z $FIXED_IMAGE_SIZE ]] && exit_with_error "Please define FIXED_IMAGE_SIZE"
# a passphrase is mandatory if rootfs encryption is enabled
if [[ $CRYPTROOT_ENABLE == yes && -z $CRYPTROOT_PASSPHRASE ]]; then
exit_with_error "Root encryption is enabled but CRYPTROOT_PASSPHRASE is not set"
fi
# small SD card with kernel, boot script and .dtb/.bin files
[[ $ROOTFS_TYPE == nfs ]] && FIXED_IMAGE_SIZE=64
# Since we are having too many options for mirror management,
# then here is yet another mirror related option.
# Respecting user's override in case a mirror is unreachable.
case $REGIONAL_MIRROR in
china)
[[ -z $USE_MAINLINE_GOOGLE_MIRROR ]] && [[ -z $MAINLINE_MIRROR ]] && MAINLINE_MIRROR=tuna
[[ -z $USE_GITHUB_UBOOT_MIRROR ]] && [[ -z $UBOOT_MIRROR ]] && UBOOT_MIRROR=gitee
[[ -z $GITHUB_MIRROR ]] && GITHUB_MIRROR=gitclone
[[ -z $DOWNLOAD_MIRROR ]] && DOWNLOAD_MIRROR=china
;;
*)
;;
esac
# used by multiple sources - reduce code duplication
[[ $USE_MAINLINE_GOOGLE_MIRROR == yes ]] && MAINLINE_MIRROR=google
case $MAINLINE_MIRROR in
google)
MAINLINE_KERNEL_SOURCE='https://kernel.googlesource.com/pub/scm/linux/kernel/git/stable/linux-stable'
MAINLINE_FIRMWARE_SOURCE='https://kernel.googlesource.com/pub/scm/linux/kernel/git/firmware/linux-firmware.git'
;;
tuna)
MAINLINE_KERNEL_SOURCE='https://mirrors.tuna.tsinghua.edu.cn/git/linux-stable.git'
MAINLINE_FIRMWARE_SOURCE='https://mirrors.tuna.tsinghua.edu.cn/git/linux-firmware.git'
;;
bfsu)
MAINLINE_KERNEL_SOURCE='https://mirrors.bfsu.edu.cn/git/linux-stable.git'
MAINLINE_FIRMWARE_SOURCE='https://mirrors.bfsu.edu.cn/git/linux-firmware.git'
;;
*)
MAINLINE_KERNEL_SOURCE='git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git'
MAINLINE_FIRMWARE_SOURCE='git://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git'
;;
esac
MAINLINE_KERNEL_DIR="$SRC/kernel"
[[ $USE_GITHUB_UBOOT_MIRROR == yes ]] && UBOOT_MIRROR=github
case $UBOOT_MIRROR in
gitee)
MAINLINE_UBOOT_SOURCE='https://github.com/orangepi-xunlong/u-boot-orangepi.git'
;;
github)
MAINLINE_UBOOT_SOURCE='https://github.com/orangepi-xunlong/u-boot-orangepi.git'
;;
*)
MAINLINE_UBOOT_SOURCE='https://source.denx.de/u-boot/u-boot.git'
;;
esac
MAINLINE_UBOOT_DIR="$SRC/u-boot"
case $GITHUB_MIRROR in
fastgit)
GITHUB_SOURCE='https://hub.fastgit.xyz'
;;
gitclone)
GITHUB_SOURCE='https://gitclone.com/github.com'
;;
*)
GITHUB_SOURCE='https://github.com'
;;
esac
# Let's set default data if not defined in board configuration above
[[ -z $OFFSET ]] && OFFSET=4 # offset to 1st partition (we use 4MiB boundaries by default)
ARCH=armhf
KERNEL_IMAGE_TYPE=zImage
CAN_BUILD_STRETCH=yes
ATF_COMPILE=yes
[[ -z $CRYPTROOT_SSH_UNLOCK ]] && CRYPTROOT_SSH_UNLOCK=yes
[[ -z $CRYPTROOT_SSH_UNLOCK_PORT ]] && CRYPTROOT_SSH_UNLOCK_PORT=2022
# Default to pdkdf2, this used to be the default with cryptroot <= 2.0, however
# cryptroot 2.1 changed that to Argon2i. Argon2i is a memory intensive
# algorithm which doesn't play well with SBCs (need 1GiB RAM by default !)
# https://gitlab.com/cryptsetup/cryptsetup/-/issues/372
[[ -z $CRYPTROOT_PARAMETERS ]] && CRYPTROOT_PARAMETERS="--pbkdf pbkdf2"
[[ -z $WIREGUARD ]] && WIREGUARD="no"
[[ -z $EXTRAWIFI ]] && EXTRAWIFI="yes"
[[ -z $SKIP_BOOTSPLASH ]] && SKIP_BOOTSPLASH="no"
[[ -z $AUFS ]] && AUFS="yes"
[[ -z $IMAGE_PARTITION_TABLE ]] && IMAGE_PARTITION_TABLE="msdos"
[[ -z $EXTRA_BSP_NAME ]] && EXTRA_BSP_NAME=""
[[ -z $EXTRA_ROOTFS_MIB_SIZE ]] && EXTRA_ROOTFS_MIB_SIZE=0
[[ -z $BUILD_KSRC ]] && BUILD_KSRC="no"
# single ext4 partition is the default and preferred configuration
#BOOTFS_TYPE=''
[[ ! -f ${EXTER}/config/sources/families/$LINUXFAMILY.conf ]] && \
exit_with_error "Sources configuration not found" "$LINUXFAMILY"
source "${EXTER}/config/sources/families/${LINUXFAMILY}.conf"
if [[ -f $USERPATCHES_PATH/sources/families/$LINUXFAMILY.conf ]]; then
display_alert "Adding user provided $LINUXFAMILY overrides"
source "$USERPATCHES_PATH/sources/families/${LINUXFAMILY}.conf"
fi
# load architecture defaults
source "${EXTER}/config/sources/${ARCH}.conf"
## Extensions: at this point we've sourced all the config files that will be used,
## and (hopefully) not yet invoked any extension methods. So this is the perfect
## place to initialize the extension manager. It will create functions
## like the 'post_family_config' that is invoked below.
initialize_extension_manager
call_extension_method "post_family_config" "config_tweaks_post_family_config" << 'POST_FAMILY_CONFIG'
*give the config a chance to override the family/arch defaults*
This hook is called after the family configuration (`sources/families/xxx.conf`) is sourced.
Since the family can override values from the user configuration and the board configuration,
it is often used to in turn override those.
POST_FAMILY_CONFIG
# Myy : Menu configuration for choosing desktop configurations
show_menu() {
provided_title=$1
provided_backtitle=$2
provided_menuname=$3
# Myy : I don't know why there's a TTY_Y - 8...
#echo "Provided title : $provided_title"
#echo "Provided backtitle : $provided_backtitle"
#echo "Provided menuname : $provided_menuname"
#echo "Provided options : " "${@:4}"
#echo "TTY X: $TTY_X Y: $TTY_Y"
whiptail --title "${provided_title}" --backtitle "${provided_backtitle}" --notags \
--menu "${provided_menuname}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
"${@:4}" \
3>&1 1>&2 2>&3
}
# Myy : FIXME Factorize
show_select_menu() {
provided_title=$1
provided_backtitle=$2
provided_menuname=$3
#dialog --stdout --title "${provided_title}" --backtitle "${provided_backtitle}" \
#--checklist "${provided_menuname}" $TTY_Y $TTY_X $((TTY_Y - 8)) "${@:4}"
#whiptail --separate-output --title "${provided_title}" --backtitle "${provided_backtitle}" \
# --checklist "${provided_menuname}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
# "${@:4}" \
# 3>&1 1>&2 2>&3
whiptail --title "${provided_title}" --backtitle "${provided_backtitle}" \
--checklist "${provided_menuname}" "${TTY_Y}" "${TTY_X}" $((TTY_Y - 8)) \
"${@:4}" \
3>&1 1>&2 2>&3
}
# Myy : Once we got a list of selected groups, parse the PACKAGE_LIST inside configuration.sh
DESKTOP_ELEMENTS_DIR="${EXTER}/config/desktop/${RELEASE}"
DESKTOP_CONFIGS_DIR="${DESKTOP_ELEMENTS_DIR}/environments"
DESKTOP_CONFIG_PREFIX="config_"
DESKTOP_APPGROUPS_DIR="${DESKTOP_ELEMENTS_DIR}/appgroups"
desktop_element_available_for_arch() {
local desktop_element_path="${1}"
local targeted_arch="${2}"
local arch_limitation_file="${1}/only_for"
echo "Checking if ${desktop_element_path} is available for ${targeted_arch} in ${arch_limitation_file}" >> "${DEST}"/${LOG_SUBPATH}/output.log
if [[ -f "${arch_limitation_file}" ]]; then
grep -- "${targeted_arch}" "${arch_limitation_file}" > /dev/null
return $?
else
return 0
fi
}
desktop_element_supported() {
local desktop_element_path="${1}"
local support_level_filepath="${desktop_element_path}/support"
if [[ -f "${support_level_filepath}" ]]; then
local support_level="$(cat "${support_level_filepath}")"
if [[ "${support_level}" != "supported" && "${EXPERT}" != "yes" ]]; then
return 65
fi
desktop_element_available_for_arch "${desktop_element_path}" "${ARCH}"
if [[ $? -ne 0 ]]; then
return 66
fi
else
return 64
fi
return 0
}
if [[ $BUILD_DESKTOP == "yes" && -z $DESKTOP_ENVIRONMENT ]]; then
desktop_environments_prepare_menu() {
for desktop_env_dir in "${DESKTOP_CONFIGS_DIR}/"*; do
local desktop_env_name=$(basename ${desktop_env_dir})
local expert_infos=""
[[ "${EXPERT}" == "yes" ]] && expert_infos="[$(cat "${desktop_env_dir}/support" 2> /dev/null)]"
desktop_element_supported "${desktop_env_dir}" "${ARCH}" && options+=("${desktop_env_name}" "${desktop_env_name^} desktop environment ${expert_infos}")
done
}
options=()
desktop_environments_prepare_menu
if [[ "${options[0]}" == "" ]]; then
exit_with_error "No desktop environment seems to be available for your board ${BOARD} (ARCH : ${ARCH} - EXPERT : ${EXPERT})"
fi
DESKTOP_ENVIRONMENT=$(show_menu "Choose a desktop environment" "$backtitle" "Select the default desktop environment to bundle with this image" "${options[@]}")
unset options
if [[ -z "${DESKTOP_ENVIRONMENT}" ]]; then
exit_with_error "No desktop environment selected..."
fi
fi
if [[ $BUILD_DESKTOP == "yes" ]]; then
# Expected environment variables :
# - options
# - ARCH
desktop_environment_check_if_valid() {
local error_msg=""
desktop_element_supported "${DESKTOP_ENVIRONMENT_DIRPATH}" "${ARCH}"
local retval=$?
if [[ ${retval} == 0 ]]; then
return
elif [[ ${retval} == 64 ]]; then
error_msg+="Either the desktop environment ${DESKTOP_ENVIRONMENT} does not exist "
error_msg+="or the file ${DESKTOP_ENVIRONMENT_DIRPATH}/support is missing"
elif [[ ${retval} == 65 ]]; then
error_msg+="Only experts can build an image with the desktop environment \"${DESKTOP_ENVIRONMENT}\", since the Armbian team won't offer any support for it (EXPERT=${EXPERT})"
elif [[ ${retval} == 66 ]]; then
error_msg+="The desktop environment \"${DESKTOP_ENVIRONMENT}\" has no packages for your targeted board architecture (BOARD=${BOARD} ARCH=${ARCH}). "
error_msg+="The supported boards architectures are : "
error_msg+="$(cat "${DESKTOP_ENVIRONMENT_DIRPATH}/only_for")"
fi
# supress error when cache is rebuilding
[[ -n "$ROOT_FS_CREATE_ONLY" ]] && exit 0
exit_with_error "${error_msg}"
}
DESKTOP_ENVIRONMENT_DIRPATH="${DESKTOP_CONFIGS_DIR}/${DESKTOP_ENVIRONMENT}"
desktop_environment_check_if_valid
fi
if [[ $BUILD_DESKTOP == "yes" && -z $DESKTOP_ENVIRONMENT_CONFIG_NAME ]]; then
# FIXME Check for empty folders, just in case the current maintainer
# messed up
# Note, we could also ignore it and don't show anything in the previous
# menu, but that hides information and make debugging harder, which I
# don't like. Adding desktop environments as a maintainer is not a
# trivial nor common task.
options=()
for configuration in "${DESKTOP_ENVIRONMENT_DIRPATH}/${DESKTOP_CONFIG_PREFIX}"*; do
config_filename=$(basename ${configuration})
config_name=${config_filename#"${DESKTOP_CONFIG_PREFIX}"}
options+=("${config_filename}" "${config_name} configuration")
done
DESKTOP_ENVIRONMENT_CONFIG_NAME=$(show_menu "Choose the desktop environment config" "$backtitle" "Select the configuration for this environment.\nThese are sourced from ${desktop_environment_config_dir}" "${options[@]}")
unset options
if [[ -z $DESKTOP_ENVIRONMENT_CONFIG_NAME ]]; then
exit_with_error "No desktop configuration selected... Do you really want a desktop environment ?"
fi
fi
if [[ $BUILD_DESKTOP == "yes" ]]; then
DESKTOP_ENVIRONMENT_PACKAGE_LIST_DIRPATH="${DESKTOP_ENVIRONMENT_DIRPATH}/${DESKTOP_ENVIRONMENT_CONFIG_NAME}"
DESKTOP_ENVIRONMENT_PACKAGE_LIST_FILEPATH="${DESKTOP_ENVIRONMENT_PACKAGE_LIST_DIRPATH}/packages"
fi
# "-z ${VAR+x}" allows to check for unset variable
# Technically, someone might want to build a desktop with no additional
# appgroups.
if [[ $BUILD_DESKTOP == "yes" && -z ${DESKTOP_APPGROUPS_SELECTED+x} && ${RELEASE} != "raspi" ]]; then
options=()
for appgroup_path in "${DESKTOP_APPGROUPS_DIR}/"*; do
appgroup="$(basename "${appgroup_path}")"
options+=("${appgroup}" "${appgroup^}" off)
done
DESKTOP_APPGROUPS_SELECTED=$(\
show_select_menu \
"Choose desktop softwares to add" \
"$backtitle" \
"Select which kind of softwares you'd like to add to your build" \
"${options[@]}")
DESKTOP_APPGROUPS_SELECTED=${DESKTOP_APPGROUPS_SELECTED//\"/}
unset options
fi
#exit_with_error 'Testing'
# Expected variables
# - aggregated_content
# - potential_paths
# - separator
# Write to variables :
# - aggregated_content
aggregate_content() {
LOG_OUTPUT_FILE="${SRC}/output/${LOG_SUBPATH}/potential-paths.log"
echo -e "Potential paths :" >> "${LOG_OUTPUT_FILE}"
show_checklist_variables potential_paths
for filepath in ${potential_paths}; do
if [[ -f "${filepath}" ]]; then
echo -e "${filepath/"$EXTER"\//} yes" >> "${LOG_OUTPUT_FILE}"
aggregated_content+=$(cat "${filepath}")
aggregated_content+="${separator}"
# else
# echo -e "${filepath/"$EXTER"\//} no\n" >> "${LOG_OUTPUT_FILE}"
fi
done
echo "" >> "${LOG_OUTPUT_FILE}"
unset LOG_OUTPUT_FILE
}
# set unique mounting directory
MOUNT_UUID=$(uuidgen)
SDCARD="${SRC}/.tmp/rootfs-${MOUNT_UUID}"
MOUNT="${SRC}/.tmp/mount-${MOUNT_UUID}"
DESTIMG="${SRC}/.tmp/image-${MOUNT_UUID}"
# dropbear needs to be configured differently
[[ $CRYPTROOT_ENABLE == yes && $RELEASE == xenial ]] && exit_with_error "Encrypted rootfs is not supported in Xenial"
[[ $RELEASE == stretch && $CAN_BUILD_STRETCH != yes ]] && exit_with_error "Building Debian Stretch images with selected kernel is not supported"
[[ $RELEASE == bionic && $CAN_BUILD_STRETCH != yes ]] && exit_with_error "Building Ubuntu Bionic images with selected kernel is not supported"
[[ $RELEASE == hirsute && $HOSTRELEASE == focal ]] && exit_with_error "Building Ubuntu Hirsute images requires Hirsute build host. Please upgrade your host or select a different target OS"
[[ -n $ATFSOURCE && -z $ATF_USE_GCC ]] && exit_with_error "Error in configuration: ATF_USE_GCC is unset"
[[ -z $UBOOT_USE_GCC ]] && exit_with_error "Error in configuration: UBOOT_USE_GCC is unset"
[[ -z $KERNEL_USE_GCC ]] && exit_with_error "Error in configuration: KERNEL_USE_GCC is unset"
BOOTCONFIG_VAR_NAME=BOOTCONFIG_${BRANCH^^}
[[ -n ${!BOOTCONFIG_VAR_NAME} ]] && BOOTCONFIG=${!BOOTCONFIG_VAR_NAME}
[[ -z $LINUXCONFIG ]] && LINUXCONFIG="linux-${LINUXFAMILY}-${BRANCH}"
[[ -z $BOOTPATCHDIR ]] && BOOTPATCHDIR="u-boot-$LINUXFAMILY"
[[ -z $ATFPATCHDIR ]] && ATFPATCHDIR="atf-$LINUXFAMILY"
[[ -z $KERNELPATCHDIR ]] && KERNELPATCHDIR="$LINUXFAMILY-$BRANCH"
if [[ "$RELEASE" =~ ^(xenial|bionic|focal|hirsute|impish|jammy)$ ]]; then
DISTRIBUTION="Ubuntu"
elif [[ "$RELEASE" == raspi ]]; then
DISTRIBUTION="Bullseye"
else
DISTRIBUTION="Debian"
fi
CLI_CONFIG_PATH="${EXTER}/config/cli/${RELEASE}"
DEBOOTSTRAP_CONFIG_PATH="${CLI_CONFIG_PATH}/debootstrap"
if [[ $? != 0 ]]; then
exit_with_error "The desktop environment ${DESKTOP_ENVIRONMENT} is not available for your architecture ${ARCH}"
fi
AGGREGATION_SEARCH_ROOT_ABSOLUTE_DIRS="
${EXTER}/config
${EXTER}/config/optional/_any_board/_config
${EXTER}/config/optional/architectures/${ARCH}/_config
${EXTER}/config/optional/families/${LINUXFAMILY}/_config
${EXTER}/config/optional/boards/${BOARD}/_config
${USERPATCHES_PATH}
"
DEBOOTSTRAP_SEARCH_RELATIVE_DIRS="
cli/_all_distributions/debootstrap
cli/${RELEASE}/debootstrap
"
CLI_SEARCH_RELATIVE_DIRS="
cli/_all_distributions/main
cli/${RELEASE}/main
"
PACKAGES_SEARCH_ROOT_ABSOLUTE_DIRS="
${EXTER}/packages
${EXTER}/config/optional/_any_board/_packages
${EXTER}/config/optional/architectures/${ARCH}/_packages
${EXTER}/config/optional/families/${LINUXFAMILY}/_packages
${EXTER}/config/optional/boards/${BOARD}/_packages
"
DESKTOP_ENVIRONMENTS_SEARCH_RELATIVE_DIRS="
desktop/_all_distributions/environments/_all_environments
desktop/_all_distributions/environments/${DESKTOP_ENVIRONMENT}
desktop/_all_distributions/environments/${DESKTOP_ENVIRONMENT}/${DESKTOP_ENVIRONMENT_CONFIG_NAME}
desktop/${RELEASE}/environments/_all_environments
desktop/${RELEASE}/environments/${DESKTOP_ENVIRONMENT}
desktop/${RELEASE}/environments/${DESKTOP_ENVIRONMENT}/${DESKTOP_ENVIRONMENT_CONFIG_NAME}
"
DESKTOP_APPGROUPS_SEARCH_RELATIVE_DIRS="
desktop/_all_distributions/appgroups
desktop/_all_distributions/environments/${DESKTOP_ENVIRONMENT}/appgroups
desktop/${RELEASE}/appgroups
desktop/${RELEASE}/environments/${DESKTOP_ENVIRONMENT}/appgroups
"
get_all_potential_paths() {
local root_dirs="${AGGREGATION_SEARCH_ROOT_ABSOLUTE_DIRS}"
local rel_dirs="${1}"
local sub_dirs="${2}"
local looked_up_subpath="${3}"
for root_dir in ${root_dirs}; do
for rel_dir in ${rel_dirs}; do
for sub_dir in ${sub_dirs}; do
potential_paths+="${root_dir}/${rel_dir}/${sub_dir}/${looked_up_subpath} "
done
done
done
# for ppath in ${potential_paths}; do
# echo "Checking for ${ppath}"
# if [[ -f "${ppath}" ]]; then
# echo "OK !|"
# else
# echo "Nope|"
# fi
# done
}
# Environment variables expected :
# - aggregated_content
# Arguments :
# 1. File to look up in each directory
# 2. The separator to add between each concatenated file
# 3. Relative directories paths added to ${3}
# 2. Relative directories paths added to ${4}
#
# The function will basically generate a list of potential paths by
# generating all the potential paths combinations leading to the
# looked up file
# ${AGGREGATION_SEARCH_ROOT_ABSOLUTE_DIRS}/${3}/${4}/${1}
# Then it will concatenate the content of all the available files
# into ${aggregated_content}
#
# TODO :
# ${4} could be removed by just adding the appropriate paths to ${3}
# dynamically for each case
# (debootstrap, cli, desktop environments, desktop appgroups, ...)
aggregate_all_root_rel_sub() {
local separator="${2}"
local potential_paths=""
get_all_potential_paths "${3}" "${4}" "${1}"
aggregate_content
}
aggregate_all_debootstrap() {
local sub_dirs_to_check=". "
if [[ ! -z "${SELECTED_CONFIGURATION+x}" ]]; then
sub_dirs_to_check+="config_${SELECTED_CONFIGURATION}"
fi
aggregate_all_root_rel_sub "${1}" "${2}" "${DEBOOTSTRAP_SEARCH_RELATIVE_DIRS}" "${sub_dirs_to_check}"
}
aggregate_all_cli() {
local sub_dirs_to_check=". "
if [[ ! -z "${SELECTED_CONFIGURATION+x}" ]]; then
sub_dirs_to_check+="config_${SELECTED_CONFIGURATION}"
fi
aggregate_all_root_rel_sub "${1}" "${2}" "${CLI_SEARCH_RELATIVE_DIRS}" "${sub_dirs_to_check}"
}
aggregate_all_desktop() {
aggregate_all_root_rel_sub "${1}" "${2}" "${DESKTOP_ENVIRONMENTS_SEARCH_RELATIVE_DIRS}" "."
aggregate_all_root_rel_sub "${1}" "${2}" "${DESKTOP_APPGROUPS_SEARCH_RELATIVE_DIRS}" "${DESKTOP_APPGROUPS_SELECTED}"
}
one_line() {
local aggregate_func_name="${1}"
local aggregated_content=""
shift 1
$aggregate_func_name "${@}"
cleanup_list aggregated_content
}
DEBOOTSTRAP_LIST="$(one_line aggregate_all_debootstrap "packages" " ")"
DEBOOTSTRAP_COMPONENTS="$(one_line aggregate_all_debootstrap "components" " ")"
DEBOOTSTRAP_COMPONENTS="${DEBOOTSTRAP_COMPONENTS// /,}"
PACKAGE_LIST="$(one_line aggregate_all_cli "packages" " ")"
PACKAGE_LIST_ADDITIONAL="$(one_line aggregate_all_cli "packages.additional" " ")"
LOG_OUTPUT_FILE="$SRC/output/${LOG_SUBPATH}/debootstrap-list.log"
show_checklist_variables "DEBOOTSTRAP_LIST DEBOOTSTRAP_COMPONENTS PACKAGE_LIST PACKAGE_LIST_ADDITIONAL PACKAGE_LIST_UNINSTALL"
# Dependent desktop packages
# Myy : Sources packages from file here
# Myy : FIXME Rename aggregate_all to aggregate_all_desktop
if [[ $BUILD_DESKTOP == "yes" ]]; then
PACKAGE_LIST_DESKTOP+="$(one_line aggregate_all_desktop "packages" " ")"
echo -e "\nGroups selected ${DESKTOP_APPGROUPS_SELECTED} -> PACKAGES :" >> "${LOG_OUTPUT_FILE}"
show_checklist_variables PACKAGE_LIST_DESKTOP
fi
unset LOG_OUTPUT_FILE
DEBIAN_MIRROR='deb.debian.org/debian'
DEBIAN_SECURTY='security.debian.org/'
UBUNTU_MIRROR='ports.ubuntu.com/'
RASPI_MIRROR='archive.raspberrypi.org/debian/'
if [[ $DOWNLOAD_MIRROR == "china" ]] ; then
if [[ ${CHINA_DOWNLOAD_MIRROR} == tsinghua ]]; then
DEBIAN_MIRROR='mirrors.tuna.tsinghua.edu.cn/debian'
DEBIAN_SECURTY='mirrors.tuna.tsinghua.edu.cn/debian-security'
UBUNTU_MIRROR='mirrors.tuna.tsinghua.edu.cn/ubuntu-ports/'
fi
if [[ ${CHINA_DOWNLOAD_MIRROR} == huawei ]]; then
DEBIAN_MIRROR='repo.huaweicloud.com/debian'
DEBIAN_SECURTY='repo.huaweicloud.com/debian-security'
UBUNTU_MIRROR='repo.huaweicloud.com/ubuntu-ports/'
fi
RASPI_MIRROR='mirrors.ustc.edu.cn/archive.raspberrypi.org/debian/'
fi
if [[ $DOWNLOAD_MIRROR == "bfsu" ]] ; then
DEBIAN_MIRROR='mirrors.bfsu.edu.cn/debian'
DEBIAN_SECURTY='mirrors.bfsu.edu.cn/debian-security'
UBUNTU_MIRROR='mirrors.bfsu.edu.cn/ubuntu-ports/'
fi
if [[ "${ARCH}" == "amd64" ]]; then
UBUNTU_MIRROR='archive.ubuntu.com/ubuntu' # ports are only for non-amd64, of course.
if [[ -n ${CUSTOM_UBUNTU_MIRROR} ]]; then # ubuntu redirector doesn't work well on amd64
UBUNTU_MIRROR="${CUSTOM_UBUNTU_MIRROR}"
fi
fi
# don't use mirrors that throws garbage on 404
if [[ -z ${ARMBIAN_MIRROR} ]]; then
while true; do
ARMBIAN_MIRROR=$(wget -SO- -T 1 -t 1 https://redirect.armbian.com 2>&1 | egrep -i "Location" | awk '{print $2}' | head -1)
[[ ${ARMBIAN_MIRROR} != *armbian.hosthatch* ]] && break
done
fi
# For (late) user override.
# Notice: it is too late to define hook functions or add extensions in lib.config, since the extension initialization already ran by now.
# in case the user tries to use them in lib.config, hopefully they'll be detected as "wishful hooking" and the user will be wrn'ed.
if [[ -f $USERPATCHES_PATH/lib.config ]]; then
display_alert "Using user configuration override" "$USERPATCHES_PATH/lib.config" "info"
source "$USERPATCHES_PATH"/lib.config
fi
call_extension_method "user_config" << 'USER_CONFIG'
*Invoke function with user override*
Allows for overriding configuration values set anywhere else.
It is called after sourcing the `lib.config` file if it exists,
but before assembling any package lists.
USER_CONFIG
call_extension_method "extension_prepare_config" << 'EXTENSION_PREPARE_CONFIG'
*allow extensions to prepare their own config, after user config is done*
Implementors should preserve variable values pre-set, but can default values an/or validate them.
This runs *after* user_config. Don't change anything not coming from other variables or meant to be configured by the user.
EXTENSION_PREPARE_CONFIG
# apt-cacher-ng mirror configurarion
if [[ $DISTRIBUTION == Ubuntu ]]; then
APT_MIRROR=$UBUNTU_MIRROR
else
APT_MIRROR=$DEBIAN_MIRROR
fi
[[ -n $APT_PROXY_ADDR ]] && display_alert "Using custom apt-cacher-ng address" "$APT_PROXY_ADDR" "info"
# Build final package list after possible override
PACKAGE_LIST="$PACKAGE_LIST $PACKAGE_LIST_RELEASE $PACKAGE_LIST_ADDITIONAL"
PACKAGE_MAIN_LIST="$(cleanup_list PACKAGE_LIST)"
[[ $BUILD_DESKTOP == yes ]] && PACKAGE_LIST="$PACKAGE_LIST $PACKAGE_LIST_DESKTOP"
PACKAGE_LIST="$(cleanup_list PACKAGE_LIST)"
# remove any packages defined in PACKAGE_LIST_RM in lib.config
aggregated_content="${PACKAGE_LIST_RM} "
aggregate_all_cli "packages.remove" " "
aggregate_all_desktop "packages.remove" " "
PACKAGE_LIST_RM="$(cleanup_list aggregated_content)"
unset aggregated_content
aggregated_content=""
aggregate_all_cli "packages.uninstall" " "
aggregate_all_desktop "packages.uninstall" " "
PACKAGE_LIST_UNINSTALL="$(cleanup_list aggregated_content)"
unset aggregated_content
if [[ -n $PACKAGE_LIST_RM ]]; then
display_alert "Package remove list ${PACKAGE_LIST_RM}"
# Turns out that \b can be tricked by dashes.
# So if you remove mesa-utils but still want to install "mesa-utils-extra"
# a "\b(mesa-utils)\b" filter will convert "mesa-utils-extra" to "-extra".
# \W is not tricked by this but consumes the surrounding spaces, so we
# replace the occurence by one space, to avoid sticking the next word to
# the previous one after consuming the spaces.
DEBOOTSTRAP_LIST=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${DEBOOTSTRAP_LIST} ")
PACKAGE_LIST=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${PACKAGE_LIST} ")
PACKAGE_MAIN_LIST=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${PACKAGE_MAIN_LIST} ")
if [[ $BUILD_DESKTOP == "yes" ]]; then
PACKAGE_LIST_DESKTOP=$(sed -r "s/\W($(tr ' ' '|' <<< ${PACKAGE_LIST_RM}))\W/ /g" <<< " ${PACKAGE_LIST_DESKTOP} ")
# Removing double spaces... AGAIN, since we might have used a sed on them
# Do not quote the variables. This would defeat the trick.
PACKAGE_LIST_DESKTOP="$(echo ${PACKAGE_LIST_DESKTOP})"
fi
# Removing double spaces... AGAIN, since we might have used a sed on them
# Do not quote the variables. This would defeat the trick.
DEBOOTSTRAP_LIST="$(echo ${DEBOOTSTRAP_LIST})"
PACKAGE_LIST="$(echo ${PACKAGE_LIST})"
PACKAGE_MAIN_LIST="$(echo ${PACKAGE_MAIN_LIST})"
fi
LOG_OUTPUT_FILE="$SRC/output/${LOG_SUBPATH}/debootstrap-list.log"
echo -e "\nVariables after manual configuration" >>$LOG_OUTPUT_FILE
show_checklist_variables "DEBOOTSTRAP_COMPONENTS DEBOOTSTRAP_LIST PACKAGE_LIST PACKAGE_MAIN_LIST"
unset LOG_OUTPUT_FILE
# Give the option to configure DNS server used in the chroot during the build process
[[ -z $NAMESERVER ]] && NAMESERVER="1.0.0.1" # default is cloudflare alternate
call_extension_method "post_aggregate_packages" "user_config_post_aggregate_packages" << 'POST_AGGREGATE_PACKAGES'
*For final user override, using a function, after all aggregations are done*
Called after aggregating all package lists, before the end of `compilation.sh`.
Packages will still be installed after this is called, so it is the last chance
to confirm or change any packages.
POST_AGGREGATE_PACKAGES
# debug
cat <<-EOF >> "${DEST}"/${LOG_SUBPATH}/output.log
## BUILD SCRIPT ENVIRONMENT
Repository: $REPOSITORY_URL
Version: $REPOSITORY_COMMIT
Host OS: $HOSTRELEASE
Host arch: $(dpkg --print-architecture)
Host system: $(uname -a)
Virtualization type: $(systemd-detect-virt)
## Build script directories
Build directory is located on:
$(findmnt -o TARGET,SOURCE,FSTYPE,AVAIL -T "${SRC}")
Build directory permissions:
$(getfacl -p "${SRC}")
Temp directory permissions:
$(getfacl -p "${SRC}"/.tmp 2> /dev/null)
## BUILD CONFIGURATION
Build target:
Board: $BOARD
Branch: $BRANCH
Minimal: $BUILD_MINIMAL
Desktop: $BUILD_DESKTOP
Desktop Environment: $DESKTOP_ENVIRONMENT
Software groups: $DESKTOP_APPGROUPS_SELECTED
Kernel configuration:
Repository: $KERNELSOURCE
Branch: $KERNELBRANCH
Config file: $LINUXCONFIG
U-boot configuration:
Repository: $BOOTSOURCE
Branch: $BOOTBRANCH
Config file: $BOOTCONFIG
Partitioning configuration: $IMAGE_PARTITION_TABLE offset: $OFFSET
Boot partition type: ${BOOTFS_TYPE:-(none)} ${BOOTSIZE:+"(${BOOTSIZE} MB)"}
Root partition type: $ROOTFS_TYPE ${FIXED_IMAGE_SIZE:+"(${FIXED_IMAGE_SIZE} MB)"}
CPU configuration: $CPUMIN - $CPUMAX with $GOVERNOR
EOF
7.3.1 rockchip-rk356x.conf
<SDK>/external/config/sources/families
目录下存放rockchip
某一型号SoC
所使用的配置文件。
当我们配置了BOARDFAMILY="rockchip-rk356x"
,就会使用rockchip-rk356x.conf
配置文件;
source "${BASH_SOURCE%/*}/include/rockchip64_common.inc"
BOOTBRANCH='branch:v2017.09-rk3588'
OVERLAY_PREFIX="rk356x"
CPUMIN=408000
CPUMAX=1800000
case $BRANCH in
legacy)
KERNELBRANCH='branch:orange-pi-5.10-rk35xx'
LINUXCONFIG='linux-rockchip-rk356x-legacy'
;;
current)
KERNELBRANCH='branch:orange-pi-6.6-rk35xx'
LINUXCONFIG='linux-rockchip-rk356x-current'
;;
esac
# 进入u-boot配置初始化
prepare_boot_configuration
install_balenaEtcher(){
if [[ $BUILD_DESKTOP == yes && $install_balena_etcher == yes ]]; then
balena_etcher_deb=balena-etcher-electron_1.7.9+5945ab1f_arm64.deb
balena_etcher_url="https://github.com/Itai-Nelken/BalenaEtcher-arm/releases/download/v1.7.9/${balena_etcher_deb}"
balena_etcher=${EXTER}/cache/debs/arm64/${balena_etcher_deb}
if [[ ! -f ${balena_etcher} ]]; then
wget -P ${EXTER}/cache/debs/arm64 ${balena_etcher_url}
fi
install_deb_chroot ${balena_etcher}
fi
}
family_tweaks_bsp()
{
install -m 755 $EXTER/packages/bsp/adb/adbd-${ARCH} ${destination}/usr/bin/adbd
}
family_tweaks_s() {
rsync -a --chown=root:root "${EXTER}"/packages/bsp/rk356x/* ${SDCARD}/
chroot $SDCARD /bin/bash -c "apt-get -y -qq install dnsmasq v4l-utils cheese swig python3-dev python3-setuptools bluez libncurses-dev" >> "${DEST}"/${LOG_SUBPATH}/install.log 2>&1
[[ ${RELEASE} == jammy ]] && rm ${SDCARD}/etc/profile.d/orangepi-ssh-title.sh # for adbd
chroot "${SDCARD}" /bin/bash -c "systemctl --no-reload enable usbdevice.service >/dev/null 2>&1"
if [[ -f "${SDCARD}"/etc/profile.d/im-config_wayland.sh && ${DESKTOP_ENVIRONMENT} =~ xfce|kde-plasma ]]; then
chroot $SDCARD /bin/bash -c "rm /etc/profile.d/im-config_wayland.sh" # for adbd
fi
install_docker
install_balenaEtcher
install_wiringop
if [[ -f ${SDCARD}/etc/pulse/default.pa && ${SELECTED_CONFIGURATION} == desktop && ${BOARD} =~ orangepicm4|orangepi3b ]]; then
sed -i "s/auto-profiles = yes/auto-profiles = no/" ${SDCARD}/usr/share/pulseaudio/alsa-mixer/profile-sets/default.conf
echo "load-module module-alsa-sink device=hw:0,0 sink_name=AudioCodec-Playback sink_properties=\"device.description='Headphone'\"" >> ${SDCARD}/etc/pulse/default.pa
echo "load-module module-alsa-sink device=hw:1,0 sink_name=HDMI-Playback sink_properties=\"device.description='HDMI Audio'\"" >> ${SDCARD}/etc/pulse/default.pa
fi
}
定义了BOOTBRANCH
、OVERLAY_PREFIX
、KERNELBRANCH
、LINUXCONFIG
等;
此外会调用prepare_boot_configuration
函数进行u-boot
配置初始化工作。
7.3.2 rockchip64_common.inc
位于<SDK>/external/config/sources/families/include
目录,保存rockchip
系列SoC
所使用的通用配置;
点击查看代码
source "${BASH_SOURCE%/*}/rk3399_gpu_vpu.inc"
source "${BASH_SOURCE%/*}/rk35xx_gpu_vpu.inc"
source "${BASH_SOURCE%/*}/rk356x_gpu_vpu.inc"
enable_extension "rkbin-tools"
ARCH=arm64
KERNEL_IMAGE_TYPE=Image
OFFSET=30
BOOTSCRIPT='boot-rockchip64.cmd:boot.cmd'
BOOTENV_FILE='rockchip.txt'
UBOOT_TARGET_MAP=";;idbloader.bin uboot.img trust.bin"
BOOTDELAY=0
OVERLAY_PREFIX='rockchip'
SERIALCON=${SERIALCON:=$([[ $BRANCH == "legacy" || $BOARDFAMILY == "rockchip-rk3588" && $BRANCH == "current" ]] && echo "ttyFIQ0:1500000" || echo "ttyS2:1500000")}
GOVERNOR="ondemand"
BOOTBRANCH="branch:v2020.10-rockchip64"
PACKAGE_LIST_FAMILY="ethtool can-utils"
if [[ $RELEASE == "jammy" ]]; then
PACKAGE_LIST_DESKTOP_FAMILY="fcitx5 fonts-wqy-zenhei xfce4-volumed"
else
PACKAGE_LIST_DESKTOP_FAMILY="fcitx fonts-wqy-zenhei"
fi
[[ $BRANCH != "next" ]] && PACKAGE_LIST_FAMILY_REMOVE="mpv"
RKBIN_DIR="$EXTER/cache/sources/rkbin-tools"
if [ "$(uname -m)" = "aarch64" ]; then
case "$(lsb_release -sc)" in
"bookworm"|"bullseye"|"focal"|"jammy")
PKG_PREFIX="qemu-x86_64-static -L /usr/x86_64-linux-gnu "
;;
*)
PKG_PREFIX="qemu-x86_64 -L /usr/x86_64-linux-gnu "
;;
esac
else
PKG_PREFIX=""
fi
# BOOTCONFIG="orangepi-3b-rk3566_defconfig",因此进入
if [[ $BOOTCONFIG =~ *3399*|*3566* ]]; then
CPUMIN=${CPUMIN:="408000"}
CPUMAX=${CPUMAX:="1800000"}
else # rk3328
CPUMIN=${CPUMIN:="408000"}
CPUMAX=${CPUMAX:="1296000"}
fi
if [[ $BOARD =~ orangepir1plus|orangepir1plus-lts ]]; then
BOOT_USE_BLOBS=yes
BOOT_SOC=rk3328
DDR_BLOB='rk33/rk3328_ddr_333MHz_v1.16.bin'
MINILOADER_BLOB='rk33/rk322xh_miniloader_v2.50.bin'
BL31_BLOB='rk33/rk322xh_bl31_v1.44.elf'
elif [[ $BOARDFAMILY == "rockchip-rk3588" ]]; then
BOOT_SCENARIO="${BOOT_SCENARIO:=spl-blobs}"
if [[ "${BOARD}x" =~ orangepi5maxx|orangepi5prox|orangepi5plusx|orangepi5x ]]; then
DDR_BLOB="${DDR_BLOB:=rk35/rk3588_ddr_lp4_2112MHz_lp5_2400MHz_v1.16.bin}"
BL31_BLOB='rk35/rk3588_bl31_v1.45_20240422.elf'
else
DDR_BLOB="${DDR_BLOB:=rk35/rk3588_ddr_lp4_2112MHz_lp5_2736MHz_v1.11.bin}"
BL31_BLOB='rk35/rk3588_bl31_v1.38.elf'
fi
# BOARD="orangepi3b",因此进入
elif [[ $BOARD =~ orangepicm4|orangepi3b ]]; then
BOOT_SCENARIO="${BOOT_SCENARIO:=spl-blobs}"
BOOT_SOC=rk3568
DDR_BLOB="${DDR_BLOB:=rk35/rk3566_ddr_1056MHz_v1.10.bin}"
BL31_BLOB='rk35/rk3568_bl31_v1.28.elf'
else
ATFPATCHDIR='atf-rk3399'
fi
prepare_boot_configuration()
{
if [[ $BOOT_USE_MAINLINE_ATF == yes ]]; then
UBOOT_TARGET_MAP="BL31=bl31.bin idbloader.img u-boot.itb;;idbloader.img u-boot.itb"
ATFSOURCE='https://github.com/ARM-software/arm-trusted-firmware'
ATF_COMPILER='aarch64-linux-gnu-'
ATFDIR='arm-trusted-firmware'
ATFBRANCH='tag:v2.2'
ATF_USE_GCC='> 6.3'
ATF_TARGET_MAP="M0_CROSS_COMPILE=arm-linux-gnueabi- PLAT=$BOOT_SOC bl31;;build/$BOOT_SOC/release/bl31/bl31.elf:bl31.bin"
ATF_TOOLCHAIN2="arm-linux-gnueabi-:> 5.0"
elif [[ $BOOT_USE_TPL_SPL_BLOB == yes ]]; then
UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB idbloader.img u-boot.itb;;idbloader.img u-boot.itb"
ATFSOURCE=''
ATF_COMPILE='no'
# 进入该分支
elif [[ $BOOT_SCENARIO == "spl-blobs" ]]; then
UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb;;idbloader.img u-boot.itb"
ATFSOURCE=''
ATF_COMPILE='no'
elif [[ $BOOT_USE_BLOBS == yes ]]; then
UBOOT_TARGET_MAP="u-boot-dtb.bin;;idbloader.bin uboot.img trust.bin"
ATFSOURCE=''
ATF_COMPILE='no'
fi
if [[ $BOOT_SUPPORT_SPI == yes ]]; then
UBOOT_TARGET_MAP="${UBOOT_TARGET_MAP} rkspi_loader.img"
fi
}
uboot_custom_postprocess()
{
# BOOT_SUPPORT_SPI="yes",进入
if [[ $BOOT_SUPPORT_SPI == yes ]]; then
if [[ $BOARDFAMILY == "rockchip-rk3588" ]]; then
tools/mkimage -n rk3588 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
dd if=/dev/zero of=rkspi_loader.img bs=1M count=0 seek=4
/sbin/parted -s rkspi_loader.img mklabel gpt
/sbin/parted -s rkspi_loader.img unit s mkpart idbloader 64 1023
/sbin/parted -s rkspi_loader.img unit s mkpart uboot 1024 7167
#/sbin/parted -s rkspi_loader.img unit s mkpart vnvm 7168 7679
#/sbin/parted -s rkspi_loader.img unit s mkpart reserved_space 7680 8063
#/sbin/parted -s rkspi_loader.img unit s mkpart reserved1 8064 8127
#/sbin/parted -s rkspi_loader.img unit s mkpart uboot_env 8128 8191
#/sbin/parted -s rkspi_loader.img unit s mkpart reserved2 8192 16383
dd if=idbloader.img of=rkspi_loader.img seek=64 conv=notrunc
dd if=u-boot.itb of=rkspi_loader.img seek=1024 conv=notrunc
#dd if=$EXTER/packages/blobs/splash/logo.bmp of=rkspi_loader.img seek=8192 conv=notrunc
# BOARDFAMILY="rockchip-rk356x",进入
elif [[ $BOARDFAMILY == "rockchip-rk356x" ]]; then
tools/mkimage -n rk3568 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
dd if=/dev/zero of=rkspi_loader.img bs=1M count=0 seek=4
/sbin/parted -s rkspi_loader.img mklabel gpt
/sbin/parted -s rkspi_loader.img unit s mkpart idbloader 64 1023
/sbin/parted -s rkspi_loader.img unit s mkpart uboot 1024 7167
dd if=idbloader.img of=rkspi_loader.img seek=64 conv=notrunc
dd if=u-boot.itb of=rkspi_loader.img seek=1024 conv=notrunc
fi
fi
if [[ $BOOT_USE_MAINLINE_ATF == yes || $BOOT_USE_TPL_SPL_BLOB == yes ]]; then
if [[ $BOOT_SUPPORT_SPI == yes ]]; then
tools/mkimage -n rk3399 -T rkspi -d tpl/u-boot-tpl.bin:spl/u-boot-spl.bin rkspi_tpl_spl.img
dd if=/dev/zero of=rkspi_loader.img count=8128 status=none
dd if=rkspi_tpl_spl.img of=rkspi_loader.img conv=notrunc status=none
dd if=u-boot.itb of=rkspi_loader.img seek=768 conv=notrunc status=none
fi
elif [[ $BOOT_USE_BLOBS == yes ]]; then
local tempfile=$(mktemp)
tools/mkimage -n $BOOT_SOC -T rksd -d $RKBIN_DIR/$DDR_BLOB idbloader.bin
cat $RKBIN_DIR/$MINILOADER_BLOB >> idbloader.bin
loaderimage --pack --uboot ./u-boot-dtb.bin uboot.img 0x200000
trust_merger --replace bl31.elf $RKBIN_DIR/$BL31_BLOB trust.ini
# BOOT_SCENARIO="spl-blobs",进入
elif [[ $BOOT_SCENARIO == "spl-blobs" || $BOOT_SCENARIO == "tpl-blob-atf-mainline" ]]; then
# BOARD="orangepi3b",进入
if [[ $BOARD =~ orangepicm4|orangepi3b ]]; then
tools/mkimage -n rk3568 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
elif [[ $BOARD =~ orangepi4-lts|orangepi800 ]]; then
tools/mkimage -n rk3399 -T rksd -d $RKBIN_DIR/$DDR_BLOB:spl/u-boot-spl.bin idbloader.img
fi
:
else
echo "[uboot_custom_postprocess]: Unsupported u-boot processing configuration!"
exit 1
fi
# MERGE_UBOOT未定义不会进入
if [[ ${MERGE_UBOOT} ]]; then
display_alert "Merge u-boot" "u-boot-${BOARD}-merged.bin" "info"
dd if=/dev/zero of=u-boot-${BOARD}-merged.bin bs=1M count=32 > /dev/null 2>&1
dd if=idbloader.bin of=u-boot-${BOARD}-merged.bin seek=64 conv=notrunc status=none
dd if=uboot.img of=u-boot-${BOARD}-merged.bin seek=16384 conv=notrunc status=none
dd if=trust.bin of=u-boot-${BOARD}-merged.bin seek=24576 conv=notrunc status=none
mv u-boot-${BOARD}-merged.bin ${DEB_STORAGE}/u-boot/
fi
}
write_uboot_platform()
{
if [[ -f $1/rksd_loader.img ]]; then # legacy rk3399 loader
dd if=$1/rksd_loader.img of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1
elif [[ -f $1/u-boot.itb ]]; then # $BOOT_USE_MAINLINE_ATF == yes || $BOOT_USE_TPL_SPL_BLOB == yes
dd if=$1/idbloader.img of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1
dd if=$1/u-boot.itb of=$2 seek=16384 conv=notrunc status=none >/dev/null 2>&1
elif [[ -f $1/uboot.img ]]; then # $BOOT_USE_BLOBS == yes
dd if=$1/idbloader.bin of=$2 seek=64 conv=notrunc status=none >/dev/null 2>&1
dd if=$1/uboot.img of=$2 seek=16384 conv=notrunc status=none >/dev/null 2>&1
dd if=$1/trust.bin of=$2 seek=24576 conv=notrunc status=none >/dev/null 2>&1
else
echo "[write_uboot_platform]: Unsupported u-boot processing configuration!"
exit 1
fi
}
write_uboot_platform_mtd()
{
if [[ -f $1/rkspi_loader.img ]]; then
dd if=$1/rkspi_loader.img of=$2 conv=notrunc status=none >/dev/null 2>&1
else
echo "SPI u-boot image not found!"
exit 1
fi
}
setup_write_uboot_platform()
{
if grep -q "ubootpart" /proc/cmdline; then
local tmp=$(cat /proc/cmdline)
tmp="${tmp##*ubootpart=}"
tmp="${tmp%% *}"
[[ -n $tmp ]] && local part=$(findfs PARTUUID=$tmp 2>/dev/null)
[[ -n $part ]] && local dev=$(lsblk -n -o PKNAME $part 2>/dev/null)
[[ -n $dev ]] && DEVICE="/dev/$dev"
fi
}
atf_custom_postprocess()
{
# remove bl31.bin which can be 4+GiB in size and thus may fill the tmpfs mount
rm -f build/rk322xh/debug/bl31.bin
# ATF
trust_merger trust.ini
}
family_tweaks()
{
# execute specific tweaks function if present
[[ $(type -t family_tweaks_s) == function ]] && family_tweaks_s
[[ "$BRANCH" =~ legacy ]] && rk3399_gpu_vpu_tweaks
[[ "$BRANCH" =~ current && $BOARDFAMILY == "rk3399" ]] && rk35xx_gpu_vpu_tweaks
[[ "$BRANCH" =~ legacy|current && $BOARDFAMILY == "rockchip-rk3588" ]] && rk35xx_gpu_vpu_tweaks
[[ "$BRANCH" == legacy && $BOARDFAMILY == "rockchip-rk356x" ]] && rk35xx_gpu_vpu_tweaks
#if [[ ${BOARD} == orangepi800 && ${SELECTED_CONFIGURATION} == desktop && ${RELEASE} =~ bullseye|bookworm ]]; then
# install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/xfce4-zorinmenulite-plugin_1.1.2_arm64.deb"
# install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx-common_1.0~beta+git20210222-1~ppa1_all.deb"
# install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx-dockx_1.0~beta+git20210222-1~ppa1_all.deb"
# install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx_1.0~beta+git20210222-1~ppa1_all.deb"
# install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/xfce4-dockbarx-plugin_0.6+git20210221-1~ppa2~20.10_arm64.deb"
# install_deb_chroot "$EXTER/cache/debs/extra/bullseye-desktop/dockbarx-theme-zorin_1.1.0_all.deb"
# ## Orange Pi Desktop layout switcher
# install_deb_chroot "$EXTER/cache/debs/arm64/layout_switcher/layoutswitcher_0.2-0_all.deb"
# #install_deb_chroot "$EXTER/cache/debs/arm64/layout_switcher/switcher_1.0-1_arm64.deb"
#fi
if [[ $BOARD =~ orangepir1plus|orangepir1plus-lts ]]; then
# rename USB based network to lan0
mkdir -p $SDCARD/etc/udev/rules.d/
echo 'SUBSYSTEM=="net", ACTION=="add", DRIVERS=="r8152", KERNEL=="eth1", NAME="lan0"' > $SDCARD/etc/udev/rules.d/70-rename-lan.rules
# workaround - create old school initial network config since network manager have issues
#echo "auto eth0" >> $SDCARD/etc/network/interfaces
#echo " iface eth0 inet dhcp" >> $SDCARD/etc/network/interfaces
#echo "auto lan0" >> $SDCARD/etc/network/interfaces
#echo " iface lan0 inet dhcp" >> $SDCARD/etc/network/interfaces
fi
if [[ -f $SDCARD/lib/systemd/system/rk3399-bluetooth.service ]]; then
# install and enable Bluetooth
chroot $SDCARD /bin/bash -c "apt-get -y -qq install rfkill bluetooth bluez bluez-tools >/dev/null 2>&1"
chroot $SDCARD /bin/bash -c "systemctl --no-reload enable rk3399-bluetooth.service >/dev/null 2>&1"
fi
if [[ $RELEASE == jammy && $BUILD_DESKTOP == yes && $install_chromium == yes ]]; then
chromium_name="chromium-browser_107.0.5304.62-0ubuntu1~ppa1~22.04.1_arm64.deb"
chromium_extra_name="chromium-codecs-ffmpeg-extra_107.0.5304.62-0ubuntu1~ppa1~22.04.1_arm64.deb"
chromium_browser_l10n_name="chromium-browser-l10n_107.0.5304.62-0ubuntu1~ppa1~22.04.1_all.deb"
chromium_url="http://ppa.launchpadcontent.net/saiarcot895/chromium-beta/ubuntu/pool/main/c/chromium-browser"
chromium=${EXTER}/cache/debs/arm64/${chromium_name}
chromium_extra=${EXTER}/cache/debs/arm64/${chromium_extra_name}
chromium_browser_l10n=${EXTER}/cache/debs/arm64/${chromium_browser_l10n_name}
if [[ ! -f $chromium_extra ]]; then
wget -P ${EXTER}/cache/debs/arm64 ${chromium_url}/${chromium_name}
wget -P ${EXTER}/cache/debs/arm64 ${chromium_url}/${chromium_extra_name}
wget -P ${EXTER}/cache/debs/arm64 ${chromium_url}/${chromium_browser_l10n_name}
fi
install_deb_chroot $chromium_extra
install_deb_chroot $chromium
install_deb_chroot $chromium_browser_l10n
fi
if [[ $RELEASE == jammy && $BUILD_DESKTOP == yes && $install_firefox == yes ]]; then
firefox_name="firefox-esr_102.6.0esr+build1-0ubuntu0.22.04.1_arm64.deb"
firefox_url="https://ppa.launchpadcontent.net/mozillateam/ppa/ubuntu/pool/main/f/firefox-esr/"
firefox=${EXTER}/cache/debs/arm64/${firefox_name}
if [[ ! -f $firefox ]]; then
wget -P ${EXTER}/cache/debs/arm64 ${firefox_url}/${firefox_name}
fi
install_deb_chroot $firefox
fi
rm $SDCARD/root/*.deb >/dev/null 2>&1
}
family_tweaks_bsp()
{
if [[ $BOOTCONFIG == *3328* ]] && [[ $BRANCH != legacy ]]; then
mkdir -p "$destination"/etc/X11/xorg.conf.d
cat <<-EOF > "$destination"/etc/X11/xorg.conf.d/02-driver.conf
# set fbdev as default driver.
Section "Device"
Identifier "NOGPU"
Driver "fbdev"
EndSection
EOF
fi
# Graphics and media
mkdir -p $destination/etc/udev/rules.d
cp $EXTER/packages/bsp/rk3399/50-mali.rules $destination/etc/udev/rules.d/
cp $EXTER/packages/bsp/rk3399/50-rk3399-vpu.rules $destination/etc/udev/rules.d/
mkdir -p $destination/etc/sysfs.d
cp $EXTER/packages/bsp/rk3399/20-gpu-governor.conf $destination/etc/sysfs.d/
}
定义了ARCH
、KERNEL_IMAGE_TYPE
、BOOTSCRIPT
、BOOTENV_FILE
、UBOOT_TARGET_MAP
、BOOTDELAY
等;
7.3.3 arm64.conf
arm64.conf
文件位于<SDK>/external/config/sources
;
#!/bin/bash
#
# Copyright (c) 2013-2021 Igor Pecovnik, igor.pecovnik@gma**.com
#
# This file is licensed under the terms of the GNU General Public
# License version 2. This program is licensed "as is" without any
# warranty of any kind, whether express or implied.
#
# This file is a part of the Armbian build script
# https://github.com/armbian/build/
QEMU_BINARY="qemu-aarch64-static"
ARCHITECTURE=arm64
ARCH=arm64
KERNEL_IMAGE_TYPE=Image
CAN_BUILD_STRETCH=yes
GIT_SERVER="https://github.com/orangepi-xunlong"
[[ -z $INITRD_ARCH ]] && INITRD_ARCH=arm64
if [[ $BOARD =~ orangepipch5|orangepioneh5 ]]; then
ARCH=armhf
QEMU_BINARY="qemu-arm-static"
fi
if [ "$(uname -m)" = "aarch64" ]; then
[[ $ATF_COMPILE != "no" && -z $ATF_COMPILER ]] && ATF_COMPILER="aarch64-linux-gnu-"
[[ -z $UBOOT_COMPILER ]] && UBOOT_COMPILER="aarch64-linux-gnu-"
[[ -z $KERNEL_COMPILER ]] && KERNEL_COMPILER="aarch64-linux-gnu-"
else
[[ $ATF_COMPILE != "no" && -z $ATF_COMPILER ]] && ATF_COMPILER="aarch64-none-linux-gnu-"
[[ -z $UBOOT_COMPILER ]] && UBOOT_COMPILER="aarch64-none-linux-gnu-"
[[ -z $KERNEL_COMPILER ]] && KERNEL_COMPILER="aarch64-none-linux-gnu-"
fi
[[ $ATF_COMPILE != "no" && -z $ATFSOURCE ]] && ATFSOURCE='https://github.com/ARM-software/arm-trusted-firmware'
[[ $ATF_COMPILE != "no" && -z $ATFDIR ]] && ATFDIR='arm-trusted-firmware-sunxi-mainline'
[[ $ATF_COMPILE != "no" && -z $ATFBRANCH ]] && ATFBRANCH='branch:master'
[[ $ATF_COMPILE != "no" && -z $ATF_USE_GCC ]] && ATF_USE_GCC='> 8.0'
[[ -z $UBOOT_USE_GCC ]] && UBOOT_USE_GCC='> 8.0'
[[ -z $BOOTSOURCE ]] && BOOTSOURCE="${GIT_SERVER}/u-boot-orangepi.git"
[[ -z $BOOTDIR ]] && BOOTDIR="${SRC}/u-boot"
[[ -z $BOOTBRANCH ]] && BOOTBRANCH='branch:v2020.04'
[[ -z $KERNEL_USE_GCC ]] && KERNEL_USE_GCC='> 8.0'
[[ -z $KERNELDIR ]] && KERNELDIR="${SRC}/kernel"
[[ -z $KERNELSOURCE ]] && KERNELSOURCE="${GIT_SERVER}/linux-orangepi.git"
[[ -z $KERNELBRANCH ]] && KERNELBRANCH='branch:orange-pi-5.4'
7.4 变量值
在Linux SDK
构建过程中会多次使用一些变量,其中一些变量的值如下;
DEST=<SDK>/output
DEB_STORAGE=$DEST/debs
CCACHE=""
BUILD_OPT="image"
KERNEL_CONFIGURE="no"
BOARD="orangepi3b"
BRANCH="current"
RELEASE="bullseye"
BUILD_DESKTOP="no"
BUILD_MINIMAL="no"
COMPRESS_OUTPUTIMAGE="sha,gpg,img"
SELECTED_CONFIGURATION="cli_standard"
BOOT_SCENARIO="spl-blobs"
BOOT_SOC=rk3568
DDR_BLOB="rk35/rk3566_ddr_1056MHz_v1.10.bin"
BL31_BLOB='rk35/rk3568_bl31_v1.28.elf'
RKBIN_DIR="external/cache/sources/rkbin-tools"
UBOOT_TARGET_MAP="BL31=$RKBIN_DIR/$BL31_BLOB spl/u-boot-spl.bin u-boot.dtb u-boot.itb;;idbloader.img u-boot.itb"
ATFSOURCE=''
ATF_COMPILE='no'