Skip to content

Commit

Permalink
Add support for Android/Termux
Browse files Browse the repository at this point in the history
As of this commit, ksh supports the Termux environment on Android.
There were several challenges to solve to make it build. Termux is
based on the typical Linux environment but with important changes,
and there is no attempt at POSIX compliance. This commit adds the
necessary workarounds to deal with this system's quirks.

This commit was tested on Android 14.0 arm64 with Termux 0.118.0 on
the Android emulator provided by Android Studio on macOS.

To build correctly, you need the clang, binutils, and getconf
packages. At runtime, the getconf package is needed for AST
getconf(1) fallbacks to function, and ncurses-utils packages
(specifically, tput(1)) is needed to enable multiline editing.

bin/package:
- Since Termux does not have 'getconf PATH' (since it lacks POSIX
  confstr(3)): move the DEFPATH/PATH code and add two fallbacks for
  'getconf PATH' that require a compiler: one that tries confstr(),
  currently expected to fail on Termux, and another one that tries
  the non-standard _PATH_DEFPATH macro, which exist on most (all?)
  Linux and BSD systems including Android. (re: 78b1a84)
- Export DEFPATH to the environment so we can eliminate repetitive
  code to determine it in iffe and mamprobe.
- hostinfo(): Use 'android' and not 'linux' as the host type base
  name on Android/Termux; this will allow us to implement necessary
  special-casing in make.probe (see below).

src/cmd/INIT/iffe.sh, src/cmd/INIT/mamprobe.sh,
src/lib/libast/comp/conf.sh, src/lib/libast/comp/conf.tab:
- Require DEFPATH in the environment (see above).
- Use it instead of the code to determine default system path.

src/cmd/INIT/make.probe, **/Mamfile:
- On Termux we need API version 26 for the system headers to
  declare a couple of functions we depend on, including catopen(3).
  This is done by adding --target=aarch64-linux-android26 to the
  compiler command line. Add a CC.TARGET variable (which becomes
  mam_cc_TARGET in the Mamfiles) to contain this flag.

src/lib/libast/features/standards:
- Add support for Android. After the make.probe change above we can
  treat it the same as GNU, as the Android headers also recognise
  _GNU_SOURCE for some things.

src/lib/libast/comp/omitted.c:
- On Android, bzero(3) is a macro that expands to __bionic_bzero(),
  so iffe doesn't detect bzero() as a library function. Check for
  both the iffe result _lib_bzero and the presence of bzero as a
  macro to avoid a build error.

src/lib/libast/comp/strtold.c:
- Avoid another build failure: work around Android system header
  incompatibilities with the AST headers by not including stdlib.h
  from ast_sys.h in this case. This is done by temporarily defining
  _STDLIB_H so the Android stdlib.h code gets skipped.

src/lib/libast/sfio/sfcvt.c:
- Work around another build failure by not relying on the _lib_isnan
  iffe test result on Android.

src/lib/libast/vmalloc/vmhdr.h:
- Include <sig.h> here to avoid yet another build failure on
  Android (undeclared function '_ast_signal').

src/lib/libcmd/fds.c:
- Since Android declares ntohs(3) in sys/endian.h and not in the
  POSIX standard location arpa/inet.h, include the former on
  Android.

src/cmd/ksh93/tests/*.sh:
- Since Android does not have /tmp, avoid using it. For testing cd,
  /dev is a good alternative.
- Android don't allow inheriting stdout in closed state. Disable
  the tests that rely on this on systems that don't allow this.
  • Loading branch information
McDutchie committed Mar 20, 2024
1 parent aa5e982 commit c3fec93
Show file tree
Hide file tree
Showing 26 changed files with 216 additions and 167 deletions.
8 changes: 8 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ This documents significant changes in the 1.0 branch of ksh 93u+m.
For full details, see the git log at: https://github.com/ksh93/ksh/tree/1.0
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.

2024-03-20:

- Android with Termux is now a supported environment for ksh 93u+m. Testing
was done on Android 14.0 with Termux 0.118.0. Please report any breakage.
Build dependencies (Termux packages): clang, binutils, getconf. Runtime
depenencies: ncurses-utils (for --multiline), getconf (fallback for the
/opt/ast/bin/getconf builtin).

2024-03-12:

- Fixed a regression in the 'printf' built-in, introduced on 2023-05-17,
Expand Down
154 changes: 115 additions & 39 deletions bin/package
Original file line number Diff line number Diff line change
Expand Up @@ -82,42 +82,6 @@ case $PWD in
exit 1 ;;
esac || exit

# Outputs sanitized system $PATH, eliminating duplicate and nonexistent dirs, '..', etc.
sanitize_PATH() (
set -fu +e
IFS=':'
unset -v CDPATH
sPATH=''
for dir in $1; do
# Sanitize this path, resolving symlinks,
# with special-casing of ksh's virtual built-ins directory
case $dir in
/opt/ast/bin)
test ! -d "$dir" && sdir=$dir ;;
*/* | [!+-]* | [+-]*[!0123456789]*)
sdir=$(cd -- $dir 2>/dev/null && pwd -P && echo X) ;;
*) sdir=$(cd ./$dir 2>/dev/null && pwd -P && echo X) ;;
esac || continue
sdir=${sdir%?X}
# Skip duplicates
case :$sPATH: in
*:"$sdir":*)
continue ;;
esac
# Found one, add it
sPATH=${sPATH:+$sPATH:}$sdir
done
printf '%s\n' "${sPATH#:}"
)

# Ensure a sane $PATH beginning with standard utilities.
# Find preferred 'getconf' on NixOS and Solaris/illumos.
DEFPATH=$(
PATH=/run/current-system/sw/bin:/usr/xpg7/bin:/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin:$PATH
getconf PATH 2>/dev/null
) || DEFPATH=/bin:/usr/bin:/sbin:/usr/sbin
PATH=$(sanitize_PATH "/opt/ast/bin:$DEFPATH:$PATH")

# shell checks
checksh()
{
Expand All @@ -137,7 +101,7 @@ lib="" # need /usr/local/lib /usr/local/shlib
ccs="/usr/kvm /usr/ccs/bin"
org="gnu GNU"
makefiles="Mamfile" # ksh 93u+m no longer uses these: Nmakefile nmakefile Makefile makefile
env="HOSTTYPE PACKAGEROOT INSTALLROOT PATH FPATH MANPATH"
env="HOSTTYPE PACKAGEROOT INSTALLROOT PATH DEFPATH FPATH MANPATH"

package_use='=$HOSTTYPE=$PACKAGEROOT=$INSTALLROOT=$EXECROOT=$CC='

Expand All @@ -151,7 +115,7 @@ command=${0##*/}
case $(getopts '[-][123:xyz]' opt --xyz 2>/dev/null; echo 0$opt) in
0123) USAGE=$'
[-?
@(#)$Id: '$command$' (ksh 93u+m) 2024-03-07 $
@(#)$Id: '$command$' (ksh 93u+m) 2024-03-20 $
]
[-author?Glenn Fowler <[email protected]>]
[-author?Contributors to https://github.com/ksh93/ksh]
Expand Down Expand Up @@ -579,7 +543,7 @@ SEE ALSO
pkgadd(1), pkgmk(1), rpm(1), sh(1), tar(1), optget(3)

IMPLEMENTATION
version package (ksh 93u+m) 2024-03-07
version package (ksh 93u+m) 2024-03-20
author Glenn Fowler <[email protected]>
author Contributors to https://github.com/ksh93/ksh
copyright (c) 1994-2012 AT&T Intellectual Property
Expand Down Expand Up @@ -1069,6 +1033,9 @@ int main(void)
*-linux-gnu* | *-linux-musl*)
# fix missing machine field, e.g. aarch64-linux-gnu => aarch64-unknown-linux-gnu
canon=${canon%%-*}-unknown-${canon#*-} ;;
*-linux-android*)
# Android/Termux is very much its own thing, so identify it as 'android', not 'linux'
canon=${canon%-linux-*}-android ;;
esac
case -${canon}- in
--|*-powerpc-*)
Expand Down Expand Up @@ -1674,6 +1641,115 @@ checkcc()
esac
}

# output sanitized system $PATH, eliminating duplicate and nonexistent dirs, '..', etc.

sanitize_PATH()
(
set -fu +e
IFS=':'
unset -v CDPATH
sPATH=''
for dir in $1; do
# Sanitize this path, resolving symlinks,
# with special-casing of ksh's virtual built-ins directory
case $dir in
/opt/ast/bin)
test ! -d "$dir" && sdir=$dir ;;
*/* | [!+-]* | [+-]*[!0123456789]*)
sdir=$(cd -- $dir 2>/dev/null && pwd -P && echo X) ;;
*) sdir=$(cd ./$dir 2>/dev/null && pwd -P && echo X) ;;
esac || continue
sdir=${sdir%?X}
# Skip duplicates
case :$sPATH: in
*:"$sdir":*)
continue ;;
esac
# Found one, add it
sPATH=${sPATH:+$sPATH:}$sdir
done
printf '%s\n' "${sPATH#:}"
)

# Ensure a sane $PATH beginning with standard utilities.
# POSIXly, a simple 'getconf PATH' should do it, but reality is otherwise.
# Find preferred getconf(1) on NixOS and Solaris/illumos.
# Compile fallback programs on systems without getconf(1).

DEFPATH=$(
# support Android/Termux, NixOS, Solaris/illumos, generic /bin:/usr/bin
PATH=/data/data/com.termux/files/usr/bin:/run/current-system/sw/bin:/usr/xpg7/bin:/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin:$PATH
getconf PATH 2>/dev/null || {
# no getconf(1) -- try confstr(3)
checkcc
t=${TMPDIR:-/tmp}/path_test.$$
trap 'set +f; rm -rf "$t."*' 0
cat > $t.c <<-EOF
#include <stdio.h>
#include <unistd.h>
int main(void)
{
char buf[8192];
size_t len = confstr(_CS_PATH, &buf, sizeof(buf));
if (len > 0 && len < sizeof(buf))
{
printf("%s\n", buf);
return 0;
}
return 1;
}
EOF
$CC $CCFLAGS $LDFLAGS -o "$t.exe" "$t.c" 2>/dev/null && "$t.exe"
} || {
# no confstr(3) either -- try non-standard _PATH_DEFPATH
cat > $t.c <<-EOF
#include <stdio.h>
#include <paths.h>
int main(void)
{
printf("%s\n", _PATH_DEFPATH);
return 0;
}
EOF
$CC $CCFLAGS $LDFLAGS -o "$t.exe" "$t.c" 2>/dev/null && "$t.exe"
}
) || DEFPATH=/bin:/usr/bin:/sbin:/usr/sbin
# Fix for NixOS. Not all POSIX standard utilities come with the default system,
# e.g. 'bc', 'file', 'vi'. The command that NixOS recommends to get missing
# utilities, e.g. 'nix-env -iA nixos.bc', installs them in a default profile
# directory that is not in $(getconf PATH). So add this path to the standard path.
# See: https://github.com/NixOS/nixpkgs/issues/65512
if test -e /etc/NIXOS &&
nix_profile_dir=/nix/var/nix/profiles/default/bin &&
test -d "$nix_profile_dir"
then case ":$DEFPATH:" in
*:"$nix_profile_dir":* )
# nothing to do
;;
* ) # insert the default profile directory as the second entry
DEFPATH=$(
set -f
IFS=:
set -- $DEFPATH
one=$1
shift
echo "$one:$nix_profile_dir${1+:}$*"
) ;;
esac
fi
# Fix for AIX. At least as of version 7.1, the system default 'find', 'diff -u' and 'patch' utilities
# are broken and/or non-compliant in ways that make them incompatible with POSIX 2018. However, GNU
# utilities are commonly installed in /opt/freeware/bin, and under standard names (no g- prefix).
if test -d /opt/freeware/bin
then case $(uname) in
AIX ) DEFPATH="/opt/freeware/bin:$DEFPATH" ;;
esac
fi

export DEFPATH # for iffe, etc.

PATH=$(sanitize_PATH "/opt/ast/bin:$DEFPATH:$PATH")

# some actions have their own PACKAGEROOT or kick out early

case $action in
Expand Down
48 changes: 9 additions & 39 deletions src/cmd/INIT/iffe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ esac
set -o noglob

command=iffe
version=2023-04-06
version=2024-03-20

# DEFPATH should be inherited from package(1)
case $DEFPATH in
/*) ;;
*) echo "$command: DEFPATH not set" >&2
exit 1 ;;
esac

compile() # $cc ...
{
Expand Down Expand Up @@ -106,44 +113,7 @@ pkg() # package
{
case $1 in
'') # Determine default system path, store in $pth.
pth=$(
PATH=/run/current-system/sw/bin:/usr/xpg7/bin:/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin:$PATH
exec getconf PATH 2>/dev/null
)
case $pth in
'' | [!/]* | *:[!/]* | *: )
pth="/bin /usr/bin /sbin /usr/sbin" ;;
*:*) pth=$(echo "$pth" | sed 's/:/ /g') ;;
esac
# Fix for NixOS. Not all POSIX standard utilities come with the default system,
# e.g. 'bc', 'file', 'vi'. The command that NixOS recommends to get missing
# utilities, e.g. 'nix-env -iA nixos.bc', installs them in a default profile
# directory that is not in $(getconf PATH). So add this path to the standard path.
# See: https://github.com/NixOS/nixpkgs/issues/65512
if test -e /etc/NIXOS &&
nix_profile_dir=/nix/var/nix/profiles/default/bin &&
test -d "$nix_profile_dir"
then case " $pth " in
*" $nix_profile_dir "* )
# nothing to do
;;
* ) # insert the default profile directory as the second entry
pth=$(
set $pth
one=$1
shift
echo "$one $nix_profile_dir${1+ }$@"
) ;;
esac
fi
# Fix for AIX. At least as of version 7.1, the system default 'find', 'diff -u' and 'patch' utilities
# are broken and/or non-compliant in ways that make them incompatible with POSIX 2018. However, GNU
# utilities are commonly installed in /opt/freeware/bin, and under standard names (no g- prefix).
if test -d /opt/freeware/bin
then case $(uname) in
AIX ) pth="/opt/freeware/bin $pth" ;;
esac
fi
pth=$(echo "$DEFPATH" | sed 's/:/ /g')
return
;;
'<') shift
Expand Down
24 changes: 23 additions & 1 deletion src/cmd/INIT/make.probe
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Glenn Fowler
# AT&T Research
#
# @(#)make.probe (ksh 93u+m) 2024-01-09
# @(#)make.probe (ksh 93u+m) 2024-03-20
#
# C probe for make
#
Expand Down Expand Up @@ -1903,6 +1903,27 @@ case $keepstdlib in
;;
esac

#
# determine the need for any API version targets to add to the compiler flags
#

CC_TARGET=
case $hosttype in
android.*)
# get current default API level
cat >_android_test.c <<-EOF
#include <stdio.h>
int main(void) { printf("%d\n", __ANDROID_API__); return 0; }
EOF
$cc -o _android_test _android_test.c
got=$(./_android_test)
(set +f; exec rm -rf _android_test*) &
# check for minimum required; we need API 26 for catopen(3) et al
if test -n "$got" && test "$got" -lt 26
then CC_TARGET="--target=$(uname -m)-linux-android26"
fi ;;
esac

#
# set up for local override
#
Expand Down Expand Up @@ -2100,6 +2121,7 @@ echo CC.SUFFIX.OBJECT = $CC_SUFFIX_OBJECT
echo CC.SUFFIX.SHARED = $CC_SUFFIX_SHARED
echo CC.SUFFIX.SOURCE = $CC_SUFFIX_SOURCE
echo CC.SUFFIX.STATIC = $CC_SUFFIX_STATIC
echo CC.TARGET = $CC_TARGET
echo CC.VERSION = $CC_VERSION
case $CC_VERSION_STRING in
*\"*) i=`echo " $CC_VERSION_STRING" | sed -e 's,",\\\\",g' -e 's,^ ,,' -e 's,.*,"&",'` ;;
Expand Down
8 changes: 1 addition & 7 deletions src/cmd/INIT/mamprobe.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,7 @@

command=mamprobe

bins=`
(
userPATH=$PATH
PATH=/run/current-system/sw/bin:/usr/xpg7/bin:/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin:$PATH
getconf PATH 2>/dev/null && echo "$userPATH" || echo /bin:/usr/bin:/sbin:/usr/sbin:"$userPATH"
) | sed 's/:/ /g'
` || exit
bins=$(echo "$DEFPATH:$PATH" | sed 's/:/ /g') || exit

# check the options

Expand Down
2 changes: 1 addition & 1 deletion src/cmd/builtin/Mamfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ setv MAMAKE_STRICT 3
setv INSTALLROOT ../../..
setv PACKAGE_ast_INCLUDE ${INSTALLROOT}/include/ast
setv CC cc
setv mam_cc_FLAGS ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?}
setv mam_cc_FLAGS ${mam_cc_TARGET} ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?}
setv CCFLAGS
setv CCLDFLAGS ${-strip-symbols?1?${mam_cc_LD_STRIP}??}
setv IFFEFLAGS
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ksh93/Mamfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ setv INSTALLROOT ../../..
setv PACKAGE_ast_INCLUDE ${INSTALLROOT}/include/ast
setv CC cc
setv AR ${mam_cc_AR} ${mam_cc_AR_ARFLAGS}
setv mam_cc_FLAGS ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?}
setv mam_cc_FLAGS ${mam_cc_TARGET} ${-debug-symbols?1?${mam_cc_DEBUG} -D_BLD_DEBUG?${mam_cc_OPTIMIZE}?}
setv CCFLAGS
setv CCLDFLAGS ${-strip-symbols?1?${mam_cc_LD_STRIP}??}
setv IFFEFLAGS
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ksh93/include/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

#define SH_RELEASE_FORK "93u+m" /* only change if you develop a new ksh93 fork */
#define SH_RELEASE_SVER "1.0.9-beta" /* semantic version number: https://semver.org */
#define SH_RELEASE_DATE "2024-03-12" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2024-03-20" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_CPYR "(c) 2020-2024 Contributors to ksh " SH_RELEASE_FORK

/* Scripts sometimes field-split ${.sh.version}, so don't change amount of whitespace. */
Expand Down
9 changes: 5 additions & 4 deletions src/cmd/ksh93/tests/basic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,10 @@ eval $bar
if [[ $foo != 'foo bar' ]]
then err_exit 'eval foo=\$bar, with bar="foo\ bar" not working'
fi
cd /tmp
cd ../../tmp || err_exit "cd ../../tmp failed"
if [[ $PWD != /tmp ]]
then err_exit 'cd ../../tmp is not /tmp'
cd /dev
cd ../../dev || err_exit "cd ../../dev failed"
if [[ $PWD != /dev ]]
then err_exit 'cd ../../dev is not /dev'
fi
( sleep .2; cat <<!
foobar
Expand Down Expand Up @@ -980,6 +980,7 @@ do
|| err_exit "last command in forked comsub exec-optimized in spite of $sig trap ($pid1 == $pid2)"
cat >script <<-EOF
trap + $sig # unignore
trap ":" $sig
echo \$\$
sh -c 'echo \$\$'
Expand Down
Loading

0 comments on commit c3fec93

Please sign in to comment.