diff --git a/lib/functions/cli/cli-build.sh b/lib/functions/cli/cli-build.sh index 77edf0413..69e2a891b 100644 --- a/lib/functions/cli/cli-build.sh +++ b/lib/functions/cli/cli-build.sh @@ -9,6 +9,25 @@ function cli_standard_build_run() { # configuration etc - it initializes the extension manager; handles its own logging sections prep_conf_main_build_single - # main_default_build_single() handles its own logging sections... - main_default_build_single + # the full build. It has its own logging sections. + do_with_default_build full_build_packages_rootfs_and_image + + # CLI-specific: how to redo the build. + if armbian_is_running_in_container; then + BUILD_CONFIG='docker' # @TODO: this is not true, CLI handles this differently, gotta ask the CLI; this whole thing is inconsistent + fi + + # Make it easy to repeat build by displaying build options used. Prepare array. @TODO this is inconsistent. Maybe something like the relaunch vars? + local -a repeat_args=("./compile.sh" "${BUILD_CONFIG}" " BRANCH=${BRANCH}") + [[ -n ${RELEASE} ]] && repeat_args+=("RELEASE=${RELEASE}") + [[ -n ${BUILD_MINIMAL} ]] && repeat_args+=("BUILD_MINIMAL=${BUILD_MINIMAL}") + [[ -n ${BUILD_DESKTOP} ]] && repeat_args+=("BUILD_DESKTOP=${BUILD_DESKTOP}") + [[ -n ${KERNEL_ONLY} ]] && repeat_args+=("KERNEL_ONLY=${KERNEL_ONLY}") + [[ -n ${KERNEL_CONFIGURE} ]] && repeat_args+=("KERNEL_CONFIGURE=${KERNEL_CONFIGURE}") + [[ -n ${DESKTOP_ENVIRONMENT} ]] && repeat_args+=("DESKTOP_ENVIRONMENT=${DESKTOP_ENVIRONMENT}") + [[ -n ${DESKTOP_ENVIRONMENT_CONFIG_NAME} ]] && repeat_args+=("DESKTOP_ENVIRONMENT_CONFIG_NAME=${DESKTOP_ENVIRONMENT_CONFIG_NAME}") + [[ -n ${DESKTOP_APPGROUPS_SELECTED} ]] && repeat_args+=("DESKTOP_APPGROUPS_SELECTED=\"${DESKTOP_APPGROUPS_SELECTED}\"") + [[ -n ${DESKTOP_APT_FLAGS_SELECTED} ]] && repeat_args+=("DESKTOP_APT_FLAGS_SELECTED=\"${DESKTOP_APT_FLAGS_SELECTED}\"") + [[ -n ${COMPRESS_OUTPUTIMAGE} ]] && repeat_args+=("COMPRESS_OUTPUTIMAGE=${COMPRESS_OUTPUTIMAGE}") + display_alert "Repeat Build Options" "${repeat_args[*]}" "ext" # * = expand array, space delimited, single-word. } diff --git a/lib/functions/cli/cli-patch.sh b/lib/functions/cli/cli-patch.sh index 2fb54330a..b328471ee 100644 --- a/lib/functions/cli/cli-patch.sh +++ b/lib/functions/cli/cli-patch.sh @@ -33,7 +33,7 @@ function cli_patch_kernel_run() { "${target_repo_url}" "kernel-${LINUXFAMILY}-${KERNEL_MAJOR_MINOR}:${target_branch}") - # Prepare the host and build kernel instead of main_default_build_single + # Prepare the host and build kernel; without using standard build prepare_host # This handles its own logging sections, and is possibly interactive. compile_kernel # This handles its own logging sections. diff --git a/lib/functions/cli/cli-rootfs.sh b/lib/functions/cli/cli-rootfs.sh new file mode 100644 index 000000000..93d409725 --- /dev/null +++ b/lib/functions/cli/cli-rootfs.sh @@ -0,0 +1,25 @@ +function cli_rootfs_pre_run() { + declare -g ARMBIAN_COMMAND_REQUIRE_BASIC_DEPS="yes" # Require prepare_host_basic to run before the command. + + # "gimme root on a Linux machine" + cli_standard_relaunch_docker_or_sudo +} + +function cli_rootfs_run() { + # configuration etc - it initializes the extension manager; handles its own logging sections + prep_conf_main_build_single + + # default build, but only invoke specific rootfs functions needed. It has its own logging sections. + do_with_default_build cli_rootfs_only_in_default_build +} + +# This is run inside do_with_default_build(), above. +function cli_rootfs_only_in_default_build() { + declare -i tmpfs_estimated_size # in MiB; set by prepare_rootfs_build_params_and_trap() + LOG_SECTION="prepare_rootfs_build_params_and_trap" do_with_logging prepare_rootfs_build_params_and_trap + + LOG_SECTION="calculate_rootfs_cache_id" do_with_logging calculate_rootfs_cache_id + + # "rootfs" CLI skips over a lot goes straight to create the rootfs. It doesn't check cache etc. + LOG_SECTION="create_new_rootfs_cache" do_with_logging create_new_rootfs_cache +} diff --git a/lib/functions/cli/commands.sh b/lib/functions/cli/commands.sh index f9f214a45..77c8d96ad 100644 --- a/lib/functions/cli/commands.sh +++ b/lib/functions/cli/commands.sh @@ -22,7 +22,9 @@ function armbian_register_commands() { ["build"]="standard_build" # implemented in cli_standard_build_pre_run and cli_standard_build_run ["distccd"]="distccd" # implemented in cli_distccd_pre_run and cli_distccd_run - # shortcuts, see vars set below + ["rootfs"]="rootfs" # implemented in cli_rootfs_pre_run and cli_rootfs_run + + # shortcuts, see vars set below. the use legacy single build, and try to control it via variables ["kernel"]="standard_build" ["u-boot"]="standard_build" ["uboot"]="standard_build" diff --git a/lib/functions/configuration/main-config.sh b/lib/functions/configuration/main-config.sh index 43ef818c7..4f33bf8ca 100644 --- a/lib/functions/configuration/main-config.sh +++ b/lib/functions/configuration/main-config.sh @@ -388,7 +388,6 @@ function do_extra_configuration() { display_alert "Done with do_extra_configuration" "do_extra_configuration" "debug" } -# This is called by main_default_build_single() but declared here for 'convenience' function write_config_summary_output_file() { local debug_dpkg_arch debug_uname debug_virt debug_src_mount debug_dpkg_arch="$(dpkg --print-architecture)" diff --git a/lib/functions/main/default-build.sh b/lib/functions/main/default-build.sh index e800f9fbe..28d9d26d1 100644 --- a/lib/functions/main/default-build.sh +++ b/lib/functions/main/default-build.sh @@ -1,8 +1,5 @@ -# This does NOT run under the logging manager. We should invoke the do_with_logging wrapper for -# strategic parts of this. Attention: almost everything does it's own logging. -function main_default_build_single() { - main_default_start_build # Has its own logging, prepares workdir, does prepare_host, aggregation, and - +# This does NOT run under the logging manager. +function full_build_packages_rootfs_and_image() { main_default_build_packages # has its own logging sections # requires aggregation # build rootfs, if not only kernel. Again, read "KERNEL_ONLY" as if it was "PACKAGES_ONLY" @@ -11,24 +8,10 @@ function main_default_build_single() { build_rootfs_and_image # old "debootstrap-ng"; has its own logging sections. display_alert "Done building image" "${BOARD}" "target-reached" fi - - main_default_end_build - - if armbian_is_running_in_container; then - BUILD_CONFIG='docker' # @TODO: this is not true, CLI handles this differently, gotta ask the CLI; this whole thing is inconsistent - fi - - # Make it easy to repeat build by displaying build options used. Prepare array. @TODO this is inconsistent. Maybe something like the relaunch vars? - local -a repeat_args=("./compile.sh" "${BUILD_CONFIG}" " BRANCH=${BRANCH}") - [[ -n ${RELEASE} ]] && repeat_args+=("RELEASE=${RELEASE}") - [[ -n ${BUILD_MINIMAL} ]] && repeat_args+=("BUILD_MINIMAL=${BUILD_MINIMAL}") - [[ -n ${BUILD_DESKTOP} ]] && repeat_args+=("BUILD_DESKTOP=${BUILD_DESKTOP}") - [[ -n ${KERNEL_ONLY} ]] && repeat_args+=("KERNEL_ONLY=${KERNEL_ONLY}") - [[ -n ${KERNEL_CONFIGURE} ]] && repeat_args+=("KERNEL_CONFIGURE=${KERNEL_CONFIGURE}") - [[ -n ${DESKTOP_ENVIRONMENT} ]] && repeat_args+=("DESKTOP_ENVIRONMENT=${DESKTOP_ENVIRONMENT}") - [[ -n ${DESKTOP_ENVIRONMENT_CONFIG_NAME} ]] && repeat_args+=("DESKTOP_ENVIRONMENT_CONFIG_NAME=${DESKTOP_ENVIRONMENT_CONFIG_NAME}") - [[ -n ${DESKTOP_APPGROUPS_SELECTED} ]] && repeat_args+=("DESKTOP_APPGROUPS_SELECTED=\"${DESKTOP_APPGROUPS_SELECTED}\"") - [[ -n ${DESKTOP_APT_FLAGS_SELECTED} ]] && repeat_args+=("DESKTOP_APT_FLAGS_SELECTED=\"${DESKTOP_APT_FLAGS_SELECTED}\"") - [[ -n ${COMPRESS_OUTPUTIMAGE} ]] && repeat_args+=("COMPRESS_OUTPUTIMAGE=${COMPRESS_OUTPUTIMAGE}") - display_alert "Repeat Build Options" "${repeat_args[*]}" "ext" # * = expand array, space delimited, single-word. +} + +function do_with_default_build() { + main_default_start_build # Has its own logging, prepares workdir, does prepare_host, aggregation, and + "${@}" + main_default_end_build } diff --git a/lib/functions/main/rootfs-image.sh b/lib/functions/main/rootfs-image.sh index 7a595736c..ed70c6751 100644 --- a/lib/functions/main/rootfs-image.sh +++ b/lib/functions/main/rootfs-image.sh @@ -2,52 +2,16 @@ function build_rootfs_and_image() { display_alert "Checking for rootfs cache" "$(echo "${BRANCH} ${BOARD} ${RELEASE} ${DESKTOP_APPGROUPS_SELECTED} ${DESKTOP_ENVIRONMENT} ${BUILD_MINIMAL}" | tr -s " ")" "info" - [[ $ROOTFS_TYPE != ext4 ]] && display_alert "Assuming ${BOARD} ${BRANCH} kernel supports ${ROOTFS_TYPE}" "" "wrn" - # add handler to cleanup when done or if something fails or is interrupted. - add_cleanup_handler trap_handler_cleanup_rootfs_and_image + declare -i tmpfs_estimated_size # in MiB; set by prepare_rootfs_build_params_and_trap() + prepare_rootfs_build_params_and_trap - # stage: clean and create directories - rm -rf "${SDCARD}" "${MOUNT}" - mkdir -p "${SDCARD}" "${MOUNT}" "${DEST}/images" "${SRC}/cache/rootfs" + # get a basic rootfs, either from cache or from scratch + build_rootfs_only - # bind mount rootfs if defined - if [[ -d "${ARMBIAN_CACHE_ROOTFS_PATH}" ]]; then - mountpoint -q "${SRC}"/cache/rootfs && umount "${SRC}"/cache/toolchain - mount --bind "${ARMBIAN_CACHE_ROOTFS_PATH}" "${SRC}/cache/rootfs" - fi - - # stage: verify tmpfs configuration and mount - # CLI needs ~2GiB, desktop ~5GiB - # vs 60% of "available" RAM (free + buffers + magic) - declare -i available_physical_memory_mib - available_physical_memory_mib=$(($(awk '/MemAvailable/ {print $2}' /proc/meminfo) * 6 / 1024 / 10)) # MiB - - # @TODO: well those are very... arbitrary numbers. At least when using cached rootfs, we can be more precise. - # predicting the size of tmpfs is hard/impossible, so would be nice to show the used size at the end so we can tune. - declare -i tmpfs_estimated_size=2000 # MiB - [[ $BUILD_DESKTOP == yes ]] && tmpfs_estimated_size=5000 # MiB - - declare use_tmpfs=no # by default - if [[ ${FORCE_USE_RAMDISK} == no ]]; then # do not use, even if it fits - display_alert "Not using tmpfs for rootfs" "due to FORCE_USE_RAMDISK=no" "info" - elif [[ ${FORCE_USE_RAMDISK} == yes || ${available_physical_memory_mib} -gt ${tmpfs_estimated_size} ]]; then # use, either force or fits - use_tmpfs=yes - display_alert "Using tmpfs for rootfs build" "RAM available: ${available_physical_memory_mib}MiB > ${tmpfs_estimated_size}MiB estimated" "info" - else - display_alert "Not using tmpfs for rootfs" "RAM available: ${available_physical_memory_mib}MiB < ${tmpfs_estimated_size}MiB estimated" "info" - fi - - if [[ $use_tmpfs == yes ]]; then - declare -g -r ROOTFS_IS_UNDER_TMPFS=yes - mount -t tmpfs tmpfs "${SDCARD}" # do not specify size; we've calculated above that it should fit, and Linux will try its best if it doesn't. - fi - - # stage: prepare basic rootfs: unpack cache or create from scratch - LOG_SECTION="get_or_create_rootfs_cache_chroot_sdcard" do_with_logging get_or_create_rootfs_cache_chroot_sdcard - - mount_chroot "${SDCARD}" # this used to be inside get_or_create_rootfs_cache_chroot_sdcard(). + # stage: with a basic rootfs available, we mount the chroot and work on it + mount_chroot "${SDCARD}" call_extension_method "pre_install_distribution_specific" "config_pre_install_distribution_specific" <<- 'PRE_INSTALL_DISTRIBUTION_SPECIFIC' *give config a chance to act before install_distribution_specific* @@ -90,7 +54,7 @@ function build_rootfs_and_image() { # warn if rootfs_size_mib is higher than the tmpfs_estimated_size if [[ ${rootfs_size_mib} -gt ${tmpfs_estimated_size} ]]; then - display_alert "Rootfs actual size is larger than estimated tmpfs size" "${rootfs_size_mib}MiB > ${tmpfs_estimated_size}MiB" "wrn" + display_alert "Rootfs post-tweaks size is larger than estimated tmpfs size" "${rootfs_size_mib}MiB > ${tmpfs_estimated_size}MiB" "wrn" fi # ------------------------------------ UP HERE IT's 'rootfs' stuff ------------------------------- @@ -106,13 +70,13 @@ function build_rootfs_and_image() { LOG_SECTION="create_image_from_sdcard_rootfs" do_with_logging create_image_from_sdcard_rootfs fi - # Completely and recursively unmount the directory. This will remove the tmpfs mount too + # Completely and recursively unmount the directory. --> This will remove the tmpfs mount too <-- umount_chroot_recursive "${SDCARD}" "SDCARD rootfs finished" # Remove the dir [[ -d "${SDCARD}" ]] && rm -rf --one-file-system "${SDCARD}" - # Run the cleanup handler. @TODO: this already does the above, so can be simpler. + # Run the cleanup handler. @TODO: this already does the above, so can be simpler. @TODO: don't forget to split MOUNT/SDCARD trap execute_and_remove_cleanup_handler trap_handler_cleanup_rootfs_and_image return 0 @@ -123,38 +87,3 @@ function list_installed_packages() { LOG_ASSET="installed_packages.txt" do_with_log_asset chroot_sdcard dpkg --get-selections "| grep -v deinstall | awk '{print \$1}' | cut -f1 -d':'" } -function trap_handler_cleanup_rootfs_and_image() { - display_alert "Cleanup for rootfs and image" "trap_handler_cleanup_rootfs_and_image" "cleanup" - - debug_tmpfs_show_usage "before cleanup of rootfs" - - cd "${SRC}" || echo "Failed to cwd to ${SRC}" # Move pwd away, so unmounts work - # those will loop until they're unmounted. - umount_chroot_recursive "${SDCARD}" "SDCARD" || true - umount_chroot_recursive "${MOUNT}" "MOUNT" || true - - # unmount tmpfs mounted on SDCARD if it exists. #@TODO: move to new tmpfs-utils scheme - mountpoint -q "${SDCARD}" && umount "${SDCARD}" - - # @TODO: rpardini: igor: why lazy umounts? - mountpoint -q "${SRC}"/cache/toolchain && umount -l "${SRC}"/cache/toolchain >&2 - mountpoint -q "${SRC}"/cache/rootfs && umount -l "${SRC}"/cache/rootfs >&2 - [[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose "${ROOT_MAPPER}" >&2 - - if [[ "${PRESERVE_SDCARD_MOUNT}" == "yes" ]]; then - display_alert "Preserving SD card mount" "trap_handler_cleanup_rootfs_and_image" "warn" - return 0 - fi - - # shellcheck disable=SC2153 # global var. - if [[ -b "${LOOP}" ]]; then - display_alert "Freeing loop" "trap_handler_cleanup_rootfs_and_image ${LOOP}" "wrn" - free_loop_device_insistent "${LOOP}" || true - fi - - [[ -d "${SDCARD}" ]] && rm -rf --one-file-system "${SDCARD}" - [[ -d "${MOUNT}" ]] && rm -rf --one-file-system "${MOUNT}" - [[ -f "${SDCARD}".raw ]] && rm -f "${SDCARD}".raw - - return 0 # short-circuit above, so exit clean here -} diff --git a/lib/functions/main/start-end.sh b/lib/functions/main/start-end.sh index 161f11c1d..964c31126 100644 --- a/lib/functions/main/start-end.sh +++ b/lib/functions/main/start-end.sh @@ -1,4 +1,4 @@ -# Common start/end build functions. Used by main_default_build_single() +# Common start/end build functions. Used by the default build and others function main_default_start_build() { wait_for_disk_sync "before starting build" # fsync, wait for disk to sync, and then continue. alert user if takes too long. diff --git a/lib/functions/rootfs/create-cache.sh b/lib/functions/rootfs/create-cache.sh index 3cba24a34..4925cb864 100644 --- a/lib/functions/rootfs/create-cache.sh +++ b/lib/functions/rootfs/create-cache.sh @@ -1,6 +1,39 @@ #!/usr/bin/env bash -# this gets from cache or produces a new rootfs, and leaves a mounted chroot "$SDCARD" at the end. +function build_rootfs_only() { + # validate that tmpfs_estimated_size is set and higher than zero, or exit_with_error + [[ -z ${tmpfs_estimated_size} ]] && exit_with_error "tmpfs_estimated_size is not set" + [[ ${tmpfs_estimated_size} -le 0 ]] && exit_with_error "tmpfs_estimated_size is not higher than zero" + + # stage: prepare basic rootfs: unpack cache or create from scratch + LOG_SECTION="get_or_create_rootfs_cache_chroot_sdcard" do_with_logging get_or_create_rootfs_cache_chroot_sdcard + + # obtain the size, in MiB, of "${SDCARD}" at this point. + declare -i rootfs_size_mib + rootfs_size_mib=$(du -sm "${SDCARD}" | awk '{print $1}') + display_alert "Actual rootfs size" "${rootfs_size_mib}MiB after basic/cache" "" + + # warn if rootfs_size_mib is higher than the tmpfs_estimated_size + if [[ ${rootfs_size_mib} -gt ${tmpfs_estimated_size} ]]; then + display_alert "Rootfs actual size is larger than estimated tmpfs size after basic/cache" "${rootfs_size_mib}MiB > ${tmpfs_estimated_size}MiB" "wrn" + fi +} + +function calculate_rootfs_cache_id() { + # Validate that AGGREGATED_ROOTFS_HASH is set + [[ -z "${AGGREGATED_ROOTFS_HASH}" ]] && exit_with_error "AGGREGATED_ROOTFS_HASH is not set at calculate_rootfs_cache_id()" + + declare -g packages_hash="${AGGREGATED_ROOTFS_HASH}" # Produced by aggregation.py - currently only AGGREGATED_PACKAGES_DEBOOTSTRAP and AGGREGATED_PACKAGES_ROOTFS + declare -g -r packages_hash=${packages_hash:0:16} # it's an md5, which is 32 hex digits; used to be 8; make readonly + + declare -g cache_type="cli" + [[ ${BUILD_DESKTOP} == yes ]] && cache_type="xfce-desktop" + [[ -n ${DESKTOP_ENVIRONMENT} ]] && cache_type="${DESKTOP_ENVIRONMENT}" + [[ ${BUILD_MINIMAL} == yes ]] && cache_type="minimal" + declare -g -r cache_type="${cache_type}" +} + +# this gets from cache or produces a basic new rootfs, ready, but not mounted, at "$SDCARD" function get_or_create_rootfs_cache_chroot_sdcard() { # validate "${SDCARD}" is set. it does not exist, yet... if [[ -z "${SDCARD}" ]]; then @@ -28,20 +61,13 @@ function get_or_create_rootfs_cache_chroot_sdcard() { else display_alert "ROOTFSCACHE_VERSION is set externally" "${ROOTFSCACHE_VERSION}" "warn" fi - + # Make ROOTFSCACHE_VERSION global at this point, in case it was not. declare -g ROOTFSCACHE_VERSION="${ROOTFSCACHE_VERSION}" display_alert "ROOTFSCACHE_VERSION found online or preset" "${ROOTFSCACHE_VERSION}" "warn" - local packages_hash="${AGGREGATED_ROOTFS_HASH}" # Produced by aggregation.py - currently only AGGREGATED_PACKAGES_DEBOOTSTRAP and AGGREGATED_PACKAGES_ROOTFS - local packages_hash=${packages_hash:0:16} # it's an md5, which is 32 hex digits; used to be 8 - - local cache_type="cli" - [[ ${BUILD_DESKTOP} == yes ]] && cache_type="xfce-desktop" - [[ -n ${DESKTOP_ENVIRONMENT} ]] && cache_type="${DESKTOP_ENVIRONMENT}" - [[ ${BUILD_MINIMAL} == yes ]] && cache_type="minimal" - + calculate_rootfs_cache_id # this sets packages_hash and cache_type # seek last cache, proceed to previous otherwise build it local cache_list readarray -t cache_list <<< "$(get_rootfs_cache_list "$cache_type" "$packages_hash" | sort -r)" @@ -108,11 +134,15 @@ function get_or_create_rootfs_cache_chroot_sdcard() { function create_new_rootfs_cache() { [[ ! -d "${SDCARD:?}" ]] && exit_with_error "create_new_rootfs_cache: ${SDCARD} is not a directory" + # validate cache_type is set + [[ -n "${cache_type}" ]] || exit_with_error "create_new_rootfs_cache: cache_type is not set" + # validate packages_hash is set + [[ -n "${packages_hash}" ]] || exit_with_error "create_new_rootfs_cache: packages_hash is not set" # This var ROOT_FS_CREATE_VERSION is only used here, afterwards it's all cache_name and cache_fname - local ROOT_FS_CREATE_VERSION=${ROOT_FS_CREATE_VERSION:-$(date --utc +"%Y%m%d")} - local cache_name=${ARCH}-${RELEASE}-${cache_type}-${packages_hash}-${ROOT_FS_CREATE_VERSION}.tar.zst - local cache_fname=${SRC}/cache/rootfs/${cache_name} + declare ROOT_FS_CREATE_VERSION="${ROOT_FS_CREATE_VERSION:-"$(date --utc +"%Y%m%d")"}" + declare cache_name=${ARCH}-${RELEASE}-${cache_type}-${packages_hash}-${ROOT_FS_CREATE_VERSION}.tar.zst + declare cache_fname=${SRC}/cache/rootfs/${cache_name} display_alert "Creating new rootfs cache for" "${RELEASE} ${ROOT_FS_CREATE_VERSION}" "info" diff --git a/lib/functions/rootfs/trap-rootfs.sh b/lib/functions/rootfs/trap-rootfs.sh new file mode 100644 index 000000000..98ead6e95 --- /dev/null +++ b/lib/functions/rootfs/trap-rootfs.sh @@ -0,0 +1,78 @@ +## Prepare/cleanup pair @TODO needs to be split between SDCARD and MOUNT, no sense doing both in rootfs trap anymore +function prepare_rootfs_build_params_and_trap() { + # add handler to cleanup when done or if something fails or is interrupted. + add_cleanup_handler trap_handler_cleanup_rootfs_and_image + + # stage: clean and create directories + run_host_command_logged rm -rfv "${SDCARD}" "${MOUNT}" + run_host_command_logged mkdir -pv "${SDCARD}" "${MOUNT}" "${SRC}/cache/rootfs" "${DEST}/images" # @TODO images shouldn't be here up + + # bind mount rootfs if defined # @TODO: is this used? it is never unmounted + if [[ -d "${ARMBIAN_CACHE_ROOTFS_PATH}" ]]; then + display_alert "Warning, using untested code path" "ARMBIAN_CACHE_ROOTFS_PATH" "warn" + mountpoint -q "${SRC}"/cache/rootfs && umount "${SRC}"/cache/rootfs + mount --bind "${ARMBIAN_CACHE_ROOTFS_PATH}" "${SRC}/cache/rootfs" + fi + + # stage: verify tmpfs configuration and mount + # CLI needs ~2GiB, desktop ~5GiB + # vs 60% of "available" RAM (free + buffers + magic) + declare -i available_physical_memory_mib + available_physical_memory_mib=$(($(awk '/MemAvailable/ {print $2}' /proc/meminfo) * 6 / 1024 / 10)) # MiB + + # @TODO: well those are very... arbitrary numbers. At least when using cached rootfs, we can be more precise. + # predicting the size of tmpfs is hard/impossible, so would be nice to show the used size at the end so we can tune. + declare -i tmpfs_estimated_size=2000 # MiB + [[ $BUILD_DESKTOP == yes ]] && tmpfs_estimated_size=5000 # MiB + + declare use_tmpfs=no # by default + if [[ ${FORCE_USE_RAMDISK} == no ]]; then # do not use, even if it fits + display_alert "Not using tmpfs for rootfs" "due to FORCE_USE_RAMDISK=no" "info" + elif [[ ${FORCE_USE_RAMDISK} == yes || ${available_physical_memory_mib} -gt ${tmpfs_estimated_size} ]]; then # use, either force or fits + use_tmpfs=yes + display_alert "Using tmpfs for rootfs build" "RAM available: ${available_physical_memory_mib}MiB > ${tmpfs_estimated_size}MiB estimated" "info" + else + display_alert "Not using tmpfs for rootfs" "RAM available: ${available_physical_memory_mib}MiB < ${tmpfs_estimated_size}MiB estimated" "info" + fi + + if [[ $use_tmpfs == yes ]]; then + declare -g -r ROOTFS_IS_UNDER_TMPFS=yes + mount -t tmpfs tmpfs "${SDCARD}" # do not specify size; we've calculated above that it should fit, and Linux will try its best if it doesn't. + fi +} + +function trap_handler_cleanup_rootfs_and_image() { + display_alert "Cleanup for rootfs and image" "trap_handler_cleanup_rootfs_and_image" "cleanup" + + debug_tmpfs_show_usage "before cleanup of rootfs" + + cd "${SRC}" || echo "Failed to cwd to ${SRC}" # Move pwd away, so unmounts work + # those will loop until they're unmounted. + umount_chroot_recursive "${SDCARD}" "SDCARD" || true + umount_chroot_recursive "${MOUNT}" "MOUNT" || true + + # unmount tmpfs mounted on SDCARD if it exists. #@TODO: move to new tmpfs-utils scheme + mountpoint -q "${SDCARD}" && umount "${SDCARD}" + + # @TODO: rpardini: igor: why lazy umounts? + mountpoint -q "${SRC}"/cache/toolchain && umount -l "${SRC}"/cache/toolchain >&2 + mountpoint -q "${SRC}"/cache/rootfs && umount -l "${SRC}"/cache/rootfs >&2 + [[ $CRYPTROOT_ENABLE == yes ]] && cryptsetup luksClose "${ROOT_MAPPER}" >&2 + + if [[ "${PRESERVE_SDCARD_MOUNT}" == "yes" ]]; then + display_alert "Preserving SD card mount" "trap_handler_cleanup_rootfs_and_image" "warn" + return 0 + fi + + # shellcheck disable=SC2153 # global var. + if [[ -b "${LOOP}" ]]; then + display_alert "Freeing loop" "trap_handler_cleanup_rootfs_and_image ${LOOP}" "wrn" + free_loop_device_insistent "${LOOP}" || true + fi + + [[ -d "${SDCARD}" ]] && rm -rf --one-file-system "${SDCARD}" + [[ -d "${MOUNT}" ]] && rm -rf --one-file-system "${MOUNT}" + [[ -f "${SDCARD}".raw ]] && rm -f "${SDCARD}".raw + + return 0 # short-circuit above, so exit clean here +} diff --git a/lib/library-functions.sh b/lib/library-functions.sh index fe58b06a0..3e37ce2a0 100644 --- a/lib/library-functions.sh +++ b/lib/library-functions.sh @@ -91,6 +91,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true # shellcheck source=lib/functions/cli/cli-requirements.sh source "${SRC}"/lib/functions/cli/cli-requirements.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/cli/cli-rootfs.sh +# shellcheck source=lib/functions/cli/cli-rootfs.sh +source "${SRC}"/lib/functions/cli/cli-rootfs.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 @@ -766,6 +775,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true # shellcheck source=lib/functions/logging/traps.sh source "${SRC}"/lib/functions/logging/traps.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/main/build-packages.sh +# shellcheck source=lib/functions/main/build-packages.sh +source "${SRC}"/lib/functions/main/build-packages.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 @@ -802,6 +820,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true # shellcheck source=lib/functions/main/rootfs-image.sh source "${SRC}"/lib/functions/main/rootfs-image.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/main/start-end.sh +# shellcheck source=lib/functions/main/start-end.sh +source "${SRC}"/lib/functions/main/start-end.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 @@ -883,6 +910,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true # shellcheck source=lib/functions/rootfs/qemu-static.sh source "${SRC}"/lib/functions/rootfs/qemu-static.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/rootfs/rootfs-create.sh +# shellcheck source=lib/functions/rootfs/rootfs-create.sh +source "${SRC}"/lib/functions/rootfs/rootfs-create.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 @@ -901,6 +937,15 @@ set -o errexit ## set -e : exit the script if any statement returns a non-true # shellcheck source=lib/functions/rootfs/systemd-utils.sh source "${SRC}"/lib/functions/rootfs/systemd-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 +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/rootfs/trap-rootfs.sh +# shellcheck source=lib/functions/rootfs/trap-rootfs.sh +source "${SRC}"/lib/functions/rootfs/trap-rootfs.sh + # no errors tolerated. one last time for the win! #set -o pipefail # trace ERR through pipes - will be enabled "soon"