diff --git a/.editorconfig b/.editorconfig index a10ef343592c4f..ed7c6fef02da2c 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 diff --git a/.github/workflows/package_updates.yml b/.github/workflows/package_updates.yml index 4febe0efc9f513..e3d56725148aff 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 84c7d62fce226a..8037560411b8cd 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,30 +199,22 @@ 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) 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 @@ -229,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 @@ -258,10 +262,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. diff --git a/build-package.sh b/build-package.sh index e57f912fe5d78f..1b9cde6fa4729e 100755 --- a/build-package.sh +++ b/build-package.sh @@ -343,15 +343,22 @@ 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 } -# 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. @@ -390,21 +397,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 $? } @@ -422,9 +431,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." @@ -433,6 +442,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/." @@ -492,23 +505,18 @@ 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_PKGS__BUILD__RM_ALL_PKGS_BUILT_MARKER_AND_INSTALL_FILES=false ;; -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) @@ -533,8 +541,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 @@ -588,12 +596,20 @@ 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_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") \ + "${PACKAGE_LIST[i]}" done exit fi @@ -628,10 +644,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/clean.sh b/clean.sh index e969b98d275d44..331ffcbd6e1b9a 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" diff --git a/packages/sudo/build.sh b/packages/sudo/build.sh new file mode 100644 index 00000000000000..b5906016ab6825 --- /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/" +} 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 00000000000000..150f47ff10cc6a --- /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 00000000000000..631882e50c80a4 --- /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 00000000000000..e640459379954b --- /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 00000000000000..e35e2af29f4a9e --- /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 00000000000000..8441cdac329613 --- /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 00000000000000..e14e991b3c90ff --- /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 00000000000000..6a488829b720bd --- /dev/null +++ b/packages/termux-core/build.sh @@ -0,0 +1,44 @@ +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 +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 00000000000000..b109eb39169007 --- /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-exec/build.sh b/packages/termux-exec/build.sh index a7f807e6040109..e6beecd9dc2353 100644 --- a/packages/termux-exec/build.sh +++ b/packages/termux-exec/build.sh @@ -1,12 +1,69 @@ 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_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 -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/packages/termux-tools/build.sh b/packages/termux-tools/build.sh index ea94eb57109d17..6bc081451542e7 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/packages/tudo/build.sh b/packages/tudo/build.sh new file mode 100644 index 00000000000000..fe9a962af667ec --- /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/" +} diff --git a/scripts/Dockerfile b/scripts/Dockerfile index bcbc74c5d48419..b0767319233df6 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 f2c4bc9526fe2f..60f972d39907ef 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/bin/check-pie.sh b/scripts/bin/check-pie.sh index 3eff38d42dc838..eccb566abb2b72 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 ec3c4cb60e3a97..0262cebf2b56e6 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 84b490f3f26336..f4ae34430a4f05 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; } @@ -63,7 +66,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 @@ -92,16 +95,33 @@ 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 - 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 + + + 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=$? + 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_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 + 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 +132,7 @@ file manually and run 'termux-bootstrap-second-stage.sh' again." log "The termux bootstrap second stage completed successfully" + return 0 } @@ -360,6 +381,74 @@ 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:-}" + 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 diff --git a/scripts/build-bootstraps.sh b/scripts/build-bootstraps.sh index d7eda1ce92c1fa..321c135339f601 100755 --- a/scripts/build-bootstraps.sh +++ b/scripts/build-bootstraps.sh @@ -206,21 +206,23 @@ 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" \ + -e "s|@TERMUX_ENV__S_TERMUX@|${TERMUX_ENV__S_TERMUX}|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" } @@ -442,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/build/get_source/termux_download_src_archive.sh b/scripts/build/get_source/termux_download_src_archive.sh index e74b691e14f746..768c380d9317e7 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/get_source/termux_git_clone_src.sh b/scripts/build/get_source/termux_git_clone_src.sh index 7f310da61b113b..579795f3c40233 100644 --- a/scripts/build/get_source/termux_git_clone_src.sh +++ b/scripts/build/get_source/termux_git_clone_src.sh @@ -1,19 +1,53 @@ 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 - rm -rf $TMP_CHECKOUT - git clone --depth 1 \ - --branch $TERMUX_PKG_GIT_BRANCH \ - ${TERMUX_PKG_SRCURL:4} \ - $TMP_CHECKOUT + 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 \ + $termux_pkg_branch_flags \ + "$termux_pkg_srcurl" \ + "$TMP_CHECKOUT" - pushd $TMP_CHECKOUT + pushd "$TMP_CHECKOUT" # Workaround some bad server behaviour # error: Server does not allow request for unadvertised object commit_no @@ -37,9 +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_step_get_source.sh b/scripts/build/get_source/termux_step_get_source.sh index 2c914ed34022a8..972524c1f77282 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 diff --git a/scripts/build/get_source/termux_unpack_src_archive.sh b/scripts/build/get_source/termux_unpack_src_archive.sh index 320b28ff162812..d6eaf6aa0ed08d 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_download.sh b/scripts/build/termux_download.sh index 08144a9b0c49e3..a605375a910d7c 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) diff --git a/scripts/build/termux_download_deb_pac.sh b/scripts/build/termux_download_deb_pac.sh index 9dd53027161a08..cd94a03bc72d0c 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_create_debscripts.sh b/scripts/build/termux_step_create_debscripts.sh new file mode 100644 index 00000000000000..c9e6080dbb34b7 --- /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//\'/\'\\\'\'}/'" + + ) + +} diff --git a/scripts/build/termux_step_elf_cleaner.sh b/scripts/build/termux_step_elf_cleaner.sh new file mode 100644 index 00000000000000..e775f5ac2a8be6 --- /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_extract_into_massagedir.sh b/scripts/build/termux_step_extract_into_massagedir.sh index 0df0b9e1e6f135..5e9ed5dd169aff 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" diff --git a/scripts/build/termux_step_get_dependencies.sh b/scripts/build/termux_step_get_dependencies.sh index fc0d39cdf8c355..1abd12385b12b5 100644 --- a/scripts/build/termux_step_get_dependencies.sh +++ b/scripts/build/termux_step_get_dependencies.sh @@ -134,7 +134,8 @@ 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_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_massage.sh b/scripts/build/termux_step_massage.sh index 2a1bb1f5b8d0b4..b4d2a19916b558 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_patch_package.sh b/scripts/build/termux_step_patch_package.sh index df1082d445d288..8d114c8e818419 100644 --- a/scripts/build/termux_step_patch_package.sh +++ b/scripts/build/termux_step_patch_package.sh @@ -26,6 +26,11 @@ 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" \ + -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/build/termux_step_setup_build_folders.sh b/scripts/build/termux_step_setup_build_folders.sh index 8c11ca9eeb3ac4..2f5cbb8eb05c72 100644 --- a/scripts/build/termux_step_setup_build_folders.sh +++ b/scripts/build/termux_step_setup_build_folders.sh @@ -7,12 +7,12 @@ 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: - 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: @@ -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" \ @@ -38,7 +44,13 @@ 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 + 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_setup_cgct_environment.sh b/scripts/build/termux_step_setup_cgct_environment.sh index fdf6877b4629eb..5a4576f28df61c 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/build/termux_step_setup_variables.sh b/scripts/build/termux_step_setup_variables.sh index b63c908f3945a8..1404fb756a0f94 100644 --- a/scripts/build/termux_step_setup_variables.sh +++ b/scripts/build/termux_step_setup_variables.sh @@ -6,7 +6,8 @@ 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_PKGS__BUILD__RM_ALL_PKG_BUILD_DEPENDENT_DIRS:="false"}" : "${TERMUX_PKG_API_LEVEL:="24"}" : "${TERMUX_CONTINUE_BUILD:="false"}" : "${TERMUX_QUIET_BUILD:="false"}" @@ -49,7 +50,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. diff --git a/scripts/build/termux_step_start_build.sh b/scripts/build/termux_step_start_build.sh index f4c889aacddfe6..8d781c05dc3c5b 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 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 00000000000000..2ffe7e0f71be82 --- /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 + ) +} diff --git a/scripts/build/toolchain/termux_setup_toolchain_23c.sh b/scripts/build/toolchain/termux_setup_toolchain_23c.sh index 9a35d524de474b..5ddee9a7d0d919 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 \ diff --git a/scripts/generate-bootstraps.sh b/scripts/generate-bootstraps.sh index d1f1130f15a230..754f5edaf2980b 100755 --- a/scripts/generate-bootstraps.sh +++ b/scripts/generate-bootstraps.sh @@ -252,22 +252,24 @@ 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" \ + -e "s|@TERMUX_ENV__S_TERMUX@|${TERMUX_ENV__S_TERMUX}|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" } @@ -466,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 f7fea5ebca4c44..18e32d9cb31cdb 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,2221 @@ 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 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`. +# +# 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 +# - 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` +## +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`. +# +# **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" + + + +## +# 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. +# +# 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, +# 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-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-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` +# 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 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`. +# +# 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-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. +# +# - 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) +# 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_SCRIPTDIR}/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") -done -for component in $(jq -r 'del(.pkg_format) | .[] | .component' ${TERMUX_SCRIPTDIR}/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 + + + + + +### +# 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 diff --git a/scripts/setup-archlinux.sh b/scripts/setup-archlinux.sh index fca629d12fc312..f0db48f51254e9 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 5892c7d82274dd..a4c03d49d05a83 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