RK3588s-OrangePi 官方编译脚本获取及代码组织架构分析(三--其二)

一.编译环境和工具平台搭建

二.Linux 系统构建基础知识储备

三.OrangePi 官方编译脚本获取及代码组织架构分析

3-1. OrangePi官方脚本包获取(build.sh获取)

3-2. OrangePi官方脚本包文件层级分析

3-3. build.sh编译失败–github自行获取u-boot,kernel源码安装包实现离线编译

3-4. orangepi官方主要编译脚本和配置文件解析(重要)

本小节主要是对重要脚本里的函数进行注解,源码中###开头的是笔者对于代码的注释,因为篇幅有限,只是对u-boot如何被脚本编译的流程相关API代码进行列出注解,kernel等构建各位大佬自行举一反三

3-4-1.build.sh

### shell脚本绝对路径空格检查并cd到该路径
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

### 【未调用】当ORANGEPI_ENABLE_CALL_TRACING使能之后,通过RETURN信号触发output/debug/calls.txt去记录索引信息
if [[ "${ORANGEPI_ENABLE_CALL_TRACING}" == "yes" ]]; then
	echo “ORANGEPI_ENABLE_CALL_TRACING!!!”
	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

### scripts/general.sh存在性判断并引用,即展开通用函数库,该库在build.sh和main.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

###  【未调用】,主要就是对本脚本参数进行检查 git branch | b 指令
#  Add the variables needed at the beginning of the path
check_args ()
{
for p in "$@"; do
	case "${p%=*}" in
		LIB_TAG)
			# Take a variable if the branch exists locally
			if [ "${p#*=}" == "$(git branch | \
				gawk -v b="${p#*=}" '{if ( $NF == b ) {print $NF}}')" ]; then
				echo -e "[\e[0;35m warn \x1B[0m] Setting $p"
				eval "$p"
			else
				echo -e "[\e[0;35m warn \x1B[0m] Skip $p setting as LIB_TAG=\"\""
				eval LIB_TAG=""
			fi
			;;
	esac
done
}
check_args "$@"

### 【未调用】,主要是用于git检测更新,但是定义在此处不会调用到,可能是别的开发板会调用到,declare用法也未知
update_src() {
	cd "${SRC}" || exit
	if [[ ! -f "${SRC}"/.ignore_changes ]]; then
		echo -e "[\e[0;32m o.k. \x1B[0m] This script will try to update"
		CHANGED_FILES=$(git diff --name-only)
		if [[ -n "${CHANGED_FILES}" ]]; then
			echo -e "[\e[0;35m warn \x1B[0m] Can't update since you made changes to: \e[0;32m\n${CHANGED_FILES}\x1B[0m"
			while true; do
				echo -e "Press \e[0;33m<Ctrl-C>\x1B[0m or \e[0;33mexit\x1B[0m to abort compilation"\
				", \e[0;33m<Enter>\x1B[0m to ignore and continue, \e[0;33mdiff\x1B[0m to display changes"
				read -r
				if [[ "${REPLY}" == "diff" ]]; then
					git diff
				elif [[ "${REPLY}" == "exit" ]]; then
					exit 1
				elif [[ "${REPLY}" == "" ]]; then
					break
				else
					echo "Unknown command!"
				fi
			done
		elif [[ $(git branch | grep "*" | awk '{print $2}') != "${LIB_TAG}" && -n "${LIB_TAG}" ]]; then
			git checkout "${LIB_TAG:-master}"
			git pull
		fi
	fi
}
TMPFILE=$(mktemp)
chmod 644 "${TMPFILE}"
{
	echo SRC="$SRC"
	echo LIB_TAG="$LIB_TAG"
	declare -f update_src
	#echo "update_src"
}  > "$TMPFILE"
#do not update/checkout git with root privileges to messup files onwership.
#due to in docker/VM, we can't su to a normal user, so do not update/checkout git.
if [[ $(systemd-detect-virt) == 'none' ]]; then
	if [[ "${EUID}" == "0" ]]; then
		su "$(stat --format=%U "${SRC}"/.git)" -c "bash ${TMPFILE}"
	else
		bash "${TMPFILE}"
	fi
fi
rm "${TMPFILE}"

### 编译脚本没有sudo权限运行检查
if [[ "${EUID}" == "0" ]] || [[ "${1}" == "vagrant" ]]; then
	:
elif [[ "${1}" == docker || "${1}" == dockerpurge || "${1}" == docker-shell ]] && grep -q "$(whoami)" <(getent group docker); then
	:
else
	display_alert "This script requires root privileges, trying to use sudo" "" "wrn"
	sudo "${SRC}/build.sh" "$@"
	exit $?
fi

### 原来应该是从general.sh中调用配置文件解析是否离线工作
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
### 调用general.sh中的函数检查基础公共程序工具,
	prepare_host_basic
fi

### 【未调用】对Vagrant | Docker images | Docker shell | Docker 组建判断,取决于shell脚本的输入情况
# Check for Vagrant
if [[ "${1}" == vagrant && -z "$(command -v vagrant)" ]]; then
	display_alert "Vagrant not installed." "Installing"
	sudo apt-get update
	sudo apt-get install -y vagrant virtualbox
fi
# Purge Orange Pi Docker images
if [[ "${1}" == dockerpurge && -f /etc/debian_version ]]; then
	display_alert "Purging Orange Pi Docker containers" "" "wrn"
	docker container ls -a | grep orangepi | awk '{print $1}' | xargs docker container rm &> /dev/null
	docker image ls | grep orangepi | awk '{print $3}' | xargs docker image rm &> /dev/null
	shift
	set -- "docker" "$@"
fi
# Docker shell
if [[ "${1}" == docker-shell ]]; then
	shift
	#shellcheck disable=SC2034
	SHELL_ONLY=yes
	set -- "docker" "$@"
fi
# Install Docker if not there but wanted. We cover only Debian based distro install. On other distros, manual Docker install is needed
if [[ "${1}" == docker && -f /etc/debian_version && -z "$(command -v docker)" ]]; then
	DOCKER_BINARY="docker-ce"
	# add exception for Ubuntu Focal until Docker provides dedicated binary
	codename=$(cat /etc/os-release | grep VERSION_CODENAME | cut -d"=" -f2)
	codeid=$(cat /etc/os-release | grep ^NAME | cut -d"=" -f2 | awk '{print tolower($0)}' | tr -d '"' | awk '{print $1}')
	[[ "${codename}" == "debbie" ]] && codename="buster" && codeid="debian"
	[[ "${codename}" == "ulyana" || "${codename}" == "jammy" ]] && codename="focal" && codeid="ubuntu"
	# different binaries for some. TBD. Need to check for all others
	[[ "${codename}" =~ focal|hirsute ]] && DOCKER_BINARY="docker containerd docker.io"
	display_alert "Docker not installed." "Installing" "Info"
	sudo bash -c "echo \"deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/${codeid} ${codename} stable\" > /etc/apt/sources.list.d/docker.list"
	sudo bash -c "curl -fsSL \"https://download.docker.com/linux/${codeid}/gpg\" | apt-key add -qq - > /dev/null 2>&1 "
	export DEBIAN_FRONTEND=noninteractive
	sudo apt-get update
	sudo apt-get install -y -qq --no-install-recommends ${DOCKER_BINARY}
	display_alert "Add yourself to docker group to avoid root privileges" "" "wrn"
	"${SRC}/build.sh" "$@"
	exit $?
fi

###  获取 external文件夹路径变量
EXTER="${SRC}/external"
###  创建./userpatches文件夹,主要是放置用户配置文件
# Create userpatches directory if not exists
mkdir -p "${SRC}"/userpatches

### 对userpatches下的config-example.conf,config-docker.conf,config-vagrant.conf进行检测,如果缺失就从其他文件夹下拷贝过来
# 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"
	### 新版本的5个文件从external文件夹下拷贝过来,其实就是部署脚本编译配置文件和Docker及Vargrant
	# 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

### 【未使用】定义自定义配置文件变量,取决于脚本输入参数,否则使用serpatches/config-default.conf
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}"

### *=*参数解析处理,比如Uboot重复编译的指令就是在这里进行处理的,本质就是把*=*导入到脚本内并打印出Log
# 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

### 如果命令传入参数BUILD_ALL = yes或demo,就运行/scripts/build-all-ng.sh脚本,否则运行/scripts/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

3-4-2.main.sh

### 打印调用main.sh脚本时传入的第一项参数列表,主要为要清空内容,在configuration.sh中被调用
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}
}

### 防呆代码,笔者猜测此处为Orangepi官方为了防止用户误认main.sh为编译启动脚本直接运行导致的人为异常
if [[ $(basename "$0") == main.sh ]]; then

	echo "Please use build.sh to start the build process"
	exit 255

fi

### 更改脚本文件权限级别,方便prepare_host直接运行,不需要chmod更改权限操作
umask 002

### 确定output输出目录路径变量,编译u-boot,所以在不为userpatches子目录,而是orangepi-build/output
# destination
if [ -d "$CONFIG_PATH/output" ]; then
	DEST="${CONFIG_PATH}"/output
else
	DEST="${SRC}"/output
fi

### 确定脚本编译版本号
[[ -z $REVISION ]] && REVISION="3.0.8"

### 如果下载镜像为中国,确定ntp服务器地址
[[ $DOWNLOAD_MIRROR == "china" ]] && NTP_SERVER="cn.pool.ntp.org"

### 如果BUILD_ALL没设置为yes,配置终端高和宽及行列数【前提是行列数被配置】
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)) 			
	TTY_Y=$(($(stty size | awk '{print $1}')-6)) 			
fi

### 背景标题和主标题配置
backtitle="Orange Pi building script, http://www.orangepi.org" 
titlestr="Choose an option"

### 语言和控制字符
# Warnings mitigation
[[ -z $LANGUAGE ]] && export LANGUAGE="en_US:en"           
[[ -z $CONSOLE_CHAR ]] && export CONSOLE_CHAR="UTF-8"      

### 其他编译库包含官方库包含
# 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  修复和编译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  chroot包构建相关
# shellcheck source=pack.sh
source "${SRC}"/scripts/pack-uboot.sh

### 如果变量LOG_SUBPATH未赋值,则赋值为debug,接着就是对该路径下的LOG进行压缩之类的处理
# 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

### 超过7天的log自动删除
# delete compressed logs older than 7 days
(cd "${DEST}"/${LOG_SUBPATH} && find . -name '*.tgz' -mtime +7 -delete) > /dev/null

### PROGRESS_DISPLAY过程展示配置
if [[ $PROGRESS_DISPLAY == none ]]; then
	OUTPUT_VERYSILENT=yes
elif [[ $PROGRESS_DISPLAY == dialog ]]; then
	OUTPUT_DIALOG=yes
fi

### 将LOG保存到文件中选项
if [[ $PROGRESS_LOG_TO_FILE != yes ]]; then unset PROGRESS_LOG_TO_FILE; fi

### 展示编译警告项
SHOW_WARNING=yes

### 使用ccache选项
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 [[ -n $REPOSITORY_UPDATE ]]; then
		# select stable/beta configuration
		if [[ $BETA == yes ]]; then
				DEB_STORAGE=$DEST/debs-beta
				REPO_STORAGE=$DEST/repository-beta
				REPO_CONFIG="aptly-beta.conf"
		else
				DEB_STORAGE=$DEST/debs
				REPO_STORAGE=$DEST/repository
				REPO_CONFIG="aptly.conf"
		fi
		# For user override
		if [[ -f "${USERPATCHES_PATH}"/lib.config ]]; then
				display_alert "Using user configuration override" "userpatches/lib.config" "info"
			source "${USERPATCHES_PATH}"/lib.config
		fi
		repo-manipulate "$REPOSITORY_UPDATE"
		exit
fi

### 界面配置:编译第一个菜单界面,选择编译u-boot kernel 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

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

### 展开板子配置文件类型并定义LINUXFAMILY
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

### 如果没有设置板子类型,且满足if中的条件,会打开Memmory配置界面
if [[ -z ${MEM_TYPE} && ${BOARD} =~ orangepizero3|orangepir1b|orangepizero2w && ${BUILD_OPT} =~ u-boot|image && ${BRANCH} == next ]]; then
	options+=("1500MB"    "1.5 GB Memory")
	options+=("Others"    "1/2/4 GB Memory")
	menustr="Please choose memory size for ${BOARD}."
	MEM_TYPE=$(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 $MEM_TYPE ]] && exit_with_error "No option selected"
fi

### 如果不是配置roots,弹出OS release package base界面
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
[[ $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

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

### 防止因界面配置导致的BUILD_MINIMAL与SELECTED_CONFIGURATION冲突问题
#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

### 评估CPUM满载状态下需要的编译时间
# optimize build time with 100% CPU usage
CPUS=$(grep -c 'processor' /proc/cpuinfo)
if [[ $USEALLCORES != no ]]; then
	CTHREADS="-j$((CPUS + CPUS/2))"
else
	CTHREADS="-j1"
fi

### 调用拓展方式,比如在编译前查看是否可以修改CTHREADS从而利用超线程。
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函数,如果main脚本没有传递参数,就默认在此函数中调用编译接口
do_default() {
start=$(date +%s)
# Check and install dependencies, directory structure and settings
# The OFFLINE_WORK variable inside the function

### step1:准备主机和环境工作
prepare_host

### step2:异常判断和清空操作
[[ "${JUST_INIT}" == "yes" ]] && exit 0
[[ $CLEAN_LEVEL == *sources* ]] && cleaning "sources"

### step3:如果没有忽略更新,此处就会获取git最新仓库同步源码
# 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 [[ ${BOARD} =~ orangepi4|orangepi4-lts|orangepi800 && $BRANCH == legacy ]]; then
		[[ $BUILD_OPT =~ image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk3399_gst_xserver_libs.git" "${EXTER}/cache/sources/rk3399_gst_xserver_libs" "branch:main"
	fi
	if [[ ${BOARD} =~ orangepi4|orangepi4-lts|orangepi800 && $RELEASE =~ focal|buster|bullseye|bookworm ]]; then
		[[ ${BUILD_OPT} == image ]] && fetch_from_repo "https://github.com/orangepi-xunlong/rk-rootfs-build.git" "${EXTER}/cache/sources/rk-rootfs-build-${RELEASE}" "branch:rk-rootfs-build-${RELEASE}"
	fi
	if [[ ${BOARDFAMILY} == "rockchip-rk3588" && $RELEASE =~ bullseye|bookworm|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 [[ ${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
	if [[ ${BOARD} =~ orangepi4|orangepi4-lts|orangepi800 && $RELEASE =~ jammy && $BRANCH == next ]]; 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 -qnc -P ${EXTER}/cache/sources/rkbin-tools/rk35/ ${rkbin_url}/rk3588_ddr_lp4_2112MHz_lp5_2736MHz_v1.15.bin
		wget -qnc -P ${EXTER}/cache/sources/rkbin-tools/rk35/ ${rkbin_url}/rk3588_bl31_v1.44.elf
	fi
fi

### step4:清空旧编译结果操作
for option in $(tr ',' ' ' <<< "$CLEAN_LEVEL"); do
	[[ $option != sources ]] && cleaning "$option"
done

### step5:编译u-boot,kernel,rootfs等操作,关键性函数【compile_*】启动编译,【create_*】创建各种包
# 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
		#if ! ls "${DEB_STORAGE}/orangepi-firmware-full_${REVISION}_all.deb" 1> /dev/null 2>&1; then
			#FULL="-full"
			#REPLACE=""
			#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 && ! -f ${DEB_STORAGE}/$RELEASE/${CHOSEN_DESKTOP}_${REVISION}_all.deb ]] && create_desktop_package
	#[[ -n $RELEASE && $DESKTOP_ENVIRONMENT && ! -f ${DEB_STORAGE}/${RELEASE}/${BSP_DESKTOP_PACKAGE_FULLNAME}.deb ]] && create_bsp_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

### step6:编译完成之后调用的钩子函数,改变$SRC的权限拥有者
# 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"

### step7:展示编译结果
# 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()

###编译分支判断,是采用do_default函数还是main.sh脚本命令行自带的脚本进行编译处理
if [[ -z $1 ]]; then
	do_default
else
	eval "$@"
fi

3-4-3.compilation.sh

###编译uboot代码
compile_uboot()
{
	# 【未走分支】
	if [[ ${BOARDFAMILY} == "sun50iw9" && ${BRANCH} =~ legacy|current && $(dpkg --print-architecture) == arm64 ]]; then
		local uboot_name=${CHOSEN_UBOOT}_${REVISION}_${ARCH}.deb
		display_alert "Compile u-boot is not supported, only copy precompiled deb package" "$uboot_name" "info"
		cp "${EXTER}/cache/debs/h618/$uboot_name" "${DEB_STORAGE}/u-boot/"
	else
	# 非必选项,在overlayfs_wrapper之前需要保证sources directory干净
	if [[ $CLEAN_LEVEL == *make* ]]; then
		display_alert "Cleaning" "$BOOTSOURCEDIR" "info"
		(cd $BOOTSOURCEDIR; make clean > /dev/null 2>&1)
	fi
	
	### step1:确定uboot文件夹目录
	if [[ $USE_OVERLAYFS == yes ]]; then
		local ubootdir
		ubootdir=$(overlayfs_wrapper "wrap" "$BOOTSOURCEDIR" "u-boot_${LINUXFAMILY}_${BRANCH}")
	else
		local ubootdir="$BOOTSOURCEDIR"
	fi
	cd "${ubootdir}" || exit
	
	### step2:获取uboot版本号,git仓库对应的hash信息,并打印Log信息
	# 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"
	
	### step3:编译amd64架构的时候寻找工具链,并打印log信息
	if [[ $(dpkg --print-architecture) == amd64 ]]; then
		local toolchain
		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
			local toolchain2_type toolchain2_ver toolchain2
			toolchain2_type=$(cut -d':' -f1 <<< "${UBOOT_TOOLCHAIN2}")
			toolchain2_ver=$(cut -d':' -f2 <<< "${UBOOT_TOOLCHAIN2}")
			toolchain2=$(find_toolchain "$toolchain2_type" "$toolchain2_ver")
			[[ -z $toolchain2 ]] && exit_with_error "Could not find required toolchain" "${toolchain2_type}gcc $toolchain2_ver"
		fi
	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"
	
	### step4:为.deb包创建目录结构
	uboottempdir=$(mktemp -d)
	chmod 700 ${uboottempdir}
	trap "ret=\$?; rm -rf \"${uboottempdir}\" ; exit \$ret" 0 1 2 3 15
	local uboot_name=${CHOSEN_UBOOT}_${REVISION}_${ARCH}
	rm -rf $uboottempdir/$uboot_name
	mkdir -p $uboottempdir/$uboot_name/usr/lib/{u-boot,$uboot_name} $uboottempdir/$uboot_name/DEBIAN
	
	### step5:【重要】为单目标或多目标运行编译
	while read -r target; do
		# 获取目标make,目标patch目录,目标文件
		local target_make target_patchdir target_files
		target_make=$(cut -d';' -f1 <<< "${target}")
		target_patchdir=$(cut -d';' -f2 <<< "${target}")
		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
		# make clean 指令在此处调用
		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
		# 将make boot配置Log保存到compilation.log中
		echo -e "\n\t== u-boot make $BOOTCONFIG ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
		# eval指令后面定义了make CROSS_COMPILE=xxx
		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'}
			# 如果编译版本是2014.07相关配置
	        if [[ "${version}" != 2014.07 ]]; then
			# orangepi specifics u-boot settings 香橙派具体uboot设置
			[[ -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 对于新版本Kernel和非spi目标的配置
			if [[ ${BOOTBRANCH} =~ ^tag:v201[8-9](.*) && ${target} != "spi" && -f .config ]]; then
				sed -i 's/^.*CONFIG_ENV_IS_IN_FAT.*/# CONFIG_ENV_IS_IN_FAT is not set/g' .config
				sed -i 's/^.*CONFIG_ENV_IS_IN_EXT4.*/CONFIG_ENV_IS_IN_EXT4=y/g' .config
				sed -i 's/^.*CONFIG_ENV_IS_IN_MMC.*/# CONFIG_ENV_IS_IN_MMC is not set/g' .config
				sed -i 's/^.*CONFIG_ENV_IS_NOWHERE.*/# CONFIG_ENV_IS_NOWHERE is not set/g' .config | echo \
				"# CONFIG_ENV_IS_NOWHERE is not set" >> .config
				echo 'CONFIG_ENV_EXT4_INTERFACE="mmc"' >> .config
				echo 'CONFIG_ENV_EXT4_DEVICE_AND_PART="0:auto"' >> .config
				echo 'CONFIG_ENV_EXT4_FILE="/boot/boot.env"' >> .config
			fi
			# 如果是全志的板子相关uboot配置
			if [[ ${BOARDFAMILY} == "sun50iw9" && ${BRANCH} == "next" ]]; then
				if [[ ${MEM_TYPE} == "1500MB" ]]; then
					sed -i 's/^.*CONFIG_DRAM_SUN50I_H616_TRIM_SIZE*/CONFIG_DRAM_SUN50I_H616_TRIM_SIZE=y/g' .config
				else
					sed -i 's/^.*CONFIG_DRAM_SUN50I_H616_TRIM_SIZE*/# CONFIG_DRAM_SUN50I_H616_TRIM_SIZE is not set/g' .config
				fi
			fi
			# logo图标相关配置
			[[ -f tools/logos/udoo.bmp ]] && cp "${EXTER}"/packages/blobs/splash/udoo.bmp tools/logos/udoo.bmp
			touch .scmversion
			# 确保uboot延时等待即使被设置为0也可以停止boot
			# $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
		# 当需要双编译器工作时走如下解决办法
		cross_compile="CROSS_COMPILE=$CCACHE $UBOOT_COMPILER";
		[[ -n $UBOOT_TOOLCHAIN2 ]] && cross_compile="ORANGEPI=foe"; # empty parameter is not allowed
		echo -e "\n\t== u-boot make $target_make ==\n" >> "${DEST}"/${LOG_SUBPATH}/compilation.log
		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"
		[[ $(type -t uboot_custom_postprocess) == function ]] && uboot_custom_postprocess
		# 拷贝一些文件到Uboot 构建目录下
		# 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"
	
	### step6:uboot打包
	if [[ $PACK_UBOOT == "yes" ]];then
		if [[ $BOARDFAMILY =~ sun50iw1 ]]; then
			if [[ $(type -t u-boot_tweaks) == function ]]; then
				u-boot_tweaks ${uboot_name}
			else
				exit_with_error "U-boot pack failed"
			fi
		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
	
	### step7:在未定义的函数上声明 -f 不执行任何操作,设置control文件,主要就是一些uboot通用信息保存,拷贝.config文件和License文件
	# 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"
	### step8:uboot编译完成之后的一些处理,像fakeroot 运行dpkg-deb,rsync同步uboot目录
	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编译流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值