armbian-build/lib/functions/compilation/uboot.sh
Igor Velkov 1b74748622
Extension: ccache-remote — shared compilation cache via Redis/HTTP (#9369)
* add hook to allow customizing before kernel make env creation
* Hook runs in docker_cli_prepare_launch() just before DOCKER_EXTRA_ARGS
is processed, allowing extensions to add Docker arguments with a more
descriptive hook name than add_host_dependencies.
* Extension: ccache-remote

Enables ccache with remote Redis storage for sharing compilation cache across build hosts.

Features:
- Auto-discovery via Avahi/mDNS (ccache.local hostname)
- Explicit Redis server configuration via CCACHE_REMOTE_STORAGE
- Build statistics display at end of build (hit/miss/error rates)
- Support for both Docker and native builds
- Hooks for kernel and u-boot compilation environments

Documentation includes server setup instructions with security warnings,
client mDNS configuration, and cache sharing requirements.


* uboot: fix ccache environment and add extension hook

U-Boot build uses `env -i` which clears all environment variables.
CCACHE_DIR and CCACHE_TEMPDIR were not explicitly passed to make,
unlike kernel build (kernel-make.sh). This caused ccache to use
default directory instead of configured Armbian one, breaking
cache statistics and shared cache functionality.

Changes:
- Add CCACHE_DIR and CCACHE_TEMPDIR to uboot_make_envs
- Add uboot_make_config hook for extensions (similar to kernel_make_config),
  allowing modification of environment variables before compilation

* add long list of allowed ccache-related env vars
* set permissions to ccache files RW for everyone if cache not private
* ccache: add ccache_post_compilation hook for extensions
* ccache-remote: use ccache_post_compilation hook instead of cleanup handler

Show remote ccache stats after each compilation (kernel, uboot) via hook,
instead of once at the end via cleanup handler. Stats now shown even on
build failure.

* ccache: show stats with safe arithmetic
* ccache/uboot: improve code comments per review feedback

- uboot.sh: clarify ARMBIAN=foe workaround for dual-compiler scenario
- ccache-remote.sh: document that CCACHE_REDIS_CONNECT_TIMEOUT must be
  set before extension loads

* ccache-remote: mask storage URLs in logs

Mask CCACHE_REMOTE_STORAGE when emitting Docker env debug logs.

* ccache-remote: extract ccache_inject_envs() helper to deduplicate passthrough loops

Extract ccache_inject_envs() helper to deduplicate identical passthrough
loops in kernel and uboot make config hooks.

ccache-remote: rename functions to follow project naming conventions

Rename get_redis_stats and mask_storage_url to ccache_get_redis_stats
and ccache_mask_storage_url to follow project naming conventions.

ccache-remote: mask credentials in debug log output for passthrough loops

Mask CCACHE_REMOTE_STORAGE value through ccache_mask_storage_url() before
logging in both Docker env and make env passthrough loops to avoid leaking
credentials into build logs.

* ccache-remote: add HTTP/WebDAV backend and DNS discovery
* ccache-remote: move extension script into directory layout
* ccache-remote: add server setup docs and config files
* ccache-remote: validate Redis credentials in URLs
* ccache-remote: document Redis auth options and safe passwords

Add separate insecure config example for trusted networks.

Recommend URL-safe hex passwords and update setup docs.

* ccache-remote: improve Docker loopback handling and IPv6 host parsing
2026-03-01 01:18:35 +01:00

595 lines
27 KiB
Bash

#!/usr/bin/env bash
#
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2013-2026 Igor Pecovnik, igor@armbian.com
#
# This file is a part of the Armbian Build Framework
# https://github.com/armbian/build/
function maybe_make_clean_uboot() {
if [[ $CLEAN_LEVEL == *make-uboot* ]]; then
display_alert "${uboot_prefix}Cleaning u-boot tree - CLEAN_LEVEL contains 'make-uboot'" "${BOOTSOURCEDIR}" "info"
(
cd "${SRC}/cache/sources/${BOOTSOURCEDIR}" || exit_with_error "crazy about ${BOOTSOURCEDIR}"
run_host_command_logged make clean
)
else
display_alert "${uboot_prefix}Not cleaning u-boot tree, use CLEAN_LEVEL=make-uboot if needed" "CLEAN_LEVEL=${CLEAN_LEVEL}" "debug"
fi
}
function patch_uboot_target() {
local uboot_work_dir=""
uboot_work_dir="$(pwd)"
# outer scope variable: uboot_git_revision, validate that it is set
if [[ -z "${uboot_git_revision}" ]]; then
exit_with_error "uboot_git_revision is not set"
fi
display_alert "${uboot_prefix} Checking out to clean sources SHA1 ${uboot_git_revision}" "{$BOOTSOURCEDIR} for ${target_make}"
git checkout -f -q "${uboot_git_revision}"
# remove all git untracked files; echo their names to screen
# this throws away the baby with the bathwater; rebuilds will be slow. but the risk of shipping wrong binaries is too high.
display_alert "${uboot_prefix} Cleaning u-boot tree" "${BOOTSOURCEDIR} for '${target_make}'"
regular_git clean -xfdq
maybe_make_clean_uboot
# Python patching for u-boot!
do_with_hooks uboot_main_patching_python
# create patch for manual source changes
if [[ $CREATE_PATCHES == yes ]]; then
userpatch_create "u-boot"
fi
}
# this receives version target uboot_name uboottempdir uboot_target_counter as variables.
# also receives uboot_prefix, target_make, target_patchdir, target_files as input
function compile_uboot_target() {
: "${artifact_version:?artifact_version is not set}"
: "${UBOOT_COMPILER:?UBOOT_COMPILER is not set}"
# prepare a CROSS_COMPILE stanza, quoted, with $CCACHE (if not empty) and $UBOOT_COMPILER
declare cross_compile="undetermined_cross_compile"
if [[ -n "${CCACHE}" ]]; then
cross_compile="CROSS_COMPILE='${CCACHE} ${UBOOT_COMPILER}'"
else
cross_compile="CROSS_COMPILE='${UBOOT_COMPILER}'"
fi
if [[ "${SHOW_DEBUG}" == "yes" ]]; then
display_alert "${uboot_prefix}Listing contents of u-boot directory" "'${version}' '${target_make}' before patching" "debug"
run_host_command_logged "ls -laht"
fi
patch_uboot_target
if [[ "${SHOW_DEBUG}" == "yes" ]]; then
display_alert "${uboot_prefix}Listing contents of u-boot directory" "'${version}' '${target_make}' after patching" "debug"
run_host_command_logged "ls -laht"
fi
if [[ $CREATE_PATCHES == yes ]]; then
return 0
fi
# atftempdir comes from atf.sh's compile_atf()
if [[ -n $ATFSOURCE && -d "${atftempdir}" ]]; then
display_alert "Copying over bin/elf/itb's from atftempdir" "${atftempdir}" "debug"
run_host_command_logged cp -pv "${atftempdir}"/*.bin "${atftempdir}"/*.elf "${atftempdir}"/*.itb ./ # only works due to nullglob
# atftempdir is under WORKDIR, so no cleanup necessary.
fi
# crusttempdir comes from crust.sh's compile_crust()
if [[ -n $CRUSTSOURCE && -d "${crusttempdir}" ]]; then
display_alert "Copying over bin/elf's from crusttempdir" "${crusttempdir}" "debug"
run_host_command_logged cp -pv "${crusttempdir}"/*.bin "${crusttempdir}"/*.elf ./ # only works due to nullglob
fi
# Hook time, for extra post-processing
call_extension_method "pre_config_uboot_target" <<- 'PRE_CONFIG_UBOOT_TARGET'
*allow extensions prepare before configuring and compiling an u-boot target*
Some u-boot targets require extra configuration or pre-processing before compiling.
For example, changing Python version can be done by replacing the `${BIN_WORK_DIR}/python` symlink.
PRE_CONFIG_UBOOT_TARGET
display_alert "${uboot_prefix}Preparing u-boot config '${BOOTCONFIG}'" "${version} ${target_make}" "info"
declare -g if_error_detail_message="${uboot_prefix}Failed to configure u-boot ${version} $BOOTCONFIG ${target_make}"
run_host_command_logged CCACHE_BASEDIR="$(pwd)" \
"KCFLAGS=-fdiagnostics-color=always" \
pipetty make "${CTHREADS}" "${BOOTCONFIG}" "${cross_compile}"
# for modern (? 2018-2019?) kernel and non spi targets @TODO: this does not belong here
if [[ ${BOOTBRANCH} =~ ^tag:v201[8-9](.*) && ${target} != "spi" && -f .config ]]; then
display_alert "Hacking ENV stuff in u-boot config 2018-2019" "for ${target}" "debug"
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
# @TODO: this does not belong here
[[ -f tools/logos/udoo.bmp ]] && run_host_command_logged cp -pv "${SRC}"/packages/blobs/splash/udoo.bmp tools/logos/udoo.bmp
# @TODO: why?
touch .scmversion
declare uboot_loglevel="${UBOOT_LOGLEVEL:-"6"}" # default to info
# use `scripts/config` instead of sed if available. Cleaner results.
if [[ ! -f scripts/config ]]; then
display_alert "scripts/config not found" "u-boot ${version} $BOOTCONFIG ${target_make}" "debug"
# Old, pre-2022.10 u-boot; does not have the scripts/config script. Do it the old way.
# $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
# armbian specifics u-boot settings; configure the version so UART boot logs show exactly what's in there
[[ -f .config ]] && sed -i "s/CONFIG_LOCALVERSION=\"\"/CONFIG_LOCALVERSION=\"_armbian-${artifact_version}\"/g" .config
[[ -f .config ]] && sed -i 's/CONFIG_LOCALVERSION_AUTO=.*/# CONFIG_LOCALVERSION_AUTO is not set/g' .config
else
display_alert "scripts/config found" "u-boot ${version} $BOOTCONFIG ${target_make}" "debug"
# $BOOTDELAY can be set in board family config, ensure autoboot can be stopped even if set to 0
if [[ $BOOTDELAY == 0 ]]; then
display_alert "Adding CONFIG_ZERO_BOOTDELAY_CHECK=y u-boot config" "BOOTDELAY==0 for ${target}" "info"
run_host_command_logged scripts/config --enable CONFIG_ZERO_BOOTDELAY_CHECK
fi
# If BOOTDELAY is set, either change a preexisting CONFIG_BOOTDELAY or add it
if [[ -n $BOOTDELAY ]]; then
display_alert "Hacking autoboot delay in u-boot config" "BOOTDELAY=${BOOTDELAY} for ${target}" "info"
run_host_command_logged scripts/config --set-val CONFIG_BOOTDELAY "${BOOTDELAY}"
fi
# Hack, up the log level to 6: "info" (default is 4: "warning")
display_alert "Hacking log level in u-boot config" "LOGLEVEL=${uboot_loglevel} for ${target}" "info"
run_host_command_logged scripts/config --enable CONFIG_LOG
run_host_command_logged scripts/config --set-val CONFIG_LOGLEVEL ${uboot_loglevel}
run_host_command_logged scripts/config --set-val CONFIG_LOG_MAX_LEVEL ${uboot_loglevel}
# Include Armbian version so UART bootlogs are drastically more useful
run_host_command_logged ./scripts/config --disable "LOCALVERSION_AUTO"
run_host_command_logged ./scripts/config --set-str "LOCALVERSION" "_armbian-${artifact_version}" # crazy quotes!
fi
if [[ "${UBOOT_DEBUGGING}" == "yes" ]]; then
display_alert "Enabling u-boot debugging" "UBOOT_DEBUGGING=yes" "debug"
# Remove unsets...
cp .config .config.pre.debug
cat .config.pre.debug | grep -v -e "CONFIG_LOG is not set" -e "CONFIG_ERRNO_STR" > .config
rm .config.pre.debug
# 0 - emergency ; 1 - alert; 2 - critical; 3 - error; 4 - warning; 5 - note; 6 - info; 7 - debug; 8 - debug content; 9 - debug hardware I/O
cat <<- EXTRA_UBOOT_DEBUG_CONFIGS >> .config
CONFIG_LOG=y
CONFIG_LOG_MAX_LEVEL=${uboot_loglevel}
CONFIG_LOG_DEFAULT_LEVEL=${uboot_loglevel}
CONFIG_LOG_CONSOLE=y
CONFIG_SPL_LOG=y
CONFIG_SPL_LOG_MAX_LEVEL=${uboot_loglevel}
CONFIG_SPL_LOG_CONSOLE=y
CONFIG_TPL_LOG=y
CONFIG_TPL_LOG_MAX_LEVEL=${uboot_loglevel}
CONFIG_TPL_LOG_CONSOLE=y
# CONFIG_ERRNO_STR is not set
EXTRA_UBOOT_DEBUG_CONFIGS
run_host_command_logged CCACHE_BASEDIR="$(pwd)" \
"KCFLAGS=-fdiagnostics-color=always" \
pipetty make "olddefconfig" "${cross_compile}"
fi
# cflags will be passed both as CFLAGS, KCFLAGS, and both as make params and as env variables. (some vendor u-boot's are cuckoo)
# boards/families/extensions can customize this via the hook below
local -a uboot_cflags_array=(
"-fdiagnostics-color=always" # color messages
"-Wno-error=maybe-uninitialized"
"-Wno-error=misleading-indentation" # patches have mismatching indentation
"-Wno-error=attributes" # for very old-uboots
"-Wno-error=address-of-packed-member" # for very old-uboots
)
if linux-version compare "${gcc_version_main}" ge "11.0"; then
uboot_cflags_array+=(
"-Wno-error=array-parameter" # very old uboots
)
fi
# Hook time, for extra post-processing
call_extension_method "post_config_uboot_target" <<- 'POST_CONFIG_UBOOT_TARGET'
*allow extensions prepare after configuring but before compiling an u-boot target*
Some u-boot targets require extra configuration or pre-processing before compiling.
Last chance to change .config for u-boot before compiling.
Also the only chance to change the (local) array `uboot_cflags_array`.
POST_CONFIG_UBOOT_TARGET
# make olddefconfig, so changes made in hook above are consolidated
display_alert "${uboot_prefix}Updating u-boot config with olddefconfig" "${version} ${target_make}" "info"
run_host_command_logged CCACHE_BASEDIR="$(pwd)" \
"KCFLAGS=-fdiagnostics-color=always" \
pipetty make "${CTHREADS}" "olddefconfig" "${cross_compile}"
if [[ "${UBOOT_CONFIGURE:-"no"}" == "yes" ]]; then
display_alert "Saving pre-config u-boot defconfig" "UBOOT_CONFIGURE=yes; experimental" "warn"
run_host_command_logged make savedefconfig
run_host_command_logged mv -v defconfig defconfig.pre.menuconfig
display_alert "Configuring u-boot" "UBOOT_CONFIGURE=yes; experimental" "warn"
run_host_command_dialog make menuconfig
display_alert "Exporting saved config" "UBOOT_CONFIGURE=yes; experimental" "warn"
run_host_command_logged make savedefconfig
run_host_command_logged cp -v defconfig "${DEST}/defconfig-uboot-${BOARD}-${BRANCH}"
display_alert "Diffing before and after defconfigs" "UBOOT_CONFIGURE=yes; experimental" "warn"
run_host_command_logged diff -u --color=always "defconfig.pre.menuconfig" "${DEST}/defconfig-uboot-${BOARD}-${BRANCH}" "2>&1" "|| true" # no errors please, all to stdout
display_alert "Exported saved config (experimental)" "${DEST}/defconfig-uboot-${BOARD}-${BRANCH}" "warn"
return 0 # exit after this
fi
##########################################
# REAL COMPILATION SECTION STARTING HERE #
##########################################
local uboot_cflags="${uboot_cflags_array[*]}"
local ts=${SECONDS}
# Collect make environment variables, similar to 'kernel-make.sh'
uboot_make_envs=(
"PATH='${PATH}'" # preserve PATH as-is
"CFLAGS='${uboot_cflags}'"
"KCFLAGS='${uboot_cflags}'"
"CCACHE_BASEDIR=$(pwd)"
"PYTHONPATH=\"${PYTHON3_INFO[MODULES_PATH]}:${PYTHONPATH}\"" # Insert the pip modules downloaded by Armbian into PYTHONPATH (needed e.g. for pyelftools)
)
# Pass the ccache directories explicitly, since we'll run under "env -i"
if [[ -n "${CCACHE_DIR}" ]]; then
uboot_make_envs+=("CCACHE_DIR=${CCACHE_DIR@Q}")
fi
if [[ -n "${CCACHE_TEMPDIR}" ]]; then
uboot_make_envs+=("CCACHE_TEMPDIR=${CCACHE_TEMPDIR@Q}")
fi
# workaround when two compilers are needed
cross_compile="CROSS_COMPILE=\"${CCACHE:+$CCACHE }$UBOOT_COMPILER\""
# When UBOOT_TOOLCHAIN2 is set, the board's uboot_custom_postprocess handles compilers;
# pass a harmless dummy env var since empty make parameters cause errors
[[ -n $UBOOT_TOOLCHAIN2 ]] && cross_compile="ARMBIAN=foe"
call_extension_method "uboot_make_config" <<- 'UBOOT_MAKE_CONFIG'
*Hook to customize u-boot make environment*
Called right before invoking make for u-boot compilation.
Available array to modify:
- uboot_make_envs[@]: environment variables passed via "env -i" (e.g., CCACHE_REMOTE_STORAGE)
UBOOT_MAKE_CONFIG
display_alert "${uboot_prefix}Compiling u-boot" "${version} ${target_make} with gcc '${gcc_version_main}'" "info"
declare -g if_error_detail_message="${uboot_prefix}Failed to build u-boot ${version} ${target_make}"
do_with_ccache_statistics run_host_command_logged_long_running \
"env" "-i" "${uboot_make_envs[@]}" \
pipetty make "$target_make" "$CTHREADS" "${cross_compile}"
display_alert "${uboot_prefix}built u-boot target" "${version} in $((SECONDS - ts)) seconds" "info"
# Save a defconfig, as that will be included as reference in the .deb package
# Do not fail here; some very (very!) old u-boots like 2011 do not have 'savedefconfig'
run_host_command_logged "env" "-i" "${uboot_make_envs[@]}" pipetty make savedefconfig "$CTHREADS" "${cross_compile}" ||
display_alert "${uboot_prefix}Failed to save defconfig" "${version} ${target_make}" "warn"
if [[ $(type -t uboot_custom_postprocess) == function ]]; then
display_alert "${uboot_prefix}Postprocessing u-boot" "${version} ${target_make}"
uboot_custom_postprocess
fi
# Hook time, for extra post-processing
call_extension_method "post_uboot_custom_postprocess" <<- 'POST_UBOOT_CUSTOM_POSTPROCESS'
*allow extensions to do extra u-boot postprocessing, after uboot_custom_postprocess*
For hacking at the produced binaries after u-boot is compiled and post-processed.
POST_UBOOT_CUSTOM_POSTPROCESS
declare -a target_dst_files=() # to be filled by deploy_built_uboot_bins_for_one_target_to_packaging_area
deploy_built_uboot_bins_for_one_target_to_packaging_area # copy according to the target_files
# Include metadata about the build, for reference; use the target_counter as part of filename to avoid overwriting
# .config and defconfig go to ${uboottempdir}/usr/lib/${uboot_name}/u-boot-config-target-${target_counter}
# ${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata-target-${uboot_target_counter}.sh has general metadata about the target
cat <<- UBOOT_TARGET_METADATA_SH > "${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata-target-${uboot_target_counter}.sh"
declare -a UBOOT_TARGET_BINS=(${target_dst_files[@]@Q})
declare UBOOT_TARGET_MAKE=${target_make@Q}
UBOOT_TARGET_METADATA_SH
if [[ -f .config ]]; then # Not all u-boots have .config; some very old did boards.cfg or whatever. Ignore in this case
run_host_command_logged cp -v .config "${uboottempdir}/usr/lib/${uboot_name}/u-boot-config-target-${uboot_target_counter}"
cat <<- UBOOT_TARGET_METADATA_SH >> "${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata-target-${uboot_target_counter}.sh"
declare UBOOT_TARGET_CONFIG="u-boot-config-target-${uboot_target_counter}"
UBOOT_TARGET_METADATA_SH
fi
if [[ -f defconfig ]]; then # Not all u-boots are capable of savedefconfig, and thus defconfig will not be available
run_host_command_logged cp -v defconfig "${uboottempdir}/usr/lib/${uboot_name}/u-boot-defconfig-target-${uboot_target_counter}"
cat <<- UBOOT_TARGET_METADATA_SH >> "${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata-target-${uboot_target_counter}.sh"
declare UBOOT_TARGET_DEFCONFIG="u-boot-defconfig-target-${uboot_target_counter}"
UBOOT_TARGET_METADATA_SH
fi
if [[ $DEBUG == yes ]]; then
display_alert "${uboot_prefix}Showing u-boot metadata for target" "${version} ${target_make}" "debug"
run_tool_batcat --file-name "/usr/lib/${uboot_name}/u-boot-metadata-target-${uboot_target_counter}.sh" "${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata-target-${uboot_target_counter}.sh"
fi
display_alert "${uboot_prefix}Done with u-boot target" "${version} ${target_make}"
return 0
}
function loop_over_uboot_targets_and_do() {
# Try very hard, to fault even, to avoid using subshells while reading a newline-delimited string.
# Sorry for the juggling with IFS.
declare _old_ifs="${IFS}" _new_ifs=$'\n'
IFS="${_new_ifs}" # split on newlines only
display_alert "Looping over u-boot targets" "'${UBOOT_TARGET_MAP}'" "debug"
declare -i uboot_target_counter=1
# save the current state of nullglob into a variable; don't fail
declare _old_nullglob
_old_nullglob="$(shopt -p nullglob || true)"
display_alert "previous state of nullglob" "'${_old_nullglob}'" "debug"
# disable nullglob; dont fail if already disabled
shopt -u nullglob || true
# store new state; don't fail
declare _new_nullglob
_new_nullglob="$(shopt -p nullglob || true)"
display_alert "new state of nullglob" "'${_new_nullglob}'" "debug"
for target in ${UBOOT_TARGET_MAP}; do
display_alert "Building u-boot target" "'${target}'" "debug"
# reset nullglob to _old_nullglob
eval "${_old_nullglob}"
IFS="${_old_ifs}" # restore for the body of loop
declare -g target uboot_name uboottempdir version
declare -g uboot_prefix="{u-boot:${uboot_target_counter}} "
declare -g target_make target_patchdir target_files
target_make=$(cut -d';' -f1 <<< "${target}")
target_patchdir=$(cut -d';' -f2 <<< "${target}")
target_files=$(cut -d';' -f3 <<< "${target}")
# Invoke our parameters directly
"$@"
# Increment the counter
uboot_target_counter=$((uboot_target_counter + 1))
IFS="${_new_ifs}" # split on newlines only for rest of loop
done
IFS="${_old_ifs}"
# reset nullglob to _old_nullglob
eval "${_old_nullglob}"
uboot_target_counter=$((uboot_target_counter - 1)) # decrement, as we incremented after the last target
declare -g -i uboot_target_counter="${uboot_target_counter}" # set global, for metadata ("how many targets?")
return 0
}
function deploy_built_uboot_bins_for_one_target_to_packaging_area() {
display_alert "${uboot_prefix}Preparing u-boot targets packaging" "${version} ${target_make}"
# copy files to build directory
declare f
for f in $target_files; do
declare f_src f_dst
f_src=$(cut -d':' -f1 <<< "${f}")
if [[ $f == *:* ]]; then
f_dst=$(cut -d':' -f2 <<< "${f}")
else
f_dst=$(basename "${f_src}")
fi
display_alert "${uboot_prefix}Deploying u-boot binary target" "${version} ${target_make} :: ${f_dst}"
[[ ! -f $f_src ]] && exit_with_error "U-boot artifact not found" "$(basename "${f_src}")"
run_host_command_logged cp -v "${f_src}" "${uboottempdir}/usr/lib/${uboot_name}/${f_dst}"
#display_alert "Done with binary target" "${version} ${target_make} :: ${f_dst}"
target_dst_files+=("${f_dst}") # for metadata
done
return 0
}
function compile_uboot() {
: "${artifact_version:?artifact_version is not set}"
display_alert "Compiling u-boot" "BOOTSOURCE: ${BOOTSOURCE}" "debug"
if [[ -n $BOOTSOURCE ]] && [[ "${BOOTSOURCE}" != "none" ]]; then
display_alert "Extensions: fetch custom uboot" "fetch_custom_uboot" "debug"
call_extension_method "fetch_custom_uboot" <<- 'FETCH_CUSTOM_UBOOT'
*allow extensions to fetch extra uboot sources*
For downstream uboot et al.
This is done after `fetch_from_repo`, but before actually compiling u-boot.
FETCH_CUSTOM_UBOOT
fi
# not optimal, but extra cleaning before overlayfs_wrapper should keep sources directory clean
maybe_make_clean_uboot
if [[ $USE_OVERLAYFS == yes ]]; then
local ubootdir
ubootdir=$(overlayfs_wrapper "wrap" "$SRC/cache/sources/$BOOTSOURCEDIR" "u-boot_${LINUXFAMILY}_${BRANCH}")
else
local ubootdir="$SRC/cache/sources/$BOOTSOURCEDIR"
fi
cd "${ubootdir}" || exit
# read uboot version
local version hash
version=$(grab_version "$ubootdir")
hash=$(git --git-dir="$ubootdir"/.git rev-parse HEAD)
display_alert "Compiling u-boot" "$version ${ubootdir}" "info"
declare gcc_version_main
gcc_version_main="$(eval env "${UBOOT_COMPILER}gcc" -dumpfullversion -dumpversion)"
display_alert "Compiler version" "${UBOOT_COMPILER}gcc '${gcc_version_main}'" "info"
local uboot_name="linux-u-boot-${BRANCH}-${BOARD}"
# create directory structure for the .deb package
declare cleanup_id="" uboottempdir=""
prepare_temp_dir_in_workdir_and_schedule_cleanup "uboot" cleanup_id uboottempdir # namerefs
mkdir -p "$uboottempdir/usr/lib/u-boot" "$uboottempdir/usr/lib/$uboot_name" "$uboottempdir/DEBIAN"
# Allow extension-based u-boot bulding. We call the hook, and if EXTENSION_BUILT_UBOOT="yes" afterwards, we skip our own compilation.
# This is to make it easy to build vendor/downstream uboot with their own quirks.
display_alert "Extensions: build custom uboot" "build_custom_uboot" "debug"
call_extension_method "build_custom_uboot" <<- 'BUILD_CUSTOM_UBOOT'
*allow extensions to build their own uboot*
For downstream uboot et al.
Set \`EXTENSION_BUILT_UBOOT=yes\` to then skip the normal compilation.
BUILD_CUSTOM_UBOOT
if [[ "${EXTENSION_BUILT_UBOOT}" != "yes" ]]; then
loop_over_uboot_targets_and_do compile_uboot_target
else
display_alert "Extensions: custom uboot built by extension" "not building regular uboot" "debug"
fi
if [[ "${ARTIFACT_WILL_NOT_BUILD:-"no"}" == "yes" ]]; then
display_alert "Extensions: artifact will not build" "not building regular uboot" "debug"
return 0
fi
display_alert "Preparing u-boot general packaging" "${version} ${target_make}"
local -a postinst_functions=()
local destination="${uboottempdir}"
call_extension_method "pre_package_uboot_image" <<- 'PRE_PACKAGE_UBOOT_IMAGE'
*allow making some last minute changes before u-boot is packaged*
This should be implemented by the config to tweak the uboot package, after the board or family has had the chance to.
You can write to `$destination` here and it will be packaged.
You can also append to the `postinst_functions` array, and the _content_ of those functions will be added to the postinst script.
PRE_PACKAGE_UBOOT_IMAGE
# Let's binwalk each file in the resulting package for analysis purposes
display_alert "Analyzing u-boot binaries with binwalk" "${uboot_name}" "info"
declare binfile base_binfile
find "${uboottempdir}" -type f | grep -v -e "u-boot-defconfig-target-" -e "u-boot-config-target-" -e "u-boot-metadata-target" | sort | while read -r binfile; do
base_binfile="$(basename "${binfile}")"
display_alert "Analyzing u-boot binary with binwalk" "'${base_binfile}' built on ${HOSTRELEASE}" "info"
run_host_command_logged file --brief "${binfile}" "||" true ";" binwalk --run-as=root "${binfile}" "||" true # do not fail, ever
if [[ "${UBOOT_BINS_TO_OUTPUT}" == "yes" ]]; then
display_alert "Copying u-boot binary to output for later binwalk inspection" "'${base_binfile}' built on ${HOSTRELEASE}" "warn"
declare target="${SRC}/output/uboot-bin-${uboot_name}-${base_binfile}-host-${HOSTRELEASE}.bin"
run_host_command_logged cp -v "${binfile}" "${target}"
fi
done
artifact_package_hook_helper_board_side_functions "postinst" uboot_postinst_base "${postinst_functions[@]}"
unset uboot_postinst_base postinst_functions destination
# declare -f on non-defined function does not do anything (but exits with errors, so ignore them with "|| true")
cat <<- EOF > "${uboottempdir}/usr/lib/u-boot/platform_install.sh"
# Armbian u-boot install script for linux-u-boot-${BOARD}-${BRANCH} ${artifact_version}
# This file provides functions for deploying u-boot to a block device.
DIR=/usr/lib/$uboot_name
$(declare -f write_uboot_platform || true)
$(declare -f write_uboot_platform_mtd || true)
$(declare -f setup_write_uboot_platform || true)
EOF
if [[ "${SHOW_DEBUG}" == "yes" ]]; then
display_alert "Showing contents of" "usr/lib/u-boot/platform_install.sh" "info"
run_tool_batcat --file-name "usr/lib/u-boot/platform_install.sh" "${uboottempdir}/usr/lib/u-boot/platform_install.sh"
fi
# Write general metadata. This is intended to be used board-side, and allows some better gui and reuse.
# Ensure that all variables used here are hashed in the artifact-uboot.sh during version calculation.
cat <<- UBOOT_GENERAL_METADATA_SH > "${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata.sh"
declare -i UBOOT_NUM_TARGETS=${uboot_target_counter}
declare UBOOT_BIN_DIR="/usr/lib/${uboot_name}"
declare UBOOT_VERSION="${version}"
declare UBOOT_ARTIFACT_VERSION="${artifact_version}"
declare UBOOT_GIT_REVISION="${hash}"
declare UBOOT_GIT_SOURCE="${BOOTSOURCE}"
declare UBOOT_GIT_BRANCH="${BOOTBRANCH}"
declare UBOOT_GIT_PATCHDIR="${BOOTPATCHDIR}"
declare UBOOT_PARTITION_TYPE="${IMAGE_PARTITION_TABLE}"
declare UBOOT_KERNEL_DTB="${BOOT_FDT_FILE}"
declare UBOOT_KERNEL_SERIALCON="${SERIALCON}"
declare UBOOT_EXTLINUX_PREFER="${SRC_EXTLINUX:-"no"}"
declare UBOOT_EXTLINUX_CMDLINE="${SRC_CMDLINE}"
UBOOT_GENERAL_METADATA_SH
if [[ $DEBUG == yes ]]; then
display_alert "${uboot_prefix}Showing u-boot metadata for target" "${version} ${target_make}" "debug"
run_tool_batcat --file-name "/usr/lib/${uboot_name}/u-boot-metadata.sh" "${uboottempdir}/usr/lib/${uboot_name}/u-boot-metadata.sh"
fi
display_alert "Running shellcheck" "usr/lib/u-boot/platform_install.sh" "info"
shellcheck_debian_control_scripts "${uboottempdir}/usr/lib/u-boot/platform_install.sh"
display_alert "Das U-Boot .deb package version" "${artifact_version}" "info"
# set up control file
cat <<- EOF > "$uboottempdir/DEBIAN/control"
Package: linux-u-boot-${BOARD}-${BRANCH}
Version: ${artifact_version}
Architecture: $ARCH
Maintainer: $MAINTAINER <$MAINTAINERMAIL>
Section: kernel
Priority: optional
Provides: armbian-u-boot
Replaces: armbian-u-boot
Conflicts: armbian-u-boot, u-boot-sunxi
Description: Das U-Boot for ${BOARD}
${artifact_version_reason:-"${version}"}
EOF
# copy license files, config, etc.
[[ -f .config && -n $BOOTCONFIG ]] && run_host_command_logged cp .config "$uboottempdir/usr/lib/u-boot/${BOOTCONFIG}" # legacy and @TODO should be removed as it has only the last target; we now have per-target configs and defconfigs
[[ -f COPYING ]] && run_host_command_logged cp COPYING "$uboottempdir/usr/lib/u-boot/LICENSE"
[[ -f Licenses/README ]] && run_host_command_logged cp Licenses/README "$uboottempdir/usr/lib/u-boot/LICENSE"
[[ -n $atftempdir && -f $atftempdir/license.md ]] && run_host_command_logged cp "${atftempdir}/license.md" "$uboottempdir/usr/lib/u-boot/LICENSE.atf"
display_alert "Building u-boot deb" "(version: ${artifact_version})"
dpkg_deb_build "${uboottempdir}" "uboot"
[[ -n $atftempdir ]] && rm -rf "${atftempdir:?}" # @TODO: intricate cleanup; u-boot's pkg uses ATF's tempdir...
done_with_temp_dir "${cleanup_id}" # changes cwd to "${SRC}" and fires the cleanup function early
display_alert "Built u-boot deb OK" "linux-u-boot-${BOARD}-${BRANCH} ${artifact_version}" "info"
return 0 # success
}
function uboot_postinst_base() {
# Source the armbian-release information file
# shellcheck source=/dev/null
[ -f /etc/armbian-release ] && . /etc/armbian-release
# shellcheck source=/dev/null
source /usr/lib/u-boot/platform_install.sh
if [ "${FORCE_UBOOT_UPDATE:-no}" == "yes" ]; then
#recognize_root
root_uuid=$(sed -e 's/^.*root=//' -e 's/ .*$//' < /proc/cmdline)
root_partition=$(blkid | tr -d '":' | grep "${root_uuid}" | awk '{print $1}')
root_partition_name=$(echo $root_partition | sed 's/\/dev\///g')
root_partition_device_name=$(lsblk -ndo pkname $root_partition)
root_partition_device=/dev/$root_partition_device_name
write_uboot_platform "$DIR" "${root_partition_device}"
sync
fi
}