armbian-next: introduce tmpfs-utils.sh; put LOGDIR and WORKDIR under tmpfs; set CCACHE_TEMPDIR under WORKDIR

- introduce generic `prepare_tmpfs_for()`, which manages it's own cleanup/dir removal
- use it for `WORKDIR` (which is `TMPDIR`) and `LOGDIR`
  - adapt previous cleanup handlers for those, so they delete their contents but not the dir itself (which might be mounted)
- also: make `ARMBIAN_LOG_CLI_ID` readonly together with other superglobals
- set `XDG_RUNTIME_DIR` & `XDG_RUNTIME_DIR` together with `TMPDIR`
- kernel-make.sh: pass `CCACHE_TEMPDIR` down to Kernel make (thanks @the-Going)
This commit is contained in:
Ricardo Pardini
2023-01-16 16:01:42 +01:00
parent d6200a30e6
commit 2d9f9216eb
6 changed files with 129 additions and 19 deletions

View File

@@ -108,9 +108,17 @@ function cli_entrypoint() {
declare -g -r MOUNT="${WORKDIR_BASE_TMP}/mount-${ARMBIAN_BUILD_UUID}" # MOUNT ("mounted on the loop") is the mounted root on final image (via loop). "image" stage
declare -g -r DESTIMG="${WORKDIR_BASE_TMP}/image-${ARMBIAN_BUILD_UUID}" # DESTIMG is where the backing image (raw, huge, sparse file) is kept (not the final destination)
LOG_SECTION="entrypoint" start_logging_section # This creates LOGDIR. @TODO: also maybe causes a spurious group to be created in the log file
# Make sure ARMBIAN_LOG_CLI_ID is set, and unique, and readonly.
# Pre-runs might change it before this, but if not set, default to ARMBIAN_COMMAND.
declare -r -g ARMBIAN_LOG_CLI_ID="${ARMBIAN_LOG_CLI_ID:-${ARMBIAN_COMMAND}}"
# If we're on Linux & root, mount tmpfs on LOGDIR. This has it's own cleanup handler.
# It also _creates_ the LOGDIR, and the cleanup handler will delete.
prepare_tmpfs_for "LOGDIR" "${LOGDIR}"
LOG_SECTION="entrypoint" start_logging_section # This will create LOGDIR if it does not exist. @TODO: also maybe causes a spurious group to be created in the log file
add_cleanup_handler trap_handler_cleanup_logging # cleanup handler for logs; it rolls it up from LOGDIR into DEST/logs
add_cleanup_handler trap_handler_reset_output_owner # make sure output folder is owned by pre-sudo user if that's the case
add_cleanup_handler trap_handler_reset_output_owner # make sure output folder is owned by pre-sudo/pre-Docker user if that's the case
# @TODO: So gigantic contention point here about logging the basic deps installation.
if [[ "${ARMBIAN_COMMAND_REQUIRE_BASIC_DEPS}" == "yes" ]]; then

View File

@@ -11,11 +11,12 @@ function run_kernel_make_internal() {
prepare_distcc_compilation_config
common_make_envs=(
"CCACHE_BASEDIR=\"$(pwd)\"" # Base directory for ccache, for cache reuse # @TODO: experiment with this and the source path to maximize hit rate
"PATH=\"${toolchain}:${PATH}\"" # Insert the toolchain first into the PATH.
"DPKG_COLORS=always" # Use colors for dpkg @TODO no dpkg is done anymore, remove?
"XZ_OPT='--threads=0'" # Use parallel XZ compression
"TERM='${TERM}'" # Pass the terminal type, so that 'make menuconfig' can work.
"CCACHE_BASEDIR=\"$(pwd)\"" # Base directory for ccache, for cache reuse # @TODO: experiment with this and the source path to maximize hit rate
"CCACHE_TEMPDIR=\"${CCACHE_TEMPDIR:?}\"" # Temporary directory for ccache, under WORKDIR
"PATH=\"${toolchain}:${PATH}\"" # Insert the toolchain first into the PATH.
"DPKG_COLORS=always" # Use colors for dpkg @TODO no dpkg is done anymore, remove?
"XZ_OPT='--threads=0'" # Use parallel XZ compression
"TERM='${TERM}'" # Pass the terminal type, so that 'make menuconfig' can work.
)
# If CCACHE_DIR is set, pass it to the kernel build; Pass the ccache dir explicitly, since we'll run under "env -i"
@@ -32,7 +33,7 @@ function run_kernel_make_internal() {
"${DISTCC_MAKE_J_PARALLEL[@]}" # Parallel compile, "-j X" for X cpus; determined by distcc, or is just "$CTHREADS" if distcc is not enabled.
"ARCH=${ARCHITECTURE}" # Key param. Everything depends on this.
"LOCALVERSION=-${LINUXFAMILY}" # Change the internal kernel version to include the family. Changing this causes recompiles # @TODO change to "localversion" file
"LOCALVERSION=-${LINUXFAMILY}" # Change the internal kernel version to include the family. Changing this causes recompiles # @TODO change hack at .config; that might handles mtime better
"CROSS_COMPILE=${CCACHE} ${DISTCC_CROSS_COMPILE_PREFIX[@]} ${KERNEL_COMPILER}" # added as prefix to every compiler invocation by make
"KCFLAGS=-fdiagnostics-color=always -Wno-error=misleading-indentation" # Force GCC colored messages, downgrade misleading indentation to warning
@@ -42,7 +43,7 @@ function run_kernel_make_internal() {
"KBUILD_BUILD_USER=armbian" # https://www.kernel.org/doc/html/latest/kbuild/kbuild.html#kbuild-build-user-kbuild-build-host
"KBUILD_BUILD_HOST=next" # https://www.kernel.org/doc/html/latest/kbuild/kbuild.html#kbuild-build-user-kbuild-build-host
"KGZIP=pigz" "KBZIP2=pbzip2" # Parallel compression, use explicit parallel compressors https://lore.kernel.org/lkml/20200901151002.988547791@linuxfoundation.org/
"KGZIP=pigz" "KBZIP2=pbzip2" # Parallel compression, use explicit parallel compressors https://lore.kernel.org/lkml/20200901151002.988547791@linuxfoundation.org/ # @TODO: what about XZ?
)
# last statement, so it passes the result to calling function. "env -i" is used for empty env

View File

@@ -0,0 +1,87 @@
# Call: prepare_tmpfs_for "NAME_OF_TMPFS_DIR" "${PATH_TO_DIR}" # this adds its own cleanup handler
function prepare_tmpfs_for() {
declare tmpfs_name="${1}"
declare tmpfs_path="${2}"
# validate parameters
if [[ -z "${tmpfs_name}" ]]; then
exit_with_error "prepare_tmpfs_for: tmpfs_name (arg 1) is empty"
fi
if [[ -z "${tmpfs_path}" ]]; then
exit_with_error "prepare_tmpfs_for: tmpfs_path (arg 2) is empty"
fi
# params for the handler; initially just repeat our own params
declare -a cleanup_params=("${tmpfs_name}" "${tmpfs_path}")
# create the dir if not exists and note that down for the cleanup handler
if [[ ! -d "${tmpfs_path}" ]]; then
display_alert "prepare_tmpfs_for: creating dir" "${tmpfs_path}" "cleanup"
mkdir -p "${tmpfs_path}"
cleanup_params+=("remove_dir")
else
display_alert "prepare_tmpfs_for: dir exists" "${tmpfs_path}" "cleanup"
cleanup_params+=("no_remove_dir")
fi
# Do nothing if we're not on Linux (detect via OSTYPE) and root. Still, setup the cleanup handler.
if [[ "${OSTYPE}" != "linux"* ]] || [[ "${EUID}" -ne 0 ]]; then
display_alert "prepare_tmpfs_for: not on Linux or not root, skipping" "${tmpfs_name}" "cleanup"
cleanup_params+=("no_umount_tmpfs")
elif [[ "${ARMBIAN_INSIDE_DOCKERFILE_BUILD}" == "yes" ]]; then
display_alert "prepare_tmpfs_for: inside Dockerfile build, skipping" "${tmpfs_name}" "cleanup"
cleanup_params+=("no_umount_tmpfs")
else
display_alert "prepare_tmpfs_for: on Linux and root, MOUNTING TMPFS" "${tmpfs_name}" "cleanup"
# mount tmpfs on it
mount -t tmpfs tmpfs "${tmpfs_path}"
cleanup_params+=("umount_tmpfs")
#cleanup_params+=("no_umount_tmpfs")
fi
# add the cleanup handler
declare cleanup_handler="cleanup_tmpfs_for ${cleanup_params[*]@Q}"
display_alert "prepare_tmpfs_for: add cleanup handler" "${cleanup_handler}" "cleanup"
add_cleanup_handler "${cleanup_handler}"
return 0
}
function cleanup_tmpfs_for() {
declare tmpfs_name="${1}"
declare tmpfs_path="${2}"
declare remove_dir="${3}"
declare umount_tmpfs="${4}"
# validate parameters
if [[ -z "${tmpfs_name}" ]]; then
exit_with_error "cleanup_tmpfs_for: tmpfs_name (arg 1) is empty"
fi
if [[ -z "${tmpfs_path}" ]]; then
exit_with_error "cleanup_tmpfs_for: tmpfs_path (arg 2) is empty"
fi
if [[ -z "${remove_dir}" ]]; then
exit_with_error "cleanup_tmpfs_for: remove_dir (arg 3) is empty"
fi
if [[ -z "${umount_tmpfs}" ]]; then
exit_with_error "cleanup_tmpfs_for: umount_tmpfs (arg 4) is empty"
fi
# umount tmpfs
if [[ "${umount_tmpfs}" == "umount_tmpfs" ]]; then
display_alert "cleanup_tmpfs_for: umount tmpfs" "${tmpfs_name}" "cleanup"
umount "${tmpfs_path}"
else
display_alert "cleanup_tmpfs_for: not umounting tmpfs" "${tmpfs_name}" "cleanup"
fi
# remove the dir if we created it
if [[ "${remove_dir}" == "remove_dir" ]]; then
display_alert "cleanup_tmpfs_for: removing dir" "${tmpfs_path}" "cleanup"
rm -rf "${tmpfs_path:?}"
else
display_alert "cleanup_tmpfs_for: not removing dir" "${tmpfs_path}" "cleanup"
fi
return 0
}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# This is called both early in compile.sh, but also after processing cmdline params in the cli entrypoint.sh
# This is called both early in compile.sh, but also after processing cmdline params in the cli entrypoint.sh
function logging_init() {
# defaults.
# if stdout is a terminal, then default SHOW_LOG to yes
@@ -101,10 +101,10 @@ function print_current_asset_log_base_file() {
}
function discard_logs_tmp_dir() {
# Linux allows us to be more careful, but really, those are log files we're talking about.
# Do not delete the dir itself, since it might be a tmpfs mount.
if [[ "$(uname)" == "Linux" ]]; then
rm -rf --one-file-system "${LOGDIR}"
rm -rf --one-file-system "${LOGDIR:?}"/* # Note this is protected by :?
else
rm -rf "${LOGDIR}"
rm -rf "${LOGDIR:?}"/* # Note this is protected by :?
fi
}

View File

@@ -7,16 +7,19 @@ function main_default_build_single() {
fi
# Check the sanity of WORKDIR_BASE_TMP regarding mount options.
check_dir_for_mount_options "${WORKDIR_BASE_TMP}" "main temporary dir"
LOG_SECTION="check_dir_for_mount_options" do_with_logging check_dir_for_mount_options "${WORKDIR_BASE_TMP}" "main temporary dir"
# Starting work. Export TMPDIR, which will be picked up by all `mktemp` invocations hopefully.
# Runner functions in logging/runners.sh will explicitly unset TMPDIR before invoking chroot.
# Invoking chroot directly will fail in subtle ways, so, please use the runner.sh functions.
display_alert "Starting single build, exporting TMPDIR" "${WORKDIR}" "debug"
mkdir -p "${WORKDIR}"
add_cleanup_handler trap_handler_cleanup_workdir
LOG_SECTION="prepare_tmpfs_workdir" do_with_logging prepare_tmpfs_for "WORKDIR" "${WORKDIR}" # this adds its own cleanup handler, which deletes it if it was created
add_cleanup_handler trap_handler_cleanup_workdir # this is for when it is NOT a tmpfs, for any reason; it does not delete the dir itself.
export TMPDIR="${WORKDIR}"
# '-x': export
declare -g -x TMPDIR="${WORKDIR}" # TMPDIR is default for a lot of stuff, but...
declare -g -x CCACHE_TEMPDIR="${WORKDIR}/ccache_tmp" # Export CCACHE_TEMPDIR, under Workdir, which is hopefully under tmpfs. Thanks @the-Going for this.
declare -g -x XDG_RUNTIME_DIR="${WORKDIR}/xdg_tmp" # XDG_RUNTIME_DIR is used by the likes of systemd/freedesktop centric apps.
start=$(date +%s)
@@ -247,10 +250,12 @@ function trap_handler_cleanup_workdir() {
unset TMPDIR
if [[ -d "${WORKDIR}" ]]; then
if [[ "${PRESERVE_WORKDIR}" != "yes" ]]; then
display_alert "Cleaning up WORKDIR" "$(du -h -s "$WORKDIR")" "debug"
rm -rf "${WORKDIR}"
display_alert "Cleaning up WORKDIR" "$(du -h -s "$WORKDIR")" "cleanup"
# Remove all files and directories in WORKDIR, but not WORKDIR itself.
rm -rf "${WORKDIR:?}"/* # Note this is protected by :?
else
display_alert "Preserving WORKDIR due to PRESERVE_WORKDIR=yes" "$(du -h -s "$WORKDIR")" "warn"
# @TODO: tmpfs might just be unmounted, though.
fi
fi
}

View File

@@ -550,6 +550,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true
# shellcheck source=lib/functions/host/prepare-host.sh
source "${SRC}"/lib/functions/host/prepare-host.sh
# no errors tolerated. invoked before each sourced file to make sure.
#set -o pipefail # trace ERR through pipes - will be enabled "soon"
#set -o nounset ## set -u : exit the script if you try to use an uninitialised variable - one day will be enabled
set -o errtrace # trace ERR through - enabled
set -o errexit ## set -e : exit the script if any statement returns a non-true return value - enabled
### lib/functions/host/tmpfs-utils.sh
# shellcheck source=lib/functions/host/tmpfs-utils.sh
source "${SRC}"/lib/functions/host/tmpfs-utils.sh
# no errors tolerated. invoked before each sourced file to make sure.
#set -o pipefail # trace ERR through pipes - will be enabled "soon"
#set -o nounset ## set -u : exit the script if you try to use an uninitialised variable - one day will be enabled