build/lib/functions/cli/entrypoint.sh

183 lines
10 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
#
# SPDX-License-Identifier: GPL-2.0
#
# Copyright (c) 2013-2023 Igor Pecovnik, igor@armbian.com
#
# This file is a part of the Armbian Build Framework
# https://github.com/armbian/build/
function cli_entrypoint() {
# array, readonly, global, for future reference, "exported" to shutup shellcheck
declare -rg -x -a ARMBIAN_ORIGINAL_ARGV=("${@}")
if [[ "${ARMBIAN_ENABLE_CALL_TRACING}" == "yes" ]]; then
set -T # inherit return/debug traps
mkdir -p "${SRC}"/output/call-traces
echo -n "" > "${SRC}"/output/call-traces/calls.txt
# See https://www.gnu.org/software/bash/manual/html_node/Bash-Variables.html
trap 'echo "${FUNCNAME[*]}|${BASH_LINENO[*]}|${BASH_SOURCE[*]}|${LINENO}" >> ${SRC}/output/call-traces/calls.txt ;' RETURN
fi
# @TODO: allow for a super-early userpatches/config-000.custom.conf.sh to be loaded, before anything else.
# This would allow for custom commands and interceptors.
# Decide what we're gonna do. We've a few hardcoded, 1st-argument "commands".
declare -g -A ARMBIAN_COMMANDS_TO_HANDLERS_DICT ARMBIAN_COMMANDS_TO_VARS_DICT
armbian_register_commands # this defines the above two dictionaries
# Process the command line, separating params (XX=YY) from non-params arguments.
# That way they can be set in any order.
declare -A -g ARMBIAN_PARSED_CMDLINE_PARAMS=() # A dict of PARAM=VALUE
declare -a -g ARMBIAN_NON_PARAM_ARGS=() # An array of all non-param arguments
parse_cmdline_params "${@}" # which fills the above vars.
# Now load the key=value pairs from cmdline into environment, before loading config or executing commands.
# This will be done _again_ later, to make sure cmdline params override config et al.
apply_cmdline_params_to_env "early" # which uses ARMBIAN_PARSED_CMDLINE_PARAMS
# From here on, no more ${1} or stuff. We've parsed it all into ARMBIAN_PARSED_CMDLINE_PARAMS or ARMBIAN_NON_PARAM_ARGS and ARMBIAN_COMMAND.
# Re-initialize logging, to take into account the new environment after parsing cmdline params.
logging_init
declare -a -g ARMBIAN_CONFIG_FILES=() # fully validated, complete paths to config files.
declare -g ARMBIAN_COMMAND_HANDLER="" ARMBIAN_COMMAND="" ARMBIAN_COMMAND_VARS="" # only valid command and handler will ever be set here.
declare -g ARMBIAN_HAS_UNKNOWN_ARG="no" # if any unknown params, bomb.
for argument in "${ARMBIAN_NON_PARAM_ARGS[@]}"; do # loop over all non-param arguments, find commands and configs.
parse_each_cmdline_arg_as_command_param_or_config "${argument}" # sets all the vars above
done
# More sanity checks.
# If unknowns, bail.
if [[ "${ARMBIAN_HAS_UNKNOWN_ARG}" == "yes" ]]; then
exit_with_error "Unknown arguments found. Please check the output above and fix them."
fi
# @TODO: Have a config that is always included? "${SRC}/userpatches/config-default.conf" ?
# If we don't have a command decided yet, use the undecided command.
if [[ "${ARMBIAN_COMMAND}" == "" ]]; then
display_alert "No command found, using default" "undecided" "debug"
ARMBIAN_COMMAND="undecided"
fi
# If we don't have a command at this stage, we should default either to 'build' or 'docker', depending on OS.
# Give the chosen command a chance to refuse running, or, even, change the final command to run.
# This allows for example the 'build' command to auto-launch under docker, even without specifying it.
# Also allows for launchers to keep themselves when re-launched, yet do something diferent. (eg: docker under docker does build).
# Or: build under Darwin does docker...
# each _pre_run can change the command and vars to run too, so do it in a loop until it stops changing.
declare -g ARMBIAN_CHANGE_COMMAND_TO="${ARMBIAN_COMMAND}"
while [[ "${ARMBIAN_CHANGE_COMMAND_TO}" != "" ]]; do
display_alert "Still a command to pre-run, this time:" "${ARMBIAN_CHANGE_COMMAND_TO}" "debug"
declare -g ARMBIAN_COMMAND_REQUIRE_BASIC_DEPS="no" # reset this before every pre_run, so only the last one wins.
ARMBIAN_COMMAND="${ARMBIAN_CHANGE_COMMAND_TO}"
armbian_prepare_cli_command_to_run "${ARMBIAN_COMMAND}"
ARMBIAN_CHANGE_COMMAND_TO=""
armbian_cli_pre_run_command
done
# IMPORTANT!!!: it is INVALID to relaunch compile.sh from here. It will cause logging mistakes.
# So the last possible moment to relaunch is in xxxxx_pre_run!
# Also form here, UUID will be generated, output created, logging enabled, etc.
# Init basic dirs.
declare -g -r DEST="${SRC}/output" USERPATCHES_PATH="${SRC}"/userpatches # DEST is the main output dir, and USERPATCHES_PATH is the userpatches dir. read-only.
mkdir -p "${DEST}" "${USERPATCHES_PATH}" # Create output and userpatches directory if not already there
display_alert "Output directory created! DEST:" "${DEST}" "debug"
# set unique mounting directory for this execution.
# basic deps, which include "uuidgen", will be installed _after_ this, so we gotta tolerate it not being there yet.
declare -g ARMBIAN_BUILD_UUID
if [[ "${ARMBIAN_BUILD_UUID}" != "" ]]; then
display_alert "Using passed-in ARMBIAN_BUILD_UUID" "${ARMBIAN_BUILD_UUID}" "debug"
else
if command -v uuidgen 1> /dev/null; then
ARMBIAN_BUILD_UUID="$(uuidgen)"
else
display_alert "uuidgen not found" "uuidgen not installed yet" "info"
ARMBIAN_BUILD_UUID="no-uuidgen-yet-${RANDOM}-$((1 + $RANDOM % 10))$((1 + $RANDOM % 10))$((1 + $RANDOM % 10))$((1 + $RANDOM % 10))"
fi
display_alert "Generated ARMBIAN_BUILD_UUID" "${ARMBIAN_BUILD_UUID}" "debug"
fi
declare -g -r ARMBIAN_BUILD_UUID="${ARMBIAN_BUILD_UUID}" # Make read-only
display_alert "Build UUID:" "${ARMBIAN_BUILD_UUID}" "debug"
# Super-global variables, used everywhere. The directories are NOT _created_ here, since this very early stage. They are all readonly, for sanity.
declare -g -r WORKDIR_BASE_TMP="${SRC}/.tmp" # a.k.a. ".tmp" dir. it is a shared base dir for all builds, but each build gets its own WORKDIR/TMPDIR.
declare -g -r WORKDIR="${WORKDIR_BASE_TMP}/work-${ARMBIAN_BUILD_UUID}" # WORKDIR at this stage. It will become TMPDIR later. It has special significance to `mktemp` and others!
declare -g -r LOGDIR="${WORKDIR_BASE_TMP}/logs-${ARMBIAN_BUILD_UUID}" # Will be initialized very soon, literally, below.
declare -g -r EXTENSION_MANAGER_TMP_DIR="${WORKDIR_BASE_TMP}/extensions-${ARMBIAN_BUILD_UUID}" # EXTENSION_MANAGER_TMP_DIR used to store extension-composed functions
# @TODO: These are used only by rootfs/image actual build, move there...
declare -g -r SDCARD="${WORKDIR_BASE_TMP}/rootfs-${ARMBIAN_BUILD_UUID}" # SDCARD (which is NOT an sdcard, but will be, maybe, one day) is where we work the rootfs before final imaging. "rootfs" stage.
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)
# 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/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
if [[ "${OFFLINE_WORK}" == "yes" ]]; then
display_alert "* " "You are working offline!"
display_alert "* " "Sources, time and host will not be checked"
else
# check and install the basic utilities;
LOG_SECTION="prepare_host_basic" do_with_logging prepare_host_basic # This includes the 'docker' case.
fi
fi
# Legacy. We used to source the extension manager here, but now it's included in the library.
# @TODO: a quick check on the globals in extensions.sh would get rid of this.
extension_manager_declare_globals
# Loop over the ARMBIAN_CONFIG_FILES array and source each. The order is important.
for config_file in "${ARMBIAN_CONFIG_FILES[@]}"; do
local config_filename="${config_file##*/}" config_dir="${config_file%/*}"
display_alert "Sourcing config file" "${config_filename}" "debug"
# use pushd/popd to change directory to the config file's directory, so that relative paths in the config file work.
pushd "${config_dir}" > /dev/null || exit_with_error "Failed to pushd to ${config_dir}"
# shellcheck source=/dev/null
LOG_SECTION="userpatches_config:${config_filename}" do_with_logging source "${config_file}"
# reset completely after sourcing config file
set -e
#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
popd > /dev/null || exit_with_error "Failed to popd from ${config_dir}"
# Apply the params received from the command line _again_ after running the config.
# This ensures that params take precedence over stuff possibly defined in the config.
apply_cmdline_params_to_env "after config '${config_filename}'" # which uses ARMBIAN_PARSED_CMDLINE_PARAMS
done
# Early check for deprecations
error_if_lib_tag_set # make sure users are not thrown off by using old parameter which does nothing anymore; explain
display_alert "Executing final CLI command" "${ARMBIAN_COMMAND}" "debug"
armbian_cli_run_command
display_alert "Done Executing final CLI command" "${ARMBIAN_COMMAND}" "debug"
# Build done, run the cleanup handlers explicitly.
# This zeroes out the list of cleanups, so it"s not done again when the main script exits normally and trap = 0 runs.
run_cleanup_handlers
}