From c4f4b9edaab0d774757334a449d1460d0b7f6b34 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 02:12:05 +0530 Subject: [PATCH 01/47] scripts(free-space.sh): remove more stuff from runner Earlier 54G -> 25G Now 53G -> 16G (10G excluded for docker image) --- scripts/free-space.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/scripts/free-space.sh b/scripts/free-space.sh index 78a883f6a7a..c0015243a02 100755 --- a/scripts/free-space.sh +++ b/scripts/free-space.sh @@ -1,6 +1,6 @@ #!/bin/sh -# This script clears about ~22G of space. +# This script clears about ~36G of space. # Test: # echo "Listing 100 largest packages after" @@ -12,11 +12,11 @@ if [ "${CI-false}" != "true" ]; then exit 1 else # shellcheck disable=SC2046 - sudo apt purge -yq $( + sudo apt purge -yq --allow-remove-essential $( dpkg -l | grep '^ii' | awk '{ print $2 }' | - grep -P '(mecab|linux-azure-tools-|aspnetcore|liblldb-|netstandard-|clang-tidy|clang-format|gfortran-|mysql-|google-cloud-cli|postgresql-|cabal-|dotnet-|ghc-|mongodb-|libmono|llvm-16|llvm-17)' + grep -P '(mecab|linux-azure-tools-|aspnetcore|liblldb-|netstandard-|llvm|clang|gcc-12|gcc-13|cpp-|g\+\+-|temurin-|gfortran-|mysql-|google-cloud-cli|postgresql-|cabal-|dotnet-|ghc-|mongodb-|libmono|temurin-|mesa-|ant|liblua|python3|grub2-|grub-|shim-signed)' ) sudo apt purge -yq \ @@ -33,13 +33,15 @@ else azure-cli \ powershell \ shellcheck \ - firefox \ - google-chrome-stable \ - microsoft-edge-stable + firefox + # google-chrome-stable + # microsoft-edge-stable already removed by the deps in the above apt purge # Directories - sudo rm -fr /opt/ghc /opt/hostedtoolcache /usr/share/dotnet /usr/share/swift - sudo rm -rf /usr/local + sudo rm -rf /opt/ghc /opt/az /opt/hostedtoolcache /opt/actionarchivecache /opt/runner-cache + sudo rm -rf /opt/pipx /usr/share/dotnet /usr/share/swift /usr/share/miniconda /usr/share/az_* /usr/share/gradle-* /usr/share/java /home/runner/.rustup + sudo rm -rf /etc/skel /home/packer /home/linuxbrew + sudo rm -rf /usr/local /usr/src/ # https://github.com/actions/runner-images/issues/709#issuecomment-612569242 sudo rm -rf "$AGENT_TOOLSDIRECTORY" @@ -53,4 +55,5 @@ else sudo apt autoremove -yq sudo apt clean + sudo rm -rf /var/lib/{apt,dpkg} fi From 7fa8ab4f3fc7d9fe04cb5712f48c577aaac30a69 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 03:49:15 +0530 Subject: [PATCH 02/47] ci: also clean docker compressed archives Does this work? Yes Is this cursed? Not as cursed as using bash for build system --- .github/workflows/packages.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 83494fa3335..7d864f95c39 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -246,6 +246,20 @@ jobs: sudo apt install ninja-build ./scripts/free-space.sh + - name: Load Docker image + if: ${{ steps.build-info.outputs.docker-build == 'true' && steps.build-info.outputs.skip-building != 'true' }} + env: + DOCKER_BUILD: ${{ steps.build-info.outputs.docker-build }} + TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES: "true" + run: | + ./scripts/run-docker.sh echo "" + + # Docker has already deflated them so the compressed parts are just collecting junk on the disk + - name: Clean compressed docker images + if: ${{ steps.build-info.outputs.docker-build == 'true' && steps.build-info.outputs.skip-building != 'true' }} + run: | + sudo rm -r /var/lib/containerd/io.containerd.content.v1.content/ + - name: Build packages if: ${{ steps.build-info.outputs.skip-building != 'true' }} env: From bf8d7d2b66c8a476f2d55dc550daf3365e359870 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 04:30:57 +0530 Subject: [PATCH 03/47] ci(packages): do all builds in docker --- .github/workflows/packages.yml | 36 +++++++--------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 7d864f95c39..8a7538ebbe6 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -98,7 +98,6 @@ jobs: # Forces CI to cancel current build with status 'passed' if grep -qiP '^\s*%ci:no-build\s*$' <(git log --format="%B" -n 1 --no-merges "HEAD"); then tar cf artifacts/debs-${{ matrix.target_arch }}.tar debs - echo "docker-build=true" >> $GITHUB_OUTPUT echo "[!] Force exiting as tag '%ci:no-build' was applied to HEAD commit message." exit 0 fi @@ -186,26 +185,22 @@ jobs: echo "packages: ${packages[*]}" - docker='true' + free_space='false' if [[ "${#packages[@]}" -gt 0 ]]; then for pkg in "${packages[@]}"; do if grep -qFx "$pkg" ./scripts/big-pkgs.list; then - docker='false' + free_space='true' break fi done fi + echo "free-space=$free_space" >> $GITHUB_OUTPUT - echo "docker-build=$docker" >> $GITHUB_OUTPUT if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then # Build local Docker image if setup scripts were changed. # Useful for pull requests submitting changes for both build environment and packages. if grep -qP '^scripts/(Dockerfile|properties\.sh|setup-android-sdk\.sh|setup-ubuntu\.sh)$' <<< "$CHANGED_FILES"; then echo "Detected changes for environment setup scripts. Building custom Docker image now." - if [ $docker == 'false' ]; then - echo "Skipping due to building large packages." - exit 0 - fi cd ./scripts docker build -t ghcr.io/termux/package-builder:latest . cd .. @@ -236,34 +231,26 @@ jobs: device_name: /dev/zram0 - name: Free additional disk space (if needed) - if: ${{ steps.build-info.outputs.docker-build == 'false' && steps.build-info.outputs.skip-building != 'true' }} + if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} run: | - ./scripts/setup-ubuntu.sh - # need to unset these for setup-android-sdk.sh. - unset NDK ANDROID_HOME - ./scripts/setup-android-sdk.sh - rm -f ${HOME}/lib/ndk-*.zip ${HOME}/lib/sdk-*.zip - sudo apt install ninja-build ./scripts/free-space.sh - name: Load Docker image - if: ${{ steps.build-info.outputs.docker-build == 'true' && steps.build-info.outputs.skip-building != 'true' }} + if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} env: - DOCKER_BUILD: ${{ steps.build-info.outputs.docker-build }} TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES: "true" run: | ./scripts/run-docker.sh echo "" # Docker has already deflated them so the compressed parts are just collecting junk on the disk - name: Clean compressed docker images - if: ${{ steps.build-info.outputs.docker-build == 'true' && steps.build-info.outputs.skip-building != 'true' }} + if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} run: | sudo rm -r /var/lib/containerd/io.containerd.content.v1.content/ - name: Build packages if: ${{ steps.build-info.outputs.skip-building != 'true' }} env: - DOCKER_BUILD: ${{ steps.build-info.outputs.docker-build }} TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES: "true" run: | declare -a packages=() @@ -277,16 +264,7 @@ jobs: echo "packages: ${packages[*]}" if [[ "${#packages[@]}" -gt 0 ]]; then - if [ "$DOCKER_BUILD" == 'false' ]; then - # these need to be unset a second time again for ./build-package.sh - # when it is run outside of Docker, because GitHub Actions does not - # support permanently unsetting variables at time of writing. - # https://github.com/actions/runner/issues/1126 - unset NDK ANDROID_HOME - ./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 + ./scripts/run-docker.sh ./build-package.sh -I -C -a "${{ matrix.target_arch }}" "${packages[@]}" fi - name: Generate build artifacts From f1cba2757c415ce352bb8a0a95fd7457fbf1498b Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 19:55:25 +0530 Subject: [PATCH 04/47] scripts: allow more flexible configuration of docker container This should allow for more flexible configuration of docker containers by allowing devs to pass on their own flags to docker Also ~/clean.sh will now not remove /home/builder/.termux-build to account for cases where /home/builder/.termux-build is a volume mounted to the docker image, where it is not possible to remove the directory --- clean.sh | 4 +++- scripts/run-docker.sh | 15 +++++++++------ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/clean.sh b/clean.sh index ee687bef988..904c41560b6 100755 --- a/clean.sh +++ b/clean.sh @@ -93,5 +93,7 @@ fi rm -Rf "/data/data/.built-packages" fi - rm -Rf "$TERMUX_TOPDIR" + # We can't use rm -Rf "$TERMUX_TOPDIR" in case the "$TERMUX_TOPDIR" is mounted as a Docker volume + find "$TERMUX_TOPDIR" -type f,l -delete + find "$TERMUX_TOPDIR" -type d ! -path "$TERMUX_TOPDIR" -delete } 5< "$TERMUX_BUILD_LOCK_FILE" diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index c706b9cdda2..efabad18a45 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -59,6 +59,8 @@ fi : ${TERMUX_BUILDER_IMAGE_NAME:=ghcr.io/termux/package-builder} : ${CONTAINER_NAME:=termux-package-builder} +: ${TERMUX_DOCKER_RUN_EXTRA_ARGS:=} +: ${TERMUX_DOCKER_EXEC_EXTRA_ARGS:=} USER=builder @@ -86,23 +88,24 @@ $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { --volume $VOLUME \ $SEC_OPT \ --tty \ + $TERMUX_DOCKER_RUN_EXTRA_ARGS \ $TERMUX_BUILDER_IMAGE_NAME if [ "$UNAME" != Darwin ]; then if [ $(id -u) -ne 1001 -a $(id -u) -ne 0 ]; then echo "Changed builder uid/gid... (this may take a while)" - $SUDO docker exec $DOCKER_TTY $CONTAINER_NAME sudo chown -R $(id -u) $CONTAINER_HOME_DIR - $SUDO docker exec $DOCKER_TTY $CONTAINER_NAME sudo chown -R $(id -u) /data - $SUDO docker exec $DOCKER_TTY $CONTAINER_NAME sudo usermod -u $(id -u) builder - $SUDO docker exec $DOCKER_TTY $CONTAINER_NAME sudo groupmod -g $(id -g) builder + $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo chown -R $(id -u) $CONTAINER_HOME_DIR + $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo chown -R $(id -u) /data + $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo usermod -u $(id -u) builder + $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo groupmod -g $(id -g) builder fi fi } -# Set traps to ensure that the process started with docker exec and all its children are killed. +# Set traps to ensure that the process started with docker exec and all its children are killed. . "$TERMUX_SCRIPTDIR/scripts/utils/docker/docker.sh"; docker__setup_docker_exec_traps if [ "$#" -eq "0" ]; then set -- bash fi -$SUDO docker exec $CI_OPT --env "DOCKER_EXEC_PID_FILE_PATH=$DOCKER_EXEC_PID_FILE_PATH" --interactive $DOCKER_TTY $CONTAINER_NAME "$@" +$SUDO docker exec $CI_OPT --env "DOCKER_EXEC_PID_FILE_PATH=$DOCKER_EXEC_PID_FILE_PATH" --interactive $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME "$@" From 07815b5c804154b546e084d6b7d4e4a630eda0af Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 20:18:05 +0530 Subject: [PATCH 05/47] ci(packages): do not prune docker We now are always using docker for builds, so can't do this --- .github/workflows/package_updates.yml | 2 +- scripts/free-space.sh | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/package_updates.yml b/.github/workflows/package_updates.yml index 67db33b7ab0..b8d67c6a3dd 100644 --- a/.github/workflows/package_updates.yml +++ b/.github/workflows/package_updates.yml @@ -114,7 +114,7 @@ jobs: priority: 100 device_name: /dev/zram0 - name: Free additional disk space - run: CLEAN_DOCKER_IMAGES=false ./scripts/free-space.sh + run: ./scripts/free-space.sh - name: Process package updates env: GITHUB_TOKEN: ${{ secrets.TERMUXBOT2_TOKEN }} diff --git a/scripts/free-space.sh b/scripts/free-space.sh index c0015243a02..efcceaace63 100755 --- a/scripts/free-space.sh +++ b/scripts/free-space.sh @@ -46,13 +46,6 @@ else # https://github.com/actions/runner-images/issues/709#issuecomment-612569242 sudo rm -rf "$AGENT_TOOLSDIRECTORY" - # We shouldn't remove docker & it's images when running from `package_updates` workflow. - if [ "${CLEAN_DOCKER_IMAGES-true}" = "true" ]; then - sudo docker image prune --all --force - sudo docker builder prune -a - sudo apt purge -yq containerd.io - fi - sudo apt autoremove -yq sudo apt clean sudo rm -rf /var/lib/{apt,dpkg} From 9d70f1529512e59ce52e125426511029103103d6 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 20:24:09 +0530 Subject: [PATCH 06/47] ci(packages): split docker image building to separate step Do not clutter what we do in "Gather build summary" step --- .github/workflows/packages.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 8a7538ebbe6..3ce25da8829 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -196,16 +196,15 @@ jobs: fi echo "free-space=$free_space" >> $GITHUB_OUTPUT + needs_docker_build=false if [ "${{ github.event_name }}" != "workflow_dispatch" ]; then # Build local Docker image if setup scripts were changed. # Useful for pull requests submitting changes for both build environment and packages. if grep -qP '^scripts/(Dockerfile|properties\.sh|setup-android-sdk\.sh|setup-ubuntu\.sh)$' <<< "$CHANGED_FILES"; then - echo "Detected changes for environment setup scripts. Building custom Docker image now." - cd ./scripts - docker build -t ghcr.io/termux/package-builder:latest . - cd .. + needs_docker_build=true fi fi + echo "needs-docker-build=$needs_docker_build" >> $GITHUB_OUTPUT - name: Lint packages run: | @@ -230,6 +229,12 @@ jobs: priority: 100 device_name: /dev/zram0 + - name: Build docker image + if: ${{ steps.build-info.outputs.needs-docker-build == 'true' }} + run: | + docker build -t ghcr.io/termux/package-builder:latest scripts/ + docker buildx prune -af + - name: Free additional disk space (if needed) if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} run: | From d6f2fd1583c4760e53d4b726ee15ac088379c6f5 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 26 Feb 2026 20:46:17 +0530 Subject: [PATCH 07/47] ci: move clean compressed docker images to free-space.sh Also make sure we use this optimization in package_updates.yml --- .github/workflows/package_updates.yml | 3 +++ .github/workflows/packages.yml | 12 ++---------- scripts/free-space.sh | 5 +++++ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/package_updates.yml b/.github/workflows/package_updates.yml index b8d67c6a3dd..5cc32d1d850 100644 --- a/.github/workflows/package_updates.yml +++ b/.github/workflows/package_updates.yml @@ -113,6 +113,9 @@ jobs: size: 16G priority: 100 device_name: /dev/zram0 + - name: Load Docker image + run: | + ./scripts/run-docker.sh echo "" - name: Free additional disk space run: ./scripts/free-space.sh - name: Process package updates diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 3ce25da8829..ec99591488c 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -235,23 +235,15 @@ jobs: docker build -t ghcr.io/termux/package-builder:latest scripts/ docker buildx prune -af - - name: Free additional disk space (if needed) - if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} - run: | - ./scripts/free-space.sh - - name: Load Docker image if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} - env: - TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES: "true" run: | ./scripts/run-docker.sh echo "" - # Docker has already deflated them so the compressed parts are just collecting junk on the disk - - name: Clean compressed docker images + - name: Free additional disk space (if needed) if: ${{ steps.build-info.outputs.free-space == 'true' && steps.build-info.outputs.skip-building != 'true' }} run: | - sudo rm -r /var/lib/containerd/io.containerd.content.v1.content/ + ./scripts/free-space.sh - name: Build packages if: ${{ steps.build-info.outputs.skip-building != 'true' }} diff --git a/scripts/free-space.sh b/scripts/free-space.sh index efcceaace63..dcccb739127 100755 --- a/scripts/free-space.sh +++ b/scripts/free-space.sh @@ -46,6 +46,11 @@ else # https://github.com/actions/runner-images/issues/709#issuecomment-612569242 sudo rm -rf "$AGENT_TOOLSDIRECTORY" + # Clean compressed docker images + # Docker has already deflated them before free-space.sh was called in CI, so + # the compressed parts are just collecting junk on the disk + sudo rm -r /var/lib/containerd/io.containerd.content.v1.content/ + sudo apt autoremove -yq sudo apt clean sudo rm -rf /var/lib/{apt,dpkg} From 1244081c9d8bef7d739b634e9841a17e3b641814 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Fri, 27 Feb 2026 18:21:46 +0530 Subject: [PATCH 08/47] scripts(run-docker.sh): add short option to mount /data and ~/.termux-build on host Should be helpful for local builds for using IDEs and host tools for development --- scripts/run-docker.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index efabad18a45..04e35fcd233 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -79,6 +79,40 @@ else DOCKER_TTY="" fi +if [ "$#" -eq 1 ] && ( [ "$1" = "-h" ] || [ "$1" = "--help" ] ); then + echo "Usage: $0 [OPTIONS] [COMMAND]" + echo "" + echo "Run a command in the Termux package builder container. If no command is given, an interactive shell will be started." + echo "" + echo "Options:" + echo " -h, --help Show this help message and exit" + echo " Run 'build-package-dry-run-simulation.sh' to check if the given command would build any packages, and if not, exit with code 0 without running the command in docker. This is useful for CI to skip unnecessary docker runs." + echo " -m, --mount-termux-dirs Mount /data and ~/.termux-build into the container. This is useful for building locally for development with host IDE and editors." + echo "" + echo "Supported environment variables:" + echo " TERMUX_BUILDER_IMAGE_NAME The name of the Docker image to use (default: 'ghcr.io/termux/package-builder')" + echo " CONTAINER_NAME The name of the Docker container to create/use (default: 'termux-package-builder')" + echo " TERMUX_DOCKER_RUN_EXTRA_ARGS Extra arguments to pass to 'docker run' while creating the container (default: '')" + echo " TERMUX_DOCKER_EXEC_EXTRA_ARGS Extra arguments to pass to 'docker exec' while running the command in the container (default: '')" + echo " TERMUX_DOCKER_USE_SUDO If set to any non-empty value, 'sudo' will be used to run 'docker' commands (default: '')" + echo "" + echo "Note that command line option -m will only be considered if it is the first argument passed to this script." + echo "Help message is only shown if the first and only argument passed to this script is -h or --help." + echo "" + echo "TERMUX_DOCKER_RUN_EXTRA_ARGS is only considered when creating the container, and will not be applied when running the command in the container if the container already exists." + echo "To apply new TERMUX_DOCKER_RUN_EXTRA_ARGS, the existing container needs to be removed first." + echo "Similar rule applies for the -m/--mount-termux-dirs option." + exit 0 +fi + + +if [ "$#" -ge 1 ]; then + if [ "$1" = "--mount-termux-dirs" ] || [ "$1" = "-m" ]; then + TERMUX_DOCKER_RUN_EXTRA_ARGS="--volume /data:/data --volume $HOME/.termux-build:$CONTAINER_HOME_DIR/.termux-build $TERMUX_DOCKER_RUN_EXTRA_ARGS" + shift 1 + fi +fi + $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { echo "Creating new container..." $SUDO docker run \ From 1597725ba984660dd791b22f5f78c4cb3922066a Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 1 Mar 2026 16:40:58 +0530 Subject: [PATCH 09/47] chore,ci(packages): Allow forcing free space In commits, %ci:free-space will force freeing space in commits In workflow dispatch, a new checkbox should be available --- .github/workflows/packages.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index ec99591488c..60fc29ed0f8 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -20,6 +20,10 @@ on: packages: description: "A space-separated names of packages selected for rebuilding" required: true + free-space: + description: "Free space even if not building large package (useful when building a large number of packages)" + type: boolean + default: false permissions: {} # none @@ -186,6 +190,13 @@ jobs: echo "packages: ${packages[*]}" free_space='false' + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + free_space=${{ github.event.inputs.free-space }} + else + if grep -qiP '^\s*%ci:free-disk\s*$' <(git log --format="%B" -n 1 --no-merges "HEAD"); then + free_space=true + fi + fi if [[ "${#packages[@]}" -gt 0 ]]; then for pkg in "${packages[@]}"; do if grep -qFx "$pkg" ./scripts/big-pkgs.list; then From 6055b64055ea7a15306d1de04090ad01177fd661 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 1 Mar 2026 17:26:56 +0530 Subject: [PATCH 10/47] fix,scripts(run-docker.sh): improved command line argument parsing Instead of only supporting one of the flags, we now support passing multiple flags at the same time for more convenience. The command line argument parser will exit as soon as it detects an argument/flag it doesn't handle to preserve maximum compatibility with existing commands --- scripts/run-docker.sh | 89 ++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 04e35fcd233..0852c335c78 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -1,14 +1,54 @@ -#!/bin/sh +#!/bin/bash set -e -u TERMUX_SCRIPTDIR=$(cd "$(realpath "$(dirname "$0")")"; cd ..; pwd) BUILDSCRIPT_NAME="build-package.sh" -if [ "${1:-}" = "-p" ] || [ "${1:-}" = "--pre-check-if-will-build-packages" ]; then - shift 1 - TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES="true" -fi +: ${TERMUX_BUILDER_IMAGE_NAME:=ghcr.io/termux/package-builder} +: ${CONTAINER_NAME:=termux-package-builder} +: ${TERMUX_DOCKER_RUN_EXTRA_ARGS:=} +: ${TERMUX_DOCKER_EXEC_EXTRA_ARGS:=} + +_show_usage() { + echo "Usage: $0 [OPTIONS] [COMMAND]" + echo "" + echo "Run a command in the Termux package builder container. If no command is given, an interactive shell will be started." + echo "" + echo "Options:" + echo " -h, --help Show this help message and exit" + echo " Run 'build-package-dry-run-simulation.sh' to check if the given command would build any packages, and if not, exit with code 0 without running the command in docker. This is useful for CI to skip unnecessary docker runs." + echo " -m, --mount-termux-dirs Mount /data and ~/.termux-build into the container. This is useful for building locally for development with host IDE and editors." + echo "" + echo "Supported environment variables:" + echo " TERMUX_BUILDER_IMAGE_NAME The name of the Docker image to use (default: 'ghcr.io/termux/package-builder')" + echo " CONTAINER_NAME The name of the Docker container to create/use (default: 'termux-package-builder')" + echo " TERMUX_DOCKER_RUN_EXTRA_ARGS Extra arguments to pass to 'docker run' while creating the container (default: '')" + echo " TERMUX_DOCKER_EXEC_EXTRA_ARGS Extra arguments to pass to 'docker exec' while running the command in the container (default: '')" + echo " TERMUX_DOCKER_USE_SUDO If set to any non-empty value, 'sudo' will be used to run 'docker' commands (default: '')" + echo "" + echo "Note that command line option -m will only be considered if it is the first argument passed to this script." + echo "Help message is only shown if the first and only argument passed to this script is -h or --help." + echo "" + echo "TERMUX_DOCKER_RUN_EXTRA_ARGS is only considered when creating the container, and will not be applied when running the command in the container if the container already exists." + echo "To apply new TERMUX_DOCKER_RUN_EXTRA_ARGS, the existing container needs to be removed first." + echo "Similar rule applies for the -m/--mount-termux-dirs option." + exit 0 +} + +while (( $# != 0 )); do + case "$1" in + -h|--help) shift 1; _show_usage;; + -p|--pre-check-if-will-build-packages) + TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES="true" + shift 1; break;; + -m|--mount-termux-dirs) + TERMUX_DOCKER_RUN_EXTRA_ARGS="--volume /data:/data --volume $HOME/.termux-build:$CONTAINER_HOME_DIR/.termux-build $TERMUX_DOCKER_RUN_EXTRA_ARGS" + shift 1; break;; + --) shift 1; break;; + *) break;; + esac +done # If 'build-package-dry-run-simulation.sh' does not return 85 (EX_C__NOOP), or if # $1 (the first argument passed to this script which runs docker) does not contain @@ -57,11 +97,6 @@ else VOLUME=$REPOROOT:$CONTAINER_HOME_DIR/termux-packages fi -: ${TERMUX_BUILDER_IMAGE_NAME:=ghcr.io/termux/package-builder} -: ${CONTAINER_NAME:=termux-package-builder} -: ${TERMUX_DOCKER_RUN_EXTRA_ARGS:=} -: ${TERMUX_DOCKER_EXEC_EXTRA_ARGS:=} - USER=builder if [ -n "${TERMUX_DOCKER_USE_SUDO-}" ]; then @@ -79,40 +114,6 @@ else DOCKER_TTY="" fi -if [ "$#" -eq 1 ] && ( [ "$1" = "-h" ] || [ "$1" = "--help" ] ); then - echo "Usage: $0 [OPTIONS] [COMMAND]" - echo "" - echo "Run a command in the Termux package builder container. If no command is given, an interactive shell will be started." - echo "" - echo "Options:" - echo " -h, --help Show this help message and exit" - echo " Run 'build-package-dry-run-simulation.sh' to check if the given command would build any packages, and if not, exit with code 0 without running the command in docker. This is useful for CI to skip unnecessary docker runs." - echo " -m, --mount-termux-dirs Mount /data and ~/.termux-build into the container. This is useful for building locally for development with host IDE and editors." - echo "" - echo "Supported environment variables:" - echo " TERMUX_BUILDER_IMAGE_NAME The name of the Docker image to use (default: 'ghcr.io/termux/package-builder')" - echo " CONTAINER_NAME The name of the Docker container to create/use (default: 'termux-package-builder')" - echo " TERMUX_DOCKER_RUN_EXTRA_ARGS Extra arguments to pass to 'docker run' while creating the container (default: '')" - echo " TERMUX_DOCKER_EXEC_EXTRA_ARGS Extra arguments to pass to 'docker exec' while running the command in the container (default: '')" - echo " TERMUX_DOCKER_USE_SUDO If set to any non-empty value, 'sudo' will be used to run 'docker' commands (default: '')" - echo "" - echo "Note that command line option -m will only be considered if it is the first argument passed to this script." - echo "Help message is only shown if the first and only argument passed to this script is -h or --help." - echo "" - echo "TERMUX_DOCKER_RUN_EXTRA_ARGS is only considered when creating the container, and will not be applied when running the command in the container if the container already exists." - echo "To apply new TERMUX_DOCKER_RUN_EXTRA_ARGS, the existing container needs to be removed first." - echo "Similar rule applies for the -m/--mount-termux-dirs option." - exit 0 -fi - - -if [ "$#" -ge 1 ]; then - if [ "$1" = "--mount-termux-dirs" ] || [ "$1" = "-m" ]; then - TERMUX_DOCKER_RUN_EXTRA_ARGS="--volume /data:/data --volume $HOME/.termux-build:$CONTAINER_HOME_DIR/.termux-build $TERMUX_DOCKER_RUN_EXTRA_ARGS" - shift 1 - fi -fi - $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { echo "Creating new container..." $SUDO docker run \ From 51e6a83ed78f4a6f0f38d9083390be90fb9944d7 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 1 Mar 2026 17:49:08 +0530 Subject: [PATCH 11/47] chore,scripts(run-docker.sh): limit line width to 80 for usage and get rid of long length variable name --- .github/workflows/package_updates.yml | 1 - .github/workflows/packages.yml | 4 +- scripts/run-docker.sh | 49 ++++++++++++------- .../utils/termux_pkg_upgrade_version.sh | 2 +- 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/.github/workflows/package_updates.yml b/.github/workflows/package_updates.yml index 5cc32d1d850..ba60d9a50ac 100644 --- a/.github/workflows/package_updates.yml +++ b/.github/workflows/package_updates.yml @@ -122,7 +122,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.TERMUXBOT2_TOKEN }} BUILD_PACKAGES: "true" - TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES: "true" CREATE_ISSUE: "true" GIT_COMMIT_PACKAGES: "true" GIT_PUSH_PACKAGES: "true" diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 60fc29ed0f8..35ea260dd61 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -258,8 +258,6 @@ jobs: - name: Build packages if: ${{ steps.build-info.outputs.skip-building != 'true' }} - env: - TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES: "true" run: | declare -a packages=() for repo_path in $(jq --raw-output 'del(.pkg_format) | keys | .[]' repo.json); do @@ -272,7 +270,7 @@ jobs: echo "packages: ${packages[*]}" if [[ "${#packages[@]}" -gt 0 ]]; then - ./scripts/run-docker.sh ./build-package.sh -I -C -a "${{ matrix.target_arch }}" "${packages[@]}" + ./scripts/run-docker.sh -d ./build-package.sh -I -C -a "${{ matrix.target_arch }}" "${packages[@]}" fi - name: Generate build artifacts diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 0852c335c78..a7145a08736 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -16,36 +16,51 @@ _show_usage() { echo "Run a command in the Termux package builder container. If no command is given, an interactive shell will be started." echo "" echo "Options:" - echo " -h, --help Show this help message and exit" - echo " Run 'build-package-dry-run-simulation.sh' to check if the given command would build any packages, and if not, exit with code 0 without running the command in docker. This is useful for CI to skip unnecessary docker runs." - echo " -m, --mount-termux-dirs Mount /data and ~/.termux-build into the container. This is useful for building locally for development with host IDE and editors." - echo "" + echo " -h, --help Show this help message and exit" + echo " -d, --dry-run Run 'build-package-dry-run-simulation.sh' before" + echo " building any package. This is useful for CI to" + echo " skip unnecessary docker runs." + echo " -m, --mount-termux-dirs Mount /data and ~/.termux-build into the container." + echo " This is useful for building locally for development" + echo " with host IDE and editors." echo "Supported environment variables:" - echo " TERMUX_BUILDER_IMAGE_NAME The name of the Docker image to use (default: 'ghcr.io/termux/package-builder')" - echo " CONTAINER_NAME The name of the Docker container to create/use (default: 'termux-package-builder')" - echo " TERMUX_DOCKER_RUN_EXTRA_ARGS Extra arguments to pass to 'docker run' while creating the container (default: '')" - echo " TERMUX_DOCKER_EXEC_EXTRA_ARGS Extra arguments to pass to 'docker exec' while running the command in the container (default: '')" - echo " TERMUX_DOCKER_USE_SUDO If set to any non-empty value, 'sudo' will be used to run 'docker' commands (default: '')" + echo " TERMUX_BUILDER_IMAGE_NAME The name of the Docker image to use" + echo " CONTAINER_NAME The name of the Docker container to create/use" + echo " TERMUX_DOCKER_RUN_EXTRA_ARGS Extra arguments to pass to 'docker run' while" + echo " creating the container" + echo " TERMUX_DOCKER_EXEC_EXTRA_ARGS Extra arguments to pass to 'docker exec' while" + echo " running the command in the container" + echo " TERMUX_DOCKER_USE_SUDO If set to any non-empty value, 'sudo' will be" + echo " used to run 'docker' commands" echo "" - echo "Note that command line option -m will only be considered if it is the first argument passed to this script." - echo "Help message is only shown if the first and only argument passed to this script is -h or --help." echo "" - echo "TERMUX_DOCKER_RUN_EXTRA_ARGS is only considered when creating the container, and will not be applied when running the command in the container if the container already exists." - echo "To apply new TERMUX_DOCKER_RUN_EXTRA_ARGS, the existing container needs to be removed first." - echo "Similar rule applies for the -m/--mount-termux-dirs option." + echo "Kindly note that:" + echo "- TERMUX_DOCKER_RUN_EXTRA_ARGS is only considered when creating the container," + echo " and will not be applied when running the command in the container if the" + echo " container already exists." + echo "- To apply new TERMUX_DOCKER_RUN_EXTRA_ARGS, the existing container needs to be" + echo " removed first." + echo "- The above rules also apply to -m/--mount-termux-dirs option as it adds the" + echo " mount arguments to TERMUX_DOCKER_RUN_EXTRA_ARGS." + echo "- The dry-run option will only work if the first argument passed to this script" + echo " which runs docker contains '$BUILDSCRIPT_NAME', and it will run" + echo " 'build-package-dry-run-simulation.sh' with arguments passed to this script." exit 0 } +dry_run="false" + while (( $# != 0 )); do case "$1" in -h|--help) shift 1; _show_usage;; - -p|--pre-check-if-will-build-packages) - TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES="true" + -d|--dry-run) + dry_run="true" shift 1; break;; -m|--mount-termux-dirs) TERMUX_DOCKER_RUN_EXTRA_ARGS="--volume /data:/data --volume $HOME/.termux-build:$CONTAINER_HOME_DIR/.termux-build $TERMUX_DOCKER_RUN_EXTRA_ARGS" shift 1; break;; --) shift 1; break;; + -*) echo "Error: Unknown option '$1'" 1>&2; shift 1; exit 1;; *) break;; esac done @@ -54,7 +69,7 @@ done # $1 (the first argument passed to this script which runs docker) does not contain # $BUILDSCRIPT_NAME, this condition will evaluate false and this script which # runs docker will continue. -if [ "${TERMUX_DOCKER__CONTAINER_EXEC_COMMAND__PRE_CHECK_IF_WILL_BUILD_PACKAGES:-}" = "true" ]; then +if [ "${dry_run}" = "true" ]; then case "${1:-}" in *"/$BUILDSCRIPT_NAME") RETURN_VALUE=0 diff --git a/scripts/updates/utils/termux_pkg_upgrade_version.sh b/scripts/updates/utils/termux_pkg_upgrade_version.sh index 32f442eca2d..35a25a684d1 100755 --- a/scripts/updates/utils/termux_pkg_upgrade_version.sh +++ b/scripts/updates/utils/termux_pkg_upgrade_version.sh @@ -139,7 +139,7 @@ termux_pkg_upgrade_version() { _termux_should_cleanup "${big_package}" && "${TERMUX_SCRIPTDIR}/scripts/run-docker.sh" ./clean.sh - if ! "${TERMUX_SCRIPTDIR}/scripts/run-docker.sh" ./build-package.sh -C -a "${TERMUX_ARCH}" -i "${TERMUX_PKG_NAME}"; then + if ! "${TERMUX_SCRIPTDIR}/scripts/run-docker.sh" -d ./build-package.sh -C -a "${TERMUX_ARCH}" -i "${TERMUX_PKG_NAME}"; then _termux_should_cleanup "${big_package}" && "${TERMUX_SCRIPTDIR}/scripts/run-docker.sh" ./clean.sh git checkout -- "${TERMUX_SCRIPTDIR}" termux_error_exit "failed to build." From f109436122ec0efa936db2c7291b41b161eb2ad1 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 2 Mar 2026 04:10:23 +0530 Subject: [PATCH 12/47] fixup! fix,scripts(run-docker.sh): improved command line argument parsing --- scripts/run-docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index a7145a08736..2eedb02ffe0 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -9,6 +9,7 @@ BUILDSCRIPT_NAME="build-package.sh" : ${CONTAINER_NAME:=termux-package-builder} : ${TERMUX_DOCKER_RUN_EXTRA_ARGS:=} : ${TERMUX_DOCKER_EXEC_EXTRA_ARGS:=} +CONTAINER_HOME_DIR=/home/builder _show_usage() { echo "Usage: $0 [OPTIONS] [COMMAND]" @@ -86,7 +87,6 @@ if [ "${dry_run}" = "true" ]; then esac fi -CONTAINER_HOME_DIR=/home/builder UNAME=$(uname) if [ "$UNAME" = Darwin ]; then # Workaround for mac readlink not supporting -f. From 81b7a8cf059dbf922fbd8cb54c15bb9c4f453e5b Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 2 Mar 2026 04:11:28 +0530 Subject: [PATCH 13/47] fix,scripts(run-docker.sh): do not bail out on first flag Logic error found by GitHub Copilot --- scripts/run-docker.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 2eedb02ffe0..14774a96019 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -56,10 +56,10 @@ while (( $# != 0 )); do -h|--help) shift 1; _show_usage;; -d|--dry-run) dry_run="true" - shift 1; break;; + shift 1;; -m|--mount-termux-dirs) TERMUX_DOCKER_RUN_EXTRA_ARGS="--volume /data:/data --volume $HOME/.termux-build:$CONTAINER_HOME_DIR/.termux-build $TERMUX_DOCKER_RUN_EXTRA_ARGS" - shift 1; break;; + shift 1;; --) shift 1; break;; -*) echo "Error: Unknown option '$1'" 1>&2; shift 1; exit 1;; *) break;; From 9ffd54f09f9345518a6a48887ecad930f4912b8f Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 2 Mar 2026 04:14:38 +0530 Subject: [PATCH 14/47] fixup! scripts: allow more flexible configuration of docker container --- clean.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clean.sh b/clean.sh index 904c41560b6..d1ea637040a 100755 --- a/clean.sh +++ b/clean.sh @@ -94,6 +94,8 @@ fi fi # We can't use rm -Rf "$TERMUX_TOPDIR" in case the "$TERMUX_TOPDIR" is mounted as a Docker volume - find "$TERMUX_TOPDIR" -type f,l -delete - find "$TERMUX_TOPDIR" -type d ! -path "$TERMUX_TOPDIR" -delete + if [ -d "$TERMUX_TOPDIR" ]; then + find "$TERMUX_TOPDIR" -type f,l -delete + find "$TERMUX_TOPDIR" -type d ! -path "$TERMUX_TOPDIR" -delete + fi } 5< "$TERMUX_BUILD_LOCK_FILE" From 7f6c357e2657968e38797742b02243c0d10ab9b4 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 2 Mar 2026 04:15:44 +0530 Subject: [PATCH 15/47] fixup! scripts(free-space.sh): remove more stuff from runner --- scripts/free-space.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/free-space.sh b/scripts/free-space.sh index dcccb739127..80208d07cac 100755 --- a/scripts/free-space.sh +++ b/scripts/free-space.sh @@ -49,9 +49,9 @@ else # Clean compressed docker images # Docker has already deflated them before free-space.sh was called in CI, so # the compressed parts are just collecting junk on the disk - sudo rm -r /var/lib/containerd/io.containerd.content.v1.content/ + sudo rm -rf /var/lib/containerd/io.containerd.content.v1.content/ sudo apt autoremove -yq sudo apt clean - sudo rm -rf /var/lib/{apt,dpkg} + sudo rm -rf /var/lib/apt /var/lib/dpkg fi From ce58061fbfeb278c15e5e7b0c785a070d234d986 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 23 Feb 2026 20:09:23 +0530 Subject: [PATCH 16/47] ci: use fuse-overlayfs to reduce runtime storage used by builder **BREAKING CHANGES** This now requires AppArmor to be installed and running with docker for limiting the capabilities that `CAP_SYS_ADMIN` provides to containers. Host kernel must support fuse. The host /dev/fuse device is passed onto the container. **DETAILED DESCRIPTION** ./scripts/run-docker.sh first starts with relaxed profile, and then after changing the uid and gid of the builder user and group, drops to restricted profile. Each container get's it's own profile so that if ./scripts/run-docker.sh is run parallel with multiple containers, there is no race condition for the when we are changing the builder uid/gid, where the other container will run with higher privileges than needed. For ensuring least privileges, only mount and umount2 syscalls have been permitted in seccomp profile. Additionally rules for allowing clone, clone3 and similar syscalls when certain contain conditions are met and only when CAP_SYS_ADMIN is not set have been removed as we aren't allowing these syscalls when CAP_SYS_ADMIN is set. The AppArmor profile is based on Docker's default AppArmor profile. The profile was extracted using nerdctl (which is an alternate CLI interface to Docker CLI). The profile can be extracted using `nerdctl apparmor inspect`. There are two AppArmor profiles we have setup, one restricted and relaxed. Currently there is little difference between relaxed and restricted profile. The only difference is that relaxed profile allows any kind of mount syscall, while the restricted profile only allows mount syscalls only for fuse.fuse-overlayfs Regarding security of passing /dev/fuse to containers, the Linux kernel documentation specifies that it should be fine to pass this to namespaces. The CAP_SYSTEM_ADMIN is needed only for the mount syscall to work. Due to some historic reasons this needs this dangerous capability. Although the syscall is needed we are only allowing mount and umount syscalls to happen inside the container, so seccomp profile and apparmor profile should be doing the damage control. Linux kernel documentation for fuse-passthrough: https://docs.kernel.org/6.16/filesystems/fuse-passthrough.html Upstream Linux kernel commit for fuse documentation: torvalds/linux@18ee43c398af0b7e2eb2a4cd8469967834b0802d Even without apparmor, things should be fine as we aren't fiddling around much with apparmor for security reasons, just currently limiting where the fuse filesystem can be used. In future, AppArmor profiles can also be used for further hardening of docker image --- .github/workflows/packages.yml | 4 + clean.sh | 7 ++ scripts/build/termux_step_setup_toolchain.sh | 4 +- .../toolchain/termux_setup_toolchain_29.sh | 76 ++++++++++--------- scripts/profile-relaxed.apparmor | 48 ++++++++++++ scripts/profile-restricted.apparmor | 55 ++++++++++++++ scripts/profile.json | 35 +-------- scripts/run-docker.sh | 17 ++++- scripts/setup-ubuntu.sh | 4 + 9 files changed, 177 insertions(+), 73 deletions(-) create mode 100644 scripts/profile-relaxed.apparmor create mode 100644 scripts/profile-restricted.apparmor diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 35ea260dd61..7bdf45cddf8 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -315,6 +315,10 @@ jobs: with: name: debs-${{ matrix.target_arch }}-${{ github.sha }} path: ./artifacts + - name: AppArmor Logs + if: always() + run: | + sudo dmesg | grep apparmor test-buildorder-random: permissions: diff --git a/clean.sh b/clean.sh index d1ea637040a..3439ab5cb4c 100755 --- a/clean.sh +++ b/clean.sh @@ -93,6 +93,13 @@ fi rm -Rf "/data/data/.built-packages" fi + # unmount overlayfs before we remove the parent directory + [ -d "$TERMUX_TOPDIR" ] && for dir in $(find "$TERMUX_TOPDIR" -type d); do + if mountpoint -q "$dir"; then + umount "$dir" + fi + done + # We can't use rm -Rf "$TERMUX_TOPDIR" in case the "$TERMUX_TOPDIR" is mounted as a Docker volume if [ -d "$TERMUX_TOPDIR" ]; then find "$TERMUX_TOPDIR" -type f,l -delete diff --git a/scripts/build/termux_step_setup_toolchain.sh b/scripts/build/termux_step_setup_toolchain.sh index e4709039a60..186e6a0cbfc 100644 --- a/scripts/build/termux_step_setup_toolchain.sh +++ b/scripts/build/termux_step_setup_toolchain.sh @@ -7,10 +7,10 @@ termux_step_setup_toolchain() { # toolchain setup to ensure that everyone gets an updated # toolchain if [ "${TERMUX_NDK_VERSION}" = "29" ]; then - TERMUX_STANDALONE_TOOLCHAIN+="-v3" + TERMUX_STANDALONE_TOOLCHAIN+="-v4" termux_setup_toolchain_29 elif [ "${TERMUX_NDK_VERSION}" = 23c ]; then - TERMUX_STANDALONE_TOOLCHAIN+="-v10" + TERMUX_STANDALONE_TOOLCHAIN+="-v11" termux_setup_toolchain_23c else termux_error_exit "We do not have a setup_toolchain function for NDK version $TERMUX_NDK_VERSION" diff --git a/scripts/build/toolchain/termux_setup_toolchain_29.sh b/scripts/build/toolchain/termux_setup_toolchain_29.sh index cd17364b794..27abb3e2422 100644 --- a/scripts/build/toolchain/termux_setup_toolchain_29.sh +++ b/scripts/build/toolchain/termux_setup_toolchain_29.sh @@ -128,14 +128,22 @@ termux_setup_toolchain_29() { return fi - if [ -d $TERMUX_STANDALONE_TOOLCHAIN ]; then - return + [ -d "$TERMUX_STANDALONE_TOOLCHAIN" ] || mkdir -p "$TERMUX_STANDALONE_TOOLCHAIN" + [ -d "${TERMUX_STANDALONE_TOOLCHAIN}-upper" ] || mkdir -p "${TERMUX_STANDALONE_TOOLCHAIN}-upper" + [ -d "${TERMUX_STANDALONE_TOOLCHAIN}-work" ] || mkdir -p "${TERMUX_STANDALONE_TOOLCHAIN}-work" + + + if ! mountpoint -q "${TERMUX_STANDALONE_TOOLCHAIN}"; then + fuse-overlayfs \ + "${TERMUX_STANDALONE_TOOLCHAIN}" \ + -o lowerdir="${NDK}/toolchains/llvm/prebuilt/linux-x86_64" \ + -o upperdir="${TERMUX_STANDALONE_TOOLCHAIN}-upper" \ + -o workdir="${TERMUX_STANDALONE_TOOLCHAIN}-work" fi - # 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 + if [ -f "${TERMUX_STANDALONE_TOOLCHAIN}/.termux-standalone-toolchain" ]; then + return + fi local _NDK_ARCHNAME=$TERMUX_ARCH if [ "$TERMUX_ARCH" = "aarch64" ]; then @@ -143,55 +151,52 @@ termux_setup_toolchain_29() { elif [ "$TERMUX_ARCH" = "i686" ]; then _NDK_ARCHNAME=x86 fi - cp $NDK/toolchains/llvm/prebuilt/linux-x86_64 $_TERMUX_TOOLCHAIN_TMPDIR -r - cp $NDK/source.properties $_TERMUX_TOOLCHAIN_TMPDIR - # Remove android-support header wrapping not needed on android-21: - rm -Rf $_TERMUX_TOOLCHAIN_TMPDIR/sysroot/usr/local + rm -Rf $TERMUX_STANDALONE_TOOLCHAIN/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 \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-clang - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT$TERMUX_PKG_API_LEVEL-clang++ \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-clang++ + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT$TERMUX_PKG_API_LEVEL-clang \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-clang + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT$TERMUX_PKG_API_LEVEL-clang++ \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-clang++ - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT$TERMUX_PKG_API_LEVEL-clang \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-cpp + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT$TERMUX_PKG_API_LEVEL-clang \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-cpp sed -i 's|"$bin_dir/clang"|& -E|' \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-cpp + $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-cpp - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-clang \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-gcc - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-clang++ \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/$HOST_PLAT-g++ + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-clang \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-gcc + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-clang++ \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/$HOST_PLAT-g++ done - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/armv7a-linux-androideabi$TERMUX_PKG_API_LEVEL-clang \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/arm-linux-androideabi-clang - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/armv7a-linux-androideabi$TERMUX_PKG_API_LEVEL-clang++ \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/arm-linux-androideabi-clang++ - cp $_TERMUX_TOOLCHAIN_TMPDIR/bin/armv7a-linux-androideabi-cpp \ - $_TERMUX_TOOLCHAIN_TMPDIR/bin/arm-linux-androideabi-cpp + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/armv7a-linux-androideabi$TERMUX_PKG_API_LEVEL-clang \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/arm-linux-androideabi-clang + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/armv7a-linux-androideabi$TERMUX_PKG_API_LEVEL-clang++ \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/arm-linux-androideabi-clang++ + cp $TERMUX_STANDALONE_TOOLCHAIN/bin/armv7a-linux-androideabi-cpp \ + $TERMUX_STANDALONE_TOOLCHAIN/bin/arm-linux-androideabi-cpp # rust 1.75.0+ expects this directory to be present - rm -fr "${_TERMUX_TOOLCHAIN_TMPDIR}"/toolchains - mkdir -p "${_TERMUX_TOOLCHAIN_TMPDIR}"/toolchains/llvm/prebuilt - ln -fs ../../.. "${_TERMUX_TOOLCHAIN_TMPDIR}"/toolchains/llvm/prebuilt/linux-x86_64 + rm -fr "${TERMUX_STANDALONE_TOOLCHAIN}"/toolchains + mkdir -p "${TERMUX_STANDALONE_TOOLCHAIN}"/toolchains/llvm/prebuilt + ln -fs ../../.. "${TERMUX_STANDALONE_TOOLCHAIN}"/toolchains/llvm/prebuilt/linux-x86_64 # Create a pkg-config wrapper. We use path to host pkg-config to # avoid picking up a cross-compiled pkg-config later on. local _HOST_PKGCONFIG _HOST_PKGCONFIG=$(command -v pkg-config) mkdir -p "$PKG_CONFIG_LIBDIR" - cat > $_TERMUX_TOOLCHAIN_TMPDIR/bin/pkg-config <<-HERE + cat > $TERMUX_STANDALONE_TOOLCHAIN/bin/pkg-config <<-HERE #!/bin/sh export PKG_CONFIG_DIR= export PKG_CONFIG_LIBDIR=$PKG_CONFIG_LIBDIR exec $_HOST_PKGCONFIG "\$@" HERE - chmod +x "$_TERMUX_TOOLCHAIN_TMPDIR"/bin/pkg-config + chmod +x "$TERMUX_STANDALONE_TOOLCHAIN"/bin/pkg-config - cd $_TERMUX_TOOLCHAIN_TMPDIR/sysroot + cd $TERMUX_STANDALONE_TOOLCHAIN/sysroot for f in $TERMUX_SCRIPTDIR/ndk-patches/$TERMUX_NDK_VERSION/*.patch; do echo "Applying ndk-patch: $(basename $f)" sed "s%\@TERMUX_PREFIX\@%${TERMUX_PREFIX}%g" "$f" | \ @@ -226,6 +231,7 @@ termux_setup_toolchain_29() { echo 'INPUT(-lunwind)' > $dir/libgcc.a done - grep -lrw $_TERMUX_TOOLCHAIN_TMPDIR/sysroot/usr/include/c++/v1 -e 'include ' | xargs -n 1 sed -i 's/include /include \"version\"/g' - mv $_TERMUX_TOOLCHAIN_TMPDIR $TERMUX_STANDALONE_TOOLCHAIN + grep -lrw $TERMUX_STANDALONE_TOOLCHAIN/sysroot/usr/include/c++/v1 -e 'include ' | xargs -n 1 sed -i 's/include /include \"version\"/g' + + touch ${TERMUX_STANDALONE_TOOLCHAIN}/.termux-standalone-toolchain } diff --git a/scripts/profile-relaxed.apparmor b/scripts/profile-relaxed.apparmor new file mode 100644 index 00000000000..94fb48c21b9 --- /dev/null +++ b/scripts/profile-relaxed.apparmor @@ -0,0 +1,48 @@ +#include + + +profile _custom-termux-package-builder-{{CONTAINER_NAME}} flags=(attach_disconnected,mediate_deleted) { + + #include + + network, + capability, + file, + umount, + # Host (privileged) processes may send signals to container processes. + signal (receive) peer=unconfined, + # runc may send signals to container processes. + signal (receive) peer=runc, + # crun may send signals to container processes. + signal (receive) peer=crun, + # Manager may send signals to container processes. + signal (receive) peer=unconfined, + # Container processes may send signals amongst themselves. + signal (send,receive) peer=_custom-termux-package-builder-{{CONTAINER_NAME}}, + + deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) + # deny write to files not in /proc//** or /proc/sys/** + deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, + deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) + deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ + deny @{PROC}/sysrq-trigger rwklx, + deny @{PROC}/mem rwklx, + deny @{PROC}/kmem rwklx, + deny @{PROC}/kcore rwklx, + + deny /sys/[^f]*/** wklx, + deny /sys/f[^s]*/** wklx, + deny /sys/fs/[^c]*/** wklx, + deny /sys/fs/c[^g]*/** wklx, + deny /sys/fs/cg[^r]*/** wklx, + deny /sys/firmware/** rwklx, + deny /sys/devices/virtual/powercap/** rwklx, + deny /sys/kernel/security/** rwklx, + + # allow processes within the container to trace each other, + # provided all other LSM and yama setting allow it. + ptrace (trace,tracedby,read,readby) peer=_custom-termux-package-builder-{{CONTAINER_NAME}}, + + # Allow all mount operations + mount, +} diff --git a/scripts/profile-restricted.apparmor b/scripts/profile-restricted.apparmor new file mode 100644 index 00000000000..ea802eaa393 --- /dev/null +++ b/scripts/profile-restricted.apparmor @@ -0,0 +1,55 @@ +#include + + +profile _custom-termux-package-builder-{{CONTAINER_NAME}} flags=(attach_disconnected,mediate_deleted) { + + #include + + network, + capability, + file, + umount, + # Host (privileged) processes may send signals to container processes. + signal (receive) peer=unconfined, + # runc may send signals to container processes. + signal (receive) peer=runc, + # crun may send signals to container processes. + signal (receive) peer=crun, + # Manager may send signals to container processes. + signal (receive) peer=unconfined, + # Container processes may send signals amongst themselves. + signal (send,receive) peer=_custom-termux-package-builder-{{CONTAINER_NAME}}, + + deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) + # deny write to files not in /proc//** or /proc/sys/** + deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, + deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) + deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ + deny @{PROC}/sysrq-trigger rwklx, + deny @{PROC}/mem rwklx, + deny @{PROC}/kmem rwklx, + deny @{PROC}/kcore rwklx, + + deny /sys/[^f]*/** wklx, + deny /sys/f[^s]*/** wklx, + deny /sys/fs/[^c]*/** wklx, + deny /sys/fs/c[^g]*/** wklx, + deny /sys/fs/cg[^r]*/** wklx, + deny /sys/firmware/** rwklx, + deny /sys/devices/virtual/powercap/** rwklx, + deny /sys/kernel/security/** rwklx, + + # allow processes within the container to trace each other, + # provided all other LSM and yama setting allow it. + ptrace (trace,tracedby,read,readby) peer=_custom-termux-package-builder-{{CONTAINER_NAME}}, + + # Allow mounting fuse overlayfs filesystems in ~/.termux-build/ + # This is used for mounting overlayfs of our build toolchain. This is + # more efficient than copying over the entire NDK as this takes up + # just under 50MB storage than 2.4GB + mount fstype=fuse.fuse-overlayfs -> /home/builder/.termux-build/**, + + # Deal with privilege escalation + deny capability setuid, + deny capability setgid, +} diff --git a/scripts/profile.json b/scripts/profile.json index 9969af8269d..d71a9e122de 100644 --- a/scripts/profile.json +++ b/scripts/profile.json @@ -1,5 +1,5 @@ { - "description": "This is a custom seccomp profile which allows the personality system call, based on https://github.com/moby/moby/blob/e258d66f176a4447931edfd9398c55b3e8ee4a07/profiles/seccomp/default.json.", + "description": "This is a custom seccomp profile which allows the personality, mount and umount2 system calls, based on https://github.com/moby/moby/blob/e258d66f176a4447931edfd9398c55b3e8ee4a07/profiles/seccomp/default.json.", "defaultAction": "SCMP_ACT_ERRNO", "defaultErrnoRet": 1, "archMap": [ @@ -579,30 +579,8 @@ }, { "names": [ - "bpf", - "clone", - "clone3", - "fanotify_init", - "fsconfig", - "fsmount", - "fsopen", - "fspick", - "lookup_dcookie", "mount", - "mount_setattr", - "move_mount", - "name_to_handle_at", - "open_tree", - "perf_event_open", - "quotactl", - "quotactl_fd", - "setdomainname", - "sethostname", - "setns", - "syslog", - "umount", - "umount2", - "unshare" + "umount2" ], "action": "SCMP_ACT_ALLOW", "includes": { @@ -624,9 +602,6 @@ } ], "excludes": { - "caps": [ - "CAP_SYS_ADMIN" - ], "arches": [ "s390", "s390x" @@ -653,9 +628,6 @@ ] }, "excludes": { - "caps": [ - "CAP_SYS_ADMIN" - ] } }, { @@ -665,9 +637,6 @@ "action": "SCMP_ACT_ERRNO", "errnoRet": 38, "excludes": { - "caps": [ - "CAP_SYS_ADMIN" - ] } }, { diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 14774a96019..5f75c336928 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -2,6 +2,8 @@ set -e -u TERMUX_SCRIPTDIR=$(cd "$(realpath "$(dirname "$0")")"; cd ..; pwd) +: ${TERMUX_BUILDER_IMAGE_NAME:=ghcr.io/termux/package-builder} +: ${CONTAINER_NAME:=termux-package-builder} BUILDSCRIPT_NAME="build-package.sh" @@ -94,7 +96,7 @@ if [ "$UNAME" = Darwin ]; then SEC_OPT="" else REPOROOT="$(dirname $(readlink -f $0))/../" - SEC_OPT=" --security-opt seccomp=$REPOROOT/scripts/profile.json" + SEC_OPT=" --security-opt seccomp=$REPOROOT/scripts/profile.json --security-opt apparmor=_custom-termux-package-builder-$CONTAINER_NAME --cap-add CAP_SYS_ADMIN --device /dev/fuse" fi if [ "${CI:-}" = "true" ]; then @@ -129,6 +131,11 @@ else DOCKER_TTY="" fi +# Log AppArmor audits (blocks), so that it is easier for maintenance +$SUDO bash -c 'echo -n noquiet >/sys/module/apparmor/parameters/audit' +# Load the relaxed AppArmor profile first as we might need to change permissions +cat ./scripts/profile-relaxed.apparmor | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | $SUDO apparmor_parser -rK + $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { echo "Creating new container..." $SUDO docker run \ @@ -143,14 +150,18 @@ $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { if [ "$UNAME" != Darwin ]; then if [ $(id -u) -ne 1001 -a $(id -u) -ne 0 ]; then echo "Changed builder uid/gid... (this may take a while)" - $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo chown -R $(id -u) $CONTAINER_HOME_DIR - $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo chown -R $(id -u) /data + $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo chown -R $(id -u):$(id -g) $CONTAINER_HOME_DIR + $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo chown -R $(id -u):$(id -g) /data $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo usermod -u $(id -u) builder $SUDO docker exec $DOCKER_TTY $TERMUX_DOCKER_EXEC_EXTRA_ARGS $CONTAINER_NAME sudo groupmod -g $(id -g) builder fi fi } +# stop the container and load restricted apparmor profile +echo "Loading restricted AppArmor profile..." +cat ./scripts/profile-restricted.apparmor | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | $SUDO apparmor_parser -rK + # Set traps to ensure that the process started with docker exec and all its children are killed. . "$TERMUX_SCRIPTDIR/scripts/utils/docker/docker.sh"; docker__setup_docker_exec_traps diff --git a/scripts/setup-ubuntu.sh b/scripts/setup-ubuntu.sh index 75dfbeecadd..b283dcf641f 100755 --- a/scripts/setup-ubuntu.sh +++ b/scripts/setup-ubuntu.sh @@ -25,6 +25,10 @@ PACKAGES+=" lzop" PACKAGES+=" lz4" PACKAGES+=" zstd" +# userspace overlayfs implementation for rootless containers +# Used to setup NDK toolchain without having to copy the whole toolchain to save some disk space +PACKAGES+=" fuse-overlayfs" + # Used by common build systems. PACKAGES+=" autoconf" PACKAGES+=" autogen" From 59acf32ba64cf28f077dba18cf68f1304f8f781e Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 23 Feb 2026 20:13:58 +0530 Subject: [PATCH 17/47] chore(CODEOWNERS): assign for docker security profiles @licy183 and me for Seccomp profile Me for apparmor profile Feel free to add yourself if you believe that you can deal with this nicely. Mostly for others looking for maintenance of the apparmor profile. Just grab the docker's default config using nerdctl apparmor inspect, diff with the current config and figure it out For the seccomp profile, just diff with the exact commit of moby/moby's seccomp profile and store the updated JSON --- CODEOWNERS | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CODEOWNERS b/CODEOWNERS index 2496769d95c..7937b7da653 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -15,6 +15,10 @@ /scripts/ @Grimler91 @thunder-coding /repo.json @Grimler91 @thunder-coding +# Docker security profiles +/scripts/profile.json @thunder-coding @licy183 +/scripts/*.apparmor @thunder-coding + # Build script linter /scripts/lint-packages.sh @TomJo2000 From f01340b7fc4e15d231f6401f2e17cb4f8c350a12 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Tue, 24 Feb 2026 14:42:24 +0530 Subject: [PATCH 18/47] chore,ci: make apparmor optional AppArmor isn't configured by default on distributions other than Ubuntu, so don't mandate it. AppArmor proper configuration and setup is a huge pain especially if you aren't familiar with it and containers in general. Even a lot of the maintainers aren't already familiar with it and using it already so let's just keep it optional and do not use it if not detected on host. --- scripts/run-docker.sh | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 5f75c336928..420d9c3765a 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -131,10 +131,22 @@ else DOCKER_TTY="" fi -# Log AppArmor audits (blocks), so that it is easier for maintenance -$SUDO bash -c 'echo -n noquiet >/sys/module/apparmor/parameters/audit' +APPARMOR_PARSER="$(command -v apparmor_parser)" +if [ -z "$APPARMOR_PARSER" ] || ! $SUDO aa-status --enabled; then + echo "WARNING: apparmor_parser not found, AppArmor profiles will not be loaded!" + echo " This is not recommended, as it may cause security issues and unexpected behavior" + echo " Avoid executing untrusted code in the container" + APPARMOR_PARSER="" +fi + +load_apparmor_profile() { + if [ -n "$APPARMOR_PARSER" ]; then + cat "$1" | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | $SUDO "$APPARMOR_PARSER" -rK + fi +} + # Load the relaxed AppArmor profile first as we might need to change permissions -cat ./scripts/profile-relaxed.apparmor | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | $SUDO apparmor_parser -rK +load_apparmor_profile ./scripts/profile-relaxed.apparmor $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { echo "Creating new container..." @@ -160,7 +172,7 @@ $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { # stop the container and load restricted apparmor profile echo "Loading restricted AppArmor profile..." -cat ./scripts/profile-restricted.apparmor | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | $SUDO apparmor_parser -rK +load_apparmor_profile ./scripts/profile-restricted.apparmor # Set traps to ensure that the process started with docker exec and all its children are killed. . "$TERMUX_SCRIPTDIR/scripts/utils/docker/docker.sh"; docker__setup_docker_exec_traps From 2e02fc5b87d27c523d667b8939556b776b20ec62 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Tue, 24 Feb 2026 16:36:43 +0530 Subject: [PATCH 19/47] scripts(run-docker.sh): always use sudo with APPARMOR_PARSER only respect TERMUX_DOCKER_USE_SUDO for running docker commands Even if docker is setup in rootless mode, apparmor needs to be run with sudo. This was the cause of CI failure and me trying to bang my head on why things weren't working on CI. Thanks, a "sudo" is all it takes. I wish it worked in real life as well on people --- scripts/run-docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 420d9c3765a..7d0b23e8062 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -141,7 +141,7 @@ fi load_apparmor_profile() { if [ -n "$APPARMOR_PARSER" ]; then - cat "$1" | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | $SUDO "$APPARMOR_PARSER" -rK + cat "$1" | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | sudo "$APPARMOR_PARSER" -rK fi } From 37bebda65cd921764695a4191f9c48dcf926963c Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Wed, 25 Feb 2026 20:09:49 +0530 Subject: [PATCH 20/47] scripts: more apparmor hardening We assume in a lot of places that all files in termux-packages are trusted. So make sure that the container isn't able to modify anything in this environment. Also do not allow any changes to ~/lib/ where we are storing Android NDK and SDK. --- scripts/profile-restricted.apparmor | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/profile-restricted.apparmor b/scripts/profile-restricted.apparmor index ea802eaa393..430aeecd0e9 100644 --- a/scripts/profile-restricted.apparmor +++ b/scripts/profile-restricted.apparmor @@ -52,4 +52,12 @@ profile _custom-termux-package-builder-{{CONTAINER_NAME}} flags=(attach_disconne # Deal with privilege escalation deny capability setuid, deny capability setgid, + + # Do not allow the container to modify trusted scripts + deny /home/builder/termux-packages/[^o]** wlk, + # Explicitly allow storing built debs + allow /home/builder/termux-packages/output/** rw, + + # Do not allow the container to modify the build toolchain + deny /home/builder/lib/** wlk, } From 4f27ace66f4cc28b2e4a501c5593bb060c3fe1fb Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 28 Feb 2026 08:45:15 +0530 Subject: [PATCH 21/47] fix,scripts(run-docker.sh): Properly handle the case when apparmor is not installeed --- scripts/run-docker.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index 7d0b23e8062..bebb072db15 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -131,7 +131,11 @@ else DOCKER_TTY="" fi -APPARMOR_PARSER="$(command -v apparmor_parser)" +APPARMOR_PARSER="" +if command -v apparmor_parser > /dev/null; then + APPARMOR_PARSER="apparmor_parser" +fi + if [ -z "$APPARMOR_PARSER" ] || ! $SUDO aa-status --enabled; then echo "WARNING: apparmor_parser not found, AppArmor profiles will not be loaded!" echo " This is not recommended, as it may cause security issues and unexpected behavior" From d64d636570176f2402c4b93271a107dcdf00653c Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 1 Mar 2026 15:35:04 +0530 Subject: [PATCH 22/47] fix,scripts(run-docker.sh): only show AppArmor log when using apparmor --- scripts/run-docker.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/run-docker.sh b/scripts/run-docker.sh index bebb072db15..cc886767fe8 100755 --- a/scripts/run-docker.sh +++ b/scripts/run-docker.sh @@ -144,8 +144,13 @@ if [ -z "$APPARMOR_PARSER" ] || ! $SUDO aa-status --enabled; then fi load_apparmor_profile() { + local profile_path="$1" + local msg="${2:-}" if [ -n "$APPARMOR_PARSER" ]; then - cat "$1" | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | sudo "$APPARMOR_PARSER" -rK + if [ -n "$msg" ]; then + echo "$msg..." + fi + cat "$profile_path" | sed -e "s/{{CONTAINER_NAME}}/$CONTAINER_NAME/g" | sudo "$APPARMOR_PARSER" -rK fi } @@ -174,9 +179,7 @@ $SUDO docker start $CONTAINER_NAME >/dev/null 2>&1 || { fi } -# stop the container and load restricted apparmor profile -echo "Loading restricted AppArmor profile..." -load_apparmor_profile ./scripts/profile-restricted.apparmor +load_apparmor_profile ./scripts/profile-restricted.apparmor "Loading restricted AppArmor profile" # Set traps to ensure that the process started with docker exec and all its children are killed. . "$TERMUX_SCRIPTDIR/scripts/utils/docker/docker.sh"; docker__setup_docker_exec_traps From cfbc90569d668487e9f322994a22e51acc640023 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Fri, 23 Jan 2026 02:53:23 +0530 Subject: [PATCH 23/47] rebuild(main/python-brotli): with python 3.13 --- packages/python-brotli/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python-brotli/build.sh b/packages/python-brotli/build.sh index 89f3e59ffb9..88e264b20b5 100644 --- a/packages/python-brotli/build.sh +++ b/packages/python-brotli/build.sh @@ -3,6 +3,7 @@ TERMUX_PKG_DESCRIPTION="lossless compression algorithm and format (Python bindin TERMUX_PKG_LICENSE="MIT" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="1.2.0" +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=https://github.com/google/brotli/archive/refs/tags/v$TERMUX_PKG_VERSION.tar.gz TERMUX_PKG_SHA256=816c96e8e8f193b40151dad7e8ff37b1221d019dbcb9c35cd3fadbfe6477dfec TERMUX_PKG_DEPENDS="python, python-pip" From 9ce15e3af6bf04d229648febb27cd8a3cc317578 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 19 Jan 2026 17:52:34 +0530 Subject: [PATCH 24/47] rebuild(main/python-greenlet): with python 3.13 --- packages/python-greenlet/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python-greenlet/build.sh b/packages/python-greenlet/build.sh index 7fbe9bc123d..f16716545d2 100644 --- a/packages/python-greenlet/build.sh +++ b/packages/python-greenlet/build.sh @@ -5,6 +5,7 @@ TERMUX_PKG_LICENSE="custom" TERMUX_PKG_LICENSE_FILE="LICENSE, LICENSE.PSF" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="3.3.2" +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=https://github.com/python-greenlet/greenlet/archive/refs/tags/${TERMUX_PKG_VERSION}.tar.gz TERMUX_PKG_SHA256=8fef0771bcf3bb4edb19fb6e997e127caa1ed4691b242080f1756ab1d1337d59 TERMUX_PKG_AUTO_UPDATE=true From 844fdc82851f63a1f96325157d19f40d80c7e964 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 12 Jan 2026 21:46:03 +0530 Subject: [PATCH 25/47] rebuild(main/python-msgpack): with python 3.13 --- packages/python-msgpack/build.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/python-msgpack/build.sh b/packages/python-msgpack/build.sh index dc9f45e35d4..bc1629876f9 100644 --- a/packages/python-msgpack/build.sh +++ b/packages/python-msgpack/build.sh @@ -3,7 +3,7 @@ TERMUX_PKG_DESCRIPTION="MessagePack serializer implementation for Python" TERMUX_PKG_LICENSE="Apache-2.0" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="1.1.2" -TERMUX_PKG_REVISION=1 +TERMUX_PKG_REVISION=2 # _cmsgpack.c is absent in https://github.com/msgpack/msgpack-python/archive/refs/tags/v${TERMUX_PKG_VERSION}.tar.gz TERMUX_PKG_SRCURL=https://pypi.org/packages/source/m/msgpack/msgpack-${TERMUX_PKG_VERSION}.tar.gz TERMUX_PKG_SHA256=3b60763c1373dd60f398488069bcdc703cd08a711477b5d480eecc9f9626f47e @@ -17,6 +17,6 @@ termux_step_make() { termux_step_make_install() { local _pyver="${TERMUX_PYTHON_VERSION//./}" - local _wheel="msgpack-${TERMUX_PKG_VERSION}-cp${_pyver}-cp${_pyver}-linux_${TERMUX_ARCH}.whl" - pip install --force-reinstal --no-deps --prefix="$TERMUX_PREFIX" "$TERMUX_PKG_SRCDIR/dist/${_wheel}" + local _wheel="msgpack-${TERMUX_PKG_VERSION}-cp${_pyver}-cp${_pyver}-android_${TERMUX_ARCH}.whl" + pip install --no-deps --prefix="$TERMUX_PREFIX" "$TERMUX_PKG_SRCDIR/dist/${_wheel}" } From 6d4fa6962bc9da33f57f7c5a87c25127102d7731 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 12 Jan 2026 21:48:45 +0530 Subject: [PATCH 26/47] rebuild(main/python-numpy): with python 3.13 --- packages/python-numpy/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/python-numpy/build.sh b/packages/python-numpy/build.sh index 482f759ef2b..c32910f698c 100644 --- a/packages/python-numpy/build.sh +++ b/packages/python-numpy/build.sh @@ -4,7 +4,7 @@ TERMUX_PKG_LICENSE="BSD 3-Clause" TERMUX_PKG_MAINTAINER="@termux" # Revbump revdeps after updating TERMUX_PKG_VERSION="2.2.5" -TERMUX_PKG_REVISION=3 +TERMUX_PKG_REVISION=4 TERMUX_PKG_SRCURL=git+https://github.com/numpy/numpy TERMUX_PKG_DEPENDS="libc++, libopenblas, python" TERMUX_PKG_AUTO_UPDATE=false @@ -73,6 +73,6 @@ termux_step_make() { termux_step_make_install() { local _pyv="${TERMUX_PYTHON_VERSION/./}" - local _whl="numpy-$TERMUX_PKG_VERSION-cp$_pyv-cp$_pyv-linux_$TERMUX_ARCH.whl" + local _whl="numpy-$TERMUX_PKG_VERSION-cp$_pyv-cp$_pyv-android_$TERMUX_ARCH.whl" pip install --no-deps --prefix=$TERMUX_PREFIX --force-reinstall $TERMUX_PKG_SRCDIR/dist/$_whl } From 84fbd6ef5ea5643248d4a4815f59c877c075cea9 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 12 Jan 2026 22:02:21 +0530 Subject: [PATCH 27/47] rebuild(main/python-onnxruntime): with python 3.13 --- packages/onnxruntime/build.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/onnxruntime/build.sh b/packages/onnxruntime/build.sh index 599c33bbb6e..b3ead912564 100644 --- a/packages/onnxruntime/build.sh +++ b/packages/onnxruntime/build.sh @@ -3,6 +3,7 @@ TERMUX_PKG_DESCRIPTION="Cross-platform, high performance ML inferencing and trai TERMUX_PKG_LICENSE="MIT" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="1.24.2" +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=git+https://github.com/microsoft/onnxruntime TERMUX_PKG_DEPENDS="abseil-cpp, libc++, protobuf, libre2" TERMUX_PKG_BUILD_IN_SRC=true @@ -63,6 +64,6 @@ termux_step_make_install() { ln -sf libonnxruntime.pc "$TERMUX_PREFIX/lib/pkgconfig/onnxruntime.pc" local _pyver="${TERMUX_PYTHON_VERSION//./}" - local _wheel="onnxruntime-${TERMUX_PKG_VERSION}-cp${_pyver}-cp${_pyver}-linux_${TERMUX_ARCH}.whl" - pip install --force-reinstall --no-deps --prefix="$TERMUX_PREFIX" "$TERMUX_PKG_SRCDIR/dist/${_wheel}" + local _wheel="onnxruntime-${TERMUX_PKG_VERSION}-cp${_pyver}-cp${_pyver}-android_${TERMUX_ARCH}.whl" + pip install --no-deps --prefix="$TERMUX_PREFIX" "$TERMUX_PKG_SRCDIR/dist/${_wheel}" } From 72aa384dfb9e28e418be60dc8b0e9685aea3080b Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 19 Jan 2026 17:52:58 +0530 Subject: [PATCH 28/47] rebuild(main/python-pillow): with python 3.13 --- packages/python-pillow/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python-pillow/build.sh b/packages/python-pillow/build.sh index a17253664d2..e741926a265 100644 --- a/packages/python-pillow/build.sh +++ b/packages/python-pillow/build.sh @@ -3,6 +3,7 @@ TERMUX_PKG_DESCRIPTION="Python Imaging Library" TERMUX_PKG_LICENSE="custom" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="12.1.1" +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=https://github.com/python-pillow/Pillow/archive/refs/tags/${TERMUX_PKG_VERSION}.tar.gz TERMUX_PKG_SHA256=d29fefc0ba637833b59cafc7649e1237186741c31b210178b0a4e9cd9e01ffdf TERMUX_PKG_AUTO_UPDATE=true From 355c1004674ea3f342daf4629de63bf1fa83d652 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Fri, 23 Jan 2026 03:40:25 +0530 Subject: [PATCH 29/47] rebuild(main/python-pycryptodomex): with python 3.13 --- packages/python-pycryptodomex/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python-pycryptodomex/build.sh b/packages/python-pycryptodomex/build.sh index 4aea305005e..b7b1e4e6581 100644 --- a/packages/python-pycryptodomex/build.sh +++ b/packages/python-pycryptodomex/build.sh @@ -4,7 +4,7 @@ TERMUX_PKG_LICENSE="BSD 2-Clause, Public Domain" TERMUX_PKG_LICENSE_FILE="LICENSE.rst" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="3.23.0" -TERMUX_PKG_REVISION=2 +TERMUX_PKG_REVISION=3 TERMUX_PKG_SRCURL="https://github.com/Legrandin/pycryptodome/archive/refs/tags/v${TERMUX_PKG_VERSION}x.tar.gz" TERMUX_PKG_SHA256=d3e12d349f62a8c3bd2e7056e2eea925abcfcdd9e2b07bff091bcc05837ac869 TERMUX_PKG_DEPENDS="python, python-pip" From 1648e600537f61af11659b152d87a3cb152980ed Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 24 Jan 2026 00:25:51 +0530 Subject: [PATCH 30/47] rebuild(main/python-pynvim): with python 3.13 --- packages/python-pynvim/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python-pynvim/build.sh b/packages/python-pynvim/build.sh index e02c949417f..cbf0c0bfaf2 100644 --- a/packages/python-pynvim/build.sh +++ b/packages/python-pynvim/build.sh @@ -3,7 +3,7 @@ TERMUX_PKG_DESCRIPTION="Python client for Neovim" TERMUX_PKG_LICENSE="Apache-2.0" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="0.6.0" -TERMUX_PKG_REVISION=1 +TERMUX_PKG_REVISION=2 TERMUX_PKG_SRCURL=https://github.com/neovim/pynvim/archive/refs/tags/${TERMUX_PKG_VERSION}.tar.gz TERMUX_PKG_SHA256=6649d630ce4a94007fe69a8debb010c7b185057f36d61576d2542c90f3c8db99 TERMUX_PKG_AUTO_UPDATE=true From 715f3191c19461bb28e6039d505491e1e45e0424 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 24 Jan 2026 01:49:17 +0530 Subject: [PATCH 31/47] rebuild(main/python-tflite-runtime): with python 3.13 --- packages/python-tflite-runtime/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/python-tflite-runtime/build.sh b/packages/python-tflite-runtime/build.sh index 9dd00a0ed31..f11ab4fe60d 100644 --- a/packages/python-tflite-runtime/build.sh +++ b/packages/python-tflite-runtime/build.sh @@ -3,7 +3,7 @@ TERMUX_PKG_DESCRIPTION="TensorFlow Lite Python bindings" TERMUX_PKG_LICENSE="Apache-2.0" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="2.20.0" -TERMUX_PKG_REVISION=1 +TERMUX_PKG_REVISION=2 TERMUX_PKG_SRCURL=git+https://github.com/tensorflow/tensorflow TERMUX_PKG_AUTO_UPDATE=true TERMUX_PKG_DEPENDS="python, python-numpy, python-pip" @@ -81,6 +81,6 @@ termux_step_make() { termux_step_make_install() { local _pyver="${TERMUX_PYTHON_VERSION//./}" - local _wheel="tflite_runtime-${TERMUX_PKG_VERSION}-cp${_pyver}-cp${_pyver}-linux_${TERMUX_ARCH}.whl" + local _wheel="tflite_runtime-${TERMUX_PKG_VERSION}-cp${_pyver}-cp${_pyver}-android_${TERMUX_ARCH}.whl" pip install --force-reinstall --no-deps --prefix="$TERMUX_PREFIX" "$TFLITE_BUILD_DIR/dist/${_wheel}" } From 92f0bcc6d6c4fcc92ee866923664a2bedb966ced Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 24 Jan 2026 02:06:01 +0530 Subject: [PATCH 32/47] rebuild(main/python-yt-dlp): with python 3.13 --- packages/python-yt-dlp/build.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/python-yt-dlp/build.sh b/packages/python-yt-dlp/build.sh index f09c11372cc..f0539012516 100644 --- a/packages/python-yt-dlp/build.sh +++ b/packages/python-yt-dlp/build.sh @@ -3,6 +3,7 @@ TERMUX_PKG_DESCRIPTION="A youtube-dl fork with additional features and fixes" TERMUX_PKG_LICENSE="Unlicense" TERMUX_PKG_MAINTAINER="Joshua Kahn " TERMUX_PKG_VERSION="2026.02.21" +TERMUX_PKG_REVISION=1 TERMUX_PKG_SRCURL=https://github.com/yt-dlp/yt-dlp/archive/refs/tags/$TERMUX_PKG_VERSION.tar.gz TERMUX_PKG_SHA256=ecbb3d617049f0d44ecbb94a6fdca601dc6abb97ea19e4cba2a8bcd635ceceda TERMUX_PKG_DEPENDS="libc++, libexpat, openssl, python, python-brotli, python-pip, python-pycryptodomex" From 16c9eebc85379dec6d1ad60d229311ac0abacb22 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 25 Dec 2025 14:07:22 +0530 Subject: [PATCH 33/47] bump(main/python): 3.13.11 patch for configure is no longer needed patch for multiprocessing.c no longer required due to python/cpython@fa1d675309c6a08b0833cf25cffe476c6166aba3 Remove 0006-do-not-use-xattr.patch since we can just add a configure argument and it works just as well We no longer need to manually add libraries for libpython as it is now added by default when building for android. See upstream commit: python/cpython@7f5e3f04f838686d65f1053a5e47f5d3faf0b228 No longer required patch for Lib/aifc.py after python/cpython@036da3bd43aa2593d17d2fb73d4794f9965c577d mailcap module has been removed in Python 3.13. https://docs.python.org/3/whatsnew/3.13.html#whatsnew313-pep594 add post install message when site-packages/ is detected for python3.12 Also mark as unsafe for on-device building. The plan is to move to building python with host-python, this isn't possible on device. I will be looking to get some fixes made in future to get on-device builds as well, but it is not the priority at the moment --- .../python/0001-fix-hardcoded-paths.patch | 23 ------------------- .../python/0005-impl-multiprocessing.patch | 12 ---------- ...rch.patch => 0006-disable-multiarch.patch} | 0 packages/python/0006-do-not-use-xattr.patch | 13 ----------- ...-link.patch => 0007-do-not-use-link.patch} | 0 packages/python/0009-build-with-fPIC.patch | 11 --------- ...dlink.patch => 0010-do-not-hardlink.patch} | 16 +++++++------ .../0010-link-modules-against-libpython.patch | 23 ------------------- packages/python/build.sh | 13 +++++++---- 9 files changed, 17 insertions(+), 94 deletions(-) rename packages/python/{0007-disable-multiarch.patch => 0006-disable-multiarch.patch} (100%) delete mode 100644 packages/python/0006-do-not-use-xattr.patch rename packages/python/{0008-do-not-use-link.patch => 0007-do-not-use-link.patch} (100%) delete mode 100644 packages/python/0009-build-with-fPIC.patch rename packages/python/{0011-do-not-hardlink.patch => 0010-do-not-hardlink.patch} (68%) delete mode 100644 packages/python/0010-link-modules-against-libpython.patch diff --git a/packages/python/0001-fix-hardcoded-paths.patch b/packages/python/0001-fix-hardcoded-paths.patch index 16a7a683194..467e7a7bc97 100644 --- a/packages/python/0001-fix-hardcoded-paths.patch +++ b/packages/python/0001-fix-hardcoded-paths.patch @@ -1,26 +1,3 @@ ---- a/Lib/aifc.py -+++ b/Lib/aifc.py -@@ -920,7 +920,7 @@ - if __name__ == '__main__': - import sys - if not sys.argv[1:]: -- sys.argv.append('/usr/demos/data/audio/bach.aiff') -+ sys.argv.append('@TERMUX_PREFIX@/demos/data/audio/bach.aiff') - fn = sys.argv[1] - with open(fn, 'r') as f: - print("Reading", fn) ---- a/Lib/mailcap.py -+++ b/Lib/mailcap.py -@@ -55,7 +55,8 @@ - # Don't bother with getpwuid() - home = '.' # Last resort - mailcaps = [home + '/.mailcap', '/etc/mailcap', -- '/usr/etc/mailcap', '/usr/local/etc/mailcap'] -+ '/usr/etc/mailcap', '/usr/local/etc/mailcap', -+ '@TERMUX_PREFIX@/etc/mailcap'] - return mailcaps - - --- a/Lib/mimetypes.py +++ b/Lib/mimetypes.py @@ -49,6 +49,7 @@ diff --git a/packages/python/0005-impl-multiprocessing.patch b/packages/python/0005-impl-multiprocessing.patch index a4468610ae4..f1ffe2f270d 100644 --- a/packages/python/0005-impl-multiprocessing.patch +++ b/packages/python/0005-impl-multiprocessing.patch @@ -10,18 +10,6 @@ _dir_candidates = [] ---- a/Modules/_multiprocessing/multiprocessing.c -+++ b/Modules/_multiprocessing/multiprocessing.c -@@ -172,7 +172,7 @@ - _MULTIPROCESSING_RECV_METHODDEF - _MULTIPROCESSING_SEND_METHODDEF - #endif --#if !defined(POSIX_SEMAPHORES_NOT_ENABLED) && !defined(__ANDROID__) -+#if !defined(POSIX_SEMAPHORES_NOT_ENABLED) - _MULTIPROCESSING_SEM_UNLINK_METHODDEF - #endif - {NULL} - --- a/Modules/_multiprocessing/posixshmem.c +++ b/Modules/_multiprocessing/posixshmem.c @@ -11,6 +11,72 @@ diff --git a/packages/python/0007-disable-multiarch.patch b/packages/python/0006-disable-multiarch.patch similarity index 100% rename from packages/python/0007-disable-multiarch.patch rename to packages/python/0006-disable-multiarch.patch diff --git a/packages/python/0006-do-not-use-xattr.patch b/packages/python/0006-do-not-use-xattr.patch deleted file mode 100644 index 1f4996315c4..00000000000 --- a/packages/python/0006-do-not-use-xattr.patch +++ /dev/null @@ -1,13 +0,0 @@ -https://github.com/termux/termux-packages/issues/16879 - ---- a/Modules/posixmodule.c -+++ b/Modules/posixmodule.c -@@ -280,7 +280,7 @@ - # undef HAVE_SCHED_SETAFFINITY - #endif - --#if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) -+#if defined(HAVE_SYS_XATTR_H) && defined(HAVE_LINUX_LIMITS_H) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) && !defined(__ANDROID__) - # define USE_XATTRS - # include // Needed for XATTR_SIZE_MAX on musl libc. - #endif diff --git a/packages/python/0008-do-not-use-link.patch b/packages/python/0007-do-not-use-link.patch similarity index 100% rename from packages/python/0008-do-not-use-link.patch rename to packages/python/0007-do-not-use-link.patch diff --git a/packages/python/0009-build-with-fPIC.patch b/packages/python/0009-build-with-fPIC.patch deleted file mode 100644 index 5fd66f38140..00000000000 --- a/packages/python/0009-build-with-fPIC.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/configure -+++ b/configure -@@ -12838,7 +12840,7 @@ - then CCSHARED="-fPIC"; - else CCSHARED="+z"; - fi;; -- Linux-android*) ;; -+ Linux-android*) CCSHARED="-fPIC";; - Linux*|GNU*) CCSHARED="-fPIC";; - Emscripten*|WASI*) - if test "x$enable_wasm_dynamic_linking" = xyes diff --git a/packages/python/0011-do-not-hardlink.patch b/packages/python/0010-do-not-hardlink.patch similarity index 68% rename from packages/python/0011-do-not-hardlink.patch rename to packages/python/0010-do-not-hardlink.patch index daca89efbcb..4291d12e96a 100644 --- a/packages/python/0011-do-not-hardlink.patch +++ b/packages/python/0010-do-not-hardlink.patch @@ -1,15 +1,17 @@ +diff --git a/Makefile.pre.in b/Makefile.pre.in +index a7dc9709d62..d6b84dc8905 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in -@@ -817,7 +817,7 @@ $(LIBRARY): $(LIBRARY_OBJS) - libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) - if test $(INSTSONAME) != $(LDLIBRARY); then \ - $(BLDSHARED) -Wl,-h$(INSTSONAME) -o $(INSTSONAME) $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ +@@ -917,7 +917,7 @@ libpython$(LDVERSION).so: $(LIBRARY_OBJS) $(DTRACE_OBJS) + $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ + fi + if test $(INSTSONAME) != $@; then \ - $(LN) -f $(INSTSONAME) $@; \ + $(LN) -sf $(INSTSONAME) $@; \ - else \ - $(BLDSHARED) -o $@ $(LIBRARY_OBJS) $(MODLIBS) $(SHLIBS) $(LIBC) $(LIBM); \ fi -@@ -1971,7 +1971,7 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ + + libpython3.so: libpython$(LDVERSION).so +@@ -2202,7 +2202,7 @@ altbininstall: $(BUILDPYTHON) @FRAMEWORKPYTHONW@ if test -f $(DESTDIR)$(BINDIR)/python$(VERSION)$(EXE) -o -h $(DESTDIR)$(BINDIR)/python$(VERSION)$(EXE); \ then rm -f $(DESTDIR)$(BINDIR)/python$(VERSION)$(EXE); \ fi; \ diff --git a/packages/python/0010-link-modules-against-libpython.patch b/packages/python/0010-link-modules-against-libpython.patch deleted file mode 100644 index 541eb1ea0c0..00000000000 --- a/packages/python/0010-link-modules-against-libpython.patch +++ /dev/null @@ -1,23 +0,0 @@ -Borrowed from https://github.com/msys2-contrib/cpython-mingw/commit/c4c698ec8fd7413068fce2de966938b3f8462de8 - ---- a/Modules/makesetup -+++ b/Modules/makesetup -@@ -97,6 +97,9 @@ - ExtraLibDir='$(LIBPL)' - fi - ExtraLibs="-L$ExtraLibDir -lpython\$(LDVERSION)";; -+Linux*) -+ ExtraLibs='$(BLDLIBRARY)' -+ ExtraLibDepends='$(LIBRARY_DEPS)';; - esac - - # Main loop -@@ -285,7 +288,7 @@ - BUILT_SHARED="$BUILT_SHARED $mod" - ;; - esac -- rule="$file: $objs" -+ rule="$file: $objs $ExtraLibDepends" - rule="$rule; \$(BLDSHARED) $objs $libs $ExtraLibs -o $file" - echo "$rule" >>$rulesf - done diff --git a/packages/python/build.sh b/packages/python/build.sh index 43ccec77d78..331ff9bf009 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -4,15 +4,14 @@ TERMUX_PKG_DESCRIPTION="Python 3 programming language intended to enable clear p TERMUX_PKG_LICENSE="custom" TERMUX_PKG_LICENSE_FILE="LICENSE" TERMUX_PKG_MAINTAINER="@termux" -TERMUX_PKG_VERSION=3.12.12 -TERMUX_PKG_REVISION=1 +TERMUX_PKG_VERSION="3.13.11" _DEBPYTHON_COMMIT=f358ab52bf2932ad55b1a72a29c9762169e6ac47 TERMUX_PKG_SRCURL=( https://www.python.org/ftp/python/${TERMUX_PKG_VERSION}/Python-${TERMUX_PKG_VERSION}.tar.xz https://salsa.debian.org/cpython-team/python3-defaults/-/archive/${_DEBPYTHON_COMMIT}/python3-defaults-${_DEBPYTHON_COMMIT}.tar.gz ) TERMUX_PKG_SHA256=( - fb85a13414b028c49ba18bbd523c2d055a30b56b18b92ce454ea2c51edc656c4 + 16ede7bb7cdbfa895d11b0642fa0e523f291e6487194d53cf6d3b338c3a17ea2 3b7a76c144d39f5c4a2c7789fd4beb3266980c2e667ad36167e1e7a357c684b0 ) TERMUX_PKG_AUTO_UPDATE=false @@ -24,6 +23,8 @@ TERMUX_PKG_BREAKS="python2 (<= 2.7.15), python-dev" TERMUX_PKG_REPLACES="python-dev" # Let "python3" will be alias to this package. TERMUX_PKG_PROVIDES="python3" +# Python build is a 2-step process. Requiring host build and cross build +TERMUX_PKG_ON_DEVICE_BUILD_NOT_SUPPORTED=true # https://github.com/termux/termux-packages/issues/15908 TERMUX_PKG_MAKE_PROCESSES=1 @@ -58,6 +59,8 @@ TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_func_shm_unlink=yes" TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_working_tzset=yes" # prevents 'configure: error: Cross compiling requires --with-build-python' (even during on-device build) TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" --with-build-python=python$_MAJOR_VERSION" +# https://github.com/termux/termux-packages/issues/16879 +TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_header_sys_xattr_h=no" TERMUX_PKG_RM_AFTER_INSTALL=" lib/python${_MAJOR_VERSION}/test @@ -165,9 +168,9 @@ termux_step_create_debscripts() { echo fi - if [ -d $TERMUX_PREFIX/lib/python3.11/site-packages ]; then + if [[ -d $TERMUX_PREFIX/lib/python3.11/site-packages || -d $TERMUX_PREFIX/lib/python3.12/site-packages ]]; then echo - echo "NOTE: The system python package has been updated to 3.12." + echo "NOTE: The system python package has been updated to 3.13." echo "NOTE: Run 'pkg upgrade' to update system python packages." echo "NOTE: Packages installed using pip needs to be re-installed." echo From 265e88d32929f524c2e804f880508a25f5f1de56 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 10 Jan 2026 03:02:32 +0530 Subject: [PATCH 34/47] fix(main/python): unexpanded autotools variable in pkgconfig --- packages/python/0006-disable-multiarch.patch | 23 ++++++++++--------- ...-fix-pkgconfig-variable-substitution.patch | 16 +++++++++++++ packages/python/build.sh | 1 + 3 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 packages/python/0008-fix-pkgconfig-variable-substitution.patch diff --git a/packages/python/0006-disable-multiarch.patch b/packages/python/0006-disable-multiarch.patch index 79c1c787135..7a429da96fc 100644 --- a/packages/python/0006-disable-multiarch.patch +++ b/packages/python/0006-disable-multiarch.patch @@ -1,11 +1,12 @@ ---- a/configure -+++ b/configure -@@ -6939,6 +6939,8 @@ - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for multiarch" >&5 - printf %s "checking for multiarch... " >&6; } - case $ac_sys_system in #( -+ Linux-android) : -+ MULTIARCH="" ;; #( - Darwin*) : - MULTIARCH="" ;; #( - FreeBSD*) : +diff --git a/configure.ac b/configure.ac +index 597a44b331a..fa78e09c192 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1134,6 +1134,7 @@ dnl architecture. PLATFORM_TRIPLET will be a pair or single value for these + dnl platforms. + AC_MSG_CHECKING([for multiarch]) + AS_CASE([$ac_sys_system], ++ [Linux-android], [MULTIARCH=""], + [Darwin*], [MULTIARCH=""], + [iOS], [MULTIARCH=""], + [FreeBSD*], [MULTIARCH=""], diff --git a/packages/python/0008-fix-pkgconfig-variable-substitution.patch b/packages/python/0008-fix-pkgconfig-variable-substitution.patch new file mode 100644 index 00000000000..70c08ec7471 --- /dev/null +++ b/packages/python/0008-fix-pkgconfig-variable-substitution.patch @@ -0,0 +1,16 @@ +Needed for proper substitution of @LIBPYTHON@ in python3.pc.in + +The former style of substitution is fine in Makefiles where variables are expanded, not in static generated files. +diff --git a/configure.ac b/configure.ac +index 043ec957f40..53bed63310e 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -6424,7 +6424,7 @@ LIBPYTHON='' + # On Android and Cygwin the shared libraries must be linked with libpython. + if test "$PY_ENABLE_SHARED" = "1" && ( test -n "$ANDROID_API_LEVEL" || test "$MACHDEP" = "cygwin"); then + MODULE_DEPS_SHARED="$MODULE_DEPS_SHARED \$(LDLIBRARY)" +- LIBPYTHON="\$(BLDLIBRARY)" ++ LIBPYTHON="$(BLDLIBRARY)" + fi + + # On iOS the shared libraries must be linked with the Python framework diff --git a/packages/python/build.sh b/packages/python/build.sh index 331ff9bf009..67a6a5c8a1b 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -117,6 +117,7 @@ termux_step_pre_configure() { sed -i -e "s|@TERMUX_PYTHON_VERSION@|${_MAJOR_VERSION}|g" \ -e "s|@TERMUX_PKG_FULLVERSION@|$(test ${TERMUX_PACKAGE_FORMAT} = pacman && echo ${TERMUX_PKG_FULLVERSION_FOR_PACMAN} || echo ${TERMUX_PKG_FULLVERSION})|g" \ $(find "$TERMUX_PKG_SRCDIR/debpython" -type f) + autoreconf -fi } termux_step_post_make_install() { From c1d7694dab61d030bf529b88b639c51fe8745124 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Tue, 13 Jan 2026 03:16:06 +0530 Subject: [PATCH 35/47] fix(main/python): build without -Wl,--as-needed Causes the built libpython3.so to not include symbols which we need in the final binary --- packages/python/build.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/python/build.sh b/packages/python/build.sh index 67a6a5c8a1b..9722bac3db6 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -82,6 +82,8 @@ termux_step_pre_configure() { # if extension modules should be built (specifically, the # zlib extension module is not built without this): CPPFLAGS+=" -I$TERMUX_STANDALONE_TOOLCHAIN/sysroot/usr/include" + # Without this all symbols are removed from the built libpython3.so + LDFLAGS="${LDFLAGS/-Wl,--as-needed/}" LDFLAGS+=" -L$TERMUX_STANDALONE_TOOLCHAIN/sysroot/usr/lib" if [ $TERMUX_ARCH = x86_64 ]; then LDFLAGS+=64; fi From 2ea71f859c1593d6e69156f4857fcea750f5a87e Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Thu, 25 Dec 2025 14:08:37 +0530 Subject: [PATCH 36/47] chore(main/python): build with parallelism [[TESTING REQUIRED]] maybe the issue with the build system is fixed now? --- packages/python/build.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/python/build.sh b/packages/python/build.sh index 9722bac3db6..0a6c68698b8 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -26,9 +26,6 @@ TERMUX_PKG_PROVIDES="python3" # Python build is a 2-step process. Requiring host build and cross build TERMUX_PKG_ON_DEVICE_BUILD_NOT_SUPPORTED=true -# https://github.com/termux/termux-packages/issues/15908 -TERMUX_PKG_MAKE_PROCESSES=1 - _MAJOR_VERSION="${TERMUX_PKG_VERSION%.*}" # Set ac_cv_func_wcsftime=no to avoid errors such as "character U+ca0025 is not in range [U+0000; U+10ffff]" From b75b1263006de62b72ca4fe01ae0c48cb9e54912 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Fri, 26 Dec 2025 10:21:45 +0530 Subject: [PATCH 37/47] scripts(termux_setup_build_python): build-python for cross compilation We need build python for cross compilation as Ubuntu's version of python is different. Also crossenv recommends building own python revisions instead of distribution compiled ones added lld to setup-ubuntu as it's needed by clang driver when using -fuse-ld=lld. Not sure how we went this far without having to deal with this during host builds Also we need libpython3.so for cross-compilation of some of the pip packages. So build with --enable-shared --- build-package.sh | 4 + packages/python/build.sh | 1 + .../build/setup/termux_setup_build_python.sh | 82 +++++++++++++++++++ .../build/setup/termux_setup_python_pip.sh | 2 +- scripts/setup-ubuntu.sh | 1 + 5 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 scripts/build/setup/termux_setup_build_python.sh diff --git a/build-package.sh b/build-package.sh index d96694a0aec..c234c26e6bf 100755 --- a/build-package.sh +++ b/build-package.sh @@ -149,6 +149,10 @@ source "$TERMUX_SCRIPTDIR/scripts/build/setup/termux_setup_ldc.sh" # shellcheck source=scripts/build/setup/termux_setup_no_integrated_as.sh source "$TERMUX_SCRIPTDIR/scripts/build/setup/termux_setup_no_integrated_as.sh" +# Utility function for setting up build-python for cross-compilation of Python and crossenv +# shellcheck source=scripts/build/setup/termux_setup_build_python.sh +source "$TERMUX_SCRIPTDIR/scripts/build/setup/termux_setup_build_python.sh" + # Utility function for python packages to setup a python. # shellcheck source=scripts/build/setup/termux_setup_python_pip.sh source "$TERMUX_SCRIPTDIR/scripts/build/setup/termux_setup_python_pip.sh" diff --git a/packages/python/build.sh b/packages/python/build.sh index 0a6c68698b8..dd1d6213af5 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -71,6 +71,7 @@ termux_step_post_get_source() { } termux_step_pre_configure() { + termux_setup_build_python # -O3 gains some additional performance on at least aarch64. CFLAGS="${CFLAGS/-Oz/-O3}" diff --git a/scripts/build/setup/termux_setup_build_python.sh b/scripts/build/setup/termux_setup_build_python.sh new file mode 100644 index 00000000000..27a13a9a204 --- /dev/null +++ b/scripts/build/setup/termux_setup_build_python.sh @@ -0,0 +1,82 @@ +# This script adds it's own python build to $PATH which overrides the Ubuntu's +# packaged version. Packages installed using apt on Ubuntu won't work. This +# python build is supposed to be used only for cross-compilation of pip +# packages. +# +# Before using this script manually anywhere it is highly recommended to read +# https://crossenv.readthedocs.io/en/latest/quickstart.html +# For cross compilation of python packages as well as python, a host build of +# python of the same version is required. For pip package cross compilation, +# ideally same version of python is recommended by crossenv. +termux_setup_build_python() { + if [ "$TERMUX_ON_DEVICE_BUILD" = "true" ]; then + if [[ "$TERMUX_APP_PACKAGE_MANAGER" = "apt" && "$(dpkg-query -W -f '${db:Status-Status}\n' python 2>/dev/null)" != "installed" ]] || + [[ "$TERMUX_APP_PACKAGE_MANAGER" = "pacman" && ! "$(pacman -Q python 2>/dev/null)" ]]; then + echo "Package 'python is not installed." + echo "You can install it with" + echo + echo " pkg install python" + echo + echo " pacman -S python" + echo + echo "Note that package 'python' is known to be problematic for building on device." + exit 1 + fi + else + local _PYTHON_VERSION + local _PYTHON_SRCURL + local _PYTHON_SHA256 + local _PYTHON_FOLDER + _PYTHON_VERSION="$(. "$TERMUX_SCRIPTDIR/packages/python/build.sh"; echo "$TERMUX_PKG_VERSION")" + _PYTHON_SRCURL="$(. "$TERMUX_SCRIPTDIR/packages/python/build.sh"; echo "$TERMUX_PKG_SRCURL")" + _PYTHON_SHA256="$(. "$TERMUX_SCRIPTDIR/packages/python/build.sh"; echo "$TERMUX_PKG_SHA256")" + if [[ "${TERMUX_PACKAGES_OFFLINE-false}" = "true" ]]; then + _PYTHON_FOLDER=${TERMUX_SCRIPTDIR}/build-tools/python-${_PYTHON_VERSION} + else + _PYTHON_FOLDER=${TERMUX_COMMON_CACHEDIR}/python-${_PYTHON_VERSION} + fi + export TERMUX_BUILD_PYTHON_DIR=$_PYTHON_FOLDER + + if [[ ! -d "$_PYTHON_FOLDER" ]]; then + local LAST_PWD="$(pwd)" + termux_download \ + "$_PYTHON_SRCURL" "python-$_PYTHON_VERSION.tar.xz" "$_PYTHON_SHA256" + mkdir "$_PYTHON_FOLDER" + tar \ + --extract \ + --strip-components=1 \ + -C "$_PYTHON_FOLDER" \ + -f "python-$_PYTHON_VERSION.tar.xz" + cd "$_PYTHON_FOLDER" + + # Perform a hostbuild of python. We are kind of doing a minimal build, which + # may break some stuff that rely on an extended python release + mkdir host-build/ + cd host-build/ + # We are using env -i as there are a lot of environment variable that need + # to be unset, so better just start from scratch + # Also whoever on crack wrote the build scripts for python, didn't think of + # supporting the standard LD environment variable or even LDFLAGS properly. + # So instead of using LDFLAGS we have to pass linker arguments to CC and CXX + # and hope that Clang C and C++ drivers keep on ignoring link flags. It is + # not at all possible to specify a separate linker without patches as it is + # hardcoded to "$(CC) -shared" and "$(CXX) -shared" + # Whoever that person is needs to stop writing build scripts and instead + # question his impact on his mere existence on the world + env -i \ + CC="clang-${TERMUX_HOST_LLVM_MAJOR_VERSION} -fuse-ld=lld" \ + CXX="clang++-${TERMUX_HOST_LLVM_MAJOR_VERSION} -fuse-ld=lld" \ + LDFLAGS="-Wl,-rpath=$_PYTHON_FOLDER/host-build-prefix/lib" \ + PATH="/usr/bin" \ + ../configure \ + --with-ensurepip=install \ + --enable-shared \ + --prefix="$_PYTHON_FOLDER/host-build-prefix" + env -i \ + make -j "$(nproc)" install + cd "$LAST_PWD" + fi + # Add our own built python to path + export PATH="$_PYTHON_FOLDER/host-build-prefix/bin:$PATH" + fi +} diff --git a/scripts/build/setup/termux_setup_python_pip.sh b/scripts/build/setup/termux_setup_python_pip.sh index a2b83511c02..3040485e868 100644 --- a/scripts/build/setup/termux_setup_python_pip.sh +++ b/scripts/build/setup/termux_setup_python_pip.sh @@ -54,7 +54,7 @@ termux_setup_python_pip() { if [ ! -d "$TERMUX_PYTHON_CROSSENV_PREFIX" ]; then cd "$TERMUX_PYTHON_CROSSENV_SRCDIR" - /usr/bin/python${TERMUX_PYTHON_VERSION} -m crossenv \ + "$TERMUX_BUILD_PYTHON_DIR/host-build-prefix/bin/python${TERMUX_PYTHON_VERSION}" -m crossenv \ "$TERMUX_PREFIX/bin/python${TERMUX_PYTHON_VERSION}" \ "${TERMUX_PYTHON_CROSSENV_PREFIX}" fi diff --git a/scripts/setup-ubuntu.sh b/scripts/setup-ubuntu.sh index b283dcf641f..024a3930e99 100755 --- a/scripts/setup-ubuntu.sh +++ b/scripts/setup-ubuntu.sh @@ -348,6 +348,7 @@ LLVM_PACKAGES="" LLVM_PACKAGES+=" llvm-${TERMUX_HOST_LLVM_MAJOR_VERSION}-dev" LLVM_PACKAGES+=" llvm-${TERMUX_HOST_LLVM_MAJOR_VERSION}-tools" LLVM_PACKAGES+=" clang-${TERMUX_HOST_LLVM_MAJOR_VERSION}" +LLVM_PACKAGES+=" lld-${TERMUX_HOST_LLVM_MAJOR_VERSION}" $SUDO apt-get -yq update From a89d75ba46607ae523bb047192b47ca80c2c3e5b Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 29 Dec 2025 00:57:38 +0530 Subject: [PATCH 38/47] chore(main/python): take up maintainership --- packages/python/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/python/build.sh b/packages/python/build.sh index dd1d6213af5..d41be1f0155 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -3,7 +3,7 @@ TERMUX_PKG_DESCRIPTION="Python 3 programming language intended to enable clear p # License: PSF-2.0 TERMUX_PKG_LICENSE="custom" TERMUX_PKG_LICENSE_FILE="LICENSE" -TERMUX_PKG_MAINTAINER="@termux" +TERMUX_PKG_MAINTAINER="Yaksh Bariya " TERMUX_PKG_VERSION="3.13.11" _DEBPYTHON_COMMIT=f358ab52bf2932ad55b1a72a29c9762169e6ac47 TERMUX_PKG_SRCURL=( From 6c5fb503e8e5433fd9a2ca4fedd0e747bbad485f Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 11 Jan 2026 00:51:04 +0530 Subject: [PATCH 39/47] fix(main/python): ctypes.util.find_library on android Relevant upstream commit https://github.com/python/cpython/commit/1f8b24ef69896680d6ba6005e75e1cc79a744f9e Also apply this same fix in host build of python as cross-python uses it's own cutils instead of what we have built for target in our cross builds. --- .../0009-fix-ctypes-util-find_library.patch | 22 +++++++++++++++++++ .../build/setup/termux_setup_build_python.sh | 5 +++++ 2 files changed, 27 insertions(+) create mode 100644 packages/python/0009-fix-ctypes-util-find_library.patch diff --git a/packages/python/0009-fix-ctypes-util-find_library.patch b/packages/python/0009-fix-ctypes-util-find_library.patch new file mode 100644 index 00000000000..a432320bd91 --- /dev/null +++ b/packages/python/0009-fix-ctypes-util-find_library.patch @@ -0,0 +1,22 @@ +This was introduced in https://github.com/python/cpython/commit/1f8b24ef69896680d6ba6005e75e1cc79a744f9e but breaks our builds as we need directories from other paths as well + +diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py +index 117bf06cb01..12d7428fe9a 100644 +--- a/Lib/ctypes/util.py ++++ b/Lib/ctypes/util.py +@@ -89,15 +89,6 @@ def find_library(name): + + from ctypes._aix import find_library + +-elif sys.platform == "android": +- def find_library(name): +- directory = "/system/lib" +- if "64" in os.uname().machine: +- directory += "64" +- +- fname = f"{directory}/lib{name}.so" +- return fname if os.path.isfile(fname) else None +- + elif os.name == "posix": + # Andreas Degert's find functions, using gcc, /sbin/ldconfig, objdump + import re, tempfile diff --git a/scripts/build/setup/termux_setup_build_python.sh b/scripts/build/setup/termux_setup_build_python.sh index 27a13a9a204..b78d92a0294 100644 --- a/scripts/build/setup/termux_setup_build_python.sh +++ b/scripts/build/setup/termux_setup_build_python.sh @@ -49,6 +49,11 @@ termux_setup_build_python() { -f "python-$_PYTHON_VERSION.tar.xz" cd "$_PYTHON_FOLDER" + for f in "$TERMUX_SCRIPTDIR"/packages/python/0009-fix-ctypes-util-find_library.patch; do + echo "[${FUNCNAME[0]}]: Applying $(basename "$f")" + cat "$f" | sed -e "s|@@TERMUX_PKG_API_LEVEL@@|${TERMUX_PKG_API_LEVEL}|g" | patch --silent -p1 + done + # Perform a hostbuild of python. We are kind of doing a minimal build, which # may break some stuff that rely on an extended python release mkdir host-build/ From 19802b4ed5267ae6f92fff801bc76644dec0ae28 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Tue, 20 Jan 2026 03:14:05 +0530 Subject: [PATCH 40/47] fix(main/python): module linking to libpython3.so --- packages/python/0011-fix-module-linking.patch | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 packages/python/0011-fix-module-linking.patch diff --git a/packages/python/0011-fix-module-linking.patch b/packages/python/0011-fix-module-linking.patch new file mode 100644 index 00000000000..ba23f4ac9e3 --- /dev/null +++ b/packages/python/0011-fix-module-linking.patch @@ -0,0 +1,14 @@ +Based off https://github.com/msys2-contrib/cpython-mingw/commit/e719663e07d6655e042f9c48910c65606cc4ae5e +diff --git a/Modules/makesetup b/Modules/makesetup +index 8bb971b152a..e6cf14460aa 100755 +--- a/Modules/makesetup ++++ b/Modules/makesetup +@@ -274,7 +274,7 @@ sed -e 's/[ ]*#.*//' -e '/^[ ]*$/d' | + ;; + esac + rule="$file: $objs" +- rule="$rule; \$(BLDSHARED) $objs $libs \$(LIBPYTHON) -o $file" ++ rule="$rule; \$(BLDSHARED) $objs $libs \$(BLDLIBRARY) -o $file" + echo "$rule" >>$rulesf + done + done From 17093501e1fe51639fdd5cbd8c3f619945b762e6 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 25 Jan 2026 18:21:02 +0530 Subject: [PATCH 41/47] fix(main/python): hardcode android api level than detecting at runtime [[TODO: add more descriptive commit message]] --- .../python/0012-hardcode-android-api-level.diff | 13 +++++++++++++ packages/python/build.sh | 7 +++++++ 2 files changed, 20 insertions(+) create mode 100644 packages/python/0012-hardcode-android-api-level.diff diff --git a/packages/python/0012-hardcode-android-api-level.diff b/packages/python/0012-hardcode-android-api-level.diff new file mode 100644 index 00000000000..4c3625248fe --- /dev/null +++ b/packages/python/0012-hardcode-android-api-level.diff @@ -0,0 +1,13 @@ +diff --git a/Lib/platform.py b/Lib/platform.py +index 8895177e326..e02999739bf 100755 +--- a/Lib/platform.py ++++ b/Lib/platform.py +@@ -599,7 +599,7 @@ def getprop(name, default): + return buffer.value.decode("UTF-8", "backslashreplace") + + release = getprop("ro.build.version.release", release) +- api_level = int(getprop("ro.build.version.sdk", api_level)) ++ api_level = @TERMUX_PKG_API_LEVEL@ + manufacturer = getprop("ro.product.manufacturer", manufacturer) + model = getprop("ro.product.model", model) + device = getprop("ro.product.device", device) diff --git a/packages/python/build.sh b/packages/python/build.sh index d41be1f0155..a189ff28bdb 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -67,6 +67,13 @@ lib/python${_MAJOR_VERSION}/site-packages/*/ " termux_step_post_get_source() { + patch="$TERMUX_PKG_BUILDER_DIR/0012-hardcode-android-api-level.diff" + echo "Applying patch: $(basename "$patch")" + test -f "$patch" && sed \ + -e "s%\@TERMUX_PKG_API_LEVEL\@%${TERMUX_PKG_API_LEVEL}%g" \ + "$patch" | patch --silent -p1 + + mv "$TERMUX_PKG_SRCDIR/python3-defaults-$_DEBPYTHON_COMMIT" "$TERMUX_PKG_SRCDIR/debpython" } From 639ee0e5911b0d05bb0875bff2dda07b1554e547 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Fri, 13 Feb 2026 11:08:45 +0530 Subject: [PATCH 42/47] bump(main/python): 3.13.12 --- packages/python/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/python/build.sh b/packages/python/build.sh index a189ff28bdb..d1866420a5e 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -4,14 +4,14 @@ TERMUX_PKG_DESCRIPTION="Python 3 programming language intended to enable clear p TERMUX_PKG_LICENSE="custom" TERMUX_PKG_LICENSE_FILE="LICENSE" TERMUX_PKG_MAINTAINER="Yaksh Bariya " -TERMUX_PKG_VERSION="3.13.11" +TERMUX_PKG_VERSION="3.13.12" _DEBPYTHON_COMMIT=f358ab52bf2932ad55b1a72a29c9762169e6ac47 TERMUX_PKG_SRCURL=( https://www.python.org/ftp/python/${TERMUX_PKG_VERSION}/Python-${TERMUX_PKG_VERSION}.tar.xz https://salsa.debian.org/cpython-team/python3-defaults/-/archive/${_DEBPYTHON_COMMIT}/python3-defaults-${_DEBPYTHON_COMMIT}.tar.gz ) TERMUX_PKG_SHA256=( - 16ede7bb7cdbfa895d11b0642fa0e523f291e6487194d53cf6d3b338c3a17ea2 + 2a84cd31dd8d8ea8aaff75de66fc1b4b0127dd5799aa50a64ae9a313885b4593 3b7a76c144d39f5c4a2c7789fd4beb3266980c2e667ad36167e1e7a357c684b0 ) TERMUX_PKG_AUTO_UPDATE=false From 4794e98ce35a87978d6b5e7d6d02455c89529bf9 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Fri, 13 Feb 2026 23:08:20 +0530 Subject: [PATCH 43/47] chore(main/python): allow on-device builds ac_cv_func_getlogin_r=no is needed to ensure that it doesn't try to use getlogin_r on Android during on-device builds. Although I have enabled on-device builds, I don't see any point in keeping this as the same version of python needs to be available for building the same version of python. Pretty much defeats the purpose unless you are rebuilding with some changes for the same minor version --- packages/python/build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/python/build.sh b/packages/python/build.sh index d1866420a5e..16d1d0c2c1a 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -23,8 +23,6 @@ TERMUX_PKG_BREAKS="python2 (<= 2.7.15), python-dev" TERMUX_PKG_REPLACES="python-dev" # Let "python3" will be alias to this package. TERMUX_PKG_PROVIDES="python3" -# Python build is a 2-step process. Requiring host build and cross build -TERMUX_PKG_ON_DEVICE_BUILD_NOT_SUPPORTED=true _MAJOR_VERSION="${TERMUX_PKG_VERSION%.*}" @@ -58,6 +56,8 @@ TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_working_tzset=yes" TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" --with-build-python=python$_MAJOR_VERSION" # https://github.com/termux/termux-packages/issues/16879 TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_header_sys_xattr_h=no" +# Prevent autotools from detecting functions which we don't have during on-device builds +TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_func_getlogin_r=no" TERMUX_PKG_RM_AFTER_INSTALL=" lib/python${_MAJOR_VERSION}/test From 09807c11b10ee7872d2aafc17d13ef873e66e8fa Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sun, 1 Mar 2026 15:56:44 +0530 Subject: [PATCH 44/47] chore(main/python): only disable getlogin_r for API level < 28 --- packages/python/build.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/python/build.sh b/packages/python/build.sh index 16d1d0c2c1a..ddda88b4618 100644 --- a/packages/python/build.sh +++ b/packages/python/build.sh @@ -56,8 +56,6 @@ TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_working_tzset=yes" TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" --with-build-python=python$_MAJOR_VERSION" # https://github.com/termux/termux-packages/issues/16879 TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_header_sys_xattr_h=no" -# Prevent autotools from detecting functions which we don't have during on-device builds -TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_func_getlogin_r=no" TERMUX_PKG_RM_AFTER_INSTALL=" lib/python${_MAJOR_VERSION}/test @@ -96,6 +94,7 @@ termux_step_pre_configure() { # on devices that have API levels newer than $TERMUX_PKG_API_LEVEL if [[ "$TERMUX_PKG_API_LEVEL" -lt 28 ]]; then TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_func_fexecve=no" + TERMUX_PKG_EXTRA_CONFIGURE_ARGS+=" ac_cv_func_getlogin_r=no" fi if [[ "$TERMUX_PKG_API_LEVEL" -lt 29 ]]; then From 6cc94bb18102e9fca82fbbb6d5078402c3cbf0d9 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 27 Dec 2025 01:21:36 +0530 Subject: [PATCH 45/47] rebuild(main/python-pip): with python3.13 --- packages/python-pip/android-api-abi.patch | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 packages/python-pip/android-api-abi.patch diff --git a/packages/python-pip/android-api-abi.patch b/packages/python-pip/android-api-abi.patch new file mode 100644 index 00000000000..9d82927a735 --- /dev/null +++ b/packages/python-pip/android-api-abi.patch @@ -0,0 +1,14 @@ +Allow installing without specifying abi version. Needed for crossenv specifically. +May not be needed for on-device, but let's just keep it just in case in case there is some psycho who is doing this already +diff --git a/src/pip/_vendor/packaging/tags.py b/src/pip/_vendor/packaging/tags.py +index 8522f59c4..ef55342fb 100644 +--- a/src/pip/_vendor/packaging/tags.py ++++ b/src/pip/_vendor/packaging/tags.py +@@ -563,6 +563,7 @@ def android_platforms( + # without major patching. Yield every API level from the maximum down to the + # minimum, inclusive. + min_api_level = 16 ++ yield f"android_{abi}" + for ver in range(api_level, min_api_level - 1, -1): + yield f"android_{ver}_{abi}" + From a40821a09c6d9b51e0c6b97e34132103898d267b Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Sat, 27 Dec 2025 00:36:21 +0530 Subject: [PATCH 46/47] rebuild(main/xcb-proto): with python3.13 --- packages/xcb-proto/build.sh | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/xcb-proto/build.sh b/packages/xcb-proto/build.sh index 26d6f6089b7..467d31de775 100644 --- a/packages/xcb-proto/build.sh +++ b/packages/xcb-proto/build.sh @@ -4,7 +4,7 @@ TERMUX_PKG_DESCRIPTION="XML-XCB protocol descriptions" TERMUX_PKG_LICENSE="MIT" TERMUX_PKG_MAINTAINER="@termux" TERMUX_PKG_VERSION="1.17.0" -TERMUX_PKG_REVISION=1 +TERMUX_PKG_REVISION=2 TERMUX_PKG_SRCURL=https://xorg.freedesktop.org/archive/individual/proto/xcb-proto-$TERMUX_PKG_VERSION.tar.xz TERMUX_PKG_SHA256=2c1bacd2110f4799f74de6ebb714b94cf6f80fb112316b1219480fd22562148c TERMUX_PKG_AUTO_UPDATE=true @@ -12,6 +12,13 @@ TERMUX_PKG_PLATFORM_INDEPENDENT=true TERMUX_PKG_CONFLICTS="xcbproto" TERMUX_PKG_REPLACES="xcbproto" TERMUX_PKG_EXTRA_CONFIGURE_ARGS=" -PYTHON=python${TERMUX_PYTHON_VERSION} +PYTHON=/usr/bin/python3 am_cv_python_pythondir=$TERMUX_PYTHON_HOME/site-packages " + +termux_step_post_make_install() { + # We are using Ubuntu's host python for installing the package which may be of + # different major version. Python bytecode isn't compatible across versions. + # So get rid of it + rm -r "$TERMUX_PREFIX/lib/python3.13/site-packages/xcbgen/__pycache__/" +} From 97e04b63edb987dedb0e932db7095f3f2a8f4f68 Mon Sep 17 00:00:00 2001 From: Yaksh Bariya Date: Mon, 2 Mar 2026 04:45:15 +0530 Subject: [PATCH 47/47] Python 3.13 rebuild 5 %ci:free-disk