2022-02-08 03:55:39 +01:00
#!/usr/bin/env bash
# shellcheck disable=SC2039,SC2059
# Title: build-bootstrap.sh
# Description: A script to build bootstrap archives for the termux-app
# from local package sources instead of debs published in
# apt repo like done by generate-bootstrap.sh. It allows
# bootstrap archives to be easily built for (forked) termux
# apps without having to publish an apt repo first.
# Usage: run "build-bootstrap.sh --help"
version = 0.1.0
set -e
. $( dirname " $( realpath " $0 " ) " ) /properties.sh
BOOTSTRAP_TMPDIR = $( mktemp -d " ${ TMPDIR :- /tmp } /bootstrap-tmp.XXXXXXXX " )
# By default, bootstrap archives are compatible with Android >=7.0
# and <10.
BOOTSTRAP_ANDROID10_COMPATIBLE = false
# By default, bootstrap archives will be built for all architectures
# supported by Termux application.
# Override with option '--architectures'.
TERMUX_DEFAULT_ARCHITECTURES = ( "aarch64" "arm" "i686" "x86_64" )
TERMUX_ARCHITECTURES = ( " ${ TERMUX_DEFAULT_ARCHITECTURES [@] } " )
TERMUX_PACKAGES_DIRECTORY = "/home/builder/termux-packages"
TERMUX_BUILT_DEBS_DIRECTORY = " $TERMUX_PACKAGES_DIRECTORY /output "
TERMUX_BUILT_PACKAGES_DIRECTORY = "/data/data/.built-packages"
IGNORE_BUILD_SCRIPT_NOT_FOUND_ERROR = 1
FORCE_BUILD_PACKAGES = 0
# A list of packages to build
declare -a PACKAGES = ( )
# A list of non-essential packages to build.
# By default it is empty, but can be filled with option '--add'.
declare -a ADDITIONAL_PACKAGES = ( )
# A list of already extracted packages
declare -a EXTRACTED_PACKAGES = ( )
# A list of options to pass to build-package.sh
declare -a BUILD_PACKAGE_OPTIONS = ( )
# Check for some important utilities that may not be available for
# some reason.
for cmd in ar awk curl grep gzip find sed tar xargs xz zip; do
if [ -z " $( command -v $cmd ) " ] ; then
echo " [!] Utility ' $cmd ' is not available in PATH. "
exit 1
fi
done
# Build deb files for package and its dependencies deb from source for arch
build_package( ) {
local return_value
local package_arch = " $1 "
local package_name = " $2 "
local build_output
# Build package from source
# stderr will be redirected to stdout and both will be captured into variable and printed on screen
cd " $TERMUX_PACKAGES_DIRECTORY "
echo $'\n\n\n' " [*] Building ' $package_name '... "
exec 99>& 1
build_output = " $( " $TERMUX_PACKAGES_DIRECTORY " /build-package.sh " ${ BUILD_PACKAGE_OPTIONS [@] } " -a " $package_arch " " $package_name " 2>& 1 | tee >( cat - >& 99) ; exit ${ PIPESTATUS [0] } ) " ;
return_value = $?
echo " [*] Building ' $package_name ' exited with exit code $return_value "
exec 99>& -
if [ $return_value -ne 0 ] ; then
echo " Failed to build package ' $package_name ' for arch ' $package_arch ' " 1>& 2
# Dependency packages may not have a build.sh, so we ignore the error.
# A better way should be implemented to validate if its actually a dependency
# and not a required package itself, by removing dependencies from PACKAGES array.
if [ [ $IGNORE_BUILD_SCRIPT_NOT_FOUND_ERROR = = "1" ] ] && [ [ " $build_output " = = *"No build.sh script at package dir" * ] ] ; then
echo "Ignoring error 'No build.sh script at package dir'" 1>& 2
return 0
fi
fi
return $return_value
}
# Extract *.deb files to the bootstrap root.
extract_debs( ) {
local current_package_name
local data_archive
local control_archive
local package_tmpdir
local deb
local file
cd " $TERMUX_BUILT_DEBS_DIRECTORY "
if [ -z " $( ls -A) " ] ; then
echo $'\n\n\n' "No debs found"
return 1
else
echo $'\n\n\n' "Deb Files:"
echo "\""
ls
echo "\""
fi
for deb in *.deb; do
current_package_name = " $( echo " $deb " | sed -E 's/^([^_]+).*/\1/' ) "
echo " current_package_name: ' $current_package_name ' "
if [ [ " $current_package_name " = = *"-static" ] ] ; then
echo " [*] Skipping static package ' $deb '... "
continue
fi
if [ [ " ${ EXTRACTED_PACKAGES [*] } " = = *" $current_package_name " * ] ] ; then
echo " [*] Skipping already extracted package ' $current_package_name '... "
continue
fi
EXTRACTED_PACKAGES += ( " $current_package_name " )
package_tmpdir = " ${ BOOTSTRAP_PKGDIR } / ${ current_package_name } "
mkdir -p " $package_tmpdir "
rm -rf " $package_tmpdir " /*
echo " [*] Extracting ' $deb '... "
( cd " $package_tmpdir "
ar x " $TERMUX_BUILT_DEBS_DIRECTORY / $deb "
# data.tar may have extension different from .xz
if [ -f "./data.tar.xz" ] ; then
data_archive = "data.tar.xz"
elif [ -f "./data.tar.gz" ] ; then
data_archive = "data.tar.gz"
else
echo " No data.tar.* found in ' $deb '. "
return 1
fi
# Do same for control.tar.
if [ -f "./control.tar.xz" ] ; then
control_archive = "control.tar.xz"
elif [ -f "./control.tar.gz" ] ; then
control_archive = "control.tar.gz"
else
echo " No control.tar.* found in ' $deb '. "
return 1
fi
# Extract files.
tar xf " $data_archive " -C " $BOOTSTRAP_ROOTFS "
if ! ${ BOOTSTRAP_ANDROID10_COMPATIBLE } ; then
# Register extracted files.
tar tf " $data_archive " | sed -E -e 's@^\./@/@' -e 's@^/$@/.@' -e 's@^([^./])@/\1@' > " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/info/ ${ current_package_name } .list "
# Generate checksums (md5).
tar xf " $data_archive "
find data -type f -print0 | xargs -0 -r md5sum | sed 's@^\.$@@g' > " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/info/ ${ current_package_name } .md5sums "
# Extract metadata.
tar xf " $control_archive "
{
cat control
echo "Status: install ok installed"
echo
} >> " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/status "
# Additional data: conffiles & scripts
for file in conffiles postinst postrm preinst prerm; do
if [ -f " ${ PWD } / ${ file } " ] ; then
cp " $file " " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/info/ ${ current_package_name } . ${ file } "
fi
done
fi
)
done
}
# Final stage: generate bootstrap archive and place it to current
# working directory.
# Information about symlinks is stored in file SYMLINKS.txt.
create_bootstrap_archive( ) {
echo $'\n\n\n' " [*] Creating 'bootstrap- ${ 1 } .zip'... "
( cd " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } "
# Do not store symlinks in bootstrap archive.
# Instead, put all information to SYMLINKS.txt
while read -r -d '' link; do
echo " $( readlink " $link " ) ← ${ link } " >> SYMLINKS.txt
rm -f " $link "
done < <( find . -type l -print0)
zip -r9 " ${ BOOTSTRAP_TMPDIR } /bootstrap- ${ 1 } .zip " ./*
)
mv -f " ${ BOOTSTRAP_TMPDIR } /bootstrap- ${ 1 } .zip " " $TERMUX_PACKAGES_DIRECTORY / "
echo " [*] Finished successfully ( ${ 1 } ). "
}
set_build_bootstrap_traps( ) {
#set traps for the build_bootstrap_trap itself
trap 'build_bootstrap_trap' EXIT
trap 'build_bootstrap_trap TERM' TERM
trap 'build_bootstrap_trap INT' INT
trap 'build_bootstrap_trap HUP' HUP
trap 'build_bootstrap_trap QUIT' QUIT
return 0
}
build_bootstrap_trap( ) {
local build_bootstrap_trap_exit_code = $?
trap - EXIT
[ -h " $TERMUX_BUILT_PACKAGES_DIRECTORY " ] && rm -f " $TERMUX_BUILT_PACKAGES_DIRECTORY "
[ -d " $BOOTSTRAP_TMPDIR " ] && rm -rf " $BOOTSTRAP_TMPDIR "
[ -n " $1 " ] && trap - " $1 " ; exit $build_bootstrap_trap_exit_code
}
show_usage( ) {
cat <<'HELP_EOF'
build-bootstraps.sh is a script to build bootstrap archives for the
termux-app from local package sources instead of debs published in
apt repo like done by generate-bootstrap.sh. It allows bootstrap archives
to be easily built for ( forked) termux apps without having to publish
an apt repo first.
Usage:
build-bootstraps.sh [ command_options]
Available command_options:
[ -h | --help ] Display this help screen
[ -f ] Force build even if packages have already been built.
[ --android10 ]
Generate bootstrap archives for Android 10+ for
apk packaging system.
[ -a | --add <packages> ]
Additional packages to include into bootstrap archive.
Multiple packages should be passed as comma-separated list.
[ --architectures <architectures> ]
Override default list of architectures for which bootstrap
archives will be created. Multiple architectures should be
passed as comma-separated list.
The package name/prefix that the bootstrap is built for is defined by
2022-05-11 18:20:56 +02:00
TERMUX_APP_PACKAGE in 'scrips/properties.sh' . It defaults to 'me.sergiotarxz.openmg.x11' .
2022-02-08 03:55:39 +01:00
If package name is changed, make sure to run
` ./scripts/run-docker.sh ./clean.sh` or pass '-f' to force rebuild of packages.
### Examples
Build default bootstrap archives for all supported archs:
./scripts/run-docker.sh ./scripts/build-bootstraps.sh & > build.log
Build default bootstrap archive for aarch64 arch only:
./scripts/run-docker.sh ./scripts/build-bootstraps.sh --architectures aarch64 & > build.log
Build bootstrap archive with additionall openssh package for aarch64 arch only:
./scripts/run-docker.sh ./scripts/build-bootstraps.sh --architectures aarch64 --add openssh & > build.log
HELP_EOF
echo $'\n' " TERMUX_APP_PACKAGE: \" $TERMUX_APP_PACKAGE \" "
echo " TERMUX_PREFIX: \" ${ TERMUX_PREFIX [*] } \" "
echo " TERMUX_ARCHITECTURES: \" ${ TERMUX_ARCHITECTURES [*] } \" "
}
main( ) {
local return_value
while ( ( $# > 0) ) ; do
case " $1 " in
-h| --help)
show_usage
return 0
; ;
--android10)
BOOTSTRAP_ANDROID10_COMPATIBLE = true
; ;
-a| --add)
if [ $# -gt 1 ] && [ -n " $2 " ] && [ [ $2 != -* ] ] ; then
for pkg in $( echo " $2 " | tr ',' ' ' ) ; do
ADDITIONAL_PACKAGES += ( " $pkg " )
done
unset pkg
shift 1
else
echo "[!] Option '--add' requires an argument." 1>& 2
show_usage
return 1
fi
; ;
--architectures)
if [ $# -gt 1 ] && [ -n " $2 " ] && [ [ $2 != -* ] ] ; then
TERMUX_ARCHITECTURES = ( )
for arch in $( echo " $2 " | tr ',' ' ' ) ; do
TERMUX_ARCHITECTURES += ( " $arch " )
done
unset arch
shift 1
else
echo "[!] Option '--architectures' requires an argument." 1>& 2
show_usage
return 1
fi
; ;
-f)
BUILD_PACKAGE_OPTIONS += ( "-f" )
FORCE_BUILD_PACKAGES = 1
; ;
*)
echo " [!] Got unknown option ' $1 ' " 1>& 2
show_usage
return 1
; ;
esac
shift 1
done
set_build_bootstrap_traps
for package_arch in " ${ TERMUX_ARCHITECTURES [@] } " ; do
if [ [ " ${ TERMUX_DEFAULT_ARCHITECTURES [*] } " != *" $package_arch " * ] ] ; then
echo " Unsupported architecture ' $package_arch ' for in architectures list: ' ${ TERMUX_ARCHITECTURES [*] } ' " 1>& 2
echo " Supported architectures: ' ${ TERMUX_DEFAULT_ARCHITECTURES [*] } ' " 1>& 2
return 1
fi
done
for package_arch in " ${ TERMUX_ARCHITECTURES [@] } " ; do
# The termux_step_finish_build stores package version in .built-packages directory, but
# its not arch independent. So instead we create an arch specific one and symlink it
# to the .built-packages directory so that users can easily switch arches without having
# to rebuild packages
TERMUX_BUILT_PACKAGES_DIRECTORY_FOR_ARCH = " $TERMUX_BUILT_PACKAGES_DIRECTORY - $package_arch "
mkdir -p " $TERMUX_BUILT_PACKAGES_DIRECTORY_FOR_ARCH "
if [ -f " $TERMUX_BUILT_PACKAGES_DIRECTORY " ] || [ -d " $TERMUX_BUILT_PACKAGES_DIRECTORY " ] ; then
rm -rf " $TERMUX_BUILT_PACKAGES_DIRECTORY "
fi
ln -sf " $TERMUX_BUILT_PACKAGES_DIRECTORY_FOR_ARCH " " $TERMUX_BUILT_PACKAGES_DIRECTORY "
if [ [ $FORCE_BUILD_PACKAGES = = "1" ] ] ; then
rm -f " $TERMUX_BUILT_PACKAGES_DIRECTORY_FOR_ARCH " /*
rm -f " $TERMUX_BUILT_DEBS_DIRECTORY " /*
fi
BOOTSTRAP_ROOTFS = " $BOOTSTRAP_TMPDIR /rootfs- ${ package_arch } "
BOOTSTRAP_PKGDIR = " $BOOTSTRAP_TMPDIR /packages- ${ package_arch } "
# Create initial directories for $TERMUX_PREFIX
if ! ${ BOOTSTRAP_ANDROID10_COMPATIBLE } ; then
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /etc/apt/apt.conf.d "
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /etc/apt/preferences.d "
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/info "
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/triggers "
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/updates "
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/log/apt "
touch " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/available "
touch " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /var/lib/dpkg/status "
fi
mkdir -p " ${ BOOTSTRAP_ROOTFS } / ${ TERMUX_PREFIX } /tmp "
PACKAGES = ( )
EXTRACTED_PACKAGES = ( )
# Package manager.
if ! ${ BOOTSTRAP_ANDROID10_COMPATIBLE } ; then
PACKAGES += ( "apt" )
fi
# Core utilities.
PACKAGES += ( "bash" )
PACKAGES += ( "bzip2" )
if ! ${ BOOTSTRAP_ANDROID10_COMPATIBLE } ; then
PACKAGES += ( "command-not-found" )
else
PACKAGES += ( "proot" )
fi
PACKAGES += ( "coreutils" )
PACKAGES += ( "curl" )
PACKAGES += ( "dash" )
PACKAGES += ( "diffutils" )
PACKAGES += ( "findutils" )
PACKAGES += ( "gawk" )
PACKAGES += ( "grep" )
PACKAGES += ( "gzip" )
PACKAGES += ( "less" )
PACKAGES += ( "procps" )
PACKAGES += ( "psmisc" )
PACKAGES += ( "sed" )
PACKAGES += ( "tar" )
PACKAGES += ( "termux-exec" )
PACKAGES += ( "termux-keyring" )
PACKAGES += ( "termux-tools" )
PACKAGES += ( "util-linux" )
PACKAGES += ( "xz-utils" )
# Additional.
PACKAGES += ( "ed" )
PACKAGES += ( "debianutils" )
PACKAGES += ( "dos2unix" )
PACKAGES += ( "inetutils" )
PACKAGES += ( "lsof" )
PACKAGES += ( "nano" )
PACKAGES += ( "net-tools" )
PACKAGES += ( "patch" )
PACKAGES += ( "unzip" )
# Handle additional packages.
for add_pkg in " ${ ADDITIONAL_PACKAGES [@] } " ; do
if [ [ " ${ PACKAGES [*] } " != *" $add_pkg " * ] ] ; then
PACKAGES += ( " $add_pkg " )
fi
done
unset add_pkg
# Build packages.
for package_name in " ${ PACKAGES [@] } " ; do
set +e
build_package " $package_arch " " $package_name " || return $?
set -e
done
# Extract all debs.
extract_debs || return $?
# Create bootstrap archive.
create_bootstrap_archive " $package_arch " || return $?
done
}
main " $@ "