From d3fe25700dacfaf82d5dde24b4b221e3fcda5240 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 19 Nov 2024 07:47:12 +0500 Subject: [PATCH 01/28] scripts(.editorconfig): use space indentation for `properties.sh` --- .editorconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index a10ef343592c4f4..ed7c6fef02da2c5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -34,6 +34,10 @@ indent_size = 2 [ndk-patches/*.h] indent_style = tab +[scripts/properties.sh] +indent_style = space +indent_size = 4 + [scripts/utils/**.sh] indent_style = space indent_size = 4 From 96eefda51fab91b75afcb530375d9cb3f0340736 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Fri, 21 Mar 2025 23:13:57 +0500 Subject: [PATCH 02/28] enhance(scripts/properties.sh): add all standardized Termux constants as scoped variables for different Termux runtime components that are to be shared between Termux packages, apps, site and `termux-exec` and add validation framework The variables are scoped under `TERMUX__` and `TERMUX_APP__` (note two underscores `__`). Alternative variables `TERMUX_APP_PACKAGE`, `TERMUX_CACHE_DIR`, `TERMUX_BASE_DIR`, `TERMUX_PREFIX`, `TERMUX_PREFIX_CLASSICAL, `TERMUX_ANDROID_HOME` are still kept and will be used for now until we can move to new scoped variables and add them to variable replacements for source and patch files. Check https://github.com/termux/termux-core-package/blob/master/site/pages/en/projects/docs/usage/utils/termux-scoped-env-variable.md for more info. The validation of variables is necessary as the Termux packages build framework is a mess and uses unquoted variables everywhere, which could have dangerous consequences. Additionally, forks may make changes that may not be supported or are invalid, so any changes must be checked as well. `termux-am-socket` uses `TERMUX_APPS_DIR` currently, but will use `TERMUX__APPS_DIR` when Termux app and `termux-am-socket` package sources get updated. The duplicate check for build time `TERMUX_PREFIX` variable against hardcoded `/data/data/com.termux/files/usr` path has been removed to check whether packages in repo can be used as build dependencies. Previously `TERMUX_REPO_PACKAGE` was checked against `TERMUX_APP_PACKAGE` to see if it was possible, now `TERMUX_REPO_APP__PACKAGE_NAME` will be used instead. The `TERMUX_REPO__APPS_DIR`, `TERMUX_REPO__ROOTFS`, `TERMUX_REPO__HOME` and `TERMUX_REPO__PREFIX` variables will also be checked in a future when repo changes are completed. This extensive checking against current built time variables will ensure compatibility for packages hosted in the repo specified so that packages are only used if compatible. The `TERMUX_ENV__S_` scoped variables will also be replaced in patch files. `properties.sh` must now be run from a `bash` shell. --- build-package.sh | 14 +- scripts/bin/check-pie.sh | 2 +- ...-termux-bootstrap-second-stage-fallback.sh | 8 +- .../termux-bootstrap-second-stage.sh | 6 +- scripts/build-bootstraps.sh | 17 +- scripts/build/termux_download_deb_pac.sh | 4 +- scripts/build/termux_step_patch_package.sh | 4 + .../termux_step_setup_cgct_environment.sh | 2 +- scripts/generate-bootstraps.sh | 17 +- scripts/properties.sh | 2201 ++++++++++++++++- 10 files changed, 2198 insertions(+), 77 deletions(-) diff --git a/build-package.sh b/build-package.sh index e57f912fe5d78f1..e1cb93f3a2f3baa 100755 --- a/build-package.sh +++ b/build-package.sh @@ -492,19 +492,13 @@ while (($# >= 1)); do -i) if [ "$TERMUX_ON_DEVICE_BUILD" = "true" ]; then termux_error_exit "./build-package.sh: option '-i' is not available for on-device builds" - elif [ "$TERMUX_PREFIX" != "/data/data/com.termux/files/usr" ]; then - termux_error_exit "./build-package.sh: option '-i' is available only when TERMUX_APP_PACKAGE is 'com.termux'" else export TERMUX_INSTALL_DEPS=true fi ;; -I) - if [ "$TERMUX_PREFIX" != "/data/data/com.termux/files/usr" ]; then - termux_error_exit "./build-package.sh: option '-I' is available only when TERMUX_APP_PACKAGE is 'com.termux'" - else - export TERMUX_INSTALL_DEPS=true - export TERMUX_NO_CLEAN=true - fi + export TERMUX_INSTALL_DEPS=true + export TERMUX_NO_CLEAN=true ;; -L) export TERMUX_GLOBAL_LIBRARY=true;; -q) export TERMUX_QUIET_BUILD=true;; @@ -533,8 +527,8 @@ unset -f _show_usage # Dependencies should be used from repo only if they are built for # same package name. -if [ "$TERMUX_REPO_PACKAGE" != "$TERMUX_APP_PACKAGE" ]; then - echo "Ignoring -i option to download dependencies since repo package name ($TERMUX_REPO_PACKAGE) does not equal app package name ($TERMUX_APP_PACKAGE)" +if [ "$TERMUX_REPO_APP__PACKAGE_NAME" != "$TERMUX_APP_PACKAGE" ]; then + echo "Ignoring -i option to download dependencies since repo package name ($TERMUX_REPO_APP__PACKAGE_NAME) does not equal app package name ($TERMUX_APP_PACKAGE)" TERMUX_INSTALL_DEPS=false fi diff --git a/scripts/bin/check-pie.sh b/scripts/bin/check-pie.sh index 3eff38d42dc8385..eccb566abb2b726 100755 --- a/scripts/bin/check-pie.sh +++ b/scripts/bin/check-pie.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash # check-pie.sh - script to detect non-PIE binaries (which does not work on Android) . $(dirname "$(realpath "$0")")/properties.sh diff --git a/scripts/bootstrap/01-termux-bootstrap-second-stage-fallback.sh b/scripts/bootstrap/01-termux-bootstrap-second-stage-fallback.sh index ec3c4cb60e3a97b..0262cebf2b56e6b 100644 --- a/scripts/bootstrap/01-termux-bootstrap-second-stage-fallback.sh +++ b/scripts/bootstrap/01-termux-bootstrap-second-stage-fallback.sh @@ -17,13 +17,13 @@ # as then second stage will run again when new shell is started # which may affect already configured packages. # The shell should still load if second stage run below fails. - if [ ! -L "@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@/termux-bootstrap-second-stage.sh.lock" ]; then + if [ ! -L "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock" ]; then echo "Starting fallback run of termux bootstrap second stage" - chmod +x "@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@/termux-bootstrap-second-stage.sh" || exit $? - "@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@/termux-bootstrap-second-stage.sh" || exit $? + chmod +x "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh" || exit $? + "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh" || exit $? fi # Delete script itself so that it is never run again - rm -f "@TERMUX_PROFILE_D_PREFIX_DIR_PATH@/01-termux-bootstrap-second-stage-fallback.sh" || exit $? + rm -f "@TERMUX__PREFIX__PROFILE_D_DIR@/01-termux-bootstrap-second-stage-fallback.sh" || exit $? ) || return $? diff --git a/scripts/bootstrap/termux-bootstrap-second-stage.sh b/scripts/bootstrap/termux-bootstrap-second-stage.sh index 84b490f3f26336b..00d0a8eb65097fb 100755 --- a/scripts/bootstrap/termux-bootstrap-second-stage.sh +++ b/scripts/bootstrap/termux-bootstrap-second-stage.sh @@ -63,7 +63,7 @@ previous failure and it must be re-run again for testing, then delete the lock file manually and run `termux-bootstrap-second-stage.sh` again. -**See Also:** +**See also:** - https://github.com/termux/termux-packages/wiki/For-maintainers#bootstraps HELP_EOF @@ -93,11 +93,11 @@ run_bootstrap_second_stage() { local return_value if ! ln -s "termux-bootstrap-second-stage.sh" \ - "@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@/termux-bootstrap-second-stage.sh.lock" 2>/dev/null; then + "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock" 2>/dev/null; then log "The termux bootstrap second stage has already been run before and cannot be run again." log "If you still want to force run it again (not recommended), \ like in case of previous failure and it must be re-run again for testing, \ -then delete the '@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@/termux-bootstrap-second-stage.sh.lock' \ +then delete the '@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock' \ file manually and run 'termux-bootstrap-second-stage.sh' again." return 0 fi diff --git a/scripts/build-bootstraps.sh b/scripts/build-bootstraps.sh index d7eda1ce92c1fa9..137233f20b06281 100755 --- a/scripts/build-bootstraps.sh +++ b/scripts/build-bootstraps.sh @@ -206,21 +206,22 @@ add_termux_bootstrap_second_stage_files() { echo $'\n\n\n'"[*] Adding termux bootstrap second stage files..." - mkdir -p "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}" + mkdir -p "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}" sed -e "s|@TERMUX_PREFIX@|${TERMUX_PREFIX}|g" \ - -e "s|@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@|${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}|g" \ + -e "s|@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@|${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}|g" \ -e "s|@TERMUX_PACKAGE_MANAGER@|${TERMUX_PACKAGE_MANAGER}|g" \ -e "s|@TERMUX_PACKAGE_ARCH@|${package_arch}|g" \ + -e "s|@TERMUX_APP__NAME@|${TERMUX_APP__NAME}|g" \ "$TERMUX_SCRIPTDIR/scripts/bootstrap/termux-bootstrap-second-stage.sh" \ - > "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}/termux-bootstrap-second-stage.sh" - chmod 700 "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}/termux-bootstrap-second-stage.sh" + > "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" + chmod 700 "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" # TODO: Remove it when Termux app supports `pacman` bootstraps installation. - sed -e "s|@TERMUX_PROFILE_D_PREFIX_DIR_PATH@|${TERMUX_PROFILE_D_PREFIX_DIR_PATH}|g" \ - -e "s|@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@|${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}|g" \ + sed -e "s|@TERMUX__PREFIX__PROFILE_D_DIR@|${TERMUX__PREFIX__PROFILE_D_DIR}|g" \ + -e "s|@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@|${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}|g" \ "$TERMUX_SCRIPTDIR/scripts/bootstrap/01-termux-bootstrap-second-stage-fallback.sh" \ - > "${BOOTSTRAP_ROOTFS}/${TERMUX_PROFILE_D_PREFIX_DIR_PATH}/01-termux-bootstrap-second-stage-fallback.sh" - chmod 600 "${BOOTSTRAP_ROOTFS}/${TERMUX_PROFILE_D_PREFIX_DIR_PATH}/01-termux-bootstrap-second-stage-fallback.sh" + > "${BOOTSTRAP_ROOTFS}/${TERMUX__PREFIX__PROFILE_D_DIR}/01-termux-bootstrap-second-stage-fallback.sh" + chmod 600 "${BOOTSTRAP_ROOTFS}/${TERMUX__PREFIX__PROFILE_D_DIR}/01-termux-bootstrap-second-stage-fallback.sh" } diff --git a/scripts/build/termux_download_deb_pac.sh b/scripts/build/termux_download_deb_pac.sh index 9dd53027161a080..cd94a03bc72d0c6 100755 --- a/scripts/build/termux_download_deb_pac.sh +++ b/scripts/build/termux_download_deb_pac.sh @@ -19,8 +19,8 @@ termux_download_deb_pac() { # The data.tar.xz extraction by termux_step_get_dependencies would # extract files to different prefix than TERMUX_PREFIX and builds # would fail when looking for -I$TERMUX_PREFIX/include files. - if [ "$TERMUX_REPO_PACKAGE" != "$TERMUX_APP_PACKAGE" ]; then - echo "Ignoring download of $PKG_FILE since repo package name ($TERMUX_REPO_PACKAGE) does not equal app package name ($TERMUX_APP_PACKAGE)" + if [ "$TERMUX_REPO_APP__PACKAGE_NAME" != "$TERMUX_APP_PACKAGE" ]; then + echo "Ignoring download of $PKG_FILE since repo package name ($TERMUX_REPO_APP__PACKAGE_NAME) does not equal app package name ($TERMUX_APP_PACKAGE)" return 1 fi diff --git a/scripts/build/termux_step_patch_package.sh b/scripts/build/termux_step_patch_package.sh index df1082d445d2887..f92eac188324c3e 100644 --- a/scripts/build/termux_step_patch_package.sh +++ b/scripts/build/termux_step_patch_package.sh @@ -26,6 +26,10 @@ termux_step_patch_package() { -e "s%\@TERMUX_HOME\@%${TERMUX_ANDROID_HOME}%g" \ -e "s%\@TERMUX_PREFIX\@%${TERMUX_PREFIX}%g" \ -e "s%\@TERMUX_PREFIX_CLASSICAL\@%${TERMUX_PREFIX_CLASSICAL}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX\@%${TERMUX_ENV__S_TERMUX}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_APP\@%${TERMUX_ENV__S_TERMUX_APP}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_API_APP\@%${TERMUX_ENV__S_TERMUX_API_APP}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_ROOTFS\@%${TERMUX_ENV__S_TERMUX_ROOTFS}%g" \ "$patch" | patch --silent -p1 done shopt -u nullglob diff --git a/scripts/build/termux_step_setup_cgct_environment.sh b/scripts/build/termux_step_setup_cgct_environment.sh index fdf6877b4629ebd..5a4576f28df61c6 100644 --- a/scripts/build/termux_step_setup_cgct_environment.sh +++ b/scripts/build/termux_step_setup_cgct_environment.sh @@ -3,7 +3,7 @@ termux_step_setup_cgct_environment() { [ "$TERMUX_ON_DEVICE_BUILD" = "true" ] && return - if [ "$TERMUX_REPO_PACKAGE" != "$TERMUX_APP_PACKAGE" ]; then + if [ "$TERMUX_REPO_APP__PACKAGE_NAME" != "$TERMUX_APP_PACKAGE" ]; then echo "WARNING: It is not possible to install glibc core packages from the repo for operation of CGCT, you must install glibc packages for your application with the prefix '$TERMUX_PREFIX' yourself (core packages: glibc and linux-api-headers-glibc)." return fi diff --git a/scripts/generate-bootstraps.sh b/scripts/generate-bootstraps.sh index d1f1130f15a2301..2096d7e00989504 100755 --- a/scripts/generate-bootstraps.sh +++ b/scripts/generate-bootstraps.sh @@ -252,22 +252,23 @@ add_termux_bootstrap_second_stage_files() { echo "[*] Adding termux bootstrap second stage files..." - mkdir -p "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}" + mkdir -p "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}" sed -e "s|@TERMUX_PREFIX@|${TERMUX_PREFIX}|g" \ - -e "s|@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@|${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}|g" \ + -e "s|@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@|${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}|g" \ -e "s|@TERMUX_PACKAGE_MANAGER@|${TERMUX_PACKAGE_MANAGER}|g" \ -e "s|@TERMUX_PACKAGE_ARCH@|${package_arch}|g" \ + -e "s|@TERMUX_APP__NAME@|${TERMUX_APP__NAME}|g" \ "$TERMUX_SCRIPTDIR/scripts/bootstrap/termux-bootstrap-second-stage.sh" \ - > "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}/termux-bootstrap-second-stage.sh" - chmod 700 "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}/termux-bootstrap-second-stage.sh" + > "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" + chmod 700 "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" # TODO: Remove it when Termux app supports `pacman` bootstraps installation. sed -e "s|@TERMUX_PREFIX@|${TERMUX_PREFIX}|g" \ - -e "s|@TERMUX_PROFILE_D_PREFIX_DIR_PATH@|${TERMUX_PROFILE_D_PREFIX_DIR_PATH}|g" \ - -e "s|@TERMUX_BOOTSTRAP_CONFIG_DIR_PATH@|${TERMUX_BOOTSTRAP_CONFIG_DIR_PATH}|g" \ + -e "s|@TERMUX__PREFIX__PROFILE_D_DIR@|${TERMUX__PREFIX__PROFILE_D_DIR}|g" \ + -e "s|@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@|${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}|g" \ "$TERMUX_SCRIPTDIR/scripts/bootstrap/01-termux-bootstrap-second-stage-fallback.sh" \ - > "${BOOTSTRAP_ROOTFS}/${TERMUX_PROFILE_D_PREFIX_DIR_PATH}/01-termux-bootstrap-second-stage-fallback.sh" - chmod 600 "${BOOTSTRAP_ROOTFS}/${TERMUX_PROFILE_D_PREFIX_DIR_PATH}/01-termux-bootstrap-second-stage-fallback.sh" + > "${BOOTSTRAP_ROOTFS}/${TERMUX__PREFIX__PROFILE_D_DIR}/01-termux-bootstrap-second-stage-fallback.sh" + chmod 600 "${BOOTSTRAP_ROOTFS}/${TERMUX__PREFIX__PROFILE_D_DIR}/01-termux-bootstrap-second-stage-fallback.sh" } diff --git a/scripts/properties.sh b/scripts/properties.sh index f7fea5ebca4c445..c5146d5b74b1318 100644 --- a/scripts/properties.sh +++ b/scripts/properties.sh @@ -1,8 +1,150 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 + # XXX: This file is sourced by repology-updater script # So avoid doing things like executing commands except of those available in # coreutils and are clearly not a default part of most Linux installations, # or sourcing any other script in our build directories. +if [ -z "${BASH_VERSION:-}" ]; then + echo "The 'properties.sh' script must be run from a 'bash' shell."; return 64 2>/dev/null|| exit 64 # EX__USAGE +fi + + + +### +# Variables for validation of Termux properties variables. +# Validation is done to ensure packages are not compiled for invalid +# values that are not supported, and values are as per Termux file +# path limits. +# +# Additionally, the Termux packages build system is an unsafe mess of +# unquoted variables in shell scripts, and so validation is necessary +# for important variables, especially specific path variables against +# `TERMUX_REGEX__SAFE_*_PATH` regexes to reduce any potential damage. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +### + +## +# The map of variable names to their space separated list of validator +# actions to perform. +# +# Following are the supported validator actions. +# - `allow_unset_value`: Allow variable to be defined but unset, and +# skip other validations. +# - `app_package_name`: Variable must match `TERMUX_REGEX__APP_PACKAGE_NAME`. +# - `invalid_termux_rootfs_paths`: Path variable must not match +# `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`. +# - `invalid_termux_home_paths`: Path variable must not match +# `TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS`. +# - `invalid_termux_prefix_paths`: Path variable must not match +# `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. +# - `path_equal_to_or_under_termux_rootfs`: Path variable must be equal +# to or be under `TERMUX__ROOTFS`. +# - `path_under_termux_rootfs`:Path variable must be under `TERMUX__ROOTFS`. +# - `safe_absolute_path`: Path variable must match +# `TERMUX_REGEX__SAFE_ABSOLUTE_PATH` and must not match +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +# - `safe_relative_path`: Path variable must match +# `TERMUX_REGEX__SAFE_RELATIVE_PATH` and must not match +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +# - `safe_rootfs_or_absolute_path`: Path variable must match +# `TERMUX_REGEX__SAFE_ROOTFS_OR_ABSOLUTE_PATH` and must not match +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +# - `apps_api_socket__server_parent_dir`: Path variable must have max +# length `<= TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN` +# including the null `\0` terminator. +# - `unix_path_max`: Path variable must have max length `<= TERMUX__UNIX_PATH_MAX` +# including the null `\0` terminator. +# - `unsigned_int`: Variable must match `TERMUX_REGEX__UNSIGNED_INT`. +## +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP; declare -A __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP=() + +## +# The list of variable names added to `__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP` +# that maintains insertion order. +## +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES; declare -a __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES=() + +## +# Whether to validate max lengths of Termux paths. Set to `false` to skip validation. +## +__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN="true" + +## +# Whether to validate `usr` merge format for `TERMUX__PREFIX`. Set to `false` to skip validation. +# Check `TERMUX__PREFIX` variable docs for more info. +## +__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT="true" + + + +## +# `__termux_build_props__add_variables_validator_actions` `` `` +## +__termux_build_props__add_variables_validator_actions() { + + if [ $# -ne 2 ]; then + echo "Invalid argument count '$#' to '__termux_build_props__add_variables_validator_actions'." 1>&2 + return 1 + fi + + local variable_name="$1" + local validator_actions="$2" + + if [[ ! "$variable_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then + echo "The variable_name '$variable_name' passed to '__termux_build_props__add_variables_validator_actions' is not a valid shell variable name." 1>&2 + return 1 + fi + + if [[ " ${__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES[*]} " != *" $variable_name "* ]]; then + __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES+=("$variable_name") + fi + + __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP["$variable_name"]+="$validator_actions" + +} + + + + + +### +# Variables for the Termux build tools. +### + +## +# The path to the `termux-packages` repo root directory. +## +TERMUX_PKGS__BUILD__REPO_ROOT_DIR="${TERMUX_PKGS__BUILD__REPO_ROOT_DIR:-}" + +__termux_build_props__set_termux_builder__repo_root_dir() { + + local relative_path="${1:-}" + local return_value=0 + if [[ -z "${TERMUX_PKGS__BUILD__REPO_ROOT_DIR:-}" ]]; then + if [[ "$(readlink --help 2>&1 || true)" =~ [\ ]-f[,\ ] ]]; then + TERMUX_PKGS__BUILD__REPO_ROOT_DIR="$(file="$(readlink -f -- "${BASH_SOURCE[0]}")" && \ + parent="$(dirname -- "$file")" && \ + readlink -f -- "${parent}${relative_path}")" || return_value=$? + else + TERMUX_PKGS__BUILD__REPO_ROOT_DIR="$(pwd)" || return_value=$? # macOS `< 12.3` compatibility. + fi + fi + if [ $return_value -ne 0 ] || [[ ! "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR" =~ ^(/[a-zA-Z0-9+,.=_-]+)+$ ]] || \ + [[ ! -f "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/scripts/properties.sh" ]]; then + echo "The TERMUX_PKGS__BUILD__REPO_ROOT_DIR '$TERMUX_PKGS__BUILD__REPO_ROOT_DIR' not found or is not valid." 1>&2 + return 1; + fi + +} +__termux_build_props__set_termux_builder__repo_root_dir "/.." || exit $? +unset __termux_build_props__set_termux_builder__repo_root_dir +TERMUX_SCRIPTDIR="${TERMUX_SCRIPTDIR:-TERMUX_PKGS__BUILD__REPO_ROOT_DIR}" # Deprecated alternative variable for `TERMUX_PKGS__BUILD__REPO_ROOT_DIR` + + + TERMUX_SDK_REVISION=9123335 TERMUX_ANDROID_BUILD_TOOLS_VERSION=33.0.1 # when changing the above: @@ -11,7 +153,7 @@ TERMUX_ANDROID_BUILD_TOOLS_VERSION=33.0.1 # and trigger rebuild of them : "${TERMUX_NDK_VERSION_NUM:="27"}" : "${TERMUX_NDK_REVISION:="c"}" -TERMUX_NDK_VERSION=$TERMUX_NDK_VERSION_NUM$TERMUX_NDK_REVISION +TERMUX_NDK_VERSION="${TERMUX_NDK_VERSION_NUM}${TERMUX_NDK_REVISION}" # when changing the above: # update version and hashsum in packages # libandroid-stub, libc++, ndk-multilib, ndk-sysroot, vulkan-loader-android @@ -19,62 +161,2041 @@ TERMUX_NDK_VERSION=$TERMUX_NDK_VERSION_NUM$TERMUX_NDK_REVISION # check all packages build and run correctly and bump if needed : "${TERMUX_JAVA_HOME:=/usr/lib/jvm/java-17-openjdk-amd64}" -export JAVA_HOME=${TERMUX_JAVA_HOME} +export JAVA_HOME="${TERMUX_JAVA_HOME}" -if [ "${TERMUX_PACKAGES_OFFLINE-false}" = "true" ]; then - export ANDROID_HOME=${TERMUX_SCRIPTDIR}/build-tools/android-sdk-$TERMUX_SDK_REVISION - export NDK=${TERMUX_SCRIPTDIR}/build-tools/android-ndk-r${TERMUX_NDK_VERSION} +if [[ "${TERMUX_PACKAGES_OFFLINE-false}" == "true" ]]; then + export ANDROID_HOME="${TERMUX_PKGS__BUILD__REPO_ROOT_DIR}/build-tools/android-sdk-${TERMUX_SDK_REVISION}" + export NDK="${TERMUX_PKGS__BUILD__REPO_ROOT_DIR}/build-tools/android-ndk-r${TERMUX_NDK_VERSION}" else - : "${ANDROID_HOME:="${HOME}/lib/android-sdk-$TERMUX_SDK_REVISION"}" - : "${NDK:="${HOME}/lib/android-ndk-r${TERMUX_NDK_VERSION}"}" + : "${ANDROID_HOME:="${HOME}/lib/android-sdk-$TERMUX_SDK_REVISION"}" + : "${NDK:="${HOME}/lib/android-ndk-r${TERMUX_NDK_VERSION}"}" fi -# Termux packages configuration. -TERMUX_APP_PACKAGE="com.termux" -TERMUX_BASE_DIR="/data/data/${TERMUX_APP_PACKAGE}/files" -TERMUX_CACHE_DIR="/data/data/${TERMUX_APP_PACKAGE}/cache" -TERMUX_ANDROID_HOME="${TERMUX_BASE_DIR}/home" -TERMUX_APPS_DIR="${TERMUX_BASE_DIR}/apps" -TERMUX_PREFIX_CLASSICAL="${TERMUX_BASE_DIR}/usr" -TERMUX_PREFIX="${TERMUX_PREFIX_CLASSICAL}" -TERMUX_ETC_PREFIX_DIR_PATH="${TERMUX_PREFIX}/etc" -TERMUX_PROFILE_D_PREFIX_DIR_PATH="${TERMUX_ETC_PREFIX_DIR_PATH}/profile.d" -TERMUX_CONFIG_PREFIX_DIR_PATH="${TERMUX_ETC_PREFIX_DIR_PATH}/termux" -TERMUX_BOOTSTRAP_CONFIG_DIR_PATH="${TERMUX_CONFIG_PREFIX_DIR_PATH}/bootstrap" -TERMUX_CLEANUP_BUILT_PACKAGES_THRESHOLD="$(( 5 * 1024 ** 3 ))" # 5 GiB -# Path to CGCT tools -CGCT_DEFAULT_PREFIX="/data/data/com.termux/files/usr/glibc" -export CGCT_DIR="/data/data/com.termux/cgct" -# Package name for the packages hosted on the repo. -# This must only equal TERMUX_APP_PACKAGE if using custom repo that -# has packages that were built with same package name. -TERMUX_REPO_PACKAGE="com.termux" +### +# Variables for the Termux apps and packages for which to compile packages. +# +# Variables defined in this file need to be in sync with `termux-app` +# (`TermuxConstants` and `TermuxCoreConstants`), termux site and `termux-exec`. +# - https://github.com/termux/termux-app/blob/master/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java +# - https://github.com/termux/termux-app/blob/master/termux-shared/src/main/java/com/termux/shared/termux/core/TermuxCoreConstants.java +# +# Following is a list of `TERMUX_` variables that are safe to modify when forking. +# **DO NOT MODIFY ANY OTHER VARIABLE UNLESS YOU KNOW WHAT YOU ARE DOING.** +# +# - `TERMUX__NAME`, `TERMUX__LNAME` and `TERMUX__UNAME`. +# - `TERMUX__REPOS_HOST_ORG_NAME` and `TERMUX__REPOS_HOST_ORG_URL`. +# - `TERMUX_*__REPO_NAME` and `TERMUX_*__REPO_URL`. +# - `TERMUX_APP__PACKAGE_NAME`. +# - `TERMUX_APP__DATA_DIR`. +# - `TERMUX__PROJECT_SUBDIR`. +# - `TERMUX__ROOTFS_SUBDIR`. +# - `TERMUX__ROOTFS` and alternates. +# - `TERMUX__PREFIX` and alternates. +# - `TERMUX_ANDROID_HOME` and alternates. +# - `TERMUX_APP__NAME` and `TERMUX_APP__LNAME`. +# - `TERMUX_APP__IDENTIFIER`. +# - `TERMUX_APP__NAMESPACE`. +# - `TERMUX_APP__SHELL_ACTIVITY__*`. +# - `TERMUX_APP__SHELL_SERVICE__*`. +# - `TERMUX_APP__RUN_COMMAND_SERVICE__*`. +# - `TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__*`. +# - `TERMUX_API_APP__PACKAGE_NAME`. +# - `TERMUX_API_APP__NAME`. +# - `TERMUX_API_APP__IDENTIFIER`. +# - `TERMUX_API_APP__NAMESPACE`. +# - `TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__*`. +# - `TERMUX_AM_APP__NAMESPACE`. +### + +## +# Termux project name. +# +# Default value: `Termux` +## +TERMUX__NAME="Termux" + +## +# The lower case value for `TERMUX__NAME`. +# +# Default value: `termux` +## +TERMUX__LNAME="${TERMUX__NAME,,}" + +## +# The upper case value for `TERMUX__NAME`. +# +# Default value: `TERMUX` +## +TERMUX__UNAME="${TERMUX__NAME^^}" + + + +## +# Termux internal project name. +# +# This is used internally for paths, filenames, and other internal use +# cases and must match the `TERMUX__INTERNAL_NAME_REGEX` regex and +# have max length `TERMUX__INTERNAL_NAME___MAX_LEN`. +# +# **This must not be changed unless doing a full fork of Termux where +# all Termux references are changed instead of just changing the +# `TERMUX__NAME`, `TERMUX_APP__PACKAGE_NAME` and urls.** +# +# Default value: `termux` +## +TERMUX__INTERNAL_NAME="termux" + +## +# The regex to validate `TERMUX__INTERNAL_NAME`. +# +# The internal name must start with characters in the range +# `[a-z0-9]`, followed by at least one character in the range +# `[a-z0-9_-]`, and end with characters in the range `[a-z0-9]`. The +# min length is `3`. The max length `7` as per +# `TERMUX__INTERNAL_NAME___MAX_LEN` is not checked by this regex and +# must be checked separately. +# +# +# Constant value: `^[a-z0-9][a-z0-9_-]+[a-z0-9]$` +## +TERMUX__INTERNAL_NAME_REGEX="^[a-z0-9][a-z0-9_-]+[a-z0-9]$" + +## +# The max length for the `TERMUX__INTERNAL_NAME`. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `7` is chosen. +# +# Constant value: `7` +## +TERMUX__INTERNAL_NAME___MAX_LEN=7 + + + +## +# Termux repositories host organization name. +# +# Default value: `termux` +## +TERMUX__REPOS_HOST_ORG_NAME="termux" + +## +# Termux repositories host organization url. +# +# Default value: `https://github.com/termux` +## +TERMUX__REPOS_HOST_ORG_URL="https://github.com/$TERMUX__REPOS_HOST_ORG_NAME" + + + +## +# Termux app package name used for `TERMUX_APP__DATA_DIR` and +# `TERMUX_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*` variables. +# +# Ideally package name should be `<= 21` characters and max `33` +# characters. If package name has not yet been chosen, then it would +# be best to keep it to `<= 10` characters. Check +# https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why. +# +# See also `TERMUX_APP__NAMESPACE`. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-private-app-data-directory +# +# Default value: `com.termux` +## +TERMUX_APP__PACKAGE_NAME="com.termux" +TERMUX_APP_PACKAGE="$TERMUX_APP__PACKAGE_NAME" # Deprecated alternative variable for `TERMUX_APP__PACKAGE_NAME` + +__termux_build_props__add_variables_validator_actions "TERMUX_APP__PACKAGE_NAME" "app_package_name" + +## +# Termux app data directory path that is expected to be assigned by +# Android to the Termux app with `TERMUX_APP__PACKAGE_NAME` for all +# its app data, that will contain the Termux project directory +# (`TERMUX__PROJECT_DIR`), and optionally the Termux rootfs directory +# (`TERMUX__ROOTFS`). +# +# The path must match `TERMUX_REGEX__APP_DATA_DIR_PATH`. +# +# The directory set will be deleted by `clean.sh` if `TERMUX__PREFIX` +# is under `TERMUX_APP__DATA_DIR` and not running on-device, so make +# sure a safe path is set if running `clean.sh` in Termux docker or +# host OS build environment. +# +# Default value: `/data/data/com.termux` +## +TERMUX_APP__DATA_DIR="/data/data/$TERMUX_APP__PACKAGE_NAME" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__DATA_DIR" "safe_absolute_path" + +## +# The max length for the `TERMUX_APP__DATA_DIR` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `69` is chosen. +# +# Constant value: `69` +## +TERMUX_APP__DATA_DIR___MAX_LEN=69 + + + + + +## +# Termux subdirectory path for `TERMUX__PROJECT_DIR`. +# +# Default value: `termux` +## +TERMUX__PROJECT_SUBDIR="$TERMUX__INTERNAL_NAME" +__termux_build_props__add_variables_validator_actions "TERMUX__PROJECT_SUBDIR" "safe_relative_path" + +## +# Termux project directory path under `TERMUX_APP__DATA_DIR`. +# +# This is an exclusive directory for all Termux files that includes +# Termux core directory (`TERMUX__CORE_DIR`), Termux apps directory +# (`TERMUX__APPS_DIR`), and optionally the Termux rootfs directory +# (`TERMUX__ROOTFS`). +# +# Currently, the default Termux rootfs directory is not under it and +# is at the `/files` subdirectory but there are plans to move it to +# `termux/rootfs/II` in future where `II` refers to rootfs id starting +# at `0` for multi-rootfs support. +# +# An exclusive directory is required so that all Termux files exist +# under a single directory, especially for when Termux is provided as +# a library, so that Termux files do not interfere with other files +# of Termux app forks or apps that may use the Termux library. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-project-directory +# +# Default value: `/data/data/com.termux/termux` +## +TERMUX__PROJECT_DIR="$TERMUX_APP__DATA_DIR/$TERMUX__PROJECT_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__PROJECT_DIR" "safe_absolute_path" + + + + + +## +# Termux subdirectory path for `TERMUX__CORE_DIR`. +# +# Constant value: `core` +## +TERMUX__CORE_SUBDIR="core" + +## +# Termux core directory path under `TERMUX__PROJECT_DIR`. +# +# This contains Termux core files for the Termux app, like user settings and configs for the app, +# which and are independent of any specific rootfs. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-core-directory +# +# Default value: `/data/data/com.termux/termux/core` +## +TERMUX__CORE_DIR="$TERMUX__PROJECT_DIR/$TERMUX__CORE_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__CORE_DIR" "safe_absolute_path" + + + + + + +## +# Termux subdirectory path for `TERMUX__APPS_DIR`. +# +# Constant value: `apps` +## +TERMUX__APPS_SUBDIR="apps" + +## +# Termux apps directory path under `TERMUX__PROJECT_DIR`. +# +# This contains app specific files for the Termux app, its plugin +# apps, and third party apps, like used for app APIs and +# filesystem/pathname socket files of servers created by the apps. +# - https://man7.org/linux/man-pages/man7/unix.7.html +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-apps-directory +# +# Default value: `/data/data/com.termux/termux/apps` +## +TERMUX__APPS_DIR="$TERMUX__PROJECT_DIR/$TERMUX__APPS_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__APPS_DIR" "safe_absolute_path" + +## +# The max length for the `TERMUX__APPS_DIR` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `84` is chosen. +# +# Constant value: `84` +## +TERMUX__APPS_DIR___MAX_LEN=84 + +## +# The max length for the Termux apps api socket server parent directory +# including the null '\0' terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `98` is chosen. +# +# Constant value: `98` +## +TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN=98 + + + +## +# Termux subdirectory path for `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Constant value: `i` +## +TERMUX__APPS_DIR_BY_IDENTIFIER_SUBDIR="i" + +## +# Termux apps directory path by app identifier under `TERMUX__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i` +## +TERMUX__APPS_DIR_BY_IDENTIFIER="$TERMUX__APPS_DIR/$TERMUX__APPS_DIR_BY_IDENTIFIER_SUBDIR" + +## +# The regex to validate a subdirectory name under the +# `TERMUX__APPS_DIR_BY_IDENTIFIER` excluding the null '\0' terminator +# that represents an app identifier. +# +# The app identifier must only contain characters in the range +# `[a-zA-Z0-9]` as segments, with `[._-]` as separators between +# segments, and with the first segment containing at least 3 +# characters. The max length `10` as per +# `TERMUX__APPS_APP_IDENTIFIER___MAX_LEN` is not checked by this regex +# and must be checked separately. +# +# Constant value: `^[a-zA-Z0-9]{3,}([._-][a-zA-Z0-9]+)*$` +## +TERMUX__APPS_APP_IDENTIFIER_REGEX="^[a-zA-Z0-9]{3,}([._-][a-zA-Z0-9]+)*$" + +## +# The max length for a subdirectory name under the +# `TERMUX__APPS_DIR_BY_IDENTIFIER` excluding the null '\0' terminator +# that represents an app identifier. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `10` is chosen. +# +# Constant value: `10` +## +TERMUX__APPS_APP_IDENTIFIER___MAX_LEN=10 + + + + + +## +# Termux subdirectory path for `TERMUX__APPS_DIR_BY_UID`. +# +# Constant value: `u` +## +TERMUX__APPS_DIR_BY_UID_SUBDIR="u" + +## +# Termux apps directory path by app uid (user_id + app_id) under +# `TERMUX__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/u` +## +TERMUX__APPS_DIR_BY_UID="$TERMUX__APPS_DIR/$TERMUX__APPS_DIR_BY_UID_SUBDIR" + +## +# The regex to validate a subdirectory name under the +# `TERMUX__APPS_DIR_BY_UID` excluding the null '\0' terminator that +# represents an app uid. +# +# The app uid must only contains `5` to `9` characters that are +# numbers and must not start with a `0`. +# +# Constant value: `^[1-9][0-9]{4,8}$` +## +TERMUX__APPS_APP_UID_REGEX="^[1-9][0-9]{4,8}$" + +## +# The max length for a subdirectory name under the +# `TERMUX__APPS_DIR_BY_UID` excluding the null '\0' terminator that +# represents an app uid. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `9` is chosen. +# +# Constant value: `9` +## +TERMUX__APPS_APP_UID___MAX_LEN=9 + + + + + +## +# `termux-am-socket` server subfile path under an app directory of +# `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `termux-am` +## +TERMUX_AM_SOCKET__SERVER_SOCKET_SUBFILE="$TERMUX__INTERNAL_NAME-am" + + + + + +## +# Termux `TERMUX__ROOTFS` id. +# +# Default value: `0` +## +TERMUX__ROOTFS_ID="0" +__termux_build_props__add_variables_validator_actions "TERMUX__ROOTFS_ID" "unsigned_int" + +## +# Termux subdirectory path for `TERMUX__ROOTFS`. +# +# Default value: `files` +## +TERMUX__ROOTFS_SUBDIR="files" +__termux_build_props__add_variables_validator_actions "TERMUX__ROOTFS_SUBDIR" "allow_unset_value safe_relative_path" + +########### +# Uncomment if to place `TERMUX__ROOTFS` under `TERMUX__PROJECT_DIR` +# instead of at `files`. This may be used for future multi-rootfs +# design. Make sure to update `TERMUX__CACHE_SUBDIR` above as well. + +## +# Termux subdirectory path for `TERMUX__ROOTFS`. +# +# Default value: `termux/rootfs/0` +## +#TERMUX__ROOTFS_SUBDIR="$TERMUX__PROJECT_SUBDIR/rootfs/$TERMUX__ROOTFS_ID" +########### + + +## +# Termux rootfs directory path under `TERMUX_APP__DATA_DIR` that +# contains the Linux environment rootfs provided by Termux. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-rootfs-directory +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03.html +# +# The Termux rootfs must not be set to path in +# `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`. It can exist outside +# the `TERMUX_APP__DATA_DIR` if compiling packages for the Android +# system or `adb` `shell` user. +# +# Default value: `/data/data/com.termux/files` +## +TERMUX__ROOTFS="$TERMUX_APP__DATA_DIR/$TERMUX__ROOTFS_SUBDIR" +TERMUX_BASE_DIR="$TERMUX__ROOTFS" # Deprecated alternative variable for `TERMUX__ROOTFS` + +__termux_build_props__add_variables_validator_actions "TERMUX__ROOTFS" "safe_rootfs_or_absolute_path invalid_termux_rootfs_paths" + +# FIXME: Remove after updating Termux app and `termux-am-socket` +# package sources and use `TERMUX__APPS_DIR`. +TERMUX_APPS_DIR="$TERMUX__ROOTFS/apps" + +## +# The max length for the `TERMUX__ROOTFS` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `86` is chosen. +# +# Constant value: `86` +## +TERMUX__ROOTFS_DIR___MAX_LEN=86 + + + + + +#### +# Variables for the Termux home. +#### + +## +# Termux subdirectory path for `TERMUX__HOME`. +# +# Default value: `home` +## +TERMUX__HOME_SUBDIR="home" +__termux_build_props__add_variables_validator_actions "TERMUX__HOME_SUBDIR" "safe_relative_path" + +## +# Termux home directory path under `TERMUX__ROOTFS` used for `$HOME`. +# +# It serves the same purpose as the `/home` directory on Linux distros. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-home-directory +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s08.html +# +# Check `TERMUX__PREFIX` variable docs for rules that apply depending +# on if `TERMUX__ROOTFS` is equal to Android/Linux rootfs `/` or not. +# The Termux home must not be set to Android/Linux rootfs `/` or any +# other path in `TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS`. +# +# Default value: `/data/data/com.termux/files/home` +## +[[ "$TERMUX__ROOTFS" != "/" ]] && TERMUX__HOME="$TERMUX__ROOTFS/$TERMUX__HOME_SUBDIR" || \ + TERMUX__HOME="/$TERMUX__HOME_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__HOME" "safe_absolute_path invalid_termux_home_paths path_under_termux_rootfs" + +TERMUX_ANDROID_HOME="$TERMUX__HOME" # Deprecated alternative variable for `TERMUX__HOME` + +## +# Termux data directory path under `TERMUX__HOME`. +# +# Default value: `/data/data/com.termux/files/home/.termux` +## +TERMUX__DATA_HOME="$TERMUX__HOME/.termux" + + + + + +#### +# Variables for the Termux prefix. +#### + +## +# Termux subdirectory path for `TERMUX__PREFIX`. +# +# Default value: `usr` +## +TERMUX__PREFIX_SUBDIR="usr" +__termux_build_props__add_variables_validator_actions "TERMUX__PREFIX_SUBDIR" "allow_unset_value safe_relative_path" + +## +# Termux prefix directory path under or equal to `TERMUX__ROOTFS` +# where all Termux packages data is installed. +# +# It serves the same purpose as the `/usr` directory on Linux distros +# and contains the `bin`, `etc`, `include`, `lib`, `libexec`, `opt`, +# `share`, `tmp` and `var` sub directories. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-prefix-directory +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch04.html +# +# If `TERMUX__ROOTFS` is not equal to `/`, then by default Termux +# uses `usr` merge format, like used by `debian`, as per +# `__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT` +# being enabled by default. In the `usr` merge format, all packages +# are installed under the `usr` subdirectory under rootfs, like under +# `$TERMUX__ROOTFS/usr/bin` and `$TERMUX__ROOTFS/usr/lib`, +# instead of under `$TERMUX__ROOTFS/bin` and `$TERMUX__ROOTFS/lib`. +# So if `usr` merge format is enabled, then DO NOT change the default +# value of `TERMUX__PREFIX_SUBDIR` from `usr`. +# The `$TERMUX__ROOTFS/usr-staging` directory is also used as a +# temporary directory for extracting bootstrap zip by the Termux app, +# before its renamed to `$TERMUX__ROOTFS/usr`. +# Additionally, `TERMUX__PREFIX` must not be equal to `TERMUX__HOME` +# and they must not be under each other, as Termux app requires that +# prefix and home are separate directories as prefix gets wiped during +# bootstrap installation or if `termux-reset` is run, and backup +# scripts require the same. Package data also needs to be kept +# separate from `home`, so it does not make sense for them to be +# equal to or be under each other. +# However, if a Termux app fork is using a modified bootstrap +# installation that does not use the `usr` merge format, then +# `__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT` can +# be set to `false` and `TERMUX__PREFIX_SUBDIR` could optionally be +# set to an empty string if `TERMUX__ROOTFS` should be equal to +# `TERMUX__PREFIX`, or a custom directory other than `usr`. In this +# case `TERMUX__HOME` can optionally be under `TERMUX__PREFIX`, but +# not be equal to it. +# +# - https://wiki.debian.org/UsrMerge +# - https://lists.debian.org/debian-devel-announce/2019/03/msg00001.html +# - https://dep-team.pages.debian.net/deps/dep17/ +# +# If `TERMUX__ROOTFS` is equal to Android/Linux rootfs `/`, then +# `TERMUX__PREFIX_SUBDIR` must not be set to an empty string as +# `TERMUX__PREFIX` must be a subdirectory under rootfs `/`, and must +# not be set to `usr` either or or any other path in +# `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. Check the +# `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS` variable docs for why +# some paths like `/usr`, etc are now allowed. +# +# Basically, the following rules apply for `TERMUX__PREFIX`. +# - If `TERMUX__ROOTFS` is not equal to `/`: +# - If `usr` merge format is enabled: +# - `TERMUX__PREFIX` must be equal to `$TERMUX__ROOTFS/usr`. +# - `TERMUX__PREFIX` must not be equal to `TERMUX__HOME` and +# they must not be under each other. +# - If `usr` merge format is disabled: +# - `TERMUX__PREFIX` must be equal to or be under `$TERMUX__ROOTFS`. +# - `TERMUX__PREFIX` must not be equal to or be under `TERMUX__HOME`. +# - If `TERMUX__ROOTFS` is equal to `/`: +# - If `usr` merge format is enabled or disabled: +# - `TERMUX__PREFIX` must be under `$TERMUX__ROOTFS` and not +# equal to `/usr` or other paths in `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. +# - `TERMUX__PREFIX` must not be equal to or be under `TERMUX__HOME`. +# +# The directory set will be deleted by `clean.sh` if not running +# on-device, so make sure a safe path is set if running `clean.sh` in +# Termux docker or host OS build environment. +# +# Default value: `/data/data/com.termux/files/usr` +## +[[ "$TERMUX__ROOTFS" != "/" ]] && TERMUX__PREFIX="$TERMUX__ROOTFS${TERMUX__PREFIX_SUBDIR:+"/$TERMUX__PREFIX_SUBDIR"}" || \ + TERMUX__PREFIX="/$TERMUX__PREFIX_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__PREFIX" "safe_absolute_path invalid_termux_prefix_paths" + +if [[ "$TERMUX__ROOTFS" != "/" ]] && [[ "$__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT" != "true" ]]; then + __termux_build_props__add_variables_validator_actions "TERMUX__PREFIX" " path_equal_to_or_under_termux_rootfs" +else + __termux_build_props__add_variables_validator_actions "TERMUX__PREFIX" " path_under_termux_rootfs" +fi + +TERMUX__PREFIX_CLASSICAL="$TERMUX__PREFIX" + +# The `glibc` modifies `TERMUX_PREFIX` during compilation to append +# `/glibc` in `termux_step_setup_variables()`. +# - https://github.com/termux/termux-packages/pull/16901 +TERMUX_PREFIX="$TERMUX__PREFIX" # Deprecated alternative variable for `TERMUX__PREFIX` +TERMUX_PREFIX_CLASSICAL="$TERMUX__PREFIX" # Deprecated alternative variable for `TERMUX__PREFIX_CLASSICAL` + +## +# The max length for the `TERMUX__PREFIX` including the null '\0' +# terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `90` is chosen. +# +# Constant value: `90` +## +TERMUX__PREFIX_DIR___MAX_LEN="$((TERMUX__ROOTFS_DIR___MAX_LEN + 1 + 3))" # "/usr" (90) + + + +## +# Termux subdirectory path for `TERMUX__PREFIX__BIN_DIR`. +# +# Constant value: `bin` +## +TERMUX__PREFIX__BIN_SUBDIR="bin" + +## +# Termux bin directory path under `TERMUX__PREFIX`. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-bin-directory +# +# Default value: `/data/data/com.termux/files/usr/bin` +## +TERMUX__PREFIX__BIN_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__BIN_SUBDIR" + + +## +# The max length for the `TERMUX__BIN_DIR` including the null '\0' terminator. +# +# Constant value: `94` +## +TERMUX__PREFIX__BIN_DIR___MAX_LEN="$((TERMUX__PREFIX_DIR___MAX_LEN + 1 + 3))" # "/bin" (94) + +## +# The max safe length for a sub file path under the `TERMUX__BIN_DIR` +# including the null '\0' terminator. +# +# This allows for a filename with max length `33` so that the path +# length is under `128` (`BINPRM_BUF_SIZE`) for Linux kernel `< 5.1`, +# and ensures `argv[0]` length is `< 128` on Android `< 6`, otherwise +# commands will fail with exit code 1 without any error on `stderr`, +# but with the `library name "" too long` error in +# `logcat` if linker debugging is enabled. +# +# **See Also:** +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# +# Constant value: `127` +## +TERMUX__PREFIX__BIN_FILE___SAFE_MAX_LEN="$((TERMUX__PREFIX__BIN_DIR___MAX_LEN + 1 + 33))" # "/" (127) + +## +# The max length for entire shebang line for `termux-exec`. +# +# Default value: `340` +## +TERMUX__FILE_HEADER__BUFFER_SIZE="340" + + + +## +# Termux subdirectory path for `TERMUX__PREFIX__ETC_DIR`. +# +# Constant value: `etc` +## +TERMUX__PREFIX__ETC_SUBDIR="etc" + +## +# Termux etc directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/etc` +## +TERMUX__PREFIX__ETC_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__ETC_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__INCLUDE_DIR`. +# +# Constant value: `include` +## +TERMUX__PREFIX__INCLUDE_SUBDIR="include" + +## +# Termux include directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/include` +## +TERMUX__PREFIX__INCLUDE_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__INCLUDE_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__LIB_DIR`. +# +# Constant value: `lib` +## +TERMUX__PREFIX__LIB_SUBDIR="lib" + +## +# Termux lib directory path under `TERMUX__PREFIX`. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-lib-directory +# +# Default value: `/data/data/com.termux/files/usr/lib` +## +TERMUX__PREFIX__LIB_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__LIB_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__LIBEXEC_DIR`. +# +# Constant value: `libexec` +## +TERMUX__PREFIX__LIBEXEC_SUBDIR="libexec" + +## +# Termux libexec directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/libexec` +## +TERMUX__PREFIX__LIBEXEC_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__LIBEXEC_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__OPT_DIR`. +# +# Constant value: `opt` +## +TERMUX__PREFIX__OPT_SUBDIR="opt" + +## +# Termux opt directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/opt` +## +TERMUX__PREFIX__OPT_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__OPT_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__SHARE_DIR`. +# +# Constant value: `share` +## +TERMUX__PREFIX__SHARE_SUBDIR="share" + +## +# Termux share directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/share` +## +TERMUX__PREFIX__SHARE_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__SHARE_SUBDIR" + + +## +# Termux subdirectory path for `TERMUX__PREFIX__TMP_DIR`. +# +# Constant value: `tmp` +## +TERMUX__PREFIX__TMP_SUBDIR="tmp" + +## +# Termux tmp directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/tmp` +## +TERMUX__PREFIX__TMP_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__TMP_SUBDIR" + +## +# The max length for the `TERMUX__PREFIX__TMP_DIR` including the null +# '\0' terminator. +# +# Check https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# for why the value `94` is chosen. +# +# Constant value: `94` +## +TERMUX__PREFIX__TMP_DIR___MAX_LEN=94 + + +## +# Termux subdirectory path for `TERMUX__PREFIX__VAR_DIR`. +# +# Constant value: `var` +## +TERMUX__PREFIX__VAR_SUBDIR="var" + +## +# Termux var directory path under `TERMUX__PREFIX`. +# +# Default value: `/data/data/com.termux/files/usr/var` +## +TERMUX__PREFIX__VAR_DIR="$TERMUX__PREFIX/$TERMUX__PREFIX__VAR_SUBDIR" + + + +## +# Termux `profile.d` directory path under `TERMUX__PREFIX__ETC_DIR`. +# +# Default value: `/data/data/com.termux/files/usr/etc/profile.d` +## +TERMUX__PREFIX__PROFILE_D_DIR="$TERMUX__PREFIX__ETC_DIR/profile.d" + + +## +# Termux data directory path under `TERMUX__PREFIX__ETC_DIR`. +# +# Default value: `/data/data/com.termux/files/usr/etc/termux` +## +TERMUX__PREFIX__TERMUX_DATA_ETC_DIR="$TERMUX__PREFIX__ETC_DIR/termux" + + + + + +#### +# Variables for the Termux cache. +#### + +## +# Termux subdirectory path for `TERMUX__CACHE_DIR`. +# +# Constant value: `cache` +## +TERMUX__CACHE_SUBDIR="cache" + +########### +# Uncomment if to place `TERMUX__ROOTFS` under `TERMUX__PROJECT_DIR` +# instead of at `files`. This may be used for future multi-rootfs +# design. This will also ensure `termux` files are not mixed with +# other cached files of an app, especially if Termux is forked or +# used as a library in other apps. Make sure to update +# `TERMUX__ROOTFS_SUBDIR` above as well. + +## +# Termux subdirectory path for `TERMUX__CACHE_DIR`. +# +# Default value: `cache/termux/rootfs/0` +## +#TERMUX__CACHE_SUBDIR="cache/termux/rootfs/$TERMUX__ROOTFS_ID" +########### + +## +# Termux app cache directory path under `TERMUX_APP__DATA_DIR` +# contains cache files that are safe to be deleted by Android or +# Termux if required. +# +# The `cache` subdirectory is hardcoded in Android and must not be +# changed. +# +# Currently this is primarily used for packages cache files of package +# managers (`apt`/`pacman`). +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-app-cache-directory +# +# Default value: `/data/data/com.termux/cache` +## +TERMUX__CACHE_DIR="$TERMUX_APP__DATA_DIR/$TERMUX__CACHE_SUBDIR" +__termux_build_props__add_variables_validator_actions "TERMUX__CACHE_DIR" "safe_absolute_path" + +TERMUX_CACHE_DIR="$TERMUX__CACHE_DIR" # Deprecated alternative variable for `TERMUX__CACHE_DIR` + + + + + +#### +# Variables for the Termux bootstraps. +#### + +## +# Termux bootstrap config directory path under `TERMUX__PREFIX__TERMUX_DATA_ETC_DIR`. +# +# Default value: `/data/data/com.termux/files/usr/etc/termux/bootstrap` +## +TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR="$TERMUX__PREFIX__TERMUX_DATA_ETC_DIR/bootstrap" + + + + + +## +# Max size in bytes for a path component or file name without the +# terminating `null` byte `\0`. +# +# On unix systems, any path component length of a path cannot be +# greater than what is supported by the filesystem under which the +# path is mounted. +# +# The common filesystems like `ext4`/`f2fs`/`btrfs`/`erofs`/`fat32`/`exfat`/`ntfs` +# all support max path component length of `255`. Check +# [filesystems limits wiki page] for more info on limits. +# +# The [POSIX standard requires `NAME_MAX` to be defined in `limits.h`] (not `c` standard). +# +# [`NAME_MAX`]: https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/kernel/uapi/linux/limits.h;l=27 +# [`readdir`]: https://www.man7.org/linux/man-pages/man3/readdir.3.html +# [`readdir_r`]: https://www.man7.org/linux/man-pages/man3/readdir_r.3.html +# [filesystems limits wiki page]: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits +# [POSIX standard requires `NAME_MAX` to be defined in `limits.h`]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html +# [path component length greater than `NAME_MAX` should be considered an error]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#4.13 +# [`NAME_MAX` value should normally be set to `255`]: https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/kernel/uapi/linux/limits.h;l=27 +# [the `NAME_MAX` value may not always be enforced, like by the GNU C library]: https://www.gnu.org/software/libc/manual/html_node/Limits-for-Files.html +# [`_PC_NAME_MAX` defined by POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html +# [`glibc` `_PC_NAME_MAX` source]: https://github.com/bminor/glibc/blob/569cfcc6/sysdeps/posix/fpathconf.c#L65 +# [`bionic` `_PC_NAME_MAX` source]: https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/bionic/pathconf.cpp;l=91 +# [`pathconf`]: https://man7.org/linux/man-pages/man3/pathconf.3.html +# [`statvfs.f_namemax`]: https://man7.org/linux/man-pages/man3/statvfs.3.html +# [`realpath(1)`]: https://man7.org/linux/man-pages/man1/realpath.1.html +# [`realpath(3)`]: https://man7.org/linux/man-pages/man3/realpath.3.html +# [To what extent does Linux support file names longer than 255 bytes?]: https://unix.stackexchange.com/questions/619625/to-what-extent-does-linux-support-file-names-longer-than-255-bytes +# [Extending ext4 File system's filename size limit to 1012 characters]: https://stackoverflow.com/questions/34980895/extending-ext4-file-systems-filename-size-limit-to-1012-characters +# [Limit on file name length in bash]: https://stackoverflow.com/questions/6571435/limit-on-file-name-length-in-bash +# +# Constant value: `255` +## +TERMUX__NAME_MAX=255 + +## +# The max length for a filesystem socket file path (pathanme UNIX domain socket) +# for the `sockaddr_un.sun_path` field including the null `\0` +# terminator as per `UNIX_PATH_MAX`. +# +# All filesystem socket path lengths created by Termux apps and packages must be `< 108`. +# +# - https://man7.org/linux/man-pages/man7/unix.7.html +# - https://cs.android.com/android/platform/superproject/+/android-13.0.0_r18:bionic/libc/kernel/uapi/linux/un.h;l=22 +# +# Constant value: `108` +## +TERMUX__UNIX_PATH_MAX=108 + + + + + +## +# Termux environment variables root scope. +# +# The name of this variable `TERMUX_ENV__S_ROOT` is considered a +# constant for Termux execution environment that's exported by Termux +# app containing the root scope and **must not be changed even for +# forks**. It can be used to check if running under Termux or any of +# its forks, and should be used to generate all Termux variable names +# that may need to be read, since Termux app forks may not export +# variables under the `TERMUX_` root scope and may do it under a +# different root scope like `FOO_`, so the `TERMUX__PREFIX` variable +# would be `FOO__PREFIX` instead. +# +# The `TERMUX_ENV__S_APP` environment variable will be exported at +# runtime for the scope of the current Termux app running the shell. +# +# The value of this variable `TERMUX_ENV__S_ROOT` may be modified, +# although not advisable since external programs would be using +# hardcoded `TERMUX_` value for reading Termux environment variables, +# and so changing variable names to say `FOO_*` would result in +# `TERMUX_*` ones being unset during execution, which would change +# external programs behaviour and may break them. +# **If the value is changed here, it must also be set to the same +# value in Termux app that is exported.** +# +# Moreover, currently, only `termux-exec` supports modifying this, all +# other termux (internal) packages, like `termux-tools`, etc do not. +# So forks should not modify it at least until all termux packages +# support modifying it. +# +# Default value: `TERMUX_` +## +TERMUX_ENV__S_ROOT="TERMUX_" + + + +## +# Termux environment variables Termux sub scope for primary variables +# or variables for currently running Termux config. +# +# **Do not modify this!** This is considered a constant Termux sub +# scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX` and variable names under it.** +# +# Default value: `_` +## +TERMUX_ENV__SS_TERMUX="_" + +## +# Termux environment variables Termux scope for primary variables or +# variables for currently running Termux config. +# +# **Do not modify this!** +# +# Default value: `TERMUX__` +## +TERMUX_ENV__S_TERMUX="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX}" + + + +## +# Termux environment variables Termux app sub scope. +# +# **Do not modify this!** This is considered a constant Termux app sub +# scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_APP` and variable names under it.** +# +# Default value: `APP__` +## +TERMUX_ENV__SS_TERMUX_APP="APP__" + +## +# Termux environment variables Termux app scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_APP__` +## +TERMUX_ENV__S_TERMUX_APP="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_APP}" + + + +## +# Termux environment variables Termux:API sub scope. +# +# **Do not modify this!** This is considered a constant Termux:API +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_API` and variable names under it.** +# +# Default value: `API__` +## +TERMUX_ENV__SS_TERMUX_API="API__" + +## +# Termux environment variables Termux:API scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_API__` +## +TERMUX_ENV__S_TERMUX_API="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_API}" + + + +## +# Termux environment variables Termux:API app sub scope. +# +# This may be allowed to be modified, in case APIs are provided under +# a different app name or under the main Termux app itself by a fork. +# Consequences for changing this haven't been fully looked at yet. +# +# Default value: `API_APP__` +## +TERMUX_ENV__SS_TERMUX_API_APP="API_APP__" + +## +# Termux environment variables Termux:API app scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_API_APP__` +## +TERMUX_ENV__S_TERMUX_API_APP="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_API_APP}" + + + +## +# Termux environment variables Termux rootfs sub scope. +# +# **Do not modify this!** This is considered a constant Termux rootfs +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_ROOTFS` and variable names under it.** +# +# Default value: `ROOTFS__` +## +TERMUX_ENV__SS_TERMUX_ROOTFS="ROOTFS__" + +## +# Termux environment variables Termux rootfs scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_ROOTFS__` +## +TERMUX_ENV__S_TERMUX_ROOTFS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_ROOTFS}" + + + +## +# Termux environment variables `termux-am-socket` sub scope. +# +# **Do not modify this!** This is considered a constant `termux-am-socket` +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_AM_SOCKET` and variable names under it.** +# +# Default value: `AM_SOCKET__` +## +TERMUX_ENV__SS_TERMUX_AM_SOCKET="AM_SOCKET__" + +## +# Termux environment variables `termux-am-socket` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_AM_SOCKET__` +## +TERMUX_ENV__S_TERMUX_AM_SOCKET="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_AM_SOCKET}" + + + + + +#### +# Variables for the Termux packages. +# +# - https://github.com/termux/termux-packages +#### + +## +# Termux packages repo name. +# +# Default value: `termux-packages` +## +TERMUX_PKGS__REPO_NAME="termux-packages" + +## +# Termux packages repo url. +# +# Default value: `https://github.com/termux/termux-packages` +## +TERMUX_PKGS__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_PKGS__REPO_NAME" + + + + + +#### +# Variables for the Termux app that hosts the packages. +# +# - https://github.com/termux/termux-app +#### + +## +# Termux app name. +# +# Default value: `Termux` +## +TERMUX_APP__NAME="$TERMUX__NAME" + + +## +# The lower case value for `TERMUX_APP__NAME`. +# +# Default value: `termux` +## +TERMUX_APP__LNAME="${TERMUX_APP__NAME,,}" + +## +# Termux app identifier for `TERMUX__APPS_DIR_BY_IDENTIFIER` subdirectory. +# +# Default value: `termux` +# Validation regex: `TERMUX__APPS_APP_IDENTIFIER_REGEX` +# Max length: `TERMUX__APPS_APP_IDENTIFIER___MAX_LEN` +## +TERMUX_APP__IDENTIFIER="termux" + + + +## +# Termux app repo name. +# +# Default value: `termux-app` +## +TERMUX_APP__REPO_NAME="termux-app" + +## +# Termux app repo url. +# +# Default value: `https://github.com/termux/termux-app` +## +TERMUX_APP__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_APP__REPO_NAME" + + + +## +# Termux app namespace, i.e the Java package name under which Termux +# classes exists used for `TERMUX_APP__*_CLASS__*` and +# `TERMUX_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*`variables. +# +# - https://github.com/termux/termux-app/tree/master/app/src/main/java/com/termux +# - https://developer.android.com/build/configure-app-module#set-namespace +# +# See also `TERMUX_APP__PACKAGE_NAME`. +# +# Default value: `com.termux` +## +TERMUX_APP__NAMESPACE="com.termux" + +__termux_build_props__add_variables_validator_actions "TERMUX_APP__NAMESPACE" "app_package_name" + + + +## +# Termux app apps directory path under `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux` +## +TERMUX_APP__APPS_DIR="$TERMUX__APPS_DIR_BY_IDENTIFIER/$TERMUX_APP__IDENTIFIER" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__APPS_DIR" "safe_absolute_path" + + + +## +# Termux app shell `Activity` class name that hosts the shell/terminal views. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxActivity.java +# +# Default value: `com.termux.app.TermuxActivity` +## +TERMUX_APP__SHELL_ACTIVITY__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.TermuxActivity" + +## +# Termux app shell `Activity` component name for `TERMUX_APP__SHELL_ACTIVITY__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.TermuxActivity` +## +TERMUX_APP__SHELL_ACTIVITY__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__SHELL_ACTIVITY__CLASS_NAME" + + + +## +# Termux app shell `Service` class name that manages the shells. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxService.java +# +# Default value: `com.termux.app.TermuxService` +## +TERMUX_APP__SHELL_SERVICE__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.TermuxService" + +## +# Termux app shell `Service` component name for `TERMUX_APP__SHELL_SERVICE__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.TermuxService` +## +TERMUX_APP__SHELL_SERVICE__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__SHELL_SERVICE__CLASS_NAME" + + + +## +# Termux app RUN_COMMAND `Service` class name that receives commands via intents. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/RunCommandService.java +# - https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent +# +# Default value: `com.termux.app.RunCommandService` +## +TERMUX_APP__RUN_COMMAND_SERVICE__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.RunCommandService" + +## +# Termux app shell `Service` component name for `TERMUX_APP__RUN_COMMAND_SERVICE__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.RunCommandService` +## +TERMUX_APP__RUN_COMMAND_SERVICE__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__RUN_COMMAND_SERVICE__CLASS_NAME" + + + +## +# Termux app data sender `BroadcastReceiver` class name that receives +# data view broadcasts and sends the data with `ACTION_SEND` and +# `ACTION_VIEW` intents to other apps, like by `termux-open`. +# +# - https://github.com/termux/termux-app/blob/master/app/src/main/java/com/termux/app/TermuxOpenReceiver.java +# - https://github.com/termux/termux-tools/blob/master/scripts/termux-open.in +# +# Default value: `com.termux.app.TermuxOpenReceiver` +## +TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__CLASS_NAME="$TERMUX_APP__NAMESPACE.app.TermuxOpenReceiver" + +## +# Termux app data sender `BroadcastReceiver` component name for `TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__CLASS_NAME`. +# +# Default value: `com.termux/com.termux.app.TermuxOpenReceiver` +## +TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__COMPONENT_NAME="$TERMUX_APP__PACKAGE_NAME/$TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__CLASS_NAME" + + + + + +## +# `termux-am-socket` server file path for the Termux app under `TERMUX_APP__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux/termux-am` +## +TERMUX_APP__AM_SOCKET__SERVER_SOCKET_FILE="$TERMUX_APP__APPS_DIR/$TERMUX_AM_SOCKET__SERVER_SOCKET_SUBFILE" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__AM_SOCKET__SERVER_SOCKET_FILE" "safe_absolute_path unix_path_max" + + + + + +#### +# Variables for the Termux:API app that hosts the packages. +# +# - https://github.com/termux/termux-api +#### + +## +# Termux:API app package name used for +# `TERMUX_API_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*` variables. +# +# See also `TERMUX_API_APP__NAMESPACE`. +# +# Default value: `com.termux.api` +## +TERMUX_API_APP__PACKAGE_NAME="com.termux.api" + +__termux_build_props__add_variables_validator_actions "TERMUX_API_APP__PACKAGE_NAME" "app_package_name" + + + +## +# Termux:API app name. +# +# Default value: `Termux:API` +## +TERMUX_API_APP__NAME="$TERMUX__NAME:API" + +## +# Termux:API app identifier for `TERMUX__APPS_DIR_BY_IDENTIFIER` subdirectory. +# +# Default value: `termuxapi` +# Validation regex: `TERMUX__APPS_APP_IDENTIFIER_REGEX` +# Max length: `TERMUX__APPS_APP_IDENTIFIER___MAX_LEN` +## +TERMUX_API_APP__IDENTIFIER="termuxapi" + + + +## +# Termux:API app repo name. +# +# Default value: `termux-api` +## +TERMUX_API_APP__REPO_NAME="termux-api" + +## +# Termux:API app repo url. +# +# Default value: `https://github.com/termux/termux-api` +## +TERMUX_API_APP__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_API_APP__REPO_NAME" + + + +## +# Termux:API app namespace, i.e the Java package name under which +# Termux:API classes exists used for `TERMUX_API_APP__*_CLASS__*` and +# `TERMUX_API_APP__*_(ACTIVITY|BROADCASTRECEIVER|SERVICE)__*`variables. +# +# - https://github.com/termux/termux-api/tree/master/app/src/main/java/com/termux/api +# - https://developer.android.com/build/configure-app-module#set-namespace +# +# See also `TERMUX_API_APP__PACKAGE_NAME`. +# +# Default value: `com.termux.api` +## +TERMUX_API_APP__NAMESPACE="com.termux.api" + +__termux_build_props__add_variables_validator_actions "TERMUX_API_APP__NAMESPACE" "app_package_name" + + + +## +# Termux:API app apps directory path under `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termuxapi` +## +TERMUX_API_APP__APPS_DIR="$TERMUX__APPS_DIR_BY_IDENTIFIER/$TERMUX_API_APP__IDENTIFIER" +__termux_build_props__add_variables_validator_actions "TERMUX_API_APP__APPS_DIR" "safe_absolute_path" + + + +## +# Termux:API app API `BroadcastReceiver` class name that receives +# and processes API requests from command line via `termux-api` native +# library. +# +# - https://github.com/termux/termux-api/blob/master/app/src/main/java/com/termux/api/TermuxApiReceiver.java +# - https://github.com/termux/termux-api-package/blob/master/termux-api.c +# +# Default value: `com.termux.api.TermuxApiReceiver` +## +TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__CLASS_NAME="$TERMUX_API_APP__NAMESPACE.TermuxApiReceiver" + +## +# Termux:API app API `BroadcastReceiver` component name for `TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__CLASS_NAME`. +# +# Default value: `com.termux.api/com.termux.api.TermuxApiReceiver` +## +TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__COMPONENT_NAME="$TERMUX_API_APP__PACKAGE_NAME/$TERMUX_API_APP__API_RECEIVER_BROADCASTRECEIVER__CLASS_NAME" + + + + + +#### +# Variables for the `termux-api` package. +# +# - https://github.com/termux/termux-api-package +#### + +## +# The `termux-api` package repo name. +# +# Default value: `termux-api-package` +## +TERMUX_API_PKG__REPO_NAME="termux-api-package" + +## +# The `termux-api` package repo url. +# +# Default value: `https://github.com/termux/termux-api-package` +## +TERMUX_API_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_API_PKG__REPO_NAME" + + + + + +#### +# Variables for the `termux-am` package. +# +# - https://github.com/termux/TermuxAm +#### + +## +# The `termux-am` package repo name. +# +# Default value: `TermuxAm` +## +TERMUX_AM_PKG__REPO_NAME="TermuxAm" + +## +# The `termux-am` package repo url. +# +# Default value: `https://github.com/termux/TermuxAm` +## +TERMUX_AM_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_AM_PKG__REPO_NAME" + + + +## +# TermuxAm namespace, i.e the Java package name under which Termux +# classes exists used for `TERMUX_AM__*_CLASS__*` variables. +# +# This must not be changed unless the classes in the `TermuxAm` repo +# are moved to a different Java package name (in forks). +# +# - https://github.com/termux/TermuxAm/tree/master/app/src/main/java/com/termux/termuxam +# - https://developer.android.com/build/configure-app-module#set-namespace +# +# Constant value: `com.termux.termuxam` +## +TERMUX_AM_APP__NAMESPACE="com.termux.termuxam" + +__termux_build_props__add_variables_validator_actions "TERMUX_AM_APP__NAMESPACE" "app_package_name" + + + +## +# TermuxAm main class that is passed as `start-class-name` to +# `/system/bin/app_process` when running `am.apk` set in `$CLASSPATH`. +# +# - https://github.com/termux/TermuxAm/blob/master/app/src/main/java/com/termux/termuxam/Am.java +# - https://github.com/termux/TermuxAm/blob/v0.8.0/am-libexec-packaged#L30 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/cmds/app_process/app_main.cpp;l=31 +# +# Default value: `com.termux.termuxam.Am` +## +TERMUX_AM_APP__AM_CLASS__CLASS_NAME="$TERMUX_AM_APP__NAMESPACE.Am" + + + + + +#### +# Variables for validating Termux variables. +#### + +## +# Regex that matches an absolute path that starts with a `/` with at +# least one characters under rootfs `/`. Duplicate or trailing path +# separators `/` are not allowed. +## +TERMUX_REGEX__ABSOLUTE_PATH='^(/[^/]+)+$' + +## +# Regex that matches a relative path that does not start with a `/`. +# Duplicate or trailing path separators `/` are not allowed. +## +TERMUX_REGEX__RELATIVE_PATH='^[^/]+(/[^/]+)*$' + +## +# Regex that matches (rootfs `/`) or (an absolute path that starts +# with a `/`). Duplicate or trailing path separators `/` are not +# allowed. +## +TERMUX_REGEX__ROOTFS_OR_ABSOLUTE_PATH='^((/)|((/[^/]+)+))$' + + +## +# Regex that matches a safe absolute path that starts with a `/` with +# at least one characters under rootfs `/`. Duplicate or trailing path +# separators `/` are not allowed. The path component characters must +# be in the range `[a-zA-Z0-9+,.=_-]`. +# +# The path must also be validated against +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +## +TERMUX_REGEX__SAFE_ABSOLUTE_PATH='^(/[a-zA-Z0-9+,.=_-]+)+$' + +## +# Regex that matches a safe relative path that does not start with a +# `/`. Duplicate or trailing path separators `/` are not allowed. The +# path component characters must be in the range `[a-zA-Z0-9+,.=_-]`. +# +# The path must also be validated against +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +## +TERMUX_REGEX__SAFE_RELATIVE_PATH='^[a-zA-Z0-9+,.=_-]+(/[a-zA-Z0-9+,.=_-]+)*$' + +## +# Regex that matches (rootfs `/`) or (a safe absolute path that starts +# with a `/`). Duplicate or trailing path separators `/` are not +# allowed. The path component characters must be in the range +# `[a-zA-Z0-9+,.=_-]`. +# +# The path must also be validated against +# `TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH`. +## +TERMUX_REGEX__SAFE_ROOTFS_OR_ABSOLUTE_PATH='^((/)|((/[a-zA-Z0-9+,.=_-]+)+))$' + + +## +# Regex that matches a path containing single `/./` or double `/../` dot components. +## +TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH='((^\./)|(^\.\./)|(/\.$)|(/\.\.$)|(/\./)|(/\.\./))' + + +## +# Regex that matches invalid Termux rootfs paths. +# +# The Termux rootfs or prefix paths must not be equal to or be under +# specific Filesystem Hierarchy Standard paths or paths used by Termux +# docker image/host OS for its own files, as Termux packages files +# must be kept separate from the build host. The Termux app data/prefix +# directories are also wiped by `clean.sh` when not running on-device, +# which wouldn't be possible if Termux and host directories are shared. +# +# The invalid paths list does not include the `/data` and `/mnt/expand` +# paths under which private app data directories are assigned to +# Android apps, or the `/data/local/tmp` directory assigned to `adb` +# `shell` user, or the `/system` directory for the Android system. +# +# - https://refspecs.linuxfoundation.org/FHS_3.0/fhs-3.0.html +# - https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-private-app-data-directory +## +TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS='^((/bin(/.*)?)|(/boot(/.*)?)|(/dev(/.*)?)|(/etc(/.*)?)|(/home)|(/lib(/.*)?)|(/lib[^/]+(/.*)?)|(/media)|(/mnt)|(/opt)|(/proc(/.*)?)|(/root)|(/run(/.*)?)|(/sbin(/.*)?)|(/srv(/.*)?)|(/sys(/.*)?)|(/tmp(/.*)?)|(/usr)|(/usr/local)|(((/usr/)|(/usr/local/))((bin)|(games)|(include)|(lib)|(libexec)|(lib[^/]+)|(sbin)|(share)|(src)|(X11R6))(/.*)?)|(/var(/.*)?)|(/bin.usr-is-merged)|(/lib.usr-is-merged)|(/sbin.usr-is-merged)|(/.dockerinit)|(/.dockerenv))$' + +## +# Regex that matches invalid Termux home paths. +# +# Same reasoning as `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`, +# and invalid paths are the same as well except that `/home` is +# allowed, and `/` and all paths under `/usr` are not allowed. +# +# `/home` is allowed as package data files are not packaged from there. +## +TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS='^((/)|(/bin(/.*)?)|(/boot(/.*)?)|(/dev(/.*)?)|(/etc(/.*)?)|(/lib(/.*)?)|(/lib[^/]+(/.*)?)|(/media)|(/mnt)|(/opt)|(/proc(/.*)?)|(/root)|(/run(/.*)?)|(/sbin(/.*)?)|(/srv(/.*)?)|(/sys(/.*)?)|(/tmp(/.*)?)|(/usr(/.*)?)|(/var(/.*)?)|(/bin.usr-is-merged)|(/lib.usr-is-merged)|(/sbin.usr-is-merged)|(/.dockerinit)|(/.dockerenv))$' + +## +# Regex that matches invalid Termux prefix paths. +# +# Same reasoning as `TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS`, +# and invalid paths are the same as well except that `/` is not +# allowed. +## +TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS='^((/)|(/bin(/.*)?)|(/boot(/.*)?)|(/dev(/.*)?)|(/etc(/.*)?)|(/home)|(/lib(/.*)?)|(/lib[^/]+(/.*)?)|(/media)|(/mnt)|(/opt)|(/proc(/.*)?)|(/root)|(/run(/.*)?)|(/sbin(/.*)?)|(/srv(/.*)?)|(/sys(/.*)?)|(/tmp(/.*)?)|(/usr)|(/usr/local)|(((/usr/)|(/usr/local/))((bin)|(games)|(include)|(lib)|(libexec)|(lib[^/]+)|(sbin)|(share)|(src)|(X11R6))(/.*)?)|(/var(/.*)?)|(/bin.usr-is-merged)|(/lib.usr-is-merged)|(/sbin.usr-is-merged)|(/.dockerinit)|(/.dockerenv))$' + + +## +# Regex that matches an unsigned integer `>= 0`. +## +TERMUX_REGEX__UNSIGNED_INT='^[0-9]+$' + + +## +# Regex to match an android app package name. +# +# The package name must have at least two segments separated by a dot +# `.`, where each segment must start with at least one character in +# the range `[a-zA-Z]`, followed by zero or more characters in the +# range `[a-zA-Z0-9_]`. The package name length must also be +# `<= 255` (`NAME_MAX` for ext4 partitions). The length is not checked +# by this regex and it must be checked with `TERMUX__NAME_MAX`, as +# `bash` `=~` regex conditional does not support lookaround. +# +# Unlike Android, the Termux app package name max length is not `255` +# as its limited by `TERMUX__APPS_DIR___MAX_LEN` and `TERMUX__ROOTFS_DIR___MAX_LEN`. +# +# - https://developer.android.com/build/configure-app-module#set-application-id +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java;l=669-677 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java;l=63-103 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/os/FileUtils.java;l=954-994 +# - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:frameworks/base/core/java/android/content/pm/PackageManager.java;l=2147-2155 +## +TERMUX_REGEX__APP_PACKAGE_NAME="^[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)+$" + +## +# Regex to match an android app data path. +# +# The supported formats are: +# - `/data/data/` (for primary user `0`) if app is to be +# installed on internal sd. +# - `/data/user//` (for all users) if app is to +# be installed on internal sd. +# `/mnt/expand//user//` if app is +# to be installed on a removable/portable volume/sd card being used as +# adoptable storage. +# +# - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-private-app-data-directory +## +TERMUX_REGEX__APP_DATA_DIR_PATH='^(((/data/data)|(/data/user/[0-9]+)|(/mnt/expand/[^/]+/user/[0-9]+))/[^/]+)$' + + + + + +### +# Variables for the Termux package repositories. +### + +# The core variable values for which the packages hosted on the +# package repos defined in `repo.json` are compiled for. +# If a custom repo is not being hosted, and official Termux repos are +# still defined in `repo.json`, then DO NOT change these values. If a +# custom repo is being hosted whose variable values equal +# `TERMUX_APP__PACKAGE_NAME`, `TERMUX_APP__DATA_DIR`, +# `TERMUX__CORE_DIR`, `TERMUX__APPS_DIR`, `TERMUX__ROOTFS`, +# `TERMUX__HOME`, and `TERMUX__PREFIX` values defined above, then +# update these values respectively to the same values. +# These values are used for the `-i/-I` flags to `build-package.sh`, +# and if respective values do not match, then those flags are ignored +# and dependency packages are not downloaded from the package repos +# and are compiled locally. +# FIXME: Checking for all variables will be added later in repo +# changes pull, currently only `TERMUX_REPO_APP__PACKAGE_NAME` is checked. +TERMUX_REPO_APP__PACKAGE_NAME="com.termux" +TERMUX_REPO_APP__DATA_DIR="/data/data/com.termux" +TERMUX_REPO__CORE_DIR="/data/data/com.termux/termux/core" +TERMUX_REPO__APPS_DIR="/data/data/com.termux/termux/apps" +TERMUX_REPO__ROOTFS="/data/data/com.termux/files" +TERMUX_REPO__HOME="/data/data/com.termux/files/home" +TERMUX_REPO__PREFIX="/data/data/com.termux/files/usr" + + + +#### +# Variables loaded from `repo.json` file for Termux package repositories. +#### -# Termux repo urls. TERMUX_REPO_URL=() TERMUX_REPO_DISTRIBUTION=() TERMUX_REPO_COMPONENT=() -export TERMUX_PACKAGES_DIRECTORIES=$(jq --raw-output 'del(.pkg_format) | keys | .[]' ${TERMUX_SCRIPTDIR}/repo.json) +export TERMUX_PACKAGES_DIRECTORIES +TERMUX_PACKAGES_DIRECTORIES=$(jq --raw-output 'del(.pkg_format) | keys | .[]' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json") -for url in $(jq -r 'del(.pkg_format) | .[] | .url' ${TERMUX_SCRIPTDIR}/repo.json); do - TERMUX_REPO_URL+=("$url") +for url in $(jq -r 'del(.pkg_format) | .[] | .url' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do + TERMUX_REPO_URL+=("$url") done -for distribution in $(jq -r 'del(.pkg_format) | .[] | .distribution' ${TERMUX_SCRIPTDIR}/repo.json); do - TERMUX_REPO_DISTRIBUTION+=("$distribution") +for distribution in $(jq -r 'del(.pkg_format) | .[] | .distribution' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do + TERMUX_REPO_DISTRIBUTION+=("$distribution") done -for component in $(jq -r 'del(.pkg_format) | .[] | .component' ${TERMUX_SCRIPTDIR}/repo.json); do - TERMUX_REPO_COMPONENT+=("$component") +for component in $(jq -r 'del(.pkg_format) | .[] | .component' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do + TERMUX_REPO_COMPONENT+=("$component") done + + + + +### +# Misc +### + +TERMUX_CLEANUP_BUILT_PACKAGES_THRESHOLD="$(( 5 * 1024 ** 3 ))" # 5 GiB +__termux_build_props__add_variables_validator_actions "TERMUX_CLEANUP_BUILT_PACKAGES_THRESHOLD" "unsigned_int" + +# Path to CGCT tools +CGCT_DEFAULT_PREFIX="/data/data/com.termux/files/usr/glibc" +__termux_build_props__add_variables_validator_actions "CGCT_DEFAULT_PREFIX" "safe_absolute_path invalid_termux_prefix_paths" + +export CGCT_DIR="/data/data/com.termux/cgct" +__termux_build_props__add_variables_validator_actions "CGCT_DIR" "safe_absolute_path invalid_termux_prefix_paths" + # Allow to override setup. for f in "${HOME}/.config/termux/termuxrc.sh" "${HOME}/.termux/termuxrc.sh" "${HOME}/.termuxrc"; do - if [ -f "$f" ]; then - echo "Using builder configuration from '$f'..." - . "$f" - break - fi + if [ -f "$f" ]; then + echo "Using builder configuration from '$f'..." + # shellcheck source=/dev/null + . "$f" + break + fi done unset f + + + + + +### +# Run Termux properties variable values validation. +### + +# Uncomment to print `TERMUX_` variables set +#compgen -v TERMUX_ | while read v; do echo "${v}=${!v}"; done + +## +# `__termux_build_props__validate_variables` +## +__termux_build_props__validate_variables() { + + local is_value_defined + local validator_action + local validator_actions + local variable_name + local variable_value + + if [[ ! "$TERMUX__INTERNAL_NAME" =~ ${TERMUX__INTERNAL_NAME_REGEX:?} ]]; then + echo "The TERMUX__INTERNAL_NAME '$TERMUX__INTERNAL_NAME' with length ${#TERMUX__INTERNAL_NAME} is invalid." 1>&2 + echo "Check 'TERMUX__INTERNAL_NAME_REGEX' variable docs for info on what is a valid internal name." 1>&2 + return 1 + fi + + if [ "${#TERMUX__INTERNAL_NAME}" -gt ${TERMUX__INTERNAL_NAME___MAX_LEN:?} ]; then + echo "The TERMUX__INTERNAL_NAME '$TERMUX__INTERNAL_NAME' with length ${#TERMUX__INTERNAL_NAME} is invalid." 1>&2 + echo "The TERMUX__INTERNAL_NAME must have max length \`<= TERMUX__INTERNAL_NAME___MAX_LEN ($TERMUX__INTERNAL_NAME___MAX_LEN)\`." 1>&2 + return 1 + fi + + if [[ ! "$TERMUX_APP__DATA_DIR" =~ ${TERMUX_REGEX__APP_DATA_DIR_PATH:?} ]]; then + echo "The TERMUX_APP__DATA_DIR '$TERMUX_APP__DATA_DIR' with length ${#TERMUX_APP__DATA_DIR} is invalid." 1>&2 + echo "The TERMUX_APP__DATA_DIR must match \`/data/data/\`, \`/data/user//\` \ +or \`/mnt/expand//user//\` formats." 1>&2 + return 1 + fi + + + for variable_name in "${__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES[@]}"; do + if [[ ! "$variable_name" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then + echo "The variable_name '$variable_name' in Termux properties variables validator actions is not a valid shell variable name." 1>&2 + return 1 + fi + + variable_value="${!variable_name:-}" + validator_actions="${__TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP["$variable_name"]}" + [[ -z "$validator_actions" ]] && continue + + if [[ -n "$variable_value" ]]; then + : + else + is_value_defined=0 + eval '[ -n "${'"$variable_name"'+x}" ] && is_value_defined=1' + + # If not defined. + if [[ "$is_value_defined" = "0" ]]; then + echo "The variable_name '$variable_name' in Termux properties variables validator actions is not defined." 1>&2 + return 1 + fi + + # If defined but unset. + [[ " ${validator_actions[*]} " == *" allow_unset_value "* ]] && continue + + echo "The Termux properties variable value for variable name '$variable_name' is not set." 1>&2 + return 1 + fi + + for validator_action in $validator_actions; do + case "$validator_action" in + allow_unset_value) + : + ;; + app_package_name) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__APP_PACKAGE_NAME:?} ]] || \ + [ "${#variable_value}" -gt "${TERMUX__NAME_MAX:?}" ]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must be a valid android app package name with \ +max length \`<= TERMUX__NAME_MAX ($TERMUX__NAME_MAX)\`." 1>&2 + echo "- https://developer.android.com/build/configure-app-module#set-application-id" 1>&2 + return 1 + fi + ;; + invalid_termux_rootfs_paths) + if [[ "$variable_value" =~ ${TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must not match one of the invalid paths \ +in TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS \`$TERMUX_REGEX__INVALID_TERMUX_ROOTFS_PATHS\`." 1>&2 + return 1 + fi + ;; + invalid_termux_home_paths) + if [[ "$variable_value" =~ ${TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must not match one of the invalid paths \ +in TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS \`$TERMUX_REGEX__INVALID_TERMUX_HOME_PATHS\`." 1>&2 + return 1 + fi + ;; + invalid_termux_prefix_paths) + if [[ "$variable_value" =~ ${TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must not match one of the invalid paths \ +in TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS \`$TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS\`." 1>&2 + return 1 + fi + ;; + path_equal_to_or_under_termux_rootfs) + if [[ "$variable_value" != "${TERMUX__ROOTFS:?}" ]] && \ + { + { [[ "${TERMUX__ROOTFS:?}" != "/" ]] && [[ "$variable_value" != "${TERMUX__ROOTFS}/"* ]]; } || \ + { [[ "${TERMUX__ROOTFS:?}" == "/" ]] && [[ "$variable_value" != "/"* ]]; }; + }; then + echo "The $variable_name '$variable_value' is invalid." 1>&2 + echo "The $variable_name must be equal to or be under TERMUX__ROOTFS \`$TERMUX__ROOTFS\`." 1>&2 + return 1 + fi + ;; + path_under_termux_rootfs) + if { [[ "${TERMUX__ROOTFS:?}" != "/" ]] && [[ "$variable_value" != "${TERMUX__ROOTFS}/"* ]]; } || \ + { [[ "${TERMUX__ROOTFS:?}" == "/" ]] && [[ "$variable_value" != "/"* ]]; }; then + echo "The $variable_name '$variable_value' is invalid." 1>&2 + echo "The $variable_name must be under TERMUX__ROOTFS \`$TERMUX__ROOTFS\`." 1>&2 + return 1 + fi + ;; + safe_absolute_path) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__SAFE_ABSOLUTE_PATH:?} ]] || \ + [[ "$variable_value" =~ ${TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must match a safe absolute path that starts with a \`/\` with at least one \ +characters under rootfs \`/\`. Duplicate or trailing path separators \`/\` are not allowed. \ +The path component characters must be in the range \`[a-zA-Z0-9+,.=_-]\`. The path must not contain single \`/./\` or \ +double \`/../\` dot components." 1>&2 + return 1 + fi + ;; + safe_relative_path) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__SAFE_RELATIVE_PATH:?} ]] || \ + [[ "$variable_value" =~ ${TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must match a safe relative path that does not start with a \`/\`. \ +Duplicate or trailing path separators \`/\` are not allowed. The path component characters must be in the \ +range \`[a-zA-Z0-9+,.=_-]\`. The path must not contain single \`/./\` or double \`/../\` dot components." 1>&2 + return 1 + fi + ;; + safe_rootfs_or_absolute_path) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__SAFE_ROOTFS_OR_ABSOLUTE_PATH:?} ]] || \ + [[ "$variable_value" =~ ${TERMUX_REGEX__SINGLE_OR_DOUBLE_DOT_CONTAINING_PATH:?} ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must match (rootfs \`/\`) or (a safe absolute path that starts with a \`/\`). \ +Duplicate or trailing path separators \`/\` are not allowed. The path component characters must be in the \ +range \`[a-zA-Z0-9+,.=_-]\`. The path must not contain single \`/./\` or double \`/../\` dot components." 1>&2 + return 1 + fi + ;; + apps_api_socket__server_parent_dir) + if [[ "${#variable_value}" -ge "${TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN:?}" ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must have max length \`<= TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN \ +($TERMUX__APPS_API_SOCKET__SERVER_PARENT_DIR___MAX_LEN)\` including the null \`\0\` terminator." 1>&2 + return 1 + fi + ;; + unix_path_max) + if [[ "${#variable_value}" -ge "${TERMUX__UNIX_PATH_MAX:?}" ]]; then + echo "The $variable_name '$variable_value' with length ${#variable_value} is invalid." 1>&2 + echo "The $variable_name must have max length \`<= TERMUX__UNIX_PATH_MAX ($TERMUX__UNIX_PATH_MAX)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + ;; + unsigned_int) + if [[ ! "$variable_value" =~ ${TERMUX_REGEX__UNSIGNED_INT:?} ]]; then + echo "The $variable_name '$variable_value' is invalid." 1>&2 + echo "The $variable_name must be an unsigned integer \`>= 0\`." 1>&2 + return 1 + fi + ;; + *) + echo "The Termux properties variables validator action '$validator_action' for \ +variable name '$variable_name' is invalid." 1>&2 + return 1 + ;; + esac + done + done + + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX_APP__DATA_DIR}" -ge ${TERMUX_APP__DATA_DIR___MAX_LEN:?} ]; then + echo "The TERMUX_APP__DATA_DIR '$TERMUX_APP__DATA_DIR' with length ${#TERMUX_APP__DATA_DIR} is invalid." 1>&2 + echo "The TERMUX_APP__DATA_DIR must have max length \`<= TERMUX_APP__DATA_DIR___MAX_LEN ($TERMUX_APP__DATA_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__APPS_DIR}" -ge ${TERMUX__APPS_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__APPS_DIR '$TERMUX__APPS_DIR' with length ${#TERMUX__APPS_DIR} is invalid." 1>&2 + echo "The TERMUX__APPS_DIR must have max length \`<= TERMUX__APPS_DIR___MAX_LEN ($TERMUX__APPS_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + + if [[ ! "$TERMUX_APP__IDENTIFIER" =~ ${TERMUX__APPS_APP_IDENTIFIER_REGEX:?} ]]; then + echo "The TERMUX_APP__IDENTIFIER '$TERMUX_APP__IDENTIFIER' with length ${#TERMUX_APP__IDENTIFIER} is invalid." 1>&2 + echo "Check 'TERMUX__APPS_APP_IDENTIFIER_REGEX' variable docs for info on what is a valid app identifier." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX_APP__IDENTIFIER}" -gt ${TERMUX__APPS_APP_IDENTIFIER___MAX_LEN:?} ]; then + echo "The TERMUX_APP__IDENTIFIER '$TERMUX_APP__IDENTIFIER' with length ${#TERMUX_APP__IDENTIFIER} is invalid." 1>&2 + echo "The TERMUX_APP__IDENTIFIER must have max length \ +\`<= TERMUX__APPS_APP_IDENTIFIER___MAX_LEN ($TERMUX__APPS_APP_IDENTIFIER___MAX_LEN)\`." 1>&2 + return 1 + fi + + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__ROOTFS}" -ge ${TERMUX__ROOTFS_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__ROOTFS '$TERMUX__ROOTFS' with length ${#TERMUX__ROOTFS} is invalid." 1>&2 + echo "The TERMUX__ROOTFS must have max length \`<= TERMUX__ROOTFS_DIR___MAX_LEN ($TERMUX__ROOTFS_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__PREFIX}" -ge ${TERMUX__PREFIX_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' with length ${#TERMUX__PREFIX} is invalid." 1>&2 + echo "The TERMUX__PREFIX must have max length \`<= TERMUX__PREFIX_DIR___MAX_LEN ($TERMUX__PREFIX_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + if [[ "$__TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN" == "true" ]] && \ + [ "${#TERMUX__PREFIX__TMP_DIR}" -ge ${TERMUX__PREFIX__TMP_DIR___MAX_LEN:?} ]; then + echo "The TERMUX__PREFIX__TMP_DIR '$TERMUX__PREFIX__TMP_DIR' with length ${#TERMUX__PREFIX__TMP_DIR} is invalid." 1>&2 + echo "The TERMUX__PREFIX__TMP_DIR must have max length \`<= TERMUX__PREFIX__TMP_DIR___MAX_LEN ($TERMUX__PREFIX__TMP_DIR___MAX_LEN)\` \ +including the null \`\0\` terminator." 1>&2 + return 1 + fi + + + if [[ "$TERMUX__ROOTFS" != "/" ]] && \ + [[ "$__TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT" == "true" ]]; then + if [[ "$TERMUX__PREFIX" != "$TERMUX__ROOTFS/usr" ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' is invalid." 1>&2 + echo "The TERMUX__PREFIX must be equal to '\$TERMUX__ROOTFS/usr' ($TERMUX__ROOTFS/usr) as per 'usr' merge format." 1>&2 + return 1 + fi + + if [[ "${TERMUX__PREFIX:?}" == "${TERMUX__HOME:?}" ]] || \ + [[ "$TERMUX__PREFIX" == "$TERMUX__HOME/"* ]] || \ + [[ "$TERMUX__HOME" == "$TERMUX__PREFIX/"* ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' or TERMUX__HOME '$TERMUX__HOME' is invalid." 1>&2 + echo "The TERMUX__PREFIX must not be equal to TERMUX__HOME and they must not be under each other as per 'usr' merge format." 1>&2 + return 1 + fi + else + if [[ "${TERMUX__PREFIX:?}" == "${TERMUX__HOME:?}" ]] || \ + [[ "$TERMUX__PREFIX" == "$TERMUX__HOME/"* ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' or TERMUX__HOME '$TERMUX__HOME' is invalid." 1>&2 + echo "The TERMUX__PREFIX must not be equal to or be under TERMUX__HOME." 1>&2 + return 1 + fi + fi + + if [[ "${TERMUX__PREFIX:?}" == "${CGCT_DIR:?}" ]] || \ + [[ "$TERMUX__PREFIX" == "$CGCT_DIR/"* ]] || \ + [[ "$CGCT_DIR" == "$TERMUX__PREFIX/"* ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' or CGCT_DIR '$CGCT_DIR' is invalid." 1>&2 + echo "The TERMUX__PREFIX must not be equal to CGCT_DIR and they must not be under each other." 1>&2 + return 1 + fi + + if [[ "$TERMUX__PREFIX" != "$TERMUX_PREFIX_CLASSICAL" ]]; then + echo "The TERMUX__PREFIX '$TERMUX__PREFIX' or TERMUX_PREFIX_CLASSICAL '$TERMUX_PREFIX_CLASSICAL' is invalid." 1>&2 + echo "The TERMUX__PREFIX must be equal to TERMUX_PREFIX_CLASSICAL." 1>&2 + return 1 + fi + +} + +__termux_build_props__validate_variables || exit $? + +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_MAP +unset __TERMUX_BUILD_PROPS__VARIABLES_VALIDATOR_ACTIONS_VARIABLE_NAMES +unset __TERMUX_BUILD_PROPS__VALIDATE_PATHS_MAX_LEN +unset __TERMUX_BUILD_PROPS__VALIDATE_TERMUX_PREFIX_USR_MERGE_FORMAT +unset __termux_build_props__add_variables_validator_actions +unset __termux_build_props__validate_variables From 0e4a2580db26d3d778ded60d872c1b3b7f06e13e Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 1 Apr 2024 06:58:40 +0500 Subject: [PATCH 03/28] fix(scripts): do not delete `/home/builder/.termux-build` build directories for package before checking if `/data/data/.built-packages` file exists for package in `termux_step_start_build` This was caused by 6445645d, where removal was called before even calling `termux_step_start_build` method in `build-package.sh`. So if you built a package, then ran build command again, it would exit with the "skipping (rm $TERMUX_BUILT_PACKAGES_DIRECTORY/$TERMUX_PKG_NAME to force rebuild" message, but built files would already have been deleted and everything will need to be built again even if you just removed `$TERMUX_BUILT_PACKAGES_DIRECTORY/$TERMUX_PKG_NAME` (`/data/data/.built-packages`) file for the package. --- build-package.sh | 4 ---- scripts/build/termux_step_setup_build_folders.sh | 6 ++++++ scripts/build/termux_step_start_build.sh | 10 +++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/build-package.sh b/build-package.sh index e1cb93f3a2f3baa..57d27ec349166d6 100755 --- a/build-package.sh +++ b/build-package.sh @@ -622,10 +622,6 @@ for ((i=0; i<${#PACKAGE_LIST[@]}; i++)); do termux_step_setup_variables termux_step_handle_buildarch - if [ "$TERMUX_CONTINUE_BUILD" == "false" ]; then - termux_step_setup_build_folders - fi - termux_step_cleanup_packages termux_step_start_build diff --git a/scripts/build/termux_step_setup_build_folders.sh b/scripts/build/termux_step_setup_build_folders.sh index 8c11ca9eeb3ac46..adfbe124ee5b883 100644 --- a/scripts/build/termux_step_setup_build_folders.sh +++ b/scripts/build/termux_step_setup_build_folders.sh @@ -41,4 +41,10 @@ termux_step_setup_build_folders() { mkdir -p $TERMUX_PREFIX/{bin,etc,lib,share,share/LICENSES,include} mkdir -p $TERMUX_PREFIX_CLASSICAL/{bin,etc,tmp} fi + + # Required for creating `BUILDING_IN_SRC.txt` file in termux_step_start_build + if [ "$TERMUX_PKG_BUILDDIR_ORIG" != "$TERMUX_PKG_BUILDDIR" ]; then + rm -Rf "$TERMUX_PKG_BUILDDIR_ORIG" + mkdir -p "$TERMUX_PKG_BUILDDIR_ORIG" + fi } diff --git a/scripts/build/termux_step_start_build.sh b/scripts/build/termux_step_start_build.sh index f4c889aacddfe6e..8d781c05dc3c5ba 100644 --- a/scripts/build/termux_step_start_build.sh +++ b/scripts/build/termux_step_start_build.sh @@ -68,8 +68,8 @@ termux_step_start_build() { # Avoid exporting PKG_CONFIG_LIBDIR until after termux_step_host_build. export TERMUX_PKG_CONFIG_LIBDIR=$TERMUX_PREFIX/lib/pkgconfig:$TERMUX_PREFIX/share/pkgconfig + local TERMUX_PKG_BUILDDIR_ORIG="$TERMUX_PKG_BUILDDIR" if [ "$TERMUX_PKG_BUILD_IN_SRC" = "true" ]; then - echo "Building in src due to TERMUX_PKG_BUILD_IN_SRC being set to true" > "$TERMUX_PKG_BUILDDIR/BUILDING_IN_SRC.txt" TERMUX_PKG_BUILDDIR=$TERMUX_PKG_SRCDIR fi @@ -88,6 +88,14 @@ termux_step_start_build() { termux_error_exit "Package '$TERMUX_PKG_NAME' is not available for on-device builds." fi + # Delete and re-create the directories used for building the package + termux_step_setup_build_folders + + if [ "$TERMUX_PKG_BUILD_IN_SRC" = "true" ]; then + # Create a file for users to know that the build directory not containing any built files is expected behaviour + echo "Building in src due to TERMUX_PKG_BUILD_IN_SRC being set to true" > "$TERMUX_PKG_BUILDDIR_ORIG/BUILDING_IN_SRC.txt" + fi + if [ "$TERMUX_PACKAGE_LIBRARY" = "bionic" ]; then if [ "$TERMUX_ON_DEVICE_BUILD" = "true" ]; then case "$TERMUX_APP_PACKAGE_MANAGER" in From 8526c61d3de68b9bcd92ae11fecf051bce545a89 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 1 Apr 2024 11:32:24 +0500 Subject: [PATCH 04/28] enhance(scripts): add support to build a package from a local source directory set in `$TERMUX_PKG_SRCURL` if its in the format `file:///path/to/source/dir` The `/path/to/source/dir` must be an absolute and normalized path to a source directory on the local file system. Whatever was the current state of the local source directory when build was started, that is what will be built, without any changes to any `git` branches/tags. Any uncommitted changes to current `git` branch will also get built. Any value for `TERMUX_PKG_GIT_BRANCH` in the `build.sh` of the package will be ignored since a `tar` file will be created from the source directory and it will be used as is. No checksum checks like one set in `$TERMUX_PKG_SHA256` will be done, and a tar for source directory will be created every time package is built, assuming `-f/-F` flags are passed for rebuilds, and the `-r` flag (to be added later) will not be required. Note that to build from a local git repo, the `git+file:///path/to/source` format must be used with the `git+` prefix. --- .../get_source/termux_download_src_archive.sh | 2 +- scripts/build/termux_download.sh | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/scripts/build/get_source/termux_download_src_archive.sh b/scripts/build/get_source/termux_download_src_archive.sh index e74b691e14f746a..768c380d9317e72 100644 --- a/scripts/build/get_source/termux_download_src_archive.sh +++ b/scripts/build/get_source/termux_download_src_archive.sh @@ -7,7 +7,7 @@ termux_download_src_archive() { for i in $(seq 0 $(( ${#PKG_SRCURL[@]}-1 ))); do local file="$TERMUX_PKG_CACHEDIR/$(basename "${PKG_SRCURL[$i]}")" - if [ "${PKG_SHA256[$i]}" == "" ]; then + if [ "${PKG_SHA256[$i]:-}" == "" ]; then termux_download "${PKG_SRCURL[$i]}" "$file" else termux_download "${PKG_SRCURL[$i]}" "$file" "${PKG_SHA256[$i]}" diff --git a/scripts/build/termux_download.sh b/scripts/build/termux_download.sh index 08144a9b0c49e3e..a605375a910d7c9 100755 --- a/scripts/build/termux_download.sh +++ b/scripts/build/termux_download.sh @@ -1,15 +1,30 @@ #!/usr/bin/bash termux_download() { - if [[ $# != 3 ]]; then - echo "termux_download(): Invalid arguments - expected \$URL \$DESTINATION \$CHECKSUM" 1>&2 + if [[ $# != 2 ]] && [[ $# != 3 ]]; then + echo "termux_download(): Invalid arguments - expected []" 1>&2 return 1 fi local URL="$1" local DESTINATION="$2" - local CHECKSUM="$3" + local CHECKSUM="${3:-SKIP_CHECKSUM}" - if [[ -f "$DESTINATION" ]] && [[ "$CHECKSUM" != "SKIP_CHECKSUM" ]]; then + if [[ "$URL" =~ ^file://(/[^/]+)+$ ]]; then + local source="${URL:7}" # Remove `file://` prefix + + if [ -d "$source" ]; then + # Create tar file from local directory + echo "Downloading local source directory at '$source'" + rm -f "$DESTINATION" + (cd "$(dirname "$source")" && tar -cf "$DESTINATION" --exclude=".git" "$(basename "$source")") + return 0 + elif [ ! -f "$source" ]; then + echo "No local source file found at path of URL '$URL'" + return 1 + fi + fi + + if [ -f "$DESTINATION" ] && [ "$CHECKSUM" != "SKIP_CHECKSUM" ]; then # Keep existing file if checksum matches. local EXISTING_CHECKSUM EXISTING_CHECKSUM=$(sha256sum "$DESTINATION" | cut -d' ' -f1) From 77319086f1cf5e5c6d4b288fcf08cd4a10c21124 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 1 Apr 2024 10:38:37 +0500 Subject: [PATCH 05/28] fix(scripts): fix building a package from a local git repository set in `$TERMUX_PKG_SRCURL` if its in the format `git+file:///path/to/source/git/dir` This got broken a while ago due to changes in pulling sources, although current format wasn't mandated. The `$TERMUX_PKG_SRCURL` must start with `git+` prefix like already used for other git `http` urls, followed by a `file://` url, where `/path/to/source/git/dir` is an absolute and normalized path to a source git directory on the local file system, with a `.git` sub directory. If the source directory has been cloned already in a previous build, then it will NOT be cloned again if `-f/-F` flags are passed for rebuilds, and the `-r` flag (to be added later) will be required to clone latest sources again/every time. Any uncommitted changes to `git` branch will NOT get built. An additional requirement is that the local git repository must have its `origin` url in `.git/config` as a `https` URL instead of a `ssh` (`git@`) URL if running in termux-packages docker container and `$TERMUX_PKG_GIT_BRANCH` is set, as it doesn't have `ssh` installed by default and `git fetch` would fail otherwise. So if local git repository needs to be cloned from an upstream git url itself, like GitHub, then use `https://github.com/org/repo.git` to clone instead of `git@github.com:org/repo.git`. Or you can install `ssh` inside docker and set up ssh keys manually. --- .../build/get_source/termux_git_clone_src.sh | 46 +++++++++++++++++-- .../get_source/termux_step_get_source.sh | 2 + 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/scripts/build/get_source/termux_git_clone_src.sh b/scripts/build/get_source/termux_git_clone_src.sh index 7f310da61b113b2..f66ef123e58290b 100644 --- a/scripts/build/get_source/termux_git_clone_src.sh +++ b/scripts/build/get_source/termux_git_clone_src.sh @@ -1,16 +1,50 @@ termux_git_clone_src() { local TMP_CHECKOUT=$TERMUX_PKG_CACHEDIR/tmp-checkout local TMP_CHECKOUT_VERSION=$TERMUX_PKG_CACHEDIR/tmp-checkout-version + local termux_pkg_srcurl="${TERMUX_PKG_SRCURL:4}" + local termux_pkg_local_srcpath="" + local termux_pkg_branch_flags="" + + if [[ "$termux_pkg_srcurl" =~ ^file://(/[^/]+)+$ ]]; then + termux_pkg_local_srcpath="${termux_pkg_srcurl:7}" # Remove `file://` prefix + + if [ ! -d "$termux_pkg_local_srcpath" ]; then + echo "No source directory found at path of TERMUX_PKG_SRCURL '$TERMUX_PKG_SRCURL' of package '$TERMUX_PKG_NAME'" + return 1 + elif [ ! -d "$termux_pkg_local_srcpath/.git" ]; then + echo "The source directory at path of TERMUX_PKG_SRCURL '$TERMUX_PKG_SRCURL' of package '$TERMUX_PKG_NAME' does not a contain a '.git' sub directory" + return 1 + fi + fi if [ ! -f $TMP_CHECKOUT_VERSION ] || [ "$(cat $TMP_CHECKOUT_VERSION)" != "$TERMUX_PKG_VERSION" ]; then - if [ "$TERMUX_PKG_GIT_BRANCH" == "" ]; then - TERMUX_PKG_GIT_BRANCH=v${TERMUX_PKG_VERSION#*:} + if [[ -n "$termux_pkg_local_srcpath" ]]; then + if [ "$TERMUX_PKG_GIT_BRANCH" != "" ]; then + # The local git repository that needs to be cloned may + # not have a branch created that is tracking its remote + # branch, so we create it if it doesn't exist without + # checking it out, otherwise when we clone below, + # git will fail to find the branch in its own origin + # i.e the local git repository, as it will not look + # into the origin of the local git repository recursively. + (cd "$termux_pkg_local_srcpath" && git fetch origin $TERMUX_PKG_GIT_BRANCH:$TERMUX_PKG_GIT_BRANCH) + termux_pkg_branch_flags="--branch $TERMUX_PKG_GIT_BRANCH" + fi + else + if [ "$TERMUX_PKG_GIT_BRANCH" == "" ]; then + termux_pkg_branch_flags="--branch v${TERMUX_PKG_VERSION#*:}" + else + termux_pkg_branch_flags="--branch $TERMUX_PKG_GIT_BRANCH" + fi fi + echo "Downloading git source $([[ "$termux_pkg_branch_flags" != "" ]] && echo "with branch '${termux_pkg_branch_flags:9}' ")from '$termux_pkg_srcurl'" + rm -rf $TMP_CHECKOUT - git clone --depth 1 \ - --branch $TERMUX_PKG_GIT_BRANCH \ - ${TERMUX_PKG_SRCURL:4} \ + git clone \ + --depth 1 \ + $termux_pkg_branch_flags \ + "$termux_pkg_srcurl" \ $TMP_CHECKOUT pushd $TMP_CHECKOUT @@ -38,6 +72,8 @@ termux_git_clone_src() { popd echo "$TERMUX_PKG_VERSION" > $TMP_CHECKOUT_VERSION + else + echo "Skipped downloading of git source from '$termux_pkg_srcurl'" fi rm -rf $TERMUX_PKG_SRCDIR diff --git a/scripts/build/get_source/termux_step_get_source.sh b/scripts/build/get_source/termux_step_get_source.sh index 2c914ed34022a83..972524c1f772821 100644 --- a/scripts/build/get_source/termux_step_get_source.sh +++ b/scripts/build/get_source/termux_step_get_source.sh @@ -2,12 +2,14 @@ termux_step_get_source() { : "${TERMUX_PKG_SRCURL:=""}" if [ "${TERMUX_PKG_SRCURL:0:4}" == "git+" ]; then + [ ! "$TERMUX_QUIET_BUILD" = true ] && echo "Downloading $TERMUX_PKG_NAME@$TERMUX_PKG_VERSION git source from '$TERMUX_PKG_SRCURL' if necessary..." termux_git_clone_src else if [ -z "${TERMUX_PKG_SRCURL}" ] || [ "${TERMUX_PKG_SKIP_SRC_EXTRACT-false}" = "true" ] || [ "$TERMUX_PKG_METAPACKAGE" = "true" ]; then mkdir -p "$TERMUX_PKG_SRCDIR" return fi + [ ! "$TERMUX_QUIET_BUILD" = true ] && echo "Downloading $TERMUX_PKG_NAME@$TERMUX_PKG_VERSION source from '$TERMUX_PKG_SRCURL' if necessary..." termux_download_src_archive cd $TERMUX_PKG_TMPDIR termux_extract_src_archive From f579c504b07e97c742595c58f9f16247f27bfb5e Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 2 Apr 2024 01:52:19 +0500 Subject: [PATCH 06/28] fix(scripts): quote variables (primarily) for `rm` commands --- scripts/build/get_source/termux_git_clone_src.sh | 12 ++++++------ .../build/get_source/termux_unpack_src_archive.sh | 4 ++-- scripts/build/termux_step_setup_build_folders.sh | 8 ++++---- .../build/toolchain/termux_setup_toolchain_23c.sh | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/scripts/build/get_source/termux_git_clone_src.sh b/scripts/build/get_source/termux_git_clone_src.sh index f66ef123e58290b..579795f3c402337 100644 --- a/scripts/build/get_source/termux_git_clone_src.sh +++ b/scripts/build/get_source/termux_git_clone_src.sh @@ -40,14 +40,14 @@ termux_git_clone_src() { echo "Downloading git source $([[ "$termux_pkg_branch_flags" != "" ]] && echo "with branch '${termux_pkg_branch_flags:9}' ")from '$termux_pkg_srcurl'" - rm -rf $TMP_CHECKOUT + rm -rf "$TMP_CHECKOUT" git clone \ --depth 1 \ $termux_pkg_branch_flags \ "$termux_pkg_srcurl" \ - $TMP_CHECKOUT + "$TMP_CHECKOUT" - pushd $TMP_CHECKOUT + pushd "$TMP_CHECKOUT" # Workaround some bad server behaviour # error: Server does not allow request for unadvertised object commit_no @@ -71,11 +71,11 @@ termux_git_clone_src() { popd - echo "$TERMUX_PKG_VERSION" > $TMP_CHECKOUT_VERSION + echo "$TERMUX_PKG_VERSION" > "$TMP_CHECKOUT_VERSION" else echo "Skipped downloading of git source from '$termux_pkg_srcurl'" fi - rm -rf $TERMUX_PKG_SRCDIR - cp -Rf $TMP_CHECKOUT $TERMUX_PKG_SRCDIR + rm -rf "$TERMUX_PKG_SRCDIR" + cp -Rf "$TMP_CHECKOUT" "$TERMUX_PKG_SRCDIR" } diff --git a/scripts/build/get_source/termux_unpack_src_archive.sh b/scripts/build/get_source/termux_unpack_src_archive.sh index 320b28ff162812a..d6eaf6aa0ed08df 100644 --- a/scripts/build/get_source/termux_unpack_src_archive.sh +++ b/scripts/build/get_source/termux_unpack_src_archive.sh @@ -10,9 +10,9 @@ termux_extract_src_archive() { set +o pipefail if [ "${file##*.}" = zip ]; then folder=$(unzip -qql "$file" | head -n1 | tr -s ' ' | cut -d' ' -f5-) - rm -Rf $folder + rm -Rf "$folder" unzip -q "$file" - mv $folder "$TERMUX_PKG_SRCDIR" + mv "$folder" "$TERMUX_PKG_SRCDIR" else test "$i" -gt 0 && STRIP=0 mkdir -p "$TERMUX_PKG_SRCDIR" diff --git a/scripts/build/termux_step_setup_build_folders.sh b/scripts/build/termux_step_setup_build_folders.sh index adfbe124ee5b883..633e1bde9c88af8 100644 --- a/scripts/build/termux_step_setup_build_folders.sh +++ b/scripts/build/termux_step_setup_build_folders.sh @@ -11,8 +11,8 @@ termux_step_setup_build_folders() { [ "$TERMUX_ON_DEVICE_BUILD" = false ]; then # Remove all previously extracted/built files from # $TERMUX_PREFIX: - rm -fr $TERMUX_PREFIX_CLASSICAL - rm -f $TERMUX_BUILT_PACKAGES_DIRECTORY/* + rm -fr "$TERMUX_PREFIX_CLASSICAL" + rm -f "$TERMUX_BUILT_PACKAGES_DIRECTORY"/* fi # Cleanup old build state: @@ -38,8 +38,8 @@ termux_step_setup_build_folders() { if [ "$TERMUX_PACKAGE_LIBRARY" = "bionic" ]; then mkdir -p $TERMUX_PREFIX/{bin,etc,lib,libexec,share,share/LICENSES,tmp,include} elif [ "$TERMUX_PACKAGE_LIBRARY" = "glibc" ]; then - mkdir -p $TERMUX_PREFIX/{bin,etc,lib,share,share/LICENSES,include} - mkdir -p $TERMUX_PREFIX_CLASSICAL/{bin,etc,tmp} + mkdir -p "$TERMUX_PREFIX"/{bin,etc,lib,share,share/LICENSES,include} + mkdir -p "$TERMUX_PREFIX_CLASSICAL"/{bin,etc,tmp} fi # Required for creating `BUILDING_IN_SRC.txt` file in termux_step_start_build diff --git a/scripts/build/toolchain/termux_setup_toolchain_23c.sh b/scripts/build/toolchain/termux_setup_toolchain_23c.sh index 9a35d524de474bc..5ddee9a7d0d9193 100644 --- a/scripts/build/toolchain/termux_setup_toolchain_23c.sh +++ b/scripts/build/toolchain/termux_setup_toolchain_23c.sh @@ -128,7 +128,7 @@ termux_setup_toolchain_23c() { # Do not put toolchain in place until we are done with setup, to avoid having a half setup # toolchain left in place if something goes wrong (or process is just aborted): local _TERMUX_TOOLCHAIN_TMPDIR=${TERMUX_STANDALONE_TOOLCHAIN}-tmp - rm -Rf $_TERMUX_TOOLCHAIN_TMPDIR + rm -Rf "$_TERMUX_TOOLCHAIN_TMPDIR" local _NDK_ARCHNAME=$TERMUX_ARCH if [ "$TERMUX_ARCH" = "aarch64" ]; then @@ -139,7 +139,7 @@ termux_setup_toolchain_23c() { cp $NDK/toolchains/llvm/prebuilt/linux-x86_64 $_TERMUX_TOOLCHAIN_TMPDIR -r # Remove android-support header wrapping not needed on android-21: - rm -Rf $_TERMUX_TOOLCHAIN_TMPDIR/sysroot/usr/local + rm -Rf "$_TERMUX_TOOLCHAIN_TMPDIR/sysroot/usr/local" for HOST_PLAT in aarch64-linux-android armv7a-linux-androideabi i686-linux-android x86_64-linux-android; do cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT$TERMUX_PKG_API_LEVEL-clang \ From 11d51ed7bd40784c22fd66f77d85edf5624a62a9 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 2 Apr 2024 03:31:12 +0500 Subject: [PATCH 07/28] change(scripts): rename `TERMUX_NO_CLEAN` variable to `TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES` --- build-package.sh | 2 +- scripts/build/termux_step_setup_build_folders.sh | 2 +- scripts/build/termux_step_setup_variables.sh | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build-package.sh b/build-package.sh index 57d27ec349166d6..4e70a494b57e1d9 100755 --- a/build-package.sh +++ b/build-package.sh @@ -498,7 +498,7 @@ while (($# >= 1)); do ;; -I) export TERMUX_INSTALL_DEPS=true - export TERMUX_NO_CLEAN=true + export TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES=false ;; -L) export TERMUX_GLOBAL_LIBRARY=true;; -q) export TERMUX_QUIET_BUILD=true;; diff --git a/scripts/build/termux_step_setup_build_folders.sh b/scripts/build/termux_step_setup_build_folders.sh index 633e1bde9c88af8..0a36f115588f1b1 100644 --- a/scripts/build/termux_step_setup_build_folders.sh +++ b/scripts/build/termux_step_setup_build_folders.sh @@ -7,7 +7,7 @@ termux_step_setup_build_folders() { if [ "$TERMUX_SKIP_DEPCHECK" = false ] && \ [ "$TERMUX_INSTALL_DEPS" = true ] && \ [ "$TERMUX_PKG_METAPACKAGE" = false ] && \ - [ "$TERMUX_NO_CLEAN" = false ] && \ + [ "$TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES" = true ] && \ [ "$TERMUX_ON_DEVICE_BUILD" = false ]; then # Remove all previously extracted/built files from # $TERMUX_PREFIX: diff --git a/scripts/build/termux_step_setup_variables.sh b/scripts/build/termux_step_setup_variables.sh index b63c908f3945a8f..a40b60687824f5f 100644 --- a/scripts/build/termux_step_setup_variables.sh +++ b/scripts/build/termux_step_setup_variables.sh @@ -6,7 +6,7 @@ termux_step_setup_variables() { : "${TERMUX_FORCE_BUILD_DEPENDENCIES:="false"}" : "${TERMUX_INSTALL_DEPS:="false"}" : "${TERMUX_PKG_MAKE_PROCESSES:="$(nproc)"}" - : "${TERMUX_NO_CLEAN:="false"}" + : "${TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES:="true"}" : "${TERMUX_PKG_API_LEVEL:="24"}" : "${TERMUX_CONTINUE_BUILD:="false"}" : "${TERMUX_QUIET_BUILD:="false"}" @@ -49,7 +49,7 @@ termux_step_setup_variables() { # For on-device builds cross-compiling is not supported so we can # store information about built packages under $TERMUX_TOPDIR. TERMUX_BUILT_PACKAGES_DIRECTORY="$TERMUX_TOPDIR/.built-packages" - TERMUX_NO_CLEAN="true" + TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES="false" if [ "$TERMUX_PACKAGE_LIBRARY" = "bionic" ]; then # On-device builds without termux-exec are unsupported. From 9159eab18ae69a102d56ca3b24cec779f1e6f20c Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 2 Apr 2024 08:37:44 +0500 Subject: [PATCH 08/28] fix(build-package.sh): fix passing flags to sub `build-package.sh` process if `-a all` is passed The `${parameter+word}` expansion will substitute word if "parameter is Set and Not Null" and also if "parameter is Set But Null". So if a variable is unset, or if set to any random value or empty string, even if its "false", then the flag will get passed. For example running `TERMUX_DEBUG_BUILD= ./build-package.sh -I -f -r -a all termux-tools` or `TERMUX_DEBUG_BUILD=false ./build-package.sh -I -f -r -a all termux-tools` will pass the `-f` flag, even though it should not be. Ideally, command option variables should set to their default values before command options are parsed. Additionally, the `${parameter:=word}` expansion will assign the word to the variable itself if "parameter is Set But Null" or "parameter is Unset", instead of just substituting temporarily, which would then cause potential problems when default value is to be set to the variables later in "current" build-package.sh process depending on some variable dependent logic, as variables would already be set. The "sub" build-package.sh process should not be passed `debian` and `bionic` as defaults, it should set its own default variables. Also pass the `-i` and `-I` flag appropriately depending on what was originally passed instead of always passing the `-i` flag if `$TERMUX_INSTALL_DEPS` is set (to whatever), which would wipe TERMUX_PREFIX and built-packages dir. https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02 --- build-package.sh | 19 +++++++++++++------ scripts/build/termux_step_get_dependencies.sh | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/build-package.sh b/build-package.sh index 4e70a494b57e1d9..e9086af89a6652f 100755 --- a/build-package.sh +++ b/build-package.sh @@ -582,12 +582,19 @@ for ((i=0; i<${#PACKAGE_LIST[@]}; i++)); do if [ "$TERMUX_ON_DEVICE_BUILD" = "false" ] && [ -n "${TERMUX_ARCH+x}" ] && [ "${TERMUX_ARCH}" = 'all' ]; then for arch in 'aarch64' 'arm' 'i686' 'x86_64'; do env TERMUX_ARCH="$arch" TERMUX_BUILD_IGNORE_LOCK=true ./build-package.sh \ - ${TERMUX_FORCE_BUILD+-f} ${TERMUX_INSTALL_DEPS+-i} ${TERMUX_IS_DISABLED+-D} \ - ${TERMUX_DEBUG_BUILD+-d} ${TERMUX_OUTPUT_DIR+-o $TERMUX_OUTPUT_DIR} \ - ${TERMUX_FORCE_BUILD_DEPENDENCIES+-F} ${TERMUX_GLOBAL_LIBRARY+-L} \ - ${TERMUX_WITHOUT_DEPVERSION_BINDING+-w} ${TERMUX_CLEANUP_BUILT_PACKAGES_ON_LOW_DISK_SPACE+-C} \ - --format ${TERMUX_PACKAGE_FORMAT:=debian} \ - --library ${TERMUX_PACKAGE_LIBRARY:=bionic} "${PACKAGE_LIST[i]}" + $(test "${TERMUX_CLEANUP_BUILT_PACKAGES_ON_LOW_DISK_SPACE:-}" = "true" && echo "-C") \ + $(test "${TERMUX_DEBUG_BUILD:-}" = "true" && echo "-d") \ + $(test "${TERMUX_IS_DISABLED:-}" = "true" && echo "-D") \ + $({ test "${TERMUX_FORCE_BUILD:-}" = "true" && test "${TERMUX_FORCE_BUILD_DEPENDENCIES:-}" != "true"; } && echo "-f") \ + $({ test "${TERMUX_FORCE_BUILD:-}" = "true" && test "${TERMUX_FORCE_BUILD_DEPENDENCIES:-}" = "true"; } && echo "-F") \ + $({ test "${TERMUX_INSTALL_DEPS:-}" = "true" && test "${TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES:-}" != "false"; } && echo "-i") \ + $({ test "${TERMUX_INSTALL_DEPS:-}" = "true" && test "${TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES:-}" = "false"; } && echo "-I") \ + $(test "${TERMUX_GLOBAL_LIBRARY:-}" = "true" && echo "-L") \ + $(test -n "${TERMUX_OUTPUT_DIR:-}" && echo "-o $TERMUX_OUTPUT_DIR") \ + $(test "${TERMUX_WITHOUT_DEPVERSION_BINDING:-}" = "true" && echo "-w") \ + $(test -n "${TERMUX_PACKAGE_FORMAT:-}" && echo "--format $TERMUX_PACKAGE_FORMAT") \ + $(test -n "${TERMUX_PACKAGE_LIBRARY:-}" && echo "--library $TERMUX_PACKAGE_LIBRARY") \ + "${PACKAGE_LIST[i]}" done exit fi diff --git a/scripts/build/termux_step_get_dependencies.sh b/scripts/build/termux_step_get_dependencies.sh index fc0d39cdf8c3556..999b830bbf21382 100644 --- a/scripts/build/termux_step_get_dependencies.sh +++ b/scripts/build/termux_step_get_dependencies.sh @@ -134,7 +134,7 @@ termux_run_build-package() { fi TERMUX_BUILD_IGNORE_LOCK=true ./build-package.sh \ $(test "${TERMUX_INSTALL_DEPS}" = "true" && echo "-I" || echo "-s") \ - $(test "${TERMUX_FORCE_BUILD_DEPENDENCIES}" = "true" && echo "-F") \ + $({ test "${TERMUX_FORCE_BUILD}" = "true" && test "${TERMUX_FORCE_BUILD_DEPENDENCIES}" = "true"; } && echo "-F") \ $(test "${TERMUX_WITHOUT_DEPVERSION_BINDING}" = "true" && echo "-w") \ --format $TERMUX_PACKAGE_FORMAT --library $set_library "${PKG_DIR}" } From ac430183256851f823a0fc83e2a3da9780197e0f Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 2 Apr 2024 07:06:48 +0500 Subject: [PATCH 09/28] enhance(build-package.sh): add -r option to remove all package build dependent directories that `-f/-F` flags alone would not remove This includes the package cache directory (`$TERMUX_PKG_CACHEDIR`) containing package sources and host build directory (`$TERMUX_PKG_HOSTBUILD_DIR`). The `-r` flag is ignored if `-f/-F` flags are not passed. With `-f/-F` flags alone, latest package sources will not be re-downloaded on rebuilds if `$TERMUX_PKG_SRCURL` refers to `git+` URL, i.e for a branch (like `master`) that's being continuously updated on a remote git repository (`git+https://`) or in a local git repository directory (`git+file://`). --- build-package.sh | 6 ++++++ scripts/build/termux_step_get_dependencies.sh | 1 + scripts/build/termux_step_setup_build_folders.sh | 6 ++++++ scripts/build/termux_step_setup_variables.sh | 1 + 4 files changed, 14 insertions(+) diff --git a/build-package.sh b/build-package.sh index e9086af89a6652f..c9469b6218e0eb6 100755 --- a/build-package.sh +++ b/build-package.sh @@ -433,6 +433,10 @@ _show_usage() { echo " -L The package and its dependencies will be based on the same library." echo " -q Quiet build." echo " -Q Loud build -- set -x debug output." + echo " -r Remove all package build dependent dirs that '-f/-F'" + echo " flags alone would not remove, like cache dir containing " + echo " package sources and host build dir. Ignored if '-f/-F'" + echo " flags are not passed." echo " -w Install dependencies without version binding." echo " -s Skip dependency check." echo " -o Specify directory where to put built packages. Default: output/." @@ -503,6 +507,7 @@ while (($# >= 1)); do -L) export TERMUX_GLOBAL_LIBRARY=true;; -q) export TERMUX_QUIET_BUILD=true;; -Q) set -x;; + -r) export TERMUX_PKGS__BUILD__RM_ALL_PKG_BUILD_DEPENDENT_DIRS=true;; -w) export TERMUX_WITHOUT_DEPVERSION_BINDING=true;; -s) export TERMUX_SKIP_DEPCHECK=true;; -o) @@ -591,6 +596,7 @@ for ((i=0; i<${#PACKAGE_LIST[@]}; i++)); do $({ test "${TERMUX_INSTALL_DEPS:-}" = "true" && test "${TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES:-}" = "false"; } && echo "-I") \ $(test "${TERMUX_GLOBAL_LIBRARY:-}" = "true" && echo "-L") \ $(test -n "${TERMUX_OUTPUT_DIR:-}" && echo "-o $TERMUX_OUTPUT_DIR") \ + $(test "${TERMUX_PKGS__BUILD__RM_ALL_PKG_BUILD_DEPENDENT_DIRS:-}" = "true" && echo "-r") \ $(test "${TERMUX_WITHOUT_DEPVERSION_BINDING:-}" = "true" && echo "-w") \ $(test -n "${TERMUX_PACKAGE_FORMAT:-}" && echo "--format $TERMUX_PACKAGE_FORMAT") \ $(test -n "${TERMUX_PACKAGE_LIBRARY:-}" && echo "--library $TERMUX_PACKAGE_LIBRARY") \ diff --git a/scripts/build/termux_step_get_dependencies.sh b/scripts/build/termux_step_get_dependencies.sh index 999b830bbf21382..1abd12385b12b5f 100644 --- a/scripts/build/termux_step_get_dependencies.sh +++ b/scripts/build/termux_step_get_dependencies.sh @@ -135,6 +135,7 @@ termux_run_build-package() { TERMUX_BUILD_IGNORE_LOCK=true ./build-package.sh \ $(test "${TERMUX_INSTALL_DEPS}" = "true" && echo "-I" || echo "-s") \ $({ test "${TERMUX_FORCE_BUILD}" = "true" && test "${TERMUX_FORCE_BUILD_DEPENDENCIES}" = "true"; } && echo "-F") \ + $(test "${TERMUX_PKGS__BUILD__RM_ALL_PKG_BUILD_DEPENDENT_DIRS}" = "true" && echo "-r") \ $(test "${TERMUX_WITHOUT_DEPVERSION_BINDING}" = "true" && echo "-w") \ --format $TERMUX_PACKAGE_FORMAT --library $set_library "${PKG_DIR}" } diff --git a/scripts/build/termux_step_setup_build_folders.sh b/scripts/build/termux_step_setup_build_folders.sh index 0a36f115588f1b1..2f5cbb8eb05c72d 100644 --- a/scripts/build/termux_step_setup_build_folders.sh +++ b/scripts/build/termux_step_setup_build_folders.sh @@ -24,6 +24,12 @@ termux_step_setup_build_folders() { "$TERMUX_PKG_TMPDIR" \ "$TERMUX_PKG_MASSAGEDIR" + # Cleanup cache directory containing package sources and hostbuild dir + if [ "$TERMUX_FORCE_BUILD" = true ] && \ + [ "$TERMUX_PKGS__BUILD__RM_ALL_PKG_BUILD_DEPENDENT_DIRS" = true ]; then + rm -Rf "$TERMUX_PKG_CACHEDIR" "$TERMUX_PKG_HOSTBUILD_DIR" + fi + # Ensure folders present (but not $TERMUX_PKG_SRCDIR, it will # be created in build) mkdir -p "$TERMUX_COMMON_CACHEDIR" \ diff --git a/scripts/build/termux_step_setup_variables.sh b/scripts/build/termux_step_setup_variables.sh index a40b60687824f5f..1404fb756a0f94f 100644 --- a/scripts/build/termux_step_setup_variables.sh +++ b/scripts/build/termux_step_setup_variables.sh @@ -7,6 +7,7 @@ termux_step_setup_variables() { : "${TERMUX_INSTALL_DEPS:="false"}" : "${TERMUX_PKG_MAKE_PROCESSES:="$(nproc)"}" : "${TERMUX_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES:="true"}" + : "${TERMUX_PKGS__BUILD__RM_ALL_PKG_BUILD_DEPENDENT_DIRS:="false"}" : "${TERMUX_PKG_API_LEVEL:="24"}" : "${TERMUX_CONTINUE_BUILD:="false"}" : "${TERMUX_QUIET_BUILD:="false"}" From 4d621c162d01f001f44ad1df06fcd27333f5f036 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Wed, 5 Mar 2025 09:31:11 +0500 Subject: [PATCH 10/28] fix(build-package.sh): update `-c` flag help --- build-package.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-package.sh b/build-package.sh index c9469b6218e0eb6..f878fb7c0098527 100755 --- a/build-package.sh +++ b/build-package.sh @@ -422,9 +422,9 @@ _show_usage() { echo echo "Available options:" [ "$TERMUX_ON_DEVICE_BUILD" = "false" ] && echo " -a The architecture to build for: aarch64(default), arm, i686, x86_64 or all." - echo " -d Build with debug symbols." - echo " -c Continue building." + echo " -c Continue previous build." echo " -C Cleanup already built packages on low disk space." + echo " -d Build with debug symbols." echo " -D Build a disabled package in disabled-packages/." echo " -f Force build even if package has already been built." echo " -F Force build even if package and its dependencies have already been built." From a85aa09d497dccf8b1fa9916bef06ac002aeb42e Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 10 Sep 2024 09:21:21 +0500 Subject: [PATCH 11/28] scripts(build-package.sh): use `grep` directly instead of `cat | grep` for checking built packages and quote variables for all related functions --- build-package.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/build-package.sh b/build-package.sh index f878fb7c0098527..d80d51fc86c4662 100755 --- a/build-package.sh +++ b/build-package.sh @@ -390,21 +390,23 @@ fi # Check if the package is in the compiled list termux_check_package_in_built_packages_list() { - [ ! -f "$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH" ] && termux_error_exit "ERROR: file '$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH' not found." - cat "$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH" | grep -q " $1 " + [ ! -f "$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH" ] && \ + termux_error_exit "ERROR: file '$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH' not found." + grep -q " $1 " "$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH" return $? } # Adds a package to the list of built packages if it is not in the list termux_add_package_to_built_packages_list() { if ! termux_check_package_in_built_packages_list "$1"; then - echo -n "$1 " >> $TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH + echo -n "$1 " >> "$TERMUX_BUILD_PACKAGE_CALL_BUILT_PACKAGES_LIST_FILE_PATH" fi } # Check if the package is in the compiling list termux_check_package_in_building_packages_list() { - [ ! -f "$TERMUX_BUILD_PACKAGE_CALL_BUILDING_PACKAGES_LIST_FILE_PATH" ] && termux_error_exit "ERROR: file '$TERMUX_BUILD_PACKAGE_CALL_BUILDING_PACKAGES_LIST_FILE_PATH' not found." + [ ! -f "$TERMUX_BUILD_PACKAGE_CALL_BUILDING_PACKAGES_LIST_FILE_PATH" ] && \ + termux_error_exit "ERROR: file '$TERMUX_BUILD_PACKAGE_CALL_BUILDING_PACKAGES_LIST_FILE_PATH' not found." grep -q "^${1}$" "$TERMUX_BUILD_PACKAGE_CALL_BUILDING_PACKAGES_LIST_FILE_PATH" return $? } From 3b9bed829d6ff27c0324d70a9f3991aaac75f2dc Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 11 Nov 2024 19:10:06 +0500 Subject: [PATCH 12/28] fix(termux-bootstrap-second-stage.sh): do not exit with the wrong "second stage has already been run before" error if `ln` process used to create lock file got killed instead of failing to create lock file if it already existed Related https://github.com/termux/termux-app/issues/4219 --- .../termux-bootstrap-second-stage.sh | 48 +++++++++++++++++-- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/scripts/bootstrap/termux-bootstrap-second-stage.sh b/scripts/bootstrap/termux-bootstrap-second-stage.sh index 00d0a8eb65097fb..6de13a96a658d7e 100755 --- a/scripts/bootstrap/termux-bootstrap-second-stage.sh +++ b/scripts/bootstrap/termux-bootstrap-second-stage.sh @@ -92,16 +92,29 @@ run_bootstrap_second_stage() { local return_value - if ! ln -s "termux-bootstrap-second-stage.sh" \ - "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock" 2>/dev/null; then - log "The termux bootstrap second stage has already been run before and cannot be run again." - log "If you still want to force run it again (not recommended), \ + local output + + output="$(ln -s "termux-bootstrap-second-stage.sh" \ + "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock" 2>&1)" + return_value=$? + if [ $return_value -ne 0 ]; then + if [ $return_value -eq 1 ] && [[ "$output" == *"File exists"* ]]; then + log "The termux bootstrap second stage has already been run before and cannot be run again." + log "If you still want to force run it again (not recommended), \ like in case of previous failure and it must be re-run again for testing, \ then delete the '@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock' \ file manually and run 'termux-bootstrap-second-stage.sh' again." - return 0 + return 0 + else + log_error "$output" + log_error "Failed to create lock file for termux bootstrap second stage at \ +'@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock'" + warn_if_process_killed "$return_value" "ln" + return $return_value + fi fi + log "Running termux bootstrap second stage" run_bootstrap_second_stage_inner return_value=$? @@ -112,6 +125,7 @@ file manually and run 'termux-bootstrap-second-stage.sh' again." log "The termux bootstrap second stage completed successfully" + return 0 } @@ -360,6 +374,30 @@ run_package_postinst_maintainer_scripts() { } + + + + +warn_if_process_killed() { + + local return_value="${1:-}" + local command="${2:-}" + + if [[ "$return_value" == "137" ]]; then + log_error "The '$command' command was apparently killed with SIGKILL (signal 9). \ +This may have been due to the security policies of the Android OS installed on your device. +Check https://github.com/termux/termux-app/issues/4219 for more info." + return 0 + fi + + return 1 + +} + + + + + # If running in bash, run script logic, otherwise exit with usage error if [ -n "${BASH_VERSION:-}" ]; then # If script is sourced, return with error, otherwise call main function From 2f72c31c3adaaa3dd37fc49538bd809b6043e99c Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 14 Dec 2024 13:53:42 +0500 Subject: [PATCH 13/28] fix(termux-bootstrap-second-stage.sh): ensure running with Termux uid --- .../termux-bootstrap-second-stage.sh | 51 +++++++++++++++++++ scripts/build-bootstraps.sh | 1 + scripts/generate-bootstraps.sh | 1 + 3 files changed, 53 insertions(+) diff --git a/scripts/bootstrap/termux-bootstrap-second-stage.sh b/scripts/bootstrap/termux-bootstrap-second-stage.sh index 6de13a96a658d7e..f4ae34430a4f057 100755 --- a/scripts/bootstrap/termux-bootstrap-second-stage.sh +++ b/scripts/bootstrap/termux-bootstrap-second-stage.sh @@ -5,6 +5,9 @@ export TERMUX_PREFIX="@TERMUX_PREFIX@" export TERMUX_PACKAGE_MANAGER="@TERMUX_PACKAGE_MANAGER@" export TERMUX_PACKAGE_ARCH="@TERMUX_PACKAGE_ARCH@" +TERMUX__USER_ID___N="@TERMUX_ENV__S_TERMUX@USER_ID" +TERMUX__USER_ID="${!TERMUX__USER_ID___N:-}" + function log() { echo "[*]" "$@"; } function log_error() { echo "[*]" "$@" 1>&2; } @@ -94,6 +97,10 @@ run_bootstrap_second_stage() { local output + + ensure_running_with_termux_uid || return $? + + output="$(ln -s "termux-bootstrap-second-stage.sh" \ "@TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR@/termux-bootstrap-second-stage.sh.lock" 2>&1)" return_value=$? @@ -378,6 +385,50 @@ run_package_postinst_maintainer_scripts() { +ensure_running_with_termux_uid() { + + local return_value + + local uid + + # Find current effective uid + uid="$(id -u 2>&1)" + return_value=$? + if [ $return_value -ne 0 ]; then + log_error "$uid" + log_error "Failed to get uid of the user running the termux-bootstrap-second-stage.sh script" + warn_if_process_killed "$return_value" "uid" + # This gets triggered if `adb install -r --abi arm64-v8a termux-app_v*_universal.apk` + # is used to install Termux on a `x86_64` Android AVD where `getprop ro.product.cpu.abilist` + # returns `x86_64,arm64-v8a`, but only `x86_64` bootstrap zip should have been extracted + # to APK native lib directory and installed to rootfs. + # Commands do work if full path is executed in the shell, but some will fail with following + # error if only the `basename` of commands is used to rely on `$PATH`. + if [[ "$uid" == *"Unable to get realpath of id"* ]]; then + log_error "You have likely installed the wrong ABI/architecture variant \ +of the @TERMUX_APP__NAME@ app APK that is not compatible with the device." + log_error "Uninstall and reinstall the correct APK variant of the @TERMUX_APP__NAME@ app." + log_error "Install 'universal' variant if you do not know the correct \ +ABI/architecture of the device." + fi + return $return_value + fi + + if [[ ! "$uid" =~ ^[0-9]+$ ]]; then + log_error "The uid '$uid' returned by 'id -u' command is not valid." + return 1 + fi + + if [[ -n "$TERMUX__UID" ]] && [[ "$uid" != "$TERMUX__UID" ]]; then + log_error "termux-bootstrap-second-stage.sh cannot be run as the uid '$uid' and \ +it must be run as the TERMUX__UID '$TERMUX__UID' exported by the @TERMUX_APP__NAME@ app." + return 1 + fi + + return 0 + +} + warn_if_process_killed() { local return_value="${1:-}" diff --git a/scripts/build-bootstraps.sh b/scripts/build-bootstraps.sh index 137233f20b06281..a1f19c712891ce4 100755 --- a/scripts/build-bootstraps.sh +++ b/scripts/build-bootstraps.sh @@ -212,6 +212,7 @@ add_termux_bootstrap_second_stage_files() { -e "s|@TERMUX_PACKAGE_MANAGER@|${TERMUX_PACKAGE_MANAGER}|g" \ -e "s|@TERMUX_PACKAGE_ARCH@|${package_arch}|g" \ -e "s|@TERMUX_APP__NAME@|${TERMUX_APP__NAME}|g" \ + -e "s|@TERMUX_ENV__S_TERMUX@|${TERMUX_ENV__S_TERMUX}|g" \ "$TERMUX_SCRIPTDIR/scripts/bootstrap/termux-bootstrap-second-stage.sh" \ > "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" chmod 700 "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" diff --git a/scripts/generate-bootstraps.sh b/scripts/generate-bootstraps.sh index 2096d7e00989504..c60ff8416d642c0 100755 --- a/scripts/generate-bootstraps.sh +++ b/scripts/generate-bootstraps.sh @@ -258,6 +258,7 @@ add_termux_bootstrap_second_stage_files() { -e "s|@TERMUX_PACKAGE_MANAGER@|${TERMUX_PACKAGE_MANAGER}|g" \ -e "s|@TERMUX_PACKAGE_ARCH@|${package_arch}|g" \ -e "s|@TERMUX_APP__NAME@|${TERMUX_APP__NAME}|g" \ + -e "s|@TERMUX_ENV__S_TERMUX@|${TERMUX_ENV__S_TERMUX}|g" \ "$TERMUX_SCRIPTDIR/scripts/bootstrap/termux-bootstrap-second-stage.sh" \ > "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" chmod 700 "${BOOTSTRAP_ROOTFS}/${TERMUX_BOOTSTRAPS__BOOTSTRAP_CONFIG_DIR}/termux-bootstrap-second-stage.sh" From 5ea72725a2b802ed3302b3649efd5a99e0b3268c Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 11 Nov 2024 19:09:43 +0500 Subject: [PATCH 14/28] fix(clean.sh): do not delete `TERMUX__ROOTFS` top level directory as it could be a different path than `/data` that is critical for build host like Termux docker Either delete entire `TERMUX_APP__DATA_DIR` if `TERMUX__PREFIX` directory exists under it, or only delete `TERMUX__PREFIX` directory itself if it exists outside it. --- clean.sh | 54 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/clean.sh b/clean.sh index e969b98d275d446..331ffcbd6e1b9a5 100755 --- a/clean.sh +++ b/clean.sh @@ -46,28 +46,48 @@ fi chmod +w -R "$TERMUX_TOPDIR" || true fi - # For on-device build cleanup /data shouldn't be erased. + # For on-device build cleanup Termux app data directory shouldn't be erased. if [[ "$TERMUX_ON_DEVICE_BUILD" == "false" ]]; then - if [[ ! "$TERMUX_BASE_DIR" =~ ^(/[^/]+)+$ ]]; then - echo "TERMUX_BASE_DIR '$TERMUX_BASE_DIR' is not an absolute path under rootfs '/'." 1>&2 - exit 1 - fi - if [[ "$TERMUX_BASE_DIR" =~ ^/[^/]+$ ]]; then - # Use `/rootfs` as is. - rootfs_top_level_dir="$TERMUX_BASE_DIR" + for variable_name in TERMUX__PREFIX TERMUX_APP__DATA_DIR CGCT_DIR; do + variable_value="${!variable_name:-}" + if [[ ! "$variable_value" =~ ^(/[^/]+)+$ ]]; then + echo "The $variable_name '$variable_value' is not an absolute path under rootfs '/' while running 'clean.sh'." 1>&2 + exit 1 + fi + done + + # If `TERMUX__PREFIX` is under `TERMUX_APP__DATA_DIR`, then + # just delete the entire `TERMUX_APP__DATA_DIR`. Otherwise, + # only delete `TERMUX__PREFIX` since its parent directories could + # be a critical directory in `TERMUX_REGEX__INVALID_TERMUX_PREFIX_PATHS`. + # This should not be an issue as package files are only packed + # from `TERMUX_PREFIX_CLASSICAL` via `termux_step_extract_into_massagedir()`. + if [[ "$TERMUX__PREFIX" == "$TERMUX_APP__DATA_DIR" ]] || \ + [[ "$TERMUX__PREFIX" == "$TERMUX_APP__DATA_DIR/"* ]]; then + deletion_dir="$TERMUX_APP__DATA_DIR" else - # Get `/path/` from `/path/to/rootfs`. - rootfs_top_level_dir="${TERMUX_BASE_DIR%"${TERMUX_BASE_DIR#/*/}"}" + deletion_dir="$TERMUX__PREFIX" fi - if [[ ! "$CGCT_DIR" =~ ^(/[^/]+)+$ ]] || [[ "$CGCT_DIR" == "$TERMUX_BASE_DIR" ]]; then - echo "CGCT_DIR '$CGCT_DIR' is not an absolute path under rootfs '/' or equals TERMUX_BASE_DIR." 1>&2 - exit 1 - fi + if [[ -e "$deletion_dir" ]]; then + if [[ ! -d "$deletion_dir" ]]; then + echo "A non-directory file exists at deletion directory '$deletion_dir' for TERMUX__PREFIX while running 'clean.sh'." 1>&2 + exit 1 + fi - # Escape '\$[](){}|^.?+*' with backslashes - cgct_dir_escaped="$(printf "%s" "$CGCT_DIR" | sed -zE -e 's/[][\.|$(){}?+*^]/\\&/g')" - find "$rootfs_top_level_dir" -mindepth 1 -regextype posix-extended ! -regex "^$cgct_dir_escaped(/.*)?" -delete 2>/dev/null || true + # If deletion directory is under rootfs `/` or not accessible + # by current user, like the `builder` user in Termux docker + # cannot access root owned directories. + if [[ ! -r "$deletion_dir" ]] || [[ ! -w "$deletion_dir" ]] || [[ ! -x "$deletion_dir" ]]; then + echo "The deletion directory '$deletion_dir' for TERMUX__PREFIX is not readable, writable or searchable while running 'clean.sh'." 1>&2 + echo "Try running 'clean.sh' with 'sudo'." 1>&2 + exit 1 + fi + + # Escape '\$[](){}|^.?+*' with backslashes. + cgct_dir_escaped="$(printf "%s" "$CGCT_DIR" | sed -zE -e 's/[][\.|$(){}?+*^]/\\&/g')" + find "$deletion_dir" -mindepth 1 -regextype posix-extended ! -regex "^$cgct_dir_escaped(/.*)?" -delete 2>/dev/null || true + fi fi rm -Rf "$TERMUX_TOPDIR" From 2f3ee4cb8474ede2c101d5642cadf620d8b39bb1 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 11 Nov 2024 19:10:11 +0500 Subject: [PATCH 15/28] fix(build-package.sh): fix wrong prefix variable being used to create directory before `cd` command for changes in a46e3e935 --- scripts/build/termux_step_extract_into_massagedir.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build/termux_step_extract_into_massagedir.sh b/scripts/build/termux_step_extract_into_massagedir.sh index 0df0b9e1e6f1359..5e9ed5dd169aff5 100644 --- a/scripts/build/termux_step_extract_into_massagedir.sh +++ b/scripts/build/termux_step_extract_into_massagedir.sh @@ -8,7 +8,7 @@ termux_step_extract_into_massagedir() { -czf "$TARBALL_ORIG" . # Extract tar in order to massage it - mkdir -p "$TERMUX_PKG_MASSAGEDIR/$TERMUX_PREFIX" + mkdir -p "$TERMUX_PKG_MASSAGEDIR/$TERMUX_PREFIX_CLASSICAL" cd "$TERMUX_PKG_MASSAGEDIR/$TERMUX_PREFIX_CLASSICAL" tar xf "$TARBALL_ORIG" rm "$TARBALL_ORIG" From 71057fadd8db76d2de8fc4a2b018826fc9a9c37c Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 4 Mar 2025 04:42:11 +0500 Subject: [PATCH 16/28] enhance(build-package.sh): add `termux_step_strip_elf_symbols` and `termux_step_elf_cleaner` for packages to hook into Packages may not want specific elf files to be stripped, like for debug `-g` test binaries, or may have elf files under different non-standard directories. --- build-package.sh | 8 ++++++++ scripts/build/termux_step_elf_cleaner.sh | 9 +++++++++ scripts/build/termux_step_massage.sh | 11 ++--------- scripts/build/termux_step_strip_elf_symbols.sh | 13 +++++++++++++ 4 files changed, 32 insertions(+), 9 deletions(-) create mode 100644 scripts/build/termux_step_elf_cleaner.sh create mode 100644 scripts/build/termux_step_strip_elf_symbols.sh diff --git a/build-package.sh b/build-package.sh index d80d51fc86c4662..4bc27f410dc8d58 100755 --- a/build-package.sh +++ b/build-package.sh @@ -343,6 +343,14 @@ source "$TERMUX_SCRIPTDIR/scripts/build/termux_create_pacman_subpackages.sh" # shellcheck source=scripts/build/termux_step_massage.sh source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_massage.sh" +# Function to run strip symbols during termux_step_massage +# shellcheck source=scripts/build/termux_step_strip_elf_symbols.sh +source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_strip_elf_symbols.sh" + +# Function to run termux-elf-cleaner during termux_step_massage +# shellcheck source=scripts/build/termux_step_elf_cleaner.sh +source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_elf_cleaner.sh" + # Hook for packages after massage step termux_step_post_massage() { return diff --git a/scripts/build/termux_step_elf_cleaner.sh b/scripts/build/termux_step_elf_cleaner.sh new file mode 100644 index 000000000000000..e775f5ac2a8be61 --- /dev/null +++ b/scripts/build/termux_step_elf_cleaner.sh @@ -0,0 +1,9 @@ +termux_step_elf_cleaner() { + termux_step_elf_cleaner__from_paths . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" -o -path "./opt/*" \) +} + +termux_step_elf_cleaner__from_paths() { + # Remove entries unsupported by Android's linker: + find "$@" -type f -print0 | xargs -r -0 \ + "$TERMUX_ELF_CLEANER" --api-level "$TERMUX_PKG_API_LEVEL" +} diff --git a/scripts/build/termux_step_massage.sh b/scripts/build/termux_step_massage.sh index 2a1bb1f5b8d0b4a..b4d2a19916b5589 100644 --- a/scripts/build/termux_step_massage.sh +++ b/scripts/build/termux_step_massage.sh @@ -52,18 +52,11 @@ termux_step_massage() { if [ "$TERMUX_PACKAGE_LIBRARY" = "bionic" ]; then if [ "$TERMUX_PKG_NO_STRIP" != "true" ] && [ "$TERMUX_DEBUG_BUILD" = "false" ]; then - # Strip binaries. file(1) may fail for certain unusual files, so disable pipefail. - set +e +o pipefail - find . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -type f | - xargs -r file | grep -E "ELF .+ (executable|shared object)" | cut -f 1 -d : | - xargs -r "$STRIP" --strip-unneeded --preserve-dates - set -e -o pipefail + termux_step_strip_elf_symbols fi if [ "$TERMUX_PKG_NO_ELF_CLEANER" != "true" ]; then - # Remove entries unsupported by Android's linker: - find . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" -o -path "./opt/*" \) -type f -print0 | xargs -r -0 \ - "$TERMUX_ELF_CLEANER" --api-level $TERMUX_PKG_API_LEVEL + termux_step_elf_cleaner fi fi diff --git a/scripts/build/termux_step_strip_elf_symbols.sh b/scripts/build/termux_step_strip_elf_symbols.sh new file mode 100644 index 000000000000000..2ffe7e0f71be826 --- /dev/null +++ b/scripts/build/termux_step_strip_elf_symbols.sh @@ -0,0 +1,13 @@ +termux_step_strip_elf_symbols() { + termux_step_strip_elf_symbols__from_paths . \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) +} + +termux_step_strip_elf_symbols__from_paths() { + # Strip binaries. file(1) may fail for certain unusual files, so disable pipefail. + ( + set +e +o pipefail && \ + find "$@" -type f -print0 | xargs -r -0 \ + file | grep -E "ELF .+ (executable|shared object)" | cut -f 1 -d : | + xargs -r "$STRIP" --strip-unneeded --preserve-dates + ) +} From 72d03486e55a5c78e7421456dd296f1541368b95 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Tue, 4 Mar 2025 06:26:57 +0500 Subject: [PATCH 17/28] enhance(build-package.sh): extract `termux_step_create_debscripts()` function to file and provide `termux_step_create_debscripts__copy_from_dir()` for packages to copy debscripts from a custom directory into the current `DEBIAN` working directory For example: ```shell termux_step_create_debscripts() { termux_step_create_debscripts__copy_from_dir "$TERMUX_PKG_SRCDIR/build/output/packaging/debian" . } ``` --- build-package.sh | 7 ++-- .../build/termux_step_create_debscripts.sh | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 scripts/build/termux_step_create_debscripts.sh diff --git a/build-package.sh b/build-package.sh index 4bc27f410dc8d58..1b9cde6fa4729ed 100755 --- a/build-package.sh +++ b/build-package.sh @@ -356,10 +356,9 @@ termux_step_post_massage() { return } -# Hook function to create {pre,post}install, {pre,post}rm-scripts and similar -termux_step_create_debscripts() { - return -} +# Function to create {pre,post}install, {pre,post}rm-scripts and similar +# shellcheck source=scripts/build/termux_step_create_debscripts.sh +source "$TERMUX_SCRIPTDIR/scripts/build/termux_step_create_debscripts.sh" # Convert Debian maintainer scripts into pacman-compatible installation hooks. # This is used only when creating pacman packages. diff --git a/scripts/build/termux_step_create_debscripts.sh b/scripts/build/termux_step_create_debscripts.sh new file mode 100644 index 000000000000000..c9e6080dbb34b75 --- /dev/null +++ b/scripts/build/termux_step_create_debscripts.sh @@ -0,0 +1,32 @@ +termux_step_create_debscripts() { + return 0 +} + +termux_step_create_debscripts__copy_from_dir() { + + local return_value + + local src_dir="${1:-}" + local dest_dir="${2:-}" + + if [[ ! -d "$src_dir" ]]; then + echo "Failed to find source directory '$src_dir' to copy debscripts from" 1>&2 + return 1 + fi + + return_value=0 + mkdir -p "$dest_dir" || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failed to create destination directory '$dest_dir' to copy debscripts to" 1>&2 + return 1 + fi + + ( + find "$src_dir" -mindepth 1 -maxdepth 1 -type f \ + -regextype posix-extended -regex "^.*/(postinst|postrm|preinst|prerm|config|conffiles|templates|triggers|clilibs|fortran_mod|runit|shlibs|starlibs|symbols)$" \ + -print0 | xargs -0 -n1 sh -c \ + 'cp -a "$0" '"'${dest_dir//\'/\'\\\'\'}/'" + + ) + +} From e4829b179ae73ca1b20fc12900011a67b955e8e1 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Wed, 26 Feb 2025 21:19:19 +0500 Subject: [PATCH 18/28] addpkg(main/termux-core): init --- ...e__bash__termux_apps_info_app_version_name | 195 +++++++ ..._core__bash__termux_apps_info_env_variable | 158 ++++++ ...mux_core__bash__termux_scoped_env_variable | 516 ++++++++++++++++++ ...ore__sh__termux_apps_info_app_version_name | 192 +++++++ ...ux_core__sh__termux_apps_info_env_variable | 158 ++++++ ...ermux_core__sh__termux_scoped_env_variable | 392 +++++++++++++ packages/termux-core/build.sh | 41 ++ .../termux-replace-termux-core-src-scripts | 258 +++++++++ packages/termux-tools/build.sh | 2 +- scripts/build-bootstraps.sh | 1 + scripts/generate-bootstraps.sh | 1 + scripts/properties.sh | 120 ++++ 12 files changed, 2033 insertions(+), 1 deletion(-) create mode 100644 packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name create mode 100644 packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable create mode 100644 packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable create mode 100644 packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name create mode 100644 packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable create mode 100644 packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable create mode 100644 packages/termux-core/build.sh create mode 100755 packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name new file mode 100644 index 000000000000000..150f47ff10cc6a2 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name @@ -0,0 +1,195 @@ +##### @TERMUX_CORE__BASH__TERMUX_APPS_INFO_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_CORE__BASH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (START) ##### + +## +# Get/Unset variable values of the app version name environment +# variables `*_APP__APP_VERSION_NAME` for Termux app `TERMUX_APP__`, +# its plugin apps `TERMUX_*_APP__` and external apps `*_APP__` app +# scoped that exist in the `termux-apps-info.env` file, with support +# for validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-app-version-name.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-app-version-name.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable +# +# +# `termux_core__bash__termux_apps_info_app_version_name` `get-value` [``] \ +# `` `` +# `termux_core__bash__termux_apps_info_app_version_name` `unset-value` \ +# `` +## +termux_core__bash__termux_apps_info_app_version_name() { + + local return_value + + local command_type="${1:-}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-value" ]; then + local opt; local opt_arg; local OPTARG=""; local OPTIND=1; + + local extended_validator='r+=^(()|((googleplay\.)?[0-9].*))$' + local skip_sourcing_option="--skip-sourcing-if-cur-app-var" + + if [ $# -eq 0 ]; then + echo "No arguments passed for the 'get-value' command. \ +The 'termux_core__bash__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + return 64 # EX__USAGE + fi + + while getopts ":-:" opt; do + opt_arg="${OPTARG:-}" + case "${opt}" in + -) + case "${OPTARG}" in *?=*) opt_arg="${OPTARG#*=}";; *) opt_arg="";; esac + case "${OPTARG}" in + extended-validator=?*) + extended_validator="$opt_arg" + ;; + extended-validator | extended-validator=) + echo "No argument set for arg option '--${OPTARG%=*}'." 1>&2 + return 64 # EX__USAGE + ;; + skip-sourcing) + skip_sourcing_option="--skip-sourcing" + ;; + skip-sourcing=*) + echo "Arguments not allowed for flag option '--${OPTARG%=*}': \`--${OPTARG:-}\`." 1>&2 + return 64 # EX__USAGE + ;; + '') + break # End of options `--`. + ;; + *) + echo "Unknown option: '--${OPTARG:-}'." 1>&2 + return 64 # EX__USAGE + ;; + esac + ;; + :) + echo "No argument passed for arg option '-$OPTARG'." 1>&2 + return 64 # EX__USAGE + ;; + \?) + echo "Unknown option${OPTARG:+": '-${OPTARG:-}'"}." 1>&2 + return 64 # EX__USAGE + ;; + esac + done + shift $((OPTIND - 1)) # Remove already processed arguments from arguments array. + + if [ $# -lt 2 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__bash__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + else + echo "The command '$command_type' passed to 'termux_core__bash__termux_apps_info_app_version_name' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "get-value" ]; then + local __app_version_name="" + + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # A valid environment variable name (like `TERMUX__VAR`) or a bash array variable (like `TERMUX__ARRAY[0]`). + local valid_bash_variable_name_regex='^[a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?$' # ' single quote for linter error false positive against `IFS=$'\n'` + + # If `output_mode` is not a valid environment variable name. + if [[ ! "$output_mode" =~ $valid_bash_variable_name_regex ]]; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__bash__termux_apps_info_app_version_name' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + + + return_value=0 + termux_core__bash__termux_apps_info_env_variable get-value \ + "$skip_sourcing_option" __app_version_name \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" "$extended_validator" || return_value=$? + + + # If getting version name of the Termux app but failed to get it, + # likely due to Termux app scoped `APP_VERSION_NAME` environment + # variable not being exported if running in Termux app version + # `<= 0.119.0` (as `TERMUX_ENV__S_ROOT` environment variable is + # not exported), then fallback to reading the old/deprecated + # `TERMUX_VERSION` environment variable. + # This may give outdated/wrong values if running in a plugin + # app like Termux:Float app and Termux app got updated in the + # background, as `TERMUX_VERSION` would be set to the version + # at the time the Termux:Float shell was started, and not the + # updated version. + if [ $return_value -eq 0 ] && [ -z "$__app_version_name" ] && \ + { [ "$scoped_var_scope_mode" = cn="termux-app" ] || [ "$scoped_var_scope_mode" = ss="APP__" ]; } && \ + [ -z "${TERMUX_ENV__S_ROOT:-}" ]; then + return_value=0 + termux_core__bash__termux_scoped_env_variable get-value \ + __app_version_name "" "" "$extended_validator" "${TERMUX_VERSION:-}" || return_value=$? + fi + + + # If either above commands failed. + if [ $return_value -ne 0 ]; then + # If a valid value not found. + if [ $return_value -eq 81 ]; then # C_EX__NOT_FOUND + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use + # that wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `var_to_get_name` generated in + # `termux_core__bash__termux_scoped_env_variable()` is + # equal to output variable in `output_mode` passed to this + # function, then `var_to_get_name` would get unset before + # its read. + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "" || return $? + fi + fi + return $return_value + fi + + + # If a valid value found. + if [ "$output_mode" = ">" ]; then + printf "%s" "$__app_version_name" + return $? + elif [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "$__app_version_name" + return $? + else + return 0 + fi + elif [ "$command_type" = "unset-value" ]; then + termux_core__bash__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" || return $? + + if [ "$scoped_var_scope_mode" = cn="termux-app" ] || \ + [ "$scoped_var_scope_mode" = ss="APP__" ]; then + unset TERMUX_VERSION + fi + + return 0 + fi + +} + +##### @TERMUX_CORE__BASH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable new file mode 100644 index 000000000000000..631882e50c80a4d --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable @@ -0,0 +1,158 @@ +##### @TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_GTAIEVV_BASH__TERMUX_APPS_INFO_ENV_VARIABLE_VALUE@ replaced at build time. (START) ##### + +## +# Source the `termux-apps-info.env` file into the current environment +# or get variable values of Termux app `TERMUX_APP__`, its plugin apps +# `TERMUX_*_APP__` and external apps `*_APP__` app scoped environment +# variables that exist in the `termux-apps-info.env` file, with +# support for fallback values and validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable +# +# +# `termux_core__bash__termux_apps_info_env_variable` `source-env` +# `termux_core__bash__termux_apps_info_env_variable` `get-value` [``] \ +# `` \ +# `` `` \ +# `` [``] +## +termux_core__bash__termux_apps_info_env_variable() { + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "source-env" ]; then + if [ $# -ne 0 ]; then + echo "Invalid argument count $# for the 'source-env' command. \ +The 'termux_core__bash__termux_apps_info_env_variable' function expects 0 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local skip_sourcing=0 + local skip_sourcing_if_cur_app_var=0 + local ensure_sourcing=0 + + if [ "${1:-}" = "--skip-sourcing" ]; then + skip_sourcing=1 + shift 1 + elif [ "${1:-}" = "--skip-sourcing-if-cur-app-var" ]; then + skip_sourcing_if_cur_app_var=1 + shift 1 + elif [ "${1:-}" = "--ensure-sourcing" ]; then + ensure_sourcing=1 + shift 1 + fi + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__bash__termux_apps_info_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + case "$scoped_var_scope_mode" in + 's='[A-Z]*'_APP__'|'ss=APP__'|'ss='[A-Z]*'_APP__'|'cn='[a-z]*'-app') :;; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__bash__termux_apps_info_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\` and ending with '-app', \ +or an environment variable scope starting with \`s=\` or \`ss=\` and ending with '_APP__'." 1>&2 + return 64 # EX__USAGE + ;; + esac + else + echo "The command '$command_type' passed to 'termux_core__bash__termux_apps_info_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "source-env" ]; then + local termux_core__apps_info_env_file="" + + # Source the `termux-apps-info.env` file. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__bash__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" r+='^(()|((/[^/]+)+))$' || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + source "$termux_core__apps_info_env_file" || return $? + return $? + else + return 69 # EX__UNAVAILABLE + fi + elif [ "$command_type" = "get-value" ]; then + # Prefix with `app_` to prevent conflict with `termux_core__bash__termux_scoped_env_variable` argument. + local app_scoped_var_scope_name="" + local termux_core__apps_info_env_file="" + + # If `skip_sourcing` is enabled, then directly get the value from + # the current environment. + if [ "$skip_sourcing" = "1" ]; then + termux_core__bash__termux_scoped_env_variable get-value "$@" + return $? + # If `skip_sourcing_if_cur_app_var` is enabled and app scope of + # the variable to get is the same as the app scope of the current + # app, then directly get the value from the current + # environment. + elif [ "$skip_sourcing_if_cur_app_var" = "1" ] && [ -n "${TERMUX_ENV__S_APP:-}" ]; then + # Get app scope of the variable to get. If caller passed + # `scoped_var_scope_mode` as `s=*`, then it will be returned + # as is, otherwise if `ss=*` or `cn=*` is passed, then full + # variable scope including Termux root scope prefix will + # be returned. The `scoped_var_sub_name` arg is passed as + # an empty string as we only need the app scope name. + termux_core__bash__termux_scoped_env_variable get-name \ + app_scoped_var_scope_name "$scoped_var_scope_mode" "" || return $? + + if [ "$TERMUX_ENV__S_APP" = "$app_scoped_var_scope_name" ]; then + termux_core__bash__termux_scoped_env_variable get-value "$@" + return $? + fi + fi + + + # Unset variable to get before sourcing, otherwise if the + # `termux-apps-info.env` file does not explicitly unset it if + # variable should not be set, then any value including empty + # (for `validator_mode` `*`) that exists in the current environment + # may get used. + termux_core__bash__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "$scoped_var_sub_name" || return $? + + + # First source the `termux-apps-info.env` file to load the latest + # value in the current environment before getting it. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__bash__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" r+='^(()|((/[^/]+)+))$' || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + source "$termux_core__apps_info_env_file" || return $? + elif [ "$ensure_sourcing" = "1" ]; then + return 69 # EX__UNAVAILABLE + fi + + termux_core__bash__termux_scoped_env_variable get-value "$@" + return $? + fi + +} + +##### @TERMUX_GTAIEVV_BASH__TERMUX_APPS_INFO_ENV_VARIABLE_VALUE@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable new file mode 100644 index 000000000000000..e640459379954b5 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable @@ -0,0 +1,516 @@ +##### TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (START) ##### + +## +# Get/Set/Unset variable names and values for `TERMUX*__` and other +# scoped environment variables exported by different Termux runtime +# components, with support for fallback values and validation of +# values. +# +# The `extended_validator` logic is based on `libalteran-sh` +# `bash___shell__validate_variable_with_extended_validator()` function. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.bash.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable +# +# +# `termux_core__bash__termux_scoped_env_variable` `get-name` \ +# `` \ +# `` `` +# `termux_core__bash__termux_scoped_env_variable` `get-value` \ +# `` \ +# `` `` \ +# `` [``] +# `termux_core__bash__termux_scoped_env_variable` `set-value` \ +# `` `` \ +# `` +# `termux_core__bash__termux_scoped_env_variable` `unset-value` \ +# `` `` +## +termux_core__bash__termux_scoped_env_variable() { + + local return_value + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-name" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'get-name' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + local extended_validator="${4:-}" + + local validator_arg + local validator_mode + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + # Remove args before `default_values` to be used in `var_value_cur` for loop below. + shift 4 + + case "$extended_validator" in + '?'|'*') + validator_arg="" + validator_mode="$extended_validator" + ;; + 'r+='?*|'r-='?*|'c+='?*|'c-='?*) + validator_arg="${extended_validator:3}" # 3:end + validator_mode="${extended_validator:0:3}" # 0:3 + ;; + *) + echo "The extended_validator '$extended_validator' \ +argument passed to 'termux_core__bash__termux_scoped_env_variable' is not valid. \ +It must either be equal to \`?\` or \`*\`, or a regex that starts with \`r+=\` or \`r-=\`, \ +or a executable or function that starts with \`c+=\` or \`c-=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + elif [ "$command_type" = "set-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + local value_to_set="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'set-value' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + + if [ $# -ne 2 ]; then + echo "Invalid argument count $# for the 'unset-value' command. \ +The 'termux_core__bash__termux_scoped_env_variable' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + else + echo "The command '$command_type' passed to 'termux_core__bash__termux_scoped_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + local i + local is_value_valid + local scoped_var_root_scope_name="" + local scoped_var_scope_name="" + local scoped_var_sub_scope_name="" + local scoped_var_name="" + local scoped_var_set="" + local scoped_var_value_invalid_error_suffix="" + local var_value_cur + + # A valid environment variable name (like `TERMUX__VAR`) or a bash array variable (like `TERMUX__ARRAY[0]`). + local valid_bash_variable_name_regex='^[a-zA-Z_][a-zA-Z0-9_]*(\[[0-9]+\])?$' + + + if [ "$command_action" = "get" ]; then + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # If `output_mode` is not a valid environment variable name. + if [[ ! "$output_mode" =~ $valid_bash_variable_name_regex ]]; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + + case "$scoped_var_scope_mode" in + s=?*) scoped_var_scope_name="${scoped_var_scope_mode:2}";; # 2:end + ss=?*) scoped_var_sub_scope_name="${scoped_var_scope_mode:3}";; # 3:end + '') ;; + cn=termux) scoped_var_sub_scope_name="_";; + cn=termux-app) scoped_var_sub_scope_name="APP__";; + cn=termux-api-app) scoped_var_sub_scope_name="API_APP__";; + cn=termux-float-app) scoped_var_sub_scope_name="FLOAT_APP__";; + cn=termux-gui-app) scoped_var_sub_scope_name="GUI_APP__";; + cn=termux-tasker-app) scoped_var_sub_scope_name="TASKER_APP__";; + cn=termux-widget-app) scoped_var_sub_scope_name="WIDGET_APP__";; + cn=termux-x11-app) scoped_var_sub_scope_name="X11_APP__";; + cn=termux-core) scoped_var_sub_scope_name="CORE__";; + cn=termux-exec) scoped_var_sub_scope_name="EXEC__";; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__bash__termux_scoped_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\`, \ +or an environment variable scope starting with \`s=\` or \`ss=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + + + if [ -n "$scoped_var_scope_mode" ]; then + if [ -n "$scoped_var_scope_name" ]; then + # Generate the full name for the variable under the provided root and sub scope. + scoped_var_name="${scoped_var_scope_name}${scoped_var_sub_name}" + else + # Generate the full name for the variable under the Termux root scope and provided sub scope. + # If `TERMUX_ENV__S_ROOT` environment variable set. + # shellcheck disable=SC2050 + if [ -n "${TERMUX_ENV__S_ROOT:-}" ]; then + scoped_var_root_scope_name="$TERMUX_ENV__S_ROOT" + if [[ ! "$scoped_var_root_scope_name" =~ $valid_bash_variable_name_regex ]]; then + echo "The TERMUX_ENV__S_ROOT environment variable value '$scoped_var_root_scope_name' \ +while running 'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + # If `TERMUX_ENV__S_ROOT` placeholder got replaced during build time. + elif [ "@TERMUX_ENV__S_ROOT@" != @"TERMUX_ENV__S_ROOT"@ ]; then + scoped_var_root_scope_name="@TERMUX_ENV__S_ROOT@" + if [[ ! "$scoped_var_root_scope_name" =~ $valid_bash_variable_name_regex ]]; then + echo "The TERMUX_ENV__S_ROOT build value '$scoped_var_root_scope_name' \ +while running 'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + else + scoped_var_root_scope_name="TERMUX_" + fi + + scoped_var_name="${scoped_var_root_scope_name}${scoped_var_sub_scope_name}${scoped_var_sub_name}" + fi + + if [[ ! "$scoped_var_name" =~ $valid_bash_variable_name_regex ]]; then + echo "The name of the variable to $command_action '$scoped_var_name' generated in \ +'termux_core__bash__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 64 # EX__USAGE + fi + + + # If command type equals `get-name`, then return the variable name and exit. + if [ "$command_type" = "get-name" ]; then + #echo "scoped_var_name=$scoped_var_name" + if [ "$output_mode" = ">" ]; then + printf "%s" "$scoped_var_name" + return $? + elif [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "$scoped_var_name" + #eval "echo $output_mode=\"\${${output_mode}}\"" # Surround `${output_mode}` with `${}` in case its a base array variable name + return $? + else + return 0 + fi + elif [ "$command_type" = "set-value" ]; then + printf -v "$scoped_var_name" "%s" "$value_to_set" + return $? + elif [ "$command_type" = "unset-value" ]; then + unset "$scoped_var_name" + return $? + fi + else + if [ "$command_type" = "get-name" ] || \ + [ "$command_type" = "set-value" ] || [ "$command_type" = "unset-value" ]; then + echo "The scoped_var_scope_mode argument for the variable \ +to $command_action passed for the '$command_type' command to \ +'termux_core__bash__termux_scoped_env_variable' is not set." 1>&2 + return 64 # EX__USAGE + fi + fi + + + # If command type equals `get-value`, then find the first valid in variable name/values passed. + if [ "$command_type" = "get-value" ]; then + if [ "$validator_mode" = "c+=" ] || [ "$validator_mode" = "c-=" ]; then + local command_arg_first_arg + local -a validator_command_args=() + + # Convert command string to array. + local -a validator_command_args=() + termux_core__bash__set_shell_command_args_array validator_command_args \ + "extended_validator command" "$validator_arg" || return $? + + command_arg_first_arg="${validator_command_args[0]}" + + # Check if a command exists, like an executable in `$PATH`, + # a shell function or a path to an executable. + # - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html + if ! command -v -- "$command_arg_first_arg" >/dev/null 2>&1; then + # Check if absolute or relative path as `command -v` will only check executables under `$PATH`. + if [[ "$command_arg_first_arg" == */* ]] && \ + [ -f "$command_arg_first_arg" ] && [ -x "$command_arg_first_arg" ]; then + : + else + echo "The validator command '$command_arg_first_arg' \ +not found while running 'termux_core__bash__termux_scoped_env_variable' \ +that is set in the extended_validator '$extended_validator' argument." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + # Loop on [`` ``] to + # find the first valid value. + i=0 + for var_value_cur in "$scoped_var_name" "$@"; do + var_value_cur="${var_value_cur:-}" + + # If first loop, then expand the `scoped_var_name` to its value. + if [ "$i" = 0 ] && [ -n "$scoped_var_name" ]; then + # If mode is `*`, expand variable if it is set, or defined + # but empty. Else if its not defined, then ignore it. + if [ "$validator_mode" = "*" ]; then + eval '[ -n "${'"$scoped_var_name"'+x}" ] && scoped_var_set=1 || scoped_var_set=0' + if [ "$scoped_var_set" = "1" ]; then + var_value_cur="${!scoped_var_name:-}" + else + i=$((i + 1)); continue; + fi + else + var_value_cur="${!scoped_var_name:-}" + fi + fi + + is_value_valid=0 + if [ "$validator_mode" = "r+=" ]; then + if [[ "$var_value_cur" =~ $validator_arg ]]; then + is_value_valid=1 + fi + elif [ "$validator_mode" = "r-=" ]; then + if [[ ! "$var_value_cur" =~ $validator_arg ]]; then + is_value_valid=1 + fi + elif [ "$validator_mode" = "c+=" ]; then + # Do not use `if command; then` to preserve `set -e` failures in called function. + return_value=0 + "${validator_command_args[@]}" "$var_value_cur" || return_value=$? + if [ $return_value -eq 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + elif [ "$validator_mode" = "c-=" ]; then + # Do not use `if ! command; then` to preserve `set -e` failures in called function. + return_value=0 + "${validator_command_args[@]}" "$var_value_cur" || return_value=$? + if [ $return_value -ne 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + else + # If mode is `?` or `*`, and value is set, + # or mode is `*` and value is not set. + if [ -n "$var_value_cur" ] || [ "$validator_mode" = "*" ]; then + is_value_valid=1 + # Else if mode is `?`, and value is not set. + else + is_value_valid=0 + fi + fi + + if [ "$is_value_valid" = "1" ]; then + #echo "var_value_cur=$var_value_cur" + if [ "$output_mode" = ">" ]; then + printf "%s" "$var_value_cur" + return $? + elif [ "$output_mode" != "-" ]; then + printf -v "$output_mode" "%s" "$var_value_cur" + #eval "echo $output_mode=\"\${${output_mode}}\"" # Surround `${output_mode}` with `${}` in case its a base array variable name + return $? + else + return 0 + fi + fi + + i=$((i + 1)) + done + + + # If a valid value not found. + + if [ -n "$scoped_var_name" ]; then + scoped_var_value_invalid_error_suffix=" that is read from the '\$$scoped_var_name' variable" + fi + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use that + # wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `scoped_var_name` is equal to output variable in + # `output_mode`, then `scoped_var_name` would get unset before + # its read. + printf -v "$output_mode" "%s" "" || return $? + + echo "Failed to find a valid value to set to the '\$$output_mode' \ +variable${scoped_var_value_invalid_error_suffix}." 1>&2 + else + echo "Failed to find a valid value${scoped_var_value_invalid_error_suffix}." 1>&2 + fi + return 81 # C_EX__NOT_FOUND + fi + +} + +## +# Set a bash indexed array (`declared -a`) to shell command arguments +# defined as a string by splitting the string as normally done by the +# shell. +# +# This function is based on `libalteran-sh` +# `bash___shell__set_shell_command_args_array()` +# function, check it for more info and comments. +# +# +# `termux_core__bash__set_shell_command_args_array` \ +# `` \ +# `` `` +## +termux_core__bash__set_shell_command_args_array() { + + local return_value + + if [ $# -ne 3 ]; then + echo "Invalid argument count to 'termux_core__bash__set_shell_command_args_array'" + return 1 + fi + + local command_args_array_variable_name="$1" + local command_args_label="$2" + local command_args_string="$3" + + local command_arg + local command_args_string_contains_shell_special_chars="false" + local command_args_string_contains_space="false" + local newline=$'\n' + local space_containing_string_regex='[[:space:]]+' + local shell_special_chars_containing_string_regex='[]["'\''*?!\]+' + local xargs_command_output + local xargs_path + + local -n command_args_array="$command_args_array_variable_name" || return $? + command_args_array=() + + + if [[ -z "$command_args_string" ]]; then + # Unset `nameref` to `command_args_array_variable_name`. + unset -n command_args_array || return $? + return 0 + fi + + if [[ "$command_args_string" == *$'\n'* ]]; then + # shellcheck disable=SC2028 + echo "The $command_args_label value to be \ +converted to a command array cannot contain a newline '\n' character:" 1>&2 + echo "\`\`\`" 1>&2 + echo "$command_args_string" 1>&2 + echo "\`\`\`" 1>&2 + return 64 # EX__USAGE + fi + + + if [[ "$command_args_string" =~ $shell_special_chars_containing_string_regex ]]; then + command_args_string_contains_shell_special_chars="true" + fi + if [[ "$command_args_string" =~ $space_containing_string_regex ]]; then + command_args_string_contains_space="true" + fi + + if [[ "$command_args_string_contains_shell_special_chars" == "true" ]] || \ + [[ "$command_args_string_contains_space" == "true" ]]; then + + # Use `xargs` if command args string contains whitespaces or + # shell special characters. + if [[ "$command_args_string_contains_shell_special_chars" == "true" ]]; then + return_value=0 + xargs_path="$(command -v "xargs")" || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failed to find 'xargs' for converting $command_args_label value to a command array." 1>&2 + return $return_value + fi + + # Android `toybox` provided `xargs` does not parse arguments + # properly and single and double quotes around arguments + # remain, nor do backslash escapes work. + # Termux `findutils` package supplied `xargs` does not + # have such issues, so we ignore the check for it only if + # `TERMUX__PREFIX` placeholder got replaced during build time. + # `/system/bin/xargs` may get called if current shell has + # `/system/bin` in `$PATH` instead of or before Termux bin path. + # shellcheck disable=SC2050 + if [[ "$xargs_path" != "@TERMUX__PREFIX@/xargs" ]] || [[ "@TERMUX__PREFIX@" == @"TERMUX__PREFIX"@ ]]; then + # The logic to check if `xargs` parsing is valid is based + # on `libalteran-sh` `bash___shell__set_is_xargs_command_args_parsing_valid()` + # function, check it for explanation. + return_value=0 + xargs_command_output="$(printf "%s" "'' 'arg' '' 'arg with space' 'arg surround by single quote' \"arg surround by double quote\" 'arg with '\'' single quote' 'arg with \" double quote' 'arg with \" double quote surround by single quote' \"arg with ' single quote surround by double quote\" arg\ with\ escaped\ space 'arg with backslash \\ and forward slash / surround by single quote' \"arg with backslash \\ and forward slash / surround by double quote\" \\/ \\\\ ''" | \ + xargs -r -n1 -- printf "%s\n" && echo -n x)" || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failed to check if xargs command args parsing is valid." 1>&2 + echo "xargs_command_output=\`$xargs_command_output\`" 1>&2 + return $return_value + fi + + if [[ "${xargs_command_output%x}" != "${newline}arg${newline}${newline}arg with space${newline}arg surround by single quote${newline}arg surround by double quote${newline}arg with ' single quote${newline}arg with \" double quote${newline}arg with \" double quote surround by single quote${newline}arg with ' single quote surround by double quote${newline}arg with escaped space${newline}arg with backslash \ and forward slash / surround by single quote${newline}arg with backslash \\ and forward slash / surround by double quote${newline}/${newline}\\${newline}" ]]; then + echo "The $command_args_label value cannot be \ +converted to a command array in current execution environment as it contains \ +shell special characters and 'xargs' command args parsing is not valid:" 1>&2 + echo "\`\`\`" 1>&2 + echo "$command_args_string" 1>&2 + echo "\`\`\`" 1>&2 + echo "xargs path: \`$(command -v "xargs" || true)\`" 1>&2 + echo "xargs version: \`$({ xargs --version 2>/dev/null || true; } | head -n 1)\`" 1>&2 + return 1 + fi + fi + + command_arg="READ_ERROR" + return_value=0 + while IFS= read -r command_arg; return_value=$?; [[ $return_value -eq 0 ]]; do + command_args_array+=("$command_arg") + done < <(printf "%s\n" "$command_args_string" | \ + xargs -r -n1 -- printf "%s\n" || echo -n "CMD_ERROR") + if [[ -n "${command_arg:-}" ]] || [ $return_value -ne 1 ]; then + echo "Failed to create command array for \ +command arguments string \`$command_args_string\`." 1>&2 + [[ -n "${command_arg:-}" ]] && echo "Error type: '$command_arg'" 1>&2 + if [[ -n ${!:-} ]]; then + wait "$!" || true + fi + return 1 + fi + else + # Avoid creating subshell and external call to xargs if + # command args string contains whitespaces but does not + # contain shell special characters and rely on whitespace + # splitting of arguments. + local old_ifs="$IFS" + IFS=$' \t\n' + # shellcheck disable=SC2206 + command_args_array=( $command_args_string ) + IFS="$old_ifs" + fi + else + # Avoid creating subshell and external call to xargs if + # command args string does not contain whitespaces or shell + # special characters. + command_args_array=( "$command_args_string" ) + fi + + # Unset `nameref` to `command_args_array_variable_name`. + unset -n command_args_array || return $? + + return 0 + +} + +##### TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name new file mode 100644 index 000000000000000..e35e2af29f4a9e9 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name @@ -0,0 +1,192 @@ +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (START) ##### + +## +# Get/Unset variable values of the app version name environment +# variables `*_APP__APP_VERSION_NAME` for Termux app `TERMUX_APP__`, +# its plugin apps `TERMUX_*_APP__` and external apps `*_APP__` app +# scoped that exist in the `termux-apps-info.env` file, with support +# for validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-app-version-name.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-app-version-name.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable +# +# +# `termux_core__sh__termux_apps_info_app_version_name` `get-value` [``] \ +# `` `` +# `termux_core__sh__termux_apps_info_app_version_name` `unset-value` \ +# `` +## +termux_core__sh__termux_apps_info_app_version_name() { + + local return_value + + local command_type="${1:-}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-value" ]; then + local opt; local opt_arg; local OPTARG=""; local OPTIND=1; + + local posix_validator="p+=''|[0-9]*|googleplay.[0-9]*" + local skip_sourcing_option="--skip-sourcing-if-cur-app-var" + + if [ $# -eq 0 ]; then + echo "No arguments passed for the 'get-value' command. \ +The 'termux_core__sh__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + return 64 # EX__USAGE + fi + + while getopts ":-:" opt; do + opt_arg="${OPTARG:-}" + case "${opt}" in + -) + case "${OPTARG}" in *?=*) opt_arg="${OPTARG#*=}";; *) opt_arg="";; esac + case "${OPTARG}" in + posix-validator=?*) + posix_validator="$opt_arg" + ;; + posix-validator | posix-validator=) + echo "No argument set for arg option '--${OPTARG%=*}'." 1>&2 + return 64 # EX__USAGE + ;; + skip-sourcing) + skip_sourcing_option="--skip-sourcing" + ;; + skip-sourcing=*) + echo "Arguments not allowed for flag option '--${OPTARG%=*}': \`--${OPTARG:-}\`." 1>&2 + return 64 # EX__USAGE + ;; + '') + break # End of options `--`. + ;; + *) + echo "Unknown option: '--${OPTARG:-}'." 1>&2 + return 64 # EX__USAGE + ;; + esac + ;; + :) + echo "No argument passed for arg option '-$OPTARG'." 1>&2 + return 64 # EX__USAGE + ;; + \?) + echo "Unknown option${OPTARG:+": '-${OPTARG:-}'"}." 1>&2 + return 64 # EX__USAGE + ;; + esac + done + shift $((OPTIND - 1)) # Remove already processed arguments from arguments array. + + if [ $# -lt 2 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__sh__termux_apps_info_app_version_name' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + else + echo "The command '$command_type' passed to 'termux_core__sh__termux_apps_info_app_version_name' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "get-value" ]; then + local __app_version_name="" + + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # If `output_mode` is not a valid environment variable name. + if ! termux_core__sh__is_valid_shell_variable_name "$output_mode"; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__sh__termux_apps_info_app_version_name' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + + + return_value=0 + termux_core__sh__termux_apps_info_env_variable get-value \ + "$skip_sourcing_option" __app_version_name \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" "$posix_validator" || return_value=$? + + + # If getting version name of the Termux app but failed to get it, + # likely due to Termux app scoped `APP_VERSION_NAME` environment + # variable not being exported if running in Termux app version + # `<= 0.119.0` (as `TERMUX_ENV__S_ROOT` environment variable is + # not exported), then fallback to reading the old/deprecated + # `TERMUX_VERSION` environment variable. + # This may give outdated/wrong values if running in a plugin + # app like Termux:Float app and Termux app got updated in the + # background, as `TERMUX_VERSION` would be set to the version + # at the time the Termux:Float shell was started, and not the + # updated version. + if [ $return_value -eq 0 ] && [ -z "$__app_version_name" ] && \ + { [ "$scoped_var_scope_mode" = cn="termux-app" ] || [ "$scoped_var_scope_mode" = ss="APP__" ]; } && \ + [ -z "${TERMUX_ENV__S_ROOT:-}" ]; then + return_value=0 + termux_core__sh__termux_scoped_env_variable get-value \ + __app_version_name "" "" "$posix_validator" "${TERMUX_VERSION:-}" || return_value=$? + fi + + + # If either above commands failed. + if [ $return_value -ne 0 ]; then + # If a valid value not found. + if [ $return_value -eq 81 ]; then # C_EX__NOT_FOUND + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use + # that wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `var_to_get_name` generated in + # `termux_core__sh__termux_scoped_env_variable()` is + # equal to output variable in `output_mode` passed to this + # function, then `var_to_get_name` would get unset before + # its read. + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\" || return $? + fi + fi + return $return_value + fi + + + # If a valid value found. + if [ "$output_mode" = ">" ]; then + printf "%s" "$__app_version_name" + return $? + elif [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\$__app_version_name\" + return $? + else + return 0 + fi + elif [ "$command_type" = "unset-value" ]; then + termux_core__sh__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "APP_VERSION_NAME" || return $? + + if [ "$scoped_var_scope_mode" = cn="termux-app" ] || \ + [ "$scoped_var_scope_mode" = ss="APP__" ]; then + unset TERMUX_VERSION + fi + + return 0 + fi + +} + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_APP_VERSION_NAME@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable new file mode 100644 index 000000000000000..8441cdac3296134 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable @@ -0,0 +1,158 @@ +##### @TERMUX_CORE__SH__TERMUX_SCOPED_ENV_VARIABLE@ to be replaced at build time. ##### + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_ENV_VARIABLE@ replaced at build time. (START) ##### + +## +# Source the `termux-apps-info.env` file into the current environment +# or get variable values of Termux app `TERMUX_APP__`, its plugin apps +# `TERMUX_*_APP__` and external apps `*_APP__` app scoped environment +# variables that exist in the `termux-apps-info.env` file, with +# support for fallback values and validation of values. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-apps-info-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-apps-info-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable +# . +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable +# +# +# `termux_core__sh__termux_apps_info_env_variable` `source-env` +# `termux_core__sh__termux_apps_info_env_variable` `get-value` [``] \ +# `` \ +# `` `` \ +# `` [``] +## +termux_core__sh__termux_apps_info_env_variable() { + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "source-env" ]; then + if [ $# -ne 0 ]; then + echo "Invalid argument count $# for the 'source-env' command. \ +The 'termux_core__sh__termux_apps_info_env_variable' function expects 0 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local skip_sourcing=0 + local skip_sourcing_if_cur_app_var=0 + local ensure_sourcing=0 + + if [ "${1:-}" = "--skip-sourcing" ]; then + skip_sourcing=1 + shift 1 + elif [ "${1:-}" = "--skip-sourcing-if-cur-app-var" ]; then + skip_sourcing_if_cur_app_var=1 + shift 1 + elif [ "${1:-}" = "--ensure-sourcing" ]; then + ensure_sourcing=1 + shift 1 + fi + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__sh__termux_apps_info_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + case "$scoped_var_scope_mode" in + 's='[A-Z]*'_APP__'|'ss=APP__'|'ss='[A-Z]*'_APP__'|'cn='[a-z]*'-app') :;; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__sh__termux_apps_info_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\` and ending with '-app', \ +or an environment variable scope starting with \`s=\` or \`ss=\` and ending with '_APP__'." 1>&2 + return 64 # EX__USAGE + ;; + esac + else + echo "The command '$command_type' passed to 'termux_core__sh__termux_apps_info_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + + if [ "$command_type" = "source-env" ]; then + local termux_core__apps_info_env_file="" + + # Source the `termux-apps-info.env` file. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__sh__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" p+="''|/*[!/]" || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + . "$termux_core__apps_info_env_file" + return $? + else + return 69 # EX__UNAVAILABLE + fi + elif [ "$command_type" = "get-value" ]; then + # Prefix with `app_` to prevent conflict with `termux_core__sh__termux_scoped_env_variable` argument. + local app_scoped_var_scope_name="" + local termux_core__apps_info_env_file="" + + # If `skip_sourcing` is enabled, then directly get the value from + # the current environment. + if [ "$skip_sourcing" = "1" ]; then + termux_core__sh__termux_scoped_env_variable get-value "$@" + return $? + # If `skip_sourcing_if_cur_app_var` is enabled and app scope of + # the variable to get is the same as the app scope of the current + # app, then directly get the value from the current + # environment. + elif [ "$skip_sourcing_if_cur_app_var" = "1" ] && [ -n "${TERMUX_ENV__S_APP:-}" ]; then + # Get app scope of the variable to get. If caller passed + # `scoped_var_scope_mode` as `s=*`, then it will be returned + # as is, otherwise if `ss=*` or `cn=*` is passed, then full + # variable scope including Termux root scope prefix will + # be returned. The `scoped_var_sub_name` arg is passed as + # an empty string as we only need the app scope name. + termux_core__sh__termux_scoped_env_variable get-name \ + app_scoped_var_scope_name "$scoped_var_scope_mode" "" || return $? + + if [ "$TERMUX_ENV__S_APP" = "$app_scoped_var_scope_name" ]; then + termux_core__sh__termux_scoped_env_variable get-value "$@" + return $? + fi + fi + + + # Unset variable to get before sourcing, otherwise if the + # `termux-apps-info.env` file does not explicitly unset it if + # variable should not be set, then any value including empty + # (for `validator_mode` `*`) that exists in the current environment + # may get used. + termux_core__sh__termux_scoped_env_variable unset-value \ + "$scoped_var_scope_mode" "$scoped_var_sub_name" || return $? + + + # First source the `termux-apps-info.env` file to load the latest + # value in the current environment before getting it. + # The path for the file is exported in the `$TERMUX_CORE__APPS_INFO_ENV_FILE` + # environment variable by the Termux app running the current shell. + termux_core__sh__termux_scoped_env_variable get-value \ + termux_core__apps_info_env_file cn="termux-core" "APPS_INFO_ENV_FILE" p+="''|/*[!/]" || return $? + if [ -n "$termux_core__apps_info_env_file" ] && [ -f "$termux_core__apps_info_env_file" ]; then + # shellcheck disable=SC1090 + . "$termux_core__apps_info_env_file" || return $? + elif [ "$ensure_sourcing" = "1" ]; then + return 69 # EX__UNAVAILABLE + fi + + termux_core__sh__termux_scoped_env_variable get-value "$@" + return $? + fi + +} + +##### @TERMUX_CORE__SH__TERMUX_APPS_INFO_ENV_VARIABLE@ replaced at build time. (END) ##### diff --git a/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable new file mode 100644 index 000000000000000..e14e991b3c90ff2 --- /dev/null +++ b/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable @@ -0,0 +1,392 @@ +##### TERMUX_CORE__SH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (START) ##### + +## +# Get/Set/Unset variable names and values for `TERMUX*__` and other +# scoped environment variables exported by different Termux runtime +# components, with support for fallback values and validation of +# values. +# +# The `posix_validator` logic is based on `libalteran-sh` +# `shell__validate_variable_with_posix_validator()` function. +# +# **See Also:** +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# - @TERMUX_CORE_PKG__REPO_URL@/blob/master/app/main/scripts/termux/shell/command/environment/termux-scoped-env-variable.sh.in +# - @TERMUX_PKGS__REPO_URL@/blob/master/packages/termux-core/app/main/scripts/termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable +# +# +# `termux_core__sh__termux_scoped_env_variable` `get-name` \ +# `` \ +# `` `` +# `termux_core__sh__termux_scoped_env_variable` `get-value` \ +# `` \ +# `` `` \ +# `` [``] +# `termux_core__sh__termux_scoped_env_variable` `set-value` \ +# `` `` \ +# `` +# `termux_core__sh__termux_scoped_env_variable` `unset-value` \ +# `` `` +## +termux_core__sh__termux_scoped_env_variable() { + + local return_value + + local command_type="${1:-}" + local command_action="${command_type%%-*}" + [ $# -gt 0 ] && shift 1 + + if [ "$command_type" = "get-name" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'get-name' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "get-value" ]; then + local output_mode="${1:-}" + local scoped_var_scope_mode="${2:-}" + local scoped_var_sub_name="${3:-}" + local posix_validator="${4:-}" + + local validator_arg + local validator_mode + + if [ $# -lt 4 ]; then + echo "Invalid argument count $# for the 'get-value' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects minimum 4 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + + # Remove args before `default_values` to be used in `var_value_cur` for loop below. + shift 4 + + case "$posix_validator" in + '?'|'*') + validator_arg="" + validator_mode="$posix_validator" + ;; + 'p+='?*|'p-='?*|'c+='?*|'c-='?*) + validator_arg="${posix_validator#???}" # 3:end + validator_mode="${posix_validator%"$validator_arg"}" # 0:3 + ;; + *) + echo "The posix_validator '$posix_validator' \ +argument passed to 'termux_core__sh__termux_scoped_env_variable' is not valid. \ +It must either be equal to \`?\` or \`*\`, or a pattern that starts with \`p+=\` or \`p-=\`, \ +or a executable or function that starts with \`c+=\` or \`c-=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + elif [ "$command_type" = "set-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + # shellcheck disable=SC2034 + local value_to_set="${3:-}" + + if [ $# -ne 3 ]; then + echo "Invalid argument count $# for the 'set-value' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects 3 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + elif [ "$command_type" = "unset-value" ]; then + local scoped_var_scope_mode="${1:-}" + local scoped_var_sub_name="${2:-}" + + if [ $# -ne 2 ]; then + echo "Invalid argument count $# for the 'unset-value' command. \ +The 'termux_core__sh__termux_scoped_env_variable' function expects 2 arguments." 1>&2 + printf 'Arguments: %s\n' "$*" 1>&2 + return 64 # EX__USAGE + fi + else + echo "The command '$command_type' passed to 'termux_core__sh__termux_scoped_env_variable' is not valid." 1>&2 + return 64 # EX__USAGE + fi + + local i + local is_value_valid + local scoped_var_root_scope_name="" + local scoped_var_scope_name="" + local scoped_var_sub_scope_name="" + local scoped_var_name="" + local scoped_var_value_invalid_error_suffix="" + local var_value_cur + + + if [ "$command_action" = "get" ]; then + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # If `output_mode` is not a valid environment variable name. + if ! termux_core__sh__is_valid_shell_variable_name "$output_mode"; then + echo "The output_mode '$output_mode' argument passed to \ +'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name, or equal to \`>\` or \`-\`." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + + case "$scoped_var_scope_mode" in + s=?*) scoped_var_scope_name="${scoped_var_scope_mode#??}";; # 2:end + ss=?*) scoped_var_sub_scope_name="${scoped_var_scope_mode#???}";; # 3:end + '') ;; + cn=termux) scoped_var_sub_scope_name="_";; + cn=termux-app) scoped_var_sub_scope_name="APP__";; + cn=termux-api-app) scoped_var_sub_scope_name="API_APP__";; + cn=termux-float-app) scoped_var_sub_scope_name="FLOAT_APP__";; + cn=termux-gui-app) scoped_var_sub_scope_name="GUI_APP__";; + cn=termux-tasker-app) scoped_var_sub_scope_name="TASKER_APP__";; + cn=termux-widget-app) scoped_var_sub_scope_name="WIDGET_APP__";; + cn=termux-x11-app) scoped_var_sub_scope_name="X11_APP__";; + cn=termux-core) scoped_var_sub_scope_name="CORE__";; + cn=termux-exec) scoped_var_sub_scope_name="EXEC__";; + *) + echo "The scoped_var_scope_mode '$scoped_var_scope_mode' \ +argument for the variable to $command_action passed to \ +'termux_core__sh__termux_scoped_env_variable' is not valid. \ +It must either be a supported component name starting with \`cn=\`, \ +or an environment variable scope starting with \`s=\` or \`ss=\`." 1>&2 + return 64 # EX__USAGE + ;; + esac + + + if [ -n "$scoped_var_scope_mode" ]; then + if [ -n "$scoped_var_scope_name" ]; then + # Generate the full name for the variable under the provided root and sub scope. + scoped_var_name="${scoped_var_scope_name}${scoped_var_sub_name}" + else + # Generate the full name for the variable under the Termux root scope and provided sub scope. + # If `TERMUX_ENV__S_ROOT` environment variable set. + # shellcheck disable=SC2050 + if [ -n "${TERMUX_ENV__S_ROOT:-}" ]; then + scoped_var_root_scope_name="$TERMUX_ENV__S_ROOT" + if ! termux_core__sh__is_valid_shell_variable_name "$scoped_var_root_scope_name"; then + echo "The TERMUX_ENV__S_ROOT environment variable value '$scoped_var_root_scope_name' \ +while running 'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + # If `TERMUX_ENV__S_ROOT` placeholder got replaced during build time. + elif [ "@TERMUX_ENV__S_ROOT@" != @"TERMUX_ENV__S_ROOT"@ ]; then + scoped_var_root_scope_name="@TERMUX_ENV__S_ROOT@" + if ! termux_core__sh__is_valid_shell_variable_name "$scoped_var_root_scope_name"; then + echo "The TERMUX_ENV__S_ROOT build value '$scoped_var_root_scope_name' \ +while running 'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 1 + fi + else + scoped_var_root_scope_name="TERMUX_" + fi + + scoped_var_name="${scoped_var_root_scope_name}${scoped_var_sub_scope_name}${scoped_var_sub_name}" + fi + + if ! termux_core__sh__is_valid_shell_variable_name "$scoped_var_name"; then + echo "The name of the variable to $command_action '$scoped_var_name' generated in \ +'termux_core__sh__termux_scoped_env_variable' is not a valid environment variable name." 1>&2 + return 64 # EX__USAGE + fi + + + # If command type equals `get-name`, then return the variable name and exit. + if [ "$command_type" = "get-name" ]; then + #echo "scoped_var_name=$scoped_var_name". + if [ "$output_mode" = ">" ]; then + printf "%s" "$scoped_var_name" + return $? + elif [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\$scoped_var_name\" + #eval "echo $output_mode=\"\${${output_mode}}\"" + return $? + else + return 0 + fi + elif [ "$command_type" = "set-value" ]; then + eval "$scoped_var_name"=\"\$value_to_set\" + return $? + elif [ "$command_type" = "unset-value" ]; then + unset "$scoped_var_name" + return $? + fi + else + if [ "$command_type" = "get-name" ] || \ + [ "$command_type" = "set-value" ] || [ "$command_type" = "unset-value" ]; then + echo "The scoped_var_scope_mode argument for the variable \ +to $command_action passed for the '$command_type' command to \ +'termux_core__sh__termux_scoped_env_variable' is not set." 1>&2 + return 64 # EX__USAGE + fi + fi + + + # If command type equals `get-value`, then find the first valid in variable name/values passed. + if [ "$command_type" = "get-value" ]; then + if [ "$validator_mode" = "c+=" ] || [ "$validator_mode" = "c-=" ]; then + local command_arg_first_arg="${validator_arg%% *}" + + # Check if a command exists, like an executable in `$PATH`, + # a shell function or a path to an executable. + # - https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html + if ! command -v -- "$command_arg_first_arg" >/dev/null 2>&1; then + # Check if absolute or relative path as `command -v` will only check executables under `$PATH`. + local validator_arg_is_path=0 + case "$command_arg_first_arg" in + *[/]*) validator_arg_is_path=1;; + esac + + if [ "$validator_arg_is_path" = "1" ] && [ -f "$command_arg_first_arg" ] && [ -x "$command_arg_first_arg" ]; then + : + else + echo "The validator command '$command_arg_first_arg' \ +not found while running 'termux_core__sh__termux_scoped_env_variable' \ +that is set in the posix_validator '$posix_validator' argument." 1>&2 + return 64 # EX__USAGE + fi + fi + fi + + # Loop on [`` ``] to + # find the first valid value. + i=0 + for var_value_cur in "$scoped_var_name" "$@"; do + var_value_cur="${var_value_cur:-}" + + # If first loop, then expand the `scoped_var_name` to its value. + if [ "$i" = 0 ] && [ -n "$scoped_var_name" ]; then + # If mode is `*`, expand variable if it is set, or defined + # but empty. Else if its not defined, then ignore it. + if [ "$validator_mode" = "*" ]; then + eval '[ -n "${'"$scoped_var_name"'+x}" ] && var_value_cur="$'"$scoped_var_name"'" || { i=$((i + 1)); continue; }' + else + eval 'var_value_cur="${'"$scoped_var_name"':-}"' + fi + fi + + is_value_valid=0 + if [ "$validator_mode" = "p+=" ]; then + return_value=0 + eval 'case "$var_value_cur" in + '"$validator_arg"') is_value_valid=1;; + esac' || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failure while using a positive shell \ +'case' statement to validate the variable value with the pattern '$validator_arg' \ +passed to 'termux_core__sh__termux_scoped_env_variable'." 1>&2 + return 64 # EX__USAGE + fi + elif [ "$validator_mode" = "p-=" ]; then + return_value=0 + eval 'case "$var_value_cur" in + '"$validator_arg"') :;; + *) is_value_valid=1;; + esac' || return_value=$? + if [ $return_value -ne 0 ]; then + echo "Failure while using a negative shell \ +'case' statement to validate the variable value with the pattern '$validator_arg' \ +passed to 'termux_core__sh__termux_scoped_env_variable'." 1>&2 + return 64 # EX__USAGE + fi + elif [ "$validator_mode" = "c+=" ]; then + # Do not use `if command; then` to preserve `set -e` failures in called function. + return_value=0 + $validator_arg "$var_value_cur" || return_value=$? + if [ $return_value -eq 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + elif [ "$validator_mode" = "c-=" ]; then + # Do not use `if ! command; then` to preserve `set -e` failures in called function. + return_value=0 + $validator_arg "$var_value_cur" || return_value=$? + if [ $return_value -ne 0 ]; then + is_value_valid=1 + else + is_value_valid=0 # Prevent using value overridden by called function. + fi + else + # If mode is `?` or `*`, and value is set, + # or mode is `*` and value is not set. + if [ -n "$var_value_cur" ] || [ "$validator_mode" = "*" ]; then + is_value_valid=1 + # Else if mode is `?`, and value is not set. + else + is_value_valid=0 + fi + fi + + if [ "$is_value_valid" = "1" ]; then + #echo "var_value_cur=$var_value_cur" + if [ "$output_mode" = ">" ]; then + printf "%s" "$var_value_cur" + return $? + elif [ "$output_mode" != "-" ]; then + eval "$output_mode"=\"\$var_value_cur\" + #eval "echo $output_mode=\"\${${output_mode}}\"" + return $? + else + return 0 + fi + fi + + i=$((i + 1)) + done + + + # If a valid value not found. + + if [ -n "$scoped_var_name" ]; then + scoped_var_value_invalid_error_suffix=" that is read from the '\$$scoped_var_name' variable" + fi + if [ "$output_mode" != ">" ] && [ "$output_mode" != "-" ]; then + # Set output variable in `output_mode` to an empty string + # since it may already be set, as callers may try to use that + # wrong value without checking the exit code. + # We unset after reading the values, otherwise if + # `scoped_var_name` is equal to output variable in + # `output_mode`, then `scoped_var_name` would get unset before + # its read. + eval "$output_mode"=\"\" || return $? + + echo "Failed to find a valid value to set to the '\$$output_mode' \ +variable${scoped_var_value_invalid_error_suffix}." 1>&2 + else + echo "Failed to find a valid value${scoped_var_value_invalid_error_suffix}." 1>&2 + fi + return 81 # C_EX__NOT_FOUND + fi + +} + +## +# Check if a string is a valid shell variable name. +# (like `TERMUX__VAR`). +# - https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#index-name +# +# +# `termux_core__sh__is_valid_shell_variable_name` `variable_name` +## +termux_core__sh__is_valid_shell_variable_name() { + + local variable_name="${1:-}" + local variable_name_rest="${variable_name#?}" # 1:end + local variable_name_first_char="${variable_name%"$variable_name_rest"}" # 0:1 + + case "$variable_name_first_char" in + [a-zA-Z_]) + case "$variable_name_rest" in + *[!a-zA-Z0-9_]*) return 1;; + *) return 0;; + esac;; + *) return 1;; + esac + +} + +##### TERMUX_CORE__SH__TERMUX_SCOPED_ENV_VARIABLE replaced at build time. (END) ##### diff --git a/packages/termux-core/build.sh b/packages/termux-core/build.sh new file mode 100644 index 000000000000000..617d2d320a3563b --- /dev/null +++ b/packages/termux-core/build.sh @@ -0,0 +1,41 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-core-package +TERMUX_PKG_DESCRIPTION="Utils and libraries for Termux core" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_ESSENTIAL=true +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_AUTO_UPDATE=true +TERMUX_PKG_EXTRA_MAKE_ARGS="TERMUX_CORE_PKG__VERSION=${TERMUX_PKG_VERSION} TERMUX_CORE_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX__REPOS_HOST_ORG_NAME=${TERMUX__REPOS_HOST_ORG_NAME} TERMUX__REPOS_HOST_ORG_URL=${TERMUX__REPOS_HOST_ORG_URL} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__HOME=${TERMUX__HOME} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX__PREFIX__TMP_DIR=${TERMUX__PREFIX__TMP_DIR} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP} \ +TERMUX_ENV__SS_TERMUX_API_APP=${TERMUX_ENV__SS_TERMUX_API_APP} TERMUX_ENV__S_TERMUX_API_APP=${TERMUX_ENV__S_TERMUX_API_APP} \ +TERMUX_ENV__SS_TERMUX_ROOTFS=${TERMUX_ENV__SS_TERMUX_ROOTFS} TERMUX_ENV__S_TERMUX_ROOTFS=${TERMUX_ENV__S_TERMUX_ROOTFS} \ +TERMUX_ENV__SS_TERMUX_CORE=${TERMUX_ENV__SS_TERMUX_CORE} TERMUX_ENV__S_TERMUX_CORE=${TERMUX_ENV__S_TERMUX_CORE} \ +TERMUX_ENV__SS_TERMUX_CORE__TESTS=${TERMUX_ENV__SS_TERMUX_CORE__TESTS} TERMUX_ENV__S_TERMUX_CORE__TESTS=${TERMUX_ENV__S_TERMUX_CORE__TESTS} \ +TERMUX_ENV__SS_TERMUX_EXEC__TESTS=${TERMUX_ENV__SS_TERMUX_EXEC__TESTS} TERMUX_ENV__S_TERMUX_EXEC__TESTS=${TERMUX_ENV__S_TERMUX_EXEC__TESTS} \ +TERMUX_APP__NAMESPACE=${TERMUX_APP__NAMESPACE} \ +TERMUX_APP__SHELL_ACTIVITY__COMPONENT_NAME=${TERMUX_APP__SHELL_ACTIVITY__COMPONENT_NAME} TERMUX_APP__SHELL_SERVICE__COMPONENT_NAME=${TERMUX_APP__SHELL_SERVICE__COMPONENT_NAME} \ +TERMUX_PKGS__REPO_NAME=${TERMUX_PKGS__REPO_NAME} TERMUX_PKGS__REPO_URL=${TERMUX_PKGS__REPO_URL} \ +TERMUX_PKGS__BUILD__REPO_ROOT_DIR=${TERMUX_PKGS__BUILD__REPO_ROOT_DIR} \ +TERMUX_CORE_PKG__REPO_NAME=${TERMUX_CORE_PKG__REPO_NAME} TERMUX_CORE_PKG__REPO_URL=${TERMUX_CORE_PKG__REPO_URL}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} + +termux_step_strip_elf_symbols() { + termux_step_strip_elf_symbols__from_paths . \ + \( \ + \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -a \ + \( ! -path "./libexec/installed-tests/termux-core/*" \) \ + \) +} diff --git a/packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts b/packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts new file mode 100755 index 000000000000000..b109eb391690076 --- /dev/null +++ b/packages/termux-core/build/scripts/termux-replace-termux-core-src-scripts @@ -0,0 +1,258 @@ +#!/bin/bash +# shellcheck shell=bash + +if [ -z "${BASH_VERSION:-}" ]; then + echo "The 'termux-replace-termux-core-src-scripts' script must be run from a 'bash' shell."; return 64 2>/dev/null|| exit 64 # EX__USAGE +fi + + + +REGEX__ABSOLUTE_PATH='^(/[^/]+)+$' + +function termux_rtcss__log_errors() { echo "$@" 1>&2; } + +termux_rtcss__show_help() { + + cat <<'HELP_EOF' +termux-replace-termux-core-src-scripts replaces inplace the `@@` +formatted build time placeholders for the termux-core package source +scripts with their content in the `input_files` supplied. + + +Usage: + termux-replace-termux-core-src-scripts + + +The scripts whose placeholders are replaced exist in the +`packages/termux-core/app/main/scripts` directory of the `termux-package` +repo. The `` in the `@@` placeholders must be equal to +uppercased script file name whose content to replace. + + +The following required variables must be exported when executed this +script, which are defined in the `scripts/properties.sh` file of the +`termux-package` repo. + +- TERMUX_ENV__S_ROOT +- TERMUX__PREFIX +- TERMUX_PKGS__REPO_URL +- TERMUX_CORE_PKG__REPO_URL +HELP_EOF + +} + +termux_rtcss__main() { + + local return_value + + if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + termux_rtcss__show_help || return $? + return 0 + else + termux_rtcss__replace_termux_core_src_scripts "$@" + return_value=$? + if [ $return_value -eq 64 ]; then # EX__USAGE + echo "" + termux_rtcss__show_help + fi + return $return_value + fi + +} + +termux_rtcss__replace_termux_core_src_scripts() { + + local return_value + + if [ $# -lt 1 ]; then + termux_rtcss__log_errors "input_files not passed." + return 64 + fi + + local exported_variable + local input_file + local src_script_file + local src_script_file_basename + local src_script_file_placeholder + local src_script_content + local src_script_escaped_content + local src_script_content_var_name + local termux_core__src_srcipts_dir + + + termux_core__src_srcipts_dir=$(realpath "$(dirname "$TERMUX_RTCSS__ARG_0")/../../app/main/scripts") + if [[ ! "$termux_core__src_srcipts_dir" =~ $REGEX__ABSOLUTE_PATH ]]; then + termux_rtcss__log_errors "The termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' with length ${#termux_core__src_srcipts_dir} is invalid." + termux_rtcss__log_errors "The termux_core__src_srcipts_dir must be an absolute path for a sub path under rootfs '/'." + return 1 + fi + + if [ ! -d "$termux_core__src_srcipts_dir" ]; then + termux_rtcss__log_errors "The termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory does not exist." + return 1 + fi + + # The order matters here. The scripts that have `@*@` placeholders + # for other scripts must come before their placeholder scripts, + # so that first the caller scripts get replaced in the input + # files, and then the placeholders for other scripts get replaced + # in the input files. + # For example, the `termux_core__bash__termux_apps_info_env_variable` script + # has the `@TERMUX_CORE__BASH__TERMUX_SCOPED_ENV_VARIABLE@` + # placeholder for the `termux_core__bash__termux_scoped_env_variable` + # script and so the `termux_core__bash__termux_apps_info_env_variable` script + # comes first. + local -a required_src_scripts=( + "termux/shell/command/environment/termux_core__bash__termux_apps_info_app_version_name" + "termux/shell/command/environment/termux_core__bash__termux_apps_info_env_variable" + "termux/shell/command/environment/termux_core__bash__termux_scoped_env_variable" + "termux/shell/command/environment/termux_core__sh__termux_apps_info_app_version_name" + "termux/shell/command/environment/termux_core__sh__termux_apps_info_env_variable" + "termux/shell/command/environment/termux_core__sh__termux_scoped_env_variable" + ) + + for src_script_file in "${required_src_scripts[@]}"; do + if [ ! -f "$termux_core__src_srcipts_dir/$src_script_file" ]; then + termux_rtcss__log_errors "The '$src_script_file' source script file \ +not found under the termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory." + return 1 + fi + done + + + local -a required_exported_variables=( + "TERMUX_ENV__S_ROOT" + "TERMUX__PREFIX" + "TERMUX_PKGS__REPO_URL" + "TERMUX_CORE_PKG__REPO_URL" + ) + + for exported_variable in "${required_exported_variables[@]}"; do + if [ -z "${!exported_variable}" ]; then + termux_rtcss__log_errors "The $exported_variable variable is not set." + return 1 + fi + done + + + for input_file in "$@"; do + if [ ! -f "$input_file" ]; then + termux_rtcss__log_errors "The input file '$input_file' passed does not exist at path" + return 1 + fi + done + + + + # For all the required_src_scripts, read their content into local variables. + for src_script_file in "${required_src_scripts[@]}"; do + # Read the src_script_file and ensure its not empty. + src_script_content="$(cat "$termux_core__src_srcipts_dir/$src_script_file")" + return_value=$? + if [ $return_value -ne 0 ]; then + termux_rtcss__log_errors "Failed to read the '$src_script_file' source script file \ +under the termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory." + return $return_value + elif [ -z "$src_script_content" ]; then + termux_rtcss__log_errors "The '$src_script_file' source script file \ +under the termux_core__src_srcipts_dir '$termux_core__src_srcipts_dir' directory is empty." + return 1 + fi + + # Replace placeholder for variables (not scripts) in the src_script_content. + src_script_content="$(printf "%s" "$src_script_content" | sed \ + -e "s%[@]TERMUX_ENV__S_ROOT[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX_ENV__S_ROOT")%g" \ + -e "s%[@]TERMUX__PREFIX[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX__PREFIX")%g" \ + -e "s%[@]TERMUX_PKGS__REPO_URL[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX_PKGS__REPO_URL")%g" \ + -e "s%[@]TERMUX_CORE_PKG__REPO_URL[@]%$(termux_rtcss__get_sed_replacement_escaped_string "$TERMUX_CORE_PKG__REPO_URL")%g" + )" || return $? + + # Create a local variable with the same name as its script file name + # with `/` replaced with `__` and `__content` appended. + src_script_content_var_name="${src_script_file//\//__}__content" + local "$src_script_content_var_name" + printf -v "$src_script_content_var_name" "%s" "$src_script_content" + + #echo "$src_script_content_var_name:"$'\n```'"${!src_script_content_var_name}"$'\n```' + done + + + + # For all the required_src_scripts. + for src_script_file in "${required_src_scripts[@]}"; do + src_script_content_var_name="${src_script_file//\//__}__content" + src_script_file_basename="${src_script_file##*/}" + + # Escape the content to be used as a sed replacement string. + src_script_escaped_content="$(termux_rtcss__get_sed_replacement_escaped_string \ + "${!src_script_content_var_name}")" || return $? + + # For all the input_files, replace the first line found that + # contains the `@src_script_file@` placeholder with the script content. + for input_file in "$@"; do + sed -i'' -e "s/.*[@]${src_script_file_basename^^}[@].*/$src_script_escaped_content/" "$input_file" + return_value=$? + if [ $return_value -ne 0 ]; then + termux_rtcss__log_errors "Failed to replace the '$src_script_file' source script \ +placeholder '@${src_script_file_basename^^}@' in the input file '$input_file' with script content." + return $return_value + fi + done + done + + return 0 + +} + +## +# Escape the following characters with a backslash `\` in a string +# that is to be used as the `replacement` string in a `sed` +# `s/regexp/replacement/` expression so that the replacement string +# is literally replaced, even if the string is multiline: +# - `&` used to reference entire string matched by the regex, +# - `\` used to reference for capturing groups, like `\1`, `\2`, etc. +# - `/` used as the delimiter in sed `s/regexp/replacement/` expression. +# - `\n` characters must be escaped. +# +# The escaped replacement string will be echoed to `stdout`. +# +# **See Also:** +# - https://stackoverflow.com/a/29613573/14686958 +# +# `termux_rtcss__get_sed_replacement_escaped_string` `` +## +termux_rtcss__get_sed_replacement_escaped_string() { + + local sed_replacement_escaped_string + + # read is used to preserve trailing newlines that would be lost if a subshell is used, but isn't posix shell compliant. + #IFS= read -d '' -r sed_replacement_escaped_string < <(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\]/\\&/g; s/\n/\\&/g' <<<"$1") + + # Instead of using the `sed -z` option, we manually add everything + # to pattern space. + # `$!` checks if we are before last line, so if we are not on it, + # then we add next line to pattern space. + # Then we escape the required characters. + # The x is added in subshell to preserve trailing newlines and then removed. + sed_replacement_escaped_string="$(printf '%s' "$1" | sed -e '$!{:a;N;$!ba;}; s/[&/\]/\\&/g; s/\n/\\&/g'; printf "%s" x)" + sed_replacement_escaped_string="${sed_replacement_escaped_string%x}" + + printf %s "${sed_replacement_escaped_string%$'\n'}" + +} + + + +TERMUX_RTCSS__ARG_0="$0" + +# If script is sourced, return with error, otherwise call main function. +# - https://stackoverflow.com/a/28776166/14686958 +# - https://stackoverflow.com/a/29835459/14686958 +if (return 0 2>/dev/null); then + echo "${0##*/} cannot be sourced as '\$0' is required by internal functions, \ +which will be invalid if functions are sourced and called directly." 1>&2 + return 64 # EX__USAGE +else + termux_rtcss__main "$@" + exit $? +fi diff --git a/packages/termux-tools/build.sh b/packages/termux-tools/build.sh index ea94eb57109d17b..6bc081451542e70 100644 --- a/packages/termux-tools/build.sh +++ b/packages/termux-tools/build.sh @@ -15,7 +15,7 @@ TERMUX_PKG_SUGGESTS="termux-api" # Some of these packages are not dependencies and used only to ensure # that core packages are installed after upgrading (we removed busybox # from essentials). -TERMUX_PKG_DEPENDS="bzip2, coreutils, curl, dash, diffutils, findutils, gawk, grep, gzip, less, procps, psmisc, sed, tar, termux-am (>= 0.8.0), termux-am-socket (>= 1.5.0), termux-exec, util-linux, xz-utils, dialog" +TERMUX_PKG_DEPENDS="bzip2, coreutils, curl, dash, diffutils, findutils, gawk, grep, gzip, less, procps, psmisc, sed, tar, termux-am (>= 0.8.0), termux-am-socket (>= 1.5.0), termux-core, termux-exec, util-linux, xz-utils, dialog" # Optional packages that are distributed as part of bootstrap archives. TERMUX_PKG_RECOMMENDS="ed, dos2unix, inetutils, net-tools, patch, unzip" diff --git a/scripts/build-bootstraps.sh b/scripts/build-bootstraps.sh index a1f19c712891ce4..321c135339f601a 100755 --- a/scripts/build-bootstraps.sh +++ b/scripts/build-bootstraps.sh @@ -444,6 +444,7 @@ main() { PACKAGES+=("psmisc") PACKAGES+=("sed") PACKAGES+=("tar") + PACKAGES+=("termux-core") PACKAGES+=("termux-exec") PACKAGES+=("termux-keyring") PACKAGES+=("termux-tools") diff --git a/scripts/generate-bootstraps.sh b/scripts/generate-bootstraps.sh index c60ff8416d642c0..754f5edaf2980b0 100755 --- a/scripts/generate-bootstraps.sh +++ b/scripts/generate-bootstraps.sh @@ -468,6 +468,7 @@ for package_arch in "${TERMUX_ARCHITECTURES[@]}"; do pull_package psmisc pull_package sed pull_package tar + pull_package termux-core pull_package termux-exec pull_package termux-keyring pull_package termux-tools diff --git a/scripts/properties.sh b/scripts/properties.sh index c5146d5b74b1318..1faf984e9698f90 100644 --- a/scripts/properties.sh +++ b/scripts/properties.sh @@ -542,6 +542,24 @@ TERMUX__APPS_APP_UID___MAX_LEN=9 +## +# Termux apps info environment subfile path under an app directory of +# `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `termux-apps-info.env` +## +TERMUX_CORE__APPS_INFO_ENV_SUBFILE="$TERMUX__INTERNAL_NAME-apps-info.env" + +## +# Termux apps info json subfile path under an app directory of +# `TERMUX__APPS_DIR_BY_IDENTIFIER`. +# +# Default value: `termux-apps-info.json` +## +TERMUX_CORE__APPS_INFO_JSON_SUBFILE="$TERMUX__INTERNAL_NAME-apps-info.json" + + + ## # `termux-am-socket` server subfile path under an app directory of # `TERMUX__APPS_DIR_BY_IDENTIFIER`. @@ -818,6 +836,7 @@ TERMUX__PREFIX__BIN_DIR___MAX_LEN="$((TERMUX__PREFIX_DIR___MAX_LEN + 1 + 3))" # # # **See Also:** # - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits +# - https://github.com/termux/termux-core-package/blob/master/lib/termux-core_nos_c_tre/include/termux/termux_core__nos__c/v1/termux/file/TermuxFile.h # # Constant value: `127` ## @@ -1122,6 +1141,16 @@ TERMUX__UNIX_PATH_MAX=108 # The `TERMUX_ENV__S_APP` environment variable will be exported at # runtime for the scope of the current Termux app running the shell. # +# Termux packages and external programs can use the +# `termux-scoped-env-variable` util from the `termux-core` +# package to get variable names and values for Termux. It uses the +# root scope from the `$TERMUX_ENV__S_ROOT` environment variable +# exported by the Termux app to dynamically generate the Termux +# variable names and/or get their values, with support for fallback +# to the build values defined here if `$TERMUX_ENV__S_ROOT` variable +# is not exported.** +# - https://github.com/termux/termux-core-package/blob/master/site/pages/en/projects/docs/usage/utils/termux/shell/command/environment/termux-scoped-env-variable.md +# # The value of this variable `TERMUX_ENV__S_ROOT` may be modified, # although not advisable since external programs would be using # hardcoded `TERMUX_` value for reading Termux environment variables, @@ -1267,6 +1296,52 @@ TERMUX_ENV__S_TERMUX_ROOTFS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_ROOTFS ## +# Termux environment variables `termux-core` sub scope. +# +# **Do not modify this!** This is considered a constant `termux-core` +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_CORE` and variable names under it.** +# +# Default value: `CORE__` +## +TERMUX_ENV__SS_TERMUX_CORE="CORE__" + +## +# Termux environment variables `termux-core` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_CORE__` +## +TERMUX_ENV__S_TERMUX_CORE="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_CORE}" + + +## +# Termux environment variables `termux-core-tests` sub scope. +# +# **Do not modify this!** This is considered a constant +# `termux-core-tests` sub scope for Termux execution environment +# that's used by `termux-core` package to generate the value for +# `$TERMUX_ENV__S_TERMUX_CORE__TESTS` and variable names under it.** +# +# Default value: `TERMUX_CORE__TESTS__` +## +TERMUX_ENV__SS_TERMUX_CORE__TESTS="CORE__TESTS__" + +## +# Termux environment variables `termux-core-tests` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_CORE__TESTS__` +## +TERMUX_ENV__S_TERMUX_CORE__TESTS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_CORE__TESTS}" + + + # Termux environment variables `termux-am-socket` sub scope. # # **Do not modify this!** This is considered a constant `termux-am-socket` @@ -1471,6 +1546,22 @@ TERMUX_APP__DATA_SENDER_BROADCASTRECEIVER__COMPONENT_NAME="$TERMUX_APP__PACKAGE_ +## +# Termux apps info environment file path for the Termux app under `TERMUX_APP__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux/termux-apps-info.env` +## +TERMUX_APP__CORE__APPS_INFO_ENV_FILE="$TERMUX_APP__APPS_DIR/$TERMUX_CORE__APPS_INFO_ENV_SUBFILE" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__CORE__APPS_INFO_ENV_FILE" "safe_absolute_path" + +## +# Termux apps info json file path for the Termux app under `TERMUX_APP__APPS_DIR`. +# +# Default value: `/data/data/com.termux/termux/apps/i/termux/termux-apps-info.json` +## +TERMUX_APP__CORE__APPS_INFO_JSON_FILE="$TERMUX_APP__APPS_DIR/$TERMUX_CORE__APPS_INFO_JSON_SUBFILE" +__termux_build_props__add_variables_validator_actions "TERMUX_APP__CORE__APPS_INFO_JSON_FILE" "safe_absolute_path" + ## # `termux-am-socket` server file path for the Termux app under `TERMUX_APP__APPS_DIR`. # @@ -1612,6 +1703,35 @@ TERMUX_API_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_API_PKG__REPO_NAME + + + + + +#### +# Variables for the `termux-core` package. +# +# - https://github.com/termux/termux-core-package +#### + +## +# The `termux-core` package repo name. +# +# Default value: `termux-core-package` +## +TERMUX_CORE_PKG__REPO_NAME="termux-core-package" + +## +# The `termux-core` package repo url. +# +# Default value: `https://github.com/termux/termux-core-package` +## +TERMUX_CORE_PKG__REPO_URL="$TERMUX__REPOS_HOST_ORG_URL/$TERMUX_CORE_PKG__REPO_NAME" + + + + + #### # Variables for the `termux-am` package. # From 7b6ac13de331d3e6a877f3b017099cf44ef8bc68 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 22 Mar 2025 05:09:23 +0500 Subject: [PATCH 19/28] bump(main/termux-core): 0.1.0 --- packages/termux-core/build.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/termux-core/build.sh b/packages/termux-core/build.sh index 617d2d320a3563b..6a488829b720bdc 100644 --- a/packages/termux-core/build.sh +++ b/packages/termux-core/build.sh @@ -2,6 +2,9 @@ TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-core-package TERMUX_PKG_DESCRIPTION="Utils and libraries for Termux core" TERMUX_PKG_LICENSE="MIT" TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_VERSION=0.1.0 +TERMUX_PKG_SRCURL=https://github.com/termux/termux-core-package/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz +TERMUX_PKG_SHA256=9eb8586be7c1a34b66cd4c77f1145c5e968da943d3dfc4f0d12f534223b165b7 TERMUX_PKG_ESSENTIAL=true TERMUX_PKG_BUILD_IN_SRC=true TERMUX_PKG_AUTO_UPDATE=true From f9718242f22f3c7ca17fba637425aae52e2cf2ee Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Mon, 28 Oct 2024 20:28:08 +0500 Subject: [PATCH 20/28] enhance(main/termux-exec): add support for v1:2.0.0 Related pull https://github.com/termux/termux-exec/pull/24 --- packages/termux-exec/build.sh | 62 +++++++++++++++++++++- scripts/build/termux_step_patch_package.sh | 1 + scripts/properties.sh | 52 ++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) diff --git a/packages/termux-exec/build.sh b/packages/termux-exec/build.sh index a7f807e60401090..9ebc9035c84580a 100644 --- a/packages/termux-exec/build.sh +++ b/packages/termux-exec/build.sh @@ -1,12 +1,70 @@ TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-exec-package -TERMUX_PKG_DESCRIPTION="An execve() wrapper to make /bin and /usr/bin shebangs work" +TERMUX_PKG_DESCRIPTION="Utils and libraries for Termux exec including a LD_PRELOAD shared library for proper functioning of the Termux execution environment" TERMUX_PKG_LICENSE="Apache-2.0" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION=1:1.0 TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=https://github.com/termux/termux-exec-package/archive/v${TERMUX_PKG_VERSION:2}.tar.gz TERMUX_PKG_SHA256=175d7c9eac6f9fc3b949d1a2cee5f5d3ace61420d418d8213369eb6aff18d28f +TERMUX_PKG_BUILD_DEPENDS="termux-core" TERMUX_PKG_ESSENTIAL=true TERMUX_PKG_BUILD_IN_SRC=true -TERMUX_PKG_EXTRA_MAKE_ARGS="TERMUX_PREFIX=${TERMUX_PREFIX} TERMUX_BASE_DIR=${TERMUX_BASE_DIR}" TERMUX_PKG_AUTO_UPDATE=true +TERMUX_PKG_EXTRA_MAKE_ARGS="TERMUX_EXEC_PKG__VERSION=${TERMUX_PKG_VERSION} TERMUX_EXEC_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP} \ +TERMUX_ENV__SS_TERMUX_ROOTFS=${TERMUX_ENV__SS_TERMUX_ROOTFS} TERMUX_ENV__S_TERMUX_ROOTFS=${TERMUX_ENV__S_TERMUX_ROOTFS} \ +TERMUX_ENV__SS_TERMUX_EXEC=${TERMUX_ENV__SS_TERMUX_EXEC} TERMUX_ENV__S_TERMUX_EXEC=${TERMUX_ENV__S_TERMUX_EXEC} \ +TERMUX_ENV__SS_TERMUX_EXEC__TESTS=${TERMUX_ENV__SS_TERMUX_EXEC__TESTS} TERMUX_ENV__S_TERMUX_EXEC__TESTS=${TERMUX_ENV__S_TERMUX_EXEC__TESTS}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} + +termux_step_strip_elf_symbols() { + termux_step_strip_elf_symbols__from_paths . \ + \( \ + \( -path "./bin/*" -o -path "./lib/*" -o -path "./libexec/*" \) -a \ + \( ! -path "./libexec/installed-tests/termux-exec/*" \) \ + \) +} + +termux_step_post_massage() { + # Hack to compile `libtermux-exec_nos_c_tre_runtime-binary-tests` for api level 28 if default (currently 24) is less than it. + if [[ "$TERMUX_PKG_API_LEVEL" -lt 28 ]]; then + export TERMUX_PKG_API_LEVEL=28 + termux_step_setup_toolchain + + local QUIET_BUILD= + if [ "$TERMUX_QUIET_BUILD" = true ]; then + QUIET_BUILD="-s" + fi + + printf "\n%s\n" "Building libtermux-exec_nos_c_tre_runtime-binary-tests for TERMUX_PKG_API_LEVEL '$TERMUX_PKG_API_LEVEL'" + cd "$TERMUX_PKG_BUILDDIR" + make $QUIET_BUILD $TERMUX_PKG_EXTRA_MAKE_ARGS TERMUX_EXEC_PKG__TESTS__API_LEVEL=28 build-libtermux-exec_nos_c_tre_runtime-binary-tests + + local TERMUX_EXEC__TESTS__TESTS_PATH="$TERMUX_PKG_MASSAGEDIR/$TERMUX_PREFIX/libexec/installed-tests/termux-exec" + local LIBTERMUX_EXEC__NOS__C__TESTS_PATH="$TERMUX_EXEC__TESTS__TESTS_PATH/lib/termux-exec_nos_c_tre" + + install -m700 build/output/usr/libexec/installed-tests/termux-exec/lib/termux-exec_nos_c_tre/bin/libtermux-exec_nos_c_tre_runtime-binary-tests-fsanitize28 \ + "$LIBTERMUX_EXEC__NOS__C__TESTS_PATH/bin/libtermux-exec_nos_c_tre_runtime-binary-tests-fsanitize28" + $TERMUX_ELF_CLEANER --api-level 28 "$LIBTERMUX_EXEC__NOS__C__TESTS_PATH/bin/libtermux-exec_nos_c_tre_runtime-binary-tests-fsanitize28" + + install -m700 build/output/usr/libexec/installed-tests/termux-exec/lib/termux-exec_nos_c_tre/bin/libtermux-exec_nos_c_tre_runtime-binary-tests-nofsanitize28 \ + "$LIBTERMUX_EXEC__NOS__C__TESTS_PATH/bin/libtermux-exec_nos_c_tre_runtime-binary-tests-nofsanitize28" + $TERMUX_ELF_CLEANER --api-level 28 "$LIBTERMUX_EXEC__NOS__C__TESTS_PATH/bin/libtermux-exec_nos_c_tre_runtime-binary-tests-nofsanitize28" + printf "%s\n\n" "Install libtermux-exec_nos_c_tre_runtime-binary-tests for TERMUX_PKG_API_LEVEL '$TERMUX_PKG_API_LEVEL' successful" + fi +} + +termux_step_create_debscripts() { + termux_step_create_debscripts__copy_from_dir "$TERMUX_PKG_SRCDIR/build/output/packaging/debian" . +} diff --git a/scripts/build/termux_step_patch_package.sh b/scripts/build/termux_step_patch_package.sh index f92eac188324c3e..8d114c8e818419a 100644 --- a/scripts/build/termux_step_patch_package.sh +++ b/scripts/build/termux_step_patch_package.sh @@ -30,6 +30,7 @@ termux_step_patch_package() { -e "s%\@TERMUX_ENV__S_TERMUX_APP\@%${TERMUX_ENV__S_TERMUX_APP}%g" \ -e "s%\@TERMUX_ENV__S_TERMUX_API_APP\@%${TERMUX_ENV__S_TERMUX_API_APP}%g" \ -e "s%\@TERMUX_ENV__S_TERMUX_ROOTFS\@%${TERMUX_ENV__S_TERMUX_ROOTFS}%g" \ + -e "s%\@TERMUX_ENV__S_TERMUX_EXEC\@%${TERMUX_ENV__S_TERMUX_EXEC}%g" \ "$patch" | patch --silent -p1 done shopt -u nullglob diff --git a/scripts/properties.sh b/scripts/properties.sh index 1faf984e9698f90..90dc8897da750cc 100644 --- a/scripts/properties.sh +++ b/scripts/properties.sh @@ -837,6 +837,7 @@ TERMUX__PREFIX__BIN_DIR___MAX_LEN="$((TERMUX__PREFIX_DIR___MAX_LEN + 1 + 3))" # # **See Also:** # - https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits # - https://github.com/termux/termux-core-package/blob/master/lib/termux-core_nos_c_tre/include/termux/termux_core__nos__c/v1/termux/file/TermuxFile.h +# - https://github.com/termux/termux-exec-package/blob/master/lib/termux-exec_nos_c_tre/include/termux/termux_exec__nos__c/v1/termux/api/termux_exec/ld_preload/direct/exec/ExecIntercept.h # # Constant value: `127` ## @@ -845,6 +846,9 @@ TERMUX__PREFIX__BIN_FILE___SAFE_MAX_LEN="$((TERMUX__PREFIX__BIN_DIR___MAX_LEN + ## # The max length for entire shebang line for `termux-exec`. # +# **See Also:** +# - https://github.com/termux/termux-exec-package/blob/master/lib/termux-exec_nos_c_tre/include/termux/termux_exec__nos__c/v1/termux/api/termux_exec/exec/ExecIntercept.h +# # Default value: `340` ## TERMUX__FILE_HEADER__BUFFER_SIZE="340" @@ -1342,6 +1346,54 @@ TERMUX_ENV__S_TERMUX_CORE__TESTS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_C +## +# Termux environment variables `termux-exec` sub scope. +# +# **Do not modify this!** This is considered a constant `termux-exec` +# sub scope for Termux execution environment that's used by external +# programs that do not use the termux packages building infrastructure +# and rely on `$TERMUX_ENV__S_ROOT` environment variable exported by +# Termux app containing the root scope to generate the value for +# `$TERMUX_ENV__S_TERMUX_EXEC` and variable names under it.** +# +# Default value: `EXEC__` +## +TERMUX_ENV__SS_TERMUX_EXEC="EXEC__" + +## +# Termux environment variables `termux-exec` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_EXEC__` +## +TERMUX_ENV__S_TERMUX_EXEC="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_EXEC}" + + +## +# Termux environment variables `termux-exec-tests` sub scope. +# +# **Do not modify this!** This is considered a constant +# `termux-exec-tests` sub scope for Termux execution environment +# that's used by `termux-exec` package to generate the value for +# `$TERMUX_ENV__S_TERMUX_EXEC__TESTS` and variable names under it.** +# +# Default value: `TERMUX_EXEC__TESTS__` +## +TERMUX_ENV__SS_TERMUX_EXEC__TESTS="EXEC__TESTS__" + +## +# Termux environment variables `termux-exec-tests` scope. +# +# **Do not modify this!** +# +# Default value: `TERMUX_EXEC__TESTS__` +## +TERMUX_ENV__S_TERMUX_EXEC__TESTS="${TERMUX_ENV__S_ROOT}${TERMUX_ENV__SS_TERMUX_EXEC__TESTS}" + + + +## # Termux environment variables `termux-am-socket` sub scope. # # **Do not modify this!** This is considered a constant `termux-am-socket` From eaf26b3df699c102004d9a354b1b2b1de4083aee Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Wed, 26 Feb 2025 21:02:24 +0500 Subject: [PATCH 21/28] bump(main/termux-exec): 1:2.0.0 --- packages/termux-exec/build.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/termux-exec/build.sh b/packages/termux-exec/build.sh index 9ebc9035c84580a..e6beecd9dc23534 100644 --- a/packages/termux-exec/build.sh +++ b/packages/termux-exec/build.sh @@ -2,10 +2,9 @@ TERMUX_PKG_HOMEPAGE=https://github.com/termux/termux-exec-package TERMUX_PKG_DESCRIPTION="Utils and libraries for Termux exec including a LD_PRELOAD shared library for proper functioning of the Termux execution environment" TERMUX_PKG_LICENSE="Apache-2.0" TERMUX_PKG_MAINTAINER="@termux" -TERMUX_PKG_VERSION=1:1.0 -TERMUX_PKG_REVISION=1 -TERMUX_PKG_SRCURL=https://github.com/termux/termux-exec-package/archive/v${TERMUX_PKG_VERSION:2}.tar.gz -TERMUX_PKG_SHA256=175d7c9eac6f9fc3b949d1a2cee5f5d3ace61420d418d8213369eb6aff18d28f +TERMUX_PKG_VERSION=1:2.0.0 +TERMUX_PKG_SRCURL=https://github.com/termux/termux-exec-package/archive/refs/tags/v${TERMUX_PKG_VERSION:2}.tar.gz +TERMUX_PKG_SHA256=480452bd0b9cf7b6eb6549825a8faebb2a4afa8c88b9e1d621fb05528ba7ee6e TERMUX_PKG_BUILD_DEPENDS="termux-core" TERMUX_PKG_ESSENTIAL=true TERMUX_PKG_BUILD_IN_SRC=true From 2d5e109b1a1e7d46a6c3efc5a0786090c194cf48 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 14 Dec 2024 12:56:25 +0500 Subject: [PATCH 22/28] addpkg(main/tudo): 1.0.0 --- packages/tudo/build.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/tudo/build.sh diff --git a/packages/tudo/build.sh b/packages/tudo/build.sh new file mode 100644 index 000000000000000..fe9a962af667ecd --- /dev/null +++ b/packages/tudo/build.sh @@ -0,0 +1,25 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/agnostic-apollo/tudo +TERMUX_PKG_DESCRIPTION="A wrapper script to drop to the supported shells or execute shell script files or their text passed as an argument as the Termux app (u_a) user in the Termux app" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="@agnostic-apollo" +TERMUX_PKG_VERSION=1.0.0 +TERMUX_PKG_SRCURL=https://github.com/agnostic-apollo/tudo/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz +TERMUX_PKG_SHA256=5f99e5c1cd13ff5ec57cfd4842f50acac382c7c8c693816c763c10589a539b66 +TERMUX_PKG_DEPENDS="bash" +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_AUTO_UPDATE=true +TERMUX_PKG_PLATFORM_INDEPENDENT=true +TERMUX_PKG_EXTRA_MAKE_ARGS="TUDO_PKG__VERSION=${TERMUX_PKG_VERSION} TUDO_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__HOME=${TERMUX__HOME} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} From d6015240e4943847544217c924b7fb09bb72a361 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 14 Dec 2024 12:56:35 +0500 Subject: [PATCH 23/28] addpkg(main/sudo): 1.0.0 --- packages/sudo/build.sh | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 packages/sudo/build.sh diff --git a/packages/sudo/build.sh b/packages/sudo/build.sh new file mode 100644 index 000000000000000..b5906016ab6825e --- /dev/null +++ b/packages/sudo/build.sh @@ -0,0 +1,27 @@ +TERMUX_PKG_HOMEPAGE=https://github.com/agnostic-apollo/sudo +TERMUX_PKG_DESCRIPTION="A wrapper script to drop to the supported shells or execute shell script files or their text passed as an argument as the root (superuser) user in the Termux app" +TERMUX_PKG_LICENSE="MIT" +TERMUX_PKG_MAINTAINER="@agnostic-apollo" +TERMUX_PKG_VERSION=1.0.0 +TERMUX_PKG_SRCURL=https://github.com/agnostic-apollo/sudo/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz +TERMUX_PKG_SHA256=3ebbc7c034cb184da46d253d8c9f90935de05161d2292aed5417d5a662a46ea9 +TERMUX_PKG_DEPENDS="bash" +TERMUX_PKG_BUILD_IN_SRC=true +TERMUX_PKG_AUTO_UPDATE=true +TERMUX_PKG_PLATFORM_INDEPENDENT=true +TERMUX_PKG_CONFLICTS="tsu" +TERMUX_PKG_REPLACES="tsu" +TERMUX_PKG_EXTRA_MAKE_ARGS="SUDO_PKG__VERSION=${TERMUX_PKG_VERSION} SUDO_PKG__ARCH=${TERMUX_ARCH} \ +TERMUX__NAME=${TERMUX__NAME} TERMUX__LNAME=${TERMUX__LNAME} \ +TERMUX_APP__NAME=${TERMUX_APP__NAME} \ +TERMUX_APP__PACKAGE_NAME=${TERMUX_APP__PACKAGE_NAME} TERMUX_APP__DATA_DIR=${TERMUX_APP__DATA_DIR} \ +TERMUX__ROOTFS=${TERMUX__ROOTFS} TERMUX__HOME=${TERMUX__HOME} TERMUX__PREFIX=${TERMUX__PREFIX} \ +TERMUX_ENV__S_ROOT=${TERMUX_ENV__S_ROOT} \ +TERMUX_ENV__SS_TERMUX=${TERMUX_ENV__SS_TERMUX} TERMUX_ENV__S_TERMUX=${TERMUX_ENV__S_TERMUX} \ +TERMUX_ENV__SS_TERMUX_APP=${TERMUX_ENV__SS_TERMUX_APP} TERMUX_ENV__S_TERMUX_APP=${TERMUX_ENV__S_TERMUX_APP}" + +termux_step_install_license() { + mkdir -p "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses" + mv "$TERMUX_PKG_SRCDIR/LICENSE" "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/copyright" + mv "$TERMUX_PKG_SRCDIR/licenses/"* "$TERMUX_PREFIX/share/doc/$TERMUX_PKG_NAME/licenses/" +} From 6b3a32c7eca12bb6016afa6493bd69fd4f20d487 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 21 Dec 2024 03:38:16 +0500 Subject: [PATCH 24/28] ci(packages|package_updates): fix `No such file or directory` errors for `output` and `debs/built_${repo}_packages.txt` --- .github/workflows/packages.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 84c7d62fce226ad..a5a064441febb7a 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -258,10 +258,12 @@ jobs: # Move only debs from built_packages into debs/ folder before # creating an archive. - while read -r pkg; do - # Match both $pkg.deb and $pkg-static.deb. - find output \( -name "$pkg_*.deb" -o -name "$pkg-static_*.deb" \) -type f -print0 | xargs -0r mv -t debs/ - done < <(cat ./debs/built_${repo}_packages.txt) + if [ -f "./debs/built_${repo}_packages.txt" ] && [ -d "output" ]; then + while read -r pkg; do + # Match both $pkg.deb and $pkg-static.deb. + find output \( -name "$pkg_*.deb" -o -name "$pkg-static_*.deb" \) -type f -print0 | xargs -0r mv -t debs/ + done < <(cat "./debs/built_${repo}_packages.txt") + fi done # Files containing certain symbols (e.g. ":") will cause failure in actions/upload-artifact. From b55d562cfa15c6c59350bd3c3da4c700ca1d4cca Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 21 Dec 2024 03:43:26 +0500 Subject: [PATCH 25/28] ci(packages): remove unused packages loop from free additional disk space step left from 44cf9681 and 5e907826 --- .github/workflows/packages.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index a5a064441febb7a..41a8f7787c40e28 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -207,14 +207,6 @@ jobs: env: DOCKER_BUILD: ${{ steps.build-info.outputs.docker-build }} run: | - declare -a packages - for repo_path in $(jq --raw-output 'del(.pkg_format) | keys | .[]' repo.json); do - repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) - if [ -f ./built_${repo}_packages.txt ]; then - packages="$packages $(cat ./built_${repo}_packages.txt | tr '\n' ' ')" - fi - done - if [ "$DOCKER_BUILD" == 'false' ]; then ./scripts/setup-ubuntu.sh sudo apt install ninja-build From 1a2eb24c6d6b391235dfe759475c181718fba0fd Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sat, 21 Dec 2024 04:06:43 +0500 Subject: [PATCH 26/28] ci(packages|package_updates): fix indexed arrays being used as string variables The `package` and `package_recipes` bash indexed array being used are using the `$var` expansion like `$packages` in the for loop while adding additional values, which will only expand to the first value of the array. Global arrays must always be unset with `=()` on initialization as `declare -a` alone will not unset them and old values with remain in case array variable is reused for a different logic. Package names should not be used in regexes without escaping them as they can contain special characters like `.`. Use fixed string line match `-Fx` with `grep` instead. --- .github/workflows/package_updates.yml | 6 ++-- .github/workflows/packages.yml | 46 +++++++++++++++++---------- 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/.github/workflows/package_updates.yml b/.github/workflows/package_updates.yml index 4febe0efc9f513b..e3d56725148affb 100644 --- a/.github/workflows/package_updates.yml +++ b/.github/workflows/package_updates.yml @@ -114,11 +114,11 @@ jobs: GIT_COMMIT_PACKAGES: "false" GIT_PUSH_PACKAGES: "false" run: | - declare -a packages + declare -a packages=() for repo_path in $(jq --raw-output 'del(.pkg_format) | keys | .[]' repo.json); do repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) - if [ -f ./built_${repo}_packages.txt ]; then - packages="$packages $(cat ./built_${repo}_packages.txt | tr '\n' ' ')" + if [ -f "./built_${repo}_packages.txt" ]; then + packages+=($(cat "./built_${repo}_packages.txt")) fi done if [ -n "$packages" ]; then diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 41a8f7787c40e28..8037560411b8cda 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -161,18 +161,26 @@ jobs: fi done - declare -a packages + declare -a packages=() for repo_path in $(jq --raw-output 'del(.pkg_format) | keys | .[]' repo.json); do repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) - if [ -f ./built_${repo}_packages.txt ]; then - packages="$packages $(cat ./built_${repo}_packages.txt | tr '\n' ' ')" + if [ -f "./built_${repo}_packages.txt" ]; then + packages+=($(cat "./built_${repo}_packages.txt")) fi done + echo "packages: ${packages[*]}" + docker='true' - [ -n "$packages" ] && if grep -qP "(^|\\s)${packages// /($|\\s)|(^|\\s)}($|\\s)" ./scripts/big-pkgs.list; then - docker='false' + if [[ "${#packages[@]}" -gt 0 ]]; then + for pkg in "${packages[@]}"; do + if grep -qFx "$pkg" ./scripts/big-pkgs.list; then + docker='false' + break + fi + done fi + echo "docker-build=$docker" >> $GITHUB_OUTPUT if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then # Build local Docker image if setup scripts were changed. @@ -191,16 +199,16 @@ jobs: - name: Lint packages run: | - declare -a package_recipes + declare -a package_recipes=() for repo_path in $(jq --raw-output 'del(.pkg_format) | keys | .[]' repo.json); do repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) - if [ -f ./built_${repo}_packages.txt ]; then - package_recipes="$package_recipes $(cat ./built_${repo}_packages.txt | repo_path=${repo_path} awk '{print ENVIRON["repo_path"]"/"$1"/build.sh"}')" + if [ -f "./built_${repo}_packages.txt" ]; then + package_recipes+=($(cat "./built_${repo}_packages.txt" | repo_path="${repo_path}" awk '{print ENVIRON["repo_path"]"/"$1"/build.sh"}')) fi done - if [ -n "$package_recipes" ]; then - ./scripts/lint-packages.sh $package_recipes + if [[ "${#package_recipes[@]}" -gt 0 ]]; then + ./scripts/lint-packages.sh "${package_recipes[@]}" fi - name: Free additional disk space (if needed) @@ -221,18 +229,22 @@ jobs: env: DOCKER_BUILD: ${{ steps.build-info.outputs.docker-build }} run: | - declare -a packages + declare -a packages=() for repo_path in $(jq --raw-output 'del(.pkg_format) | keys | .[]' repo.json); do repo=$(jq --raw-output '.["'${repo_path}'"].name' repo.json) - if [ -f ./built_${repo}_packages.txt ]; then - packages="$packages $(cat ./built_${repo}_packages.txt | tr '\n' ' ')" + if [ -f "./built_${repo}_packages.txt" ]; then + packages+=($(cat "./built_${repo}_packages.txt")) fi done - if [ "$DOCKER_BUILD" == 'false' ]; then - NDK=$ANDROID_NDK ANDROID_HOME=$ANDROID_SDK_ROOT ./build-package.sh -I -C -a ${{ matrix.target_arch }} $packages - elif [ -n "$packages" ]; then - ./scripts/run-docker.sh ./build-package.sh -I -C -a ${{ matrix.target_arch }} $packages + echo "packages: ${packages[*]}" + + if [[ "${#packages[@]}" -gt 0 ]]; then + if [ "$DOCKER_BUILD" == 'false' ]; then + NDK="$ANDROID_NDK" ANDROID_HOME="$ANDROID_SDK_ROOT" ./build-package.sh -I -C -a "${{ matrix.target_arch }}" "${packages[@]}" + else + ./scripts/run-docker.sh ./build-package.sh -I -C -a "${{ matrix.target_arch }}" "${packages[@]}" + fi fi - name: Generate build artifacts From 4c916b39606367ec419f69c38dc746a77341f930 Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sun, 22 Dec 2024 02:25:44 +0500 Subject: [PATCH 27/28] scripts(setup-ubuntu|setup-archlinux): do not use hardcoded `/data` path for setting ownership of `TERMUX__PREFIX` and create `TERMUX_APP__DATA_DIR` as well in case `TERMUX__PREFIX` is not under it Ownership of `TERMUX__PREFIX` must be fixed before `TERMUX_APP__DATA_DIR` if its under it, otherwise `TERMUX__ROOTFS` will not have its ownership fixed and following error would occur during package dependency extraction. `tar: data/data/com.termux/files: Cannot change mode to rwxr-xr-x: Operation not permitted` --- scripts/setup-archlinux.sh | 9 +++++++-- scripts/setup-ubuntu.sh | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/setup-archlinux.sh b/scripts/setup-archlinux.sh index fca629d12fc3123..f0db48f51254e9d 100755 --- a/scripts/setup-archlinux.sh +++ b/scripts/setup-archlinux.sh @@ -53,8 +53,13 @@ fi $SUDO pacman -Syq --needed --noconfirm $PACKAGES . $(dirname "$(realpath "$0")")/properties.sh -$SUDO mkdir -p $TERMUX_PREFIX -$SUDO chown -R $(whoami) /data + +# Ownership of `TERMUX__PREFIX` must be fixed before `TERMUX_APP__DATA_DIR` +# if its under it, otherwise `TERMUX__ROOTFS` will not have its ownership fixed. +$SUDO mkdir -p "$TERMUX__PREFIX" +$SUDO chown -R "$(whoami)" "$TERMUX__PREFIX" +$SUDO mkdir -p "$TERMUX_APP__DATA_DIR" +$SUDO chown -R "$(whoami)" "${TERMUX_APP__DATA_DIR%"${TERMUX_APP__DATA_DIR#/*/}"}" # Get `/path/` from `/path/to/app__data_dir`. echo "Please also install the following packages from the AUR before continuing" echo diff --git a/scripts/setup-ubuntu.sh b/scripts/setup-ubuntu.sh index 5892c7d82274ddc..a4c03d49d05a839 100755 --- a/scripts/setup-ubuntu.sh +++ b/scripts/setup-ubuntu.sh @@ -344,8 +344,14 @@ $SUDO locale-gen --purge en_US.UTF-8 echo -e 'LANG="en_US.UTF-8"\nLANGUAGE="en_US:en"\n' | $SUDO tee -a /etc/default/locale . $(dirname "$(realpath "$0")")/properties.sh -$SUDO mkdir -p $TERMUX_PREFIX -$SUDO chown -R $(whoami) /data + +# Ownership of `TERMUX__PREFIX` must be fixed before `TERMUX_APP__DATA_DIR` +# if its under it, otherwise `TERMUX__ROOTFS` will not have its ownership fixed. +$SUDO mkdir -p "$TERMUX__PREFIX" +$SUDO chown -R "$(whoami)" "$TERMUX__PREFIX" +$SUDO mkdir -p "$TERMUX_APP__DATA_DIR" +$SUDO chown -R "$(whoami)" "${TERMUX_APP__DATA_DIR%"${TERMUX_APP__DATA_DIR#/*/}"}" # Get `/path/` from `/path/to/app__data_dir`. + $SUDO ln -sf /data/data/com.termux/files/usr/opt/aosp /system # Install newer pkg-config then what ubuntu provides, as the stock From 8a03e336c8aa90329a810b211ab3be103f0c128c Mon Sep 17 00:00:00 2001 From: agnostic-apollo Date: Sun, 22 Dec 2024 03:56:55 +0500 Subject: [PATCH 28/28] fix(scripts/Dockerfile): create termux-packages repo directory hierarchy under `/tmp` so that `TERMUX_PKGS__BUILD__REPO_ROOT_DIR` validation does not fail in `properties.sh` file and export `TERMUX_PKGS__BUILD__IS_DOCKER_BUILD` to skip reading `repo.json` into variables The `TERMUX_PKGS__BUILD__REPO_ROOT_DIR` validation checks if `scripts/properties.sh` file exists, which wouldn't exist under `scripts` directory if proper hierarchy is not created under `/tmp` directory. Files above the `scripts` directory are not accessible during `cd scripts; docker build`, so `repo.json` cannot be copied to `tmp`, which is read by `properties.sh`. So skip reading its variables until it can be moved to under `scripts` directory in future during docker build. --- scripts/Dockerfile | 15 +++++++++------ scripts/Dockerfile.cgct | 2 +- scripts/properties.sh | 30 +++++++++++++++++++----------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/scripts/Dockerfile b/scripts/Dockerfile index bcbc74c5d484197..b0767319233df68 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -10,9 +10,12 @@ FROM ubuntu:24.04 ENV LANG=en_US.UTF-8 # Needed for setup: -COPY ./llvm-snapshot.gpg.key ./properties.sh ./setup-android-sdk.sh ./setup-cgct.sh ./setup-ubuntu.sh /tmp/ -RUN mkdir /tmp/build -COPY ./build/termux_download.sh /tmp/build/ +RUN rm -rf /tmp/termux-packages +RUN mkdir -p /tmp/termux-packages +RUN mkdir -p /tmp/termux-packages/scripts +COPY ./llvm-snapshot.gpg.key ./properties.sh ./setup-android-sdk.sh ./setup-cgct.sh ./setup-ubuntu.sh /tmp/termux-packages/scripts/ +RUN mkdir -p /tmp/termux-packages/scripts/build +COPY ./build/termux_download.sh /tmp/termux-packages/scripts/build/ # Setup needed packages and the Android SDK and NDK and the CGCT: RUN apt-get update && \ @@ -22,9 +25,9 @@ RUN apt-get update && \ useradd -u 1001 -U -m -s /bin/bash builder && \ echo "builder ALL=(root) NOPASSWD:ALL" > /etc/sudoers.d/builder && \ chmod 0440 /etc/sudoers.d/builder && \ - chmod a+rx /tmp/*.sh /tmp/build/termux_download.sh && \ - su - builder -c /tmp/setup-ubuntu.sh && \ - su - builder -c /tmp/setup-android-sdk.sh && \ + chmod a+rx /tmp/termux-packages/scripts/*.sh /tmp/termux-packages/scripts/build/termux_download.sh && \ + su - builder -c "TERMUX_PKGS__BUILD__IS_DOCKER_BUILD='true' /tmp/termux-packages/scripts/setup-ubuntu.sh" && \ + su - builder -c "TERMUX_PKGS__BUILD__IS_DOCKER_BUILD='true' /tmp/termux-packages/scripts/setup-android-sdk.sh" && \ # Removed unused parts to make a smaller Docker image: apt-get remove -yq --autoremove lsb-release software-properties-common && \ apt-get clean && \ diff --git a/scripts/Dockerfile.cgct b/scripts/Dockerfile.cgct index f2c4bc9526fe2f8..60f972d39907ef9 100644 --- a/scripts/Dockerfile.cgct +++ b/scripts/Dockerfile.cgct @@ -7,4 +7,4 @@ # This is done after changing this file or any of the # scripts/setup-{ubuntu,android-sdk,cgct}.sh setup scripts. FROM ghcr.io/termux/package-builder -RUN /tmp/setup-cgct.sh +RUN TERMUX_PKGS__BUILD__IS_DOCKER_BUILD='true' /tmp/termux-packages/scripts/setup-cgct.sh diff --git a/scripts/properties.sh b/scripts/properties.sh index 90dc8897da750cc..18e32d9cb31cdb1 100644 --- a/scripts/properties.sh +++ b/scripts/properties.sh @@ -2031,18 +2031,26 @@ TERMUX_REPO_URL=() TERMUX_REPO_DISTRIBUTION=() TERMUX_REPO_COMPONENT=() -export TERMUX_PACKAGES_DIRECTORIES -TERMUX_PACKAGES_DIRECTORIES=$(jq --raw-output 'del(.pkg_format) | keys | .[]' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json") +# FIXME: Move `repo.json` file to under `scripts/` directory and COPY it to `/tmp/termux-packages` in `Dockerfile`. +if [[ ! -f "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json" ]]; then + if [[ "${TERMUX_PKGS__BUILD__IS_DOCKER_BUILD:-}" != "true" ]]; then + echo "The 'repo.json' file not found at the '$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json' path." 1>&2 + exit 1 + fi +else + export TERMUX_PACKAGES_DIRECTORIES + TERMUX_PACKAGES_DIRECTORIES=$(jq --raw-output 'del(.pkg_format) | keys | .[]' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json") -for url in $(jq -r 'del(.pkg_format) | .[] | .url' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do - TERMUX_REPO_URL+=("$url") -done -for distribution in $(jq -r 'del(.pkg_format) | .[] | .distribution' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do - TERMUX_REPO_DISTRIBUTION+=("$distribution") -done -for component in $(jq -r 'del(.pkg_format) | .[] | .component' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do - TERMUX_REPO_COMPONENT+=("$component") -done + for url in $(jq -r 'del(.pkg_format) | .[] | .url' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do + TERMUX_REPO_URL+=("$url") + done + for distribution in $(jq -r 'del(.pkg_format) | .[] | .distribution' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do + TERMUX_REPO_DISTRIBUTION+=("$distribution") + done + for component in $(jq -r 'del(.pkg_format) | .[] | .component' "$TERMUX_PKGS__BUILD__REPO_ROOT_DIR/repo.json"); do + TERMUX_REPO_COMPONENT+=("$component") + done +fi