#!/bin/sh # # Type `build -h` for usage and see https://github.com/romkatv/zsh-bin # for documentation. set -ue if [ -n "${BASH_VERSION:-}" ]; then if [ -z "${BASH_VERSION##[0-4].*}" ]; then >&2 echo "[error] bash version < 5 is not supported; try another interpreter" exit 1 fi fi if [ -n "${ZSH_VERSION:-}" ]; then emulate sh -o err_exit -o no_unset fi usage="$(cat <<\END Usage: build [-m ARCH] [-c CPU] [-i IMAGE] [-d CMD] Creates an archive containing statically-linked, hermetic, relocatable Zsh 5.8. Installation of Zsh from the archive does not require libc, terminfo, ncurses or root access. As long as the target machine has a compatible CPU and kernel, it will work. Options: -m ARCH `uname -m` from the target machine; defaults to `uname -m` from the local machine -c CPU generate machine instructions for CPU of this type; this value gets passed as `-march` (or `-mcpu` for ppc64le) to gcc; inferred from ARCH if not set explicitly -i IMAGE docker image used for building zsh; inferred from ARCH if not set explicitly -d CMD use this command instead of 'docker'; it must understand the same command line arguments END )" build="$(cat <<\END outdir="$(pwd)" unset TERMINFO TERMINFO_DIRS if command -v mktemp >/dev/null 2>&1; then workdir="$(mktemp -d "${TMPDIR:-/tmp}"/zsh-bin.XXXXXXXXXX)" else workdir="${TMPDIR:-/tmp}/zsh-bin.$$" mkdir -- "$workdir" fi cd -- "$workdir" workdir="$(pwd)" narg() { echo $#; } if [ "$(narg $workdir)" != 1 -o -z "${workdir##*:*}" ]; then >&2 echo "[error] cannot build in this directory: $workdir" exit 1 fi cleanup() { trap - INT QUIT TERM EXIT ILL PIPE cd / if ! rm -rf -- "$workdir"; then sleep 5 rm -rf -- "$workdir" fi } trap cleanup INT QUIT TERM EXIT ILL PIPE libiconv_version=1.16 icmake_version=9.03.01 yodl_version=4.02.01 ncurses_version=6.2 kitty_version=0.25.2 termite_version=15 kakoune_version=2020.09.01 foot_version=1.7 zsh_doc_version=5.0.1 ncurses_url=https://ftp.gnu.org/pub/gnu/ncurses/ncurses-$ncurses_version.tar.gz kitty_url=https://raw.githubusercontent.com/kovidgoyal/kitty/v$kitty_version/terminfo/kitty.terminfo termite_url=https://raw.githubusercontent.com/thestinger/termite/v$termite_version/termite.terminfo kakoune_url=https://raw.githubusercontent.com/mawww/kakoune/v$kakoune_version/contrib/tmux-256color.terminfo foot_url=https://codeberg.org/dnkl/foot/raw/branch/releases/$foot_version/foot.info libiconv_url=https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$libiconv_version.tar.gz icmake_url=https://gitlab.com/fbb-git/icmake/-/archive/$icmake_version/icmake-$icmake_version.tar.gz yodl_url=https://gitlab.com/fbb-git/yodl/-/archive/$yodl_version/yodl-$yodl_version.tar.gz zsh_url=https://github.com/zsh-users/zsh/archive/zsh-5.8.tar.gz zsh_doc_url=https://github.com/romkatv/zsh-bin/releases/download/v$zsh_doc_version/zsh-5.8-linux-x86_64.tar.gz ncurses_sha256=30306e0c76e0f9f1f0de987cf1c82a5c21e1ce6568b9227f7da5b71cbea86c9d kitty_sha256=bdcac2c8cbae7bf2e2843e500ded9f550a6a8b3e66a14565b0156d8301dfc6aa termite_sha256=771ecbde374ccefc40f06fb8ee0191bac1a81ab1f8f46b88288d1a9092770529 kakoune_sha256=b1a0c61f6ce3d123878c501c01183aac5cf951ed718eb1c73949eb86b00678be foot_sha256=59769813ed6545c8176af291f49e281860ac70c30ceb5f72f307ec7ce9c992f4 libiconv_sha256=e6a1b1b589654277ee790cce3734f07876ac4ccfaecbee8afa0b649cf529cc04 icmake_sha256=7c5c091f58f576da580238c5e3636e2038d9ecf5efb6562ae7e402910d9036e6 yodl_sha256=0118efd6f05ddeed4910bb5e280a37a4d8e5c2f42fb2009c840b3d5c70f700f8 zsh_sha256=db6cdfadac8d3d1f85c55c3644620cf5a0f65bf01ca26a58ff06f041bf159a5d zsh_doc_sha256=e1310318c6d0a14e595f71f2f393bf915758f834e50a5d101a98e246b7a137cf cpus="$(getconf _NPROCESSORS_ONLN)" || cpus="$(sysctl -n hw.ncpu)" || cpus=8 fetch() { local url="$1" local sha256="$2" local filename="${url##*/}" local base="${filename%.tar.gz}" printf '%s %s\n' "$sha256" "$filename" >"$base".sha256 if ! cp -- "$outdir/src/$filename" . 2>/dev/null || ! shasum -b -a 256 -c "$base".sha256; then rm -f -- "$filename" wget -- "$url" shasum -b -a 256 -c "$base".sha256 mkdir -p -- "$outdir"/src cp -f -- "$filename" "$outdir"/src/tmp."$filename" mv -f -- "$outdir"/src/tmp."$filename" "$outdir"/src/"$filename" fi case "$filename" in *.tar.gz) tar -xzf "$filename" ;; esac } ncurses_configure= case "$ZSH_BIN_KERNEL" in linux) apk update apk add \ autoconf \ bash \ binutils \ file \ g++ \ gcc \ gdbm-dev \ groff \ make \ man \ musl-dev \ pcre-dev \ perl-utils \ rsync \ tar \ texinfo \ util-linux cd -- "$workdir" fetch "$libiconv_url" "$libiconv_sha256" cd libiconv-"$libiconv_version" ./configure \ --prefix=/usr \ --disable-dependency-tracking \ --enable-static \ --disable-shared \ --enable-extra-encodings \ --disable-rpath \ --enable-relocatable make -j "$cpus" install ;; freebsd) pkg install -y \ autoconf \ binutils \ gcc \ gmake \ groff \ libiconv \ perl5 \ pcre \ rsync \ texinfo \ yodl ;; darwin) if ! command -v make >/dev/null 2>&1 || ! command -v gcc >/dev/null 2>&1; then >&2 echo "[error] please run 'xcode-select --install' and retry" exit 1 fi if command -v port >/dev/null 2>&1; then sudo port -N install autoconf libiconv pcre wget elif command -v brew >/dev/null 2>&1; then for formula in autoconf libiconv pcre wget; do if brew ls --version "$formula" >/dev/null; then brew upgrade "$formula" else brew install "$formula" fi done else >&2 echo "[error] please install MacPorts or Homebrew and retry" exit 1 fi mkdir -- "$workdir"/zsh_doc cd -- "$workdir"/zsh_doc fetch "$zsh_doc_url" "$zsh_doc_sha256" ;; msys*|mingw*) ncurses_configure='--with-pcre2' pacman -Syu --noconfirm pacman -S --needed --noconfirm \ autoconf \ binutils \ gcc \ groff \ libiconv-devel \ make \ man \ perl \ pcre-devel \ rsync \ texinfo \ yodl ;; cygwin*) for cmd in autoconf bash colcrt gcc g++ groff ld make makeinfo shasum tar wget; do if ! command -v "$cmd" >/dev/null 2>&1; then >&2 echo "[error] command not found: $cmd" >&2 echo "" >&2 echo "Make sure the following Cygwin packages are installed:" >&2 echo "" >&2 echo " autoconf" >&2 echo " bash" >&2 echo " binutils" >&2 echo " gcc-core" >&2 echo " gcc-g++" >&2 echo " groff" >&2 echo " libiconv-devel" >&2 echo " make" >&2 echo " perl" >&2 echo " tar" >&2 echo " texinfo" >&2 echo " rsync" >&2 echo " util-linux" >&2 echo " wget" exit 1 fi done for file in /usr/lib/libiconv.a; do if [ ! -e "$file" ]; then >&2 echo "[error] not found: $file" exit 1 fi done ;; *) >&2 echo "[internal error] unhandled kernel: $ZSH_BIN_KERNEL" exit 1 ;; esac if [ ! -e "$workdir"/zsh_doc ] && ! command -v yodl >/dev/null 2>&1; then if ! command -v icmake >/dev/null 2>&1; then cd -- "$workdir" fetch "$icmake_url" "$icmake_sha256" cd icmake-"$icmake_version"/icmake ./icm_prepare / ./icm_bootstrap x ./icm_install all fi cd -- "$workdir" fetch "$yodl_url" "$yodl_sha256" cd yodl-"$yodl_version" mkdir fake-bin touch fake-bin/tput chmod +x fake-bin/tput fake_bin_dir="$(pwd)"/fake-bin cd yodl PATH="$fake_bin_dir:$PATH" ./build programs PATH="$fake_bin_dir:$PATH" ./build macros PATH="$fake_bin_dir:$PATH" ./build install programs PATH="$fake_bin_dir:$PATH" ./build install macros fi cd -- "$workdir" fetch "$ncurses_url" "$ncurses_sha256" cd ncurses-"$ncurses_version" mkdir fake-bin echo 'false' >fake-bin/ldconfig chmod +x fake-bin/ldconfig fake_bin_dir="$(pwd)"/fake-bin PATH="$fake_bin_dir:$PATH" ./configure \ --prefix "$workdir"/ncurses \ --disable-pc-files \ --disable-mixed-case \ --disable-rpath \ --disable-bsdpad \ --disable-termcap \ --disable-rpath-hack \ --enable-root-environ \ --enable-widec \ --without-ada \ --without-manpages \ --without-tack \ --without-tests \ --without-pc-suffix \ --without-pkg-config-libdir \ --without-shared \ --without-debug \ --without-cxx-shared \ --without-gmp \ --without-dlsym \ --without-pcre2 \ --with-terminfo-dirs="/etc/terminfo:/usr/share/terminfo:/lib/terminfo:/usr/lib/terminfo" \ $ncurses_configure if command -v gmake >/dev/null 2>/dev/null; then gmake -j "$cpus" install else make -j "$cpus" install fi cd -- "$workdir" fetch "$kitty_url" "$kitty_sha256" ncurses/bin/tic -x -o ncurses/share/terminfo kitty.terminfo cd -- "$workdir" fetch "$termite_url" "$termite_sha256" ncurses/bin/tic -x -o ncurses/share/terminfo termite.terminfo cd -- "$workdir" fetch "$kakoune_url" "$kakoune_sha256" ncurses/bin/tic -x -o ncurses/share/terminfo tmux-256color.terminfo cd -- "$workdir" fetch "$foot_url" "$foot_sha256" ncurses/bin/tic -x -o ncurses/share/terminfo foot.info cp ncurses/share/terminfo/66/foot ncurses/share/terminfo/66/foot-extra cp ncurses/share/terminfo/66/foot+base ncurses/share/terminfo/66/foot+base-extra cp ncurses/share/terminfo/66/foot-direct ncurses/share/terminfo/66/foot-direct-extra cd -- "$workdir" fetch "$zsh_url" "$zsh_sha256" cd zsh-zsh-5.8 patch -u aczsh.m4 <<-\END --- aczsh.m4 2020-12-21 19:06:04.847363893 +0100 +++ aczsh.m4.new 2020-12-21 19:05:51.975418771 +0100 @@ -118,6 +118,7 @@ AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&AC_FD_CC) && AC_TRY_COMMAND($DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&AC_FD_CC); then AC_TRY_RUN([ +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED @@ -199,6 +200,7 @@ AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&AC_FD_CC) && AC_TRY_COMMAND($DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&AC_FD_CC); then AC_TRY_RUN([ +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED @@ -274,6 +276,7 @@ AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest2.c 1>&AC_FD_CC) && AC_TRY_COMMAND($DLLD -o conftest2.$DL_EXT $LDFLAGS $DLLDFLAGS conftest2.o $LIBS 1>&AC_FD_CC); then AC_TRY_RUN([ +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED @@ -343,6 +346,7 @@ save_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS" AC_TRY_RUN([ +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED @@ -416,6 +420,7 @@ save_ldflags=$LDFLAGS LDFLAGS="$LDFLAGS $EXTRA_LDFLAGS -s" AC_TRY_RUN([ +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED @@ -483,6 +488,7 @@ if AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest1.c 1>&AC_FD_CC) && AC_TRY_COMMAND($DLLD -o conftest1.$DL_EXT $LDFLAGS $DLLDFLAGS -s conftest1.o $LIBS 1>&AC_FD_CC); then AC_TRY_RUN([ +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED END patch -u configure.ac <<-\END --- configure.ac 2020-12-21 19:06:04.851363875 +0100 +++ configure.ac.new 2020-12-21 19:05:59.199387974 +0100 @@ -1394,6 +1394,10 @@ AC_CACHE_CHECK(if tgetent accepts NULL, zsh_cv_func_tgetent_accepts_null, [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +int tgetent(char *, char *); +char *tgetstr(char *, char **); main() { char buf[4096]; @@ -1418,6 +1422,10 @@ AC_CACHE_CHECK(if tgetent returns 0 on success, zsh_cv_func_tgetent_zero_success, [AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include +#include +int tgetent(char *, char*); +char *tgetstr(char *, char **); main() { char buf[4096]; @@ -1855,6 +1863,7 @@ #include #endif #include +#include main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));}]])],[zsh_cv_rlim_t_is_longer=yes],[zsh_cv_rlim_t_is_longer=no],[zsh_cv_rlim_t_is_longer=yes])]) if test x$zsh_cv_rlim_t_is_longer = xyes; then AC_CACHE_CHECK(if rlim_t is a quad, @@ -1865,6 +1874,7 @@ #endif #include #include +#include main() { struct rlimit r; char buf[20]; @@ -1887,6 +1897,7 @@ #include #endif #include +#include main(){struct rlimit r;r.rlim_cur=-1;exit(r.rlim_cur<0);}]])],[zsh_cv_type_rlim_t_is_unsigned=yes],[zsh_cv_type_rlim_t_is_unsigned=no],[zsh_cv_type_rlim_t_is_unsigned=no])]) if test x$zsh_cv_type_rlim_t_is_unsigned = xyes; then AC_DEFINE(RLIM_T_IS_UNSIGNED) @@ -2258,6 +2269,9 @@ [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include +#include +#include +#include main() { char c; @@ -2299,6 +2313,7 @@ [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include +#include main() { int ret; @@ -2331,6 +2346,7 @@ #include #include #include +#include main() { int pid = (getpid() + 10000) & 0xffffff; @@ -2356,6 +2372,7 @@ [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include +#include int child=0; void handler(sig) int sig; @@ -2407,6 +2424,7 @@ #include #include #include +#include main() { int fd; int ret; @@ -2450,6 +2468,10 @@ zsh_cv_sys_getpwnam_faked, [AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include +#include +#include +#include +#include main() { struct passwd *pw1, *pw2; char buf[1024], name[1024]; @@ -2777,6 +2799,7 @@ [AC_RUN_IFELSE([AC_LANG_SOURCE([[/* Test for whether ELF binaries are produced */ #include #include +#include main(argc, argv) int argc; char *argv[]; @@ -2930,6 +2953,7 @@ AC_TRY_COMMAND($DLLD $LDFLAGS $DLLDFLAGS -o conftest.$DL_EXT conftest.o 1>&AS_MESSAGE_LOG_FD) && AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include +#include #ifdef HPUX10DYNAMIC #include #define RTLD_LAZY BIND_DEFERRED END case "$ZSH_BIN_CPU" in powerpc64le) archflag="-mcpu";; *) archflag="-march";; esac name=zsh-5.8-"$ZSH_BIN_KERNEL"-"$ZSH_BIN_ARCH" export CFLAGS="-Wall -Wmissing-prototypes $archflag=$ZSH_BIN_CPU -DNDEBUG -O2 -flto -fno-strict-aliasing" export LDFLAGS= export EXELDFLAGS= export LIBS= CFLAGS="$CFLAGS -I$workdir/ncurses/include" LDFLAGS="$LDFLAGS -L$workdir/ncurses/lib" case "$ZSH_BIN_KERNEL" in linux|freebsd|msys*|mingw*) EXELDFLAGS="$EXELDFLAGS -static" LIBS="$LIBS -lpcre" ;; darwin) mkdir -- "$workdir"/lib if [ -e /opt/local/lib/libiconv.a -a -e /opt/local/lib/libpcre.a ]; then ln -s -- /opt/local/lib/libiconv.a "$workdir"/lib ln -s -- /opt/local/lib/libpcre.a "$workdir"/lib CFLAGS="$CFLAGS -I/opt/local/include -I/opt/local/include" else brew_prefix="$(command brew --prefix)" opt="$brew_prefix"/opt ln -s -- "$opt"/libiconv/lib/libiconv.a "$workdir"/lib ln -s -- "$opt"/pcre/lib/libpcre.a "$workdir"/lib CFLAGS="$CFLAGS -I$opt/libiconv/include -I$opt/pcre/include" fi LDFLAGS="$LDFLAGS -L$workdir/lib" LIBS="$LIBS -lpcre" ;; cygwin*) EXELDFLAGS="$EXELDFLAGS -static" ;; *) >&2 echo "[internal error] unhandled kernel: $ZSH_BIN_KERNEL" exit 1 ;; esac export DLLDFLAGS="-shared" export LDFLAGS="$CFLAGS $LDFLAGS" sed_i() { [ $# -gt 1 ] local script="$1" while [ $# != 1 ]; do shift local file="$1" sed "$script" "$file" >"$file".tmp mv -- "$file".tmp "$file" done } sed_i 's/armv\*/armv* | arm64/' config.sub host="$(./config.guess)" if [ -z "$host" -o -n "${host##*?-?*}" ]; then >&2 echo "[error] unexpected output from ./config.guess: $host" exit 1 fi ./Util/preconfig ./configure \ --prefix="$workdir/$name" \ --disable-etcdir \ --disable-zshenv \ --disable-zshrc \ --disable-zlogin \ --disable-zprofile \ --disable-zlogout \ --disable-site-fndir \ --disable-site-scriptdir \ --enable-cap \ --enable-unicode9 \ --with-tcsetpgrp \ --disable-dynamic \ --host="$ZSH_BIN_ARCH-${host#*-}" \ --enable-custom-patchlevel=zsh-5.8-0-g77d203f sed_i 's/link=no/link=static/' config.modules case "$ZSH_BIN_KERNEL" in linux);; freebsd) sed_i 's/attr.mdd link=static/attr.mdd link=no/' config.modules sed_i 's/db_gdbm.mdd link=static/db_gdbm.mdd link=no/' config.modules ;; darwin) sed_i 's/db_gdbm.mdd link=static/db_gdbm.mdd link=no/' config.modules ;; msys*|mingw*) sed_i 's/db_gdbm.mdd link=static/db_gdbm.mdd link=no/' config.modules ;; cygwin*) sed_i 's/db_gdbm.mdd link=static/db_gdbm.mdd link=no/' config.modules sed_i 's/pcre.mdd link=static/pcre.mdd link=no/' config.modules ;; *) >&2 echo "[internal error] unhandled kernel: $ZSH_BIN_KERNEL" exit 1 ;; esac magic=iLWDLaG9dUlsxzEQp10k sed_i '46i\ #include ' Src/params.c sed_i 's|mv -f zshpaths.h.tmp zshpaths.h|cp -f ../zshpaths.h ./|' Src/zsh.mdd sed_i 's/\(\$(LN_S).*\);/( cd -- $(DESTDIR)$(runhelpdir) \&\& \1; );/' Doc/Makefile.in sed_i 's/tgetent/tgetent_with_env/g' Src/init.c patch -u Src/init.c <<-\END --- Src/init.c 2020-12-22 14:41:00.524162742 +0100 +++ Src/init.c.new 2020-12-22 14:49:40.535136789 +0100 @@ -869,6 +869,10 @@ return 1; } +#if defined(__APPLE__) +static void init_paths_from_exe_path(void); +#endif + /* Initialize lots of global variables and hash tables */ /**/ @@ -905,6 +909,10 @@ #endif int close_fds[10], tmppipe[2]; +#if defined(__APPLE__) + init_paths_from_exe_path(); +#endif + /* * Workaround a problem with NIS (in one guise or another) which * grabs file descriptors and keeps them for future reference. END cat >>Src/init.c <<-\END volatile char tagged_script_dir[sizeof(SCRIPT_DIR_TAG) + 4096] = { SCRIPT_DIR_TAG "/usr/share/zsh/5.8/scripts" }; volatile char tagged_fpath_dir[sizeof(FPATH_DIR_TAG) + 4096] = { FPATH_DIR_TAG "/usr/share/zsh/5.8/functions" }; volatile char tagged_terminfo_dir[sizeof(TERMINFO_DIR_TAG) + 4096] = { TERMINFO_DIR_TAG "/usr/share/terminfo" }; extern int tgetent_with_env(char *termbuf, char *term) { int res; int ev_len; char **ev; char *orig; char *patched; orig = getenv("TERMINFO_DIRS"); if (orig) { orig -= strlen("TERMINFO_DIRS") + 1; patched = tricat(orig, ":", TERMINFO_DIR); putenv(patched); /* ignore ENOMEM */ } else { patched = bicat("TERMINFO_DIRS=", TERMINFO_DIR); ev = environ; ev_len = arrlen(environ); environ = (char **)zalloc(sizeof(char *) * (ev_len + 2)); memcpy(environ, ev, sizeof(char *) * ev_len); environ[ev_len] = patched; environ[ev_len + 1] = NULL; } res = tgetent(termbuf, term); if (orig) { putenv(orig); /* ignore ENOMEM */ } else { zfree(environ, sizeof(char *) * (ev_len + 2)); environ = ev; } zsfree(patched); return res; } #if defined(__APPLE__) #include #include #include #include #include #include static void init_paths_from_exe_path(void) { int n, i; pid_t pid; char exepath[PROC_PIDPATHINFO_MAXSIZE]; pid = getpid(); if (proc_pidpath(pid, exepath, sizeof(exepath)) < 0) { perror("proc_pidpath"); exit(1); } n = strlen(exepath); for (i = 0; i != 2; --n) { if (n == 0) { fprintf(stderr, "zsh: invalid exe path: %s\n", exepath); exit(1); } if (exepath[n-1] == '/') ++i; } memcpy((char*)tagged_script_dir + strlen(SCRIPT_DIR_TAG), exepath, n); memcpy((char*)tagged_fpath_dir + strlen(FPATH_DIR_TAG), exepath, n); memcpy((char*)tagged_terminfo_dir + strlen(TERMINFO_DIR_TAG), exepath, n); strcpy((char*)tagged_script_dir + strlen(SCRIPT_DIR_TAG) + n, "/share/zsh/5.8/scripts"); strcpy((char*)tagged_fpath_dir + strlen(FPATH_DIR_TAG) + n, "/share/zsh/5.8/functions"); strcpy((char*)tagged_terminfo_dir + strlen(TERMINFO_DIR_TAG) + n, "/share/terminfo"); } #endif END cat >zshpaths.h <<-\END #define MODULE_DIR "/dev/null" #define SCRIPT_DIR_TAG ":@ZSH_BIN_MAGIC@:script:" #define FPATH_DIR_TAG ":@ZSH_BIN_MAGIC@:fpath:" #define TERMINFO_DIR_TAG ":@ZSH_BIN_MAGIC@:terminfo:" extern volatile char tagged_script_dir[sizeof(SCRIPT_DIR_TAG) + 4096]; extern volatile char tagged_fpath_dir[sizeof(FPATH_DIR_TAG) + 4096]; extern volatile char tagged_terminfo_dir[sizeof(TERMINFO_DIR_TAG) + 4096]; #define SCRIPT_DIR ((const char *)(tagged_script_dir + sizeof(SCRIPT_DIR_TAG) - 1)) #define FPATH_DIR ((const char *)(tagged_fpath_dir + sizeof(FPATH_DIR_TAG) - 1)) #define TERMINFO_DIR ((const char *)(tagged_terminfo_dir + sizeof(TERMINFO_DIR_TAG) - 1)) extern int tgetent_with_env(char *, char *); END sed_i "s/@ZSH_BIN_MAGIC@/$magic/g" zshpaths.h if [ -e "$workdir"/zsh_doc ]; then make -j "$cpus" install.bin install.modules install.fns else make -j "$cpus" install install.info fi rsync -a --copy-links --copy-dirlinks -- "$workdir/$name"/ "$workdir/$name".tmp/ rm -r -- "$workdir/$name" mv -- "$workdir/$name".tmp "$workdir/$name" cd -- "$workdir/$name" strip bin/zsh rm -- bin/zsh-5.8 rsync -a --copy-links --copy-dirlinks -- "$workdir"/ncurses/share/terminfo share/ for dir in share/terminfo/*; do mv -- "$dir" "$dir".zsh-bin done if [ -e "$workdir"/zsh_doc ]; then cp -r -- "$workdir"/zsh_doc/share/man "$workdir"/zsh_doc/share/info share/ cp -r -- "$workdir"/zsh_doc/share/zsh/5.8/help share/zsh/5.8/ fi cp -r -- share/man share/info ./ sed_i 's/local HELPDIR=.*/[[ -n "${HELPDIR:-}" ]] || local HELPDIR="$(<<\\'"$magic"'\ '"$magic"'\ )"/' \ share/zsh/5.8/functions/run-help \ share/zsh/5.8/functions/_run-help cat >share/zsh/5.8/scripts/relocate <<-\END #!/bin/sh set -ue magic='@ZSH_BIN_MAGIC@' fpath_pos=@ZSH_BIN_FPATH_POS@ script_pos=@ZSH_BIN_SCRIPT_POS@ terminfo_pos=@ZSH_BIN_TERMINFO_POS@ do_patch_bin=@ZSH_BIN_DO_PATCH_BIN@ usage="$(cat <<-\USAGE Usage: relocate [-s SRC] [-d DST] Modifies hard-coded paths within Zsh residing in directory SRC so that they point to directory DST. The source directory must exist but the destination directory does not have to. Options: -s SRC directory where Zsh is currently installed; this is the directory with 'bin' and 'usr' as subdirectories; if not specified, this directory is automatically derived from path to 'relocate' -d DST directory from which Zsh will be used; this is the directory with 'bin' and 'usr' as subdirectories; if not specified, this directory is automatically derived from path to 'relocate' USAGE )" src= dst= while getopts ':s:d:h' opt "$@"; do case "$opt" in h) printf '%s\n' "$usage" exit ;; s) if [ -n "$src" ]; then >&2 echo "[error] duplicate option: -$opt" exit 1 fi if [ -z "$OPTARG" ]; then >&2 echo "[error] incorrect value of -$opt: $OPTARG" exit 1 fi src="$OPTARG" ;; d) if [ -n "$dst" ]; then >&2 echo "[error] duplicate option: -$opt" exit 1 fi if [ -z "$OPTARG" ]; then >&2 echo "[error] incorrect value of -$opt: $OPTARG" exit 1 fi dst="$OPTARG" ;; \?) >&2 echo "[error] invalid option: -$OPTARG" ; exit 1;; :) >&2 echo "[error] missing required argument: -$OPTARG"; exit 1;; *) >&2 echo "[internal error] unhandled option: -$opt" ; exit 1;; esac done if [ "$OPTIND" -le $# ]; then >&2 echo "[error] unexpected positional argument" exit 1 fi if [ -z "$src" ]; then src="$(dirname -- "$0")"/../../../.. fi absdir() { ( [ -d "$1" ] && cd -- "$1" && pwd ) } if ! src="$(absdir "$src")"; then >&2 echo "[error] not a directory: $src" exit 1 fi if [ -n "$dst" ]; then if [ -n "${dst##/*}" ]; then >&2 echo "[error] path not absolute: $dst" exit 1 fi else dst="$(dirname -- "$0")"/../../../.. if ! dst="$(absdir "$dst")"; then >&2 echo "[error] not a directory: $dst" exit 1 fi fi zsh="$src"/bin/zsh runhelp="$src"/share/zsh/5.8/functions/run-help runhelpcomp="$src"/share/zsh/5.8/functions/_run-help if [ ! -x "$zsh" ]; then >&2 echo "[error] not an executable file: $zsh" exit 1 fi if ! grep -qF 'local HELPDIR' -- "$runhelp"; then >&2 echo "[error] cannot relocate zsh from this directory: $src" exit 1 fi if ! grep -qF 'local HELPDIR' -- "$runhelpcomp"; then >&2 echo "[error] cannot relocate zsh from this directory: $src" exit 1 fi dst="${dst%/}/" # 4096 minus 23 for "share/zsh/5.8/functions" if [ ${#dst} -gt 4073 ]; then >&2 echo "[error] directory name too long: $dst" exit 1 fi if [ -z "${dst##*$magic*}" ]; then >&2 echo "[error] cannot relocate to this directory: $dst" exit 1 fi cp -pf -- "$zsh" "$zsh".tmp patch_help() { local data data="$(cat -- "$1")" local prefix="${data%%$magic*}" local suffix="${data##*$magic}" if [ "$prefix" = "$data" -o "$suffix" = "$data" ]; then >&2 echo "[error] not a relocatable zsh directory: $src" exit 1 fi local dir="$dst"share/zsh/5.8/help printf '%s\n%s\n%s\n' "$prefix$magic" "$dir" "$magic$suffix" >"$1".tmp } patch_bin() { "$do_patch_bin" || return 0 local header_len=$((1 + ${#magic} + 1 + ${#2} + 1)) local header if ! header="$(dd if="$zsh" bs=1 skip="$1" count="$header_len" 2>/dev/null)"; then header="$(dd if="$zsh" bs=1 skip="$1" count="$header_len")" fi if [ "$header" != ":$magic:$2:" ]; then >&2 echo "[error] not a relocatable zsh binary: $zsh" exit 1 fi local pos=$(($1 + header_len)) local dir="${dst}$3" local err if ! err="$(dd if=/dev/zero of="$zsh".tmp bs=1 seek="$pos" count=4096 conv=notrunc 2>&1)"; then >&2 printf '%s\n' "$err" exit 1 fi if ! err="$(printf '%s' "$dir" | dd of="$zsh".tmp bs=1 seek="$pos" count=${#dir} conv=notrunc 2>&1)"; then >&2 printf '%s\n' "$err" exit 1 fi } patch_help "$runhelp" patch_help "$runhelpcomp" patch_bin "$fpath_pos" fpath share/zsh/5.8/functions patch_bin "$script_pos" script share/zsh/5.8/scripts patch_bin "$terminfo_pos" terminfo share/terminfo if "$do_patch_bin"; then if ! fpath="$(unset FPATH && "$zsh".tmp -c 'print -r -- $fpath[1]')" || [ "${fpath#$dst}" = "$fpath" ]; then >&2 echo "[error] failed to relocate zsh" exit 1 fi fi mv -f -- "$zsh".tmp "$zsh" mv -f -- "$runhelp".tmp "$runhelp" mv -f -- "$runhelpcomp".tmp "$runhelpcomp" END sed_i "s/@ZSH_BIN_MAGIC@/$magic/g" share/zsh/5.8/scripts/relocate if [ "$ZSH_BIN_KERNEL" = darwin ]; then sed_i 's/@ZSH_BIN_DO_PATCH_BIN@/false/g' share/zsh/5.8/scripts/relocate else sed_i 's/@ZSH_BIN_DO_PATCH_BIN@/true/g' share/zsh/5.8/scripts/relocate fi embed_pos() { local cmd=' bin=$(LC_ALL=C tr -c "[:alnum:]:" " " <$0) parts=("${(@ps:$1:)bin}") (( $#parts == 2 )) print -r -- $#parts[1]' local pos pos="$(bin/zsh -fuec "$cmd" bin/zsh ":$magic:$1:")" printf '%s' "$pos" | grep -qxE '[0-9]+' local upper upper="$(printf '%s' "$1" | tr '[a-z]' '[A-Z]')" sed_i "s/@ZSH_BIN_${upper}_POS@/$pos/g" share/zsh/5.8/scripts/relocate } embed_pos fpath embed_pos script embed_pos terminfo chmod +x share/zsh/5.8/scripts/relocate share/zsh/5.8/scripts/relocate bin/zsh -fec 'zmodload zsh/stat; autoload -Uz add-zsh-hook; add-zsh-hook precmd x' share/zsh/5.8/scripts/relocate -d /usr/local tar -pczf "$outdir"/tmp."$name".tar.gz * mv -f -- "$outdir"/tmp."$name".tar.gz "$outdir"/"$name".tar.gz cleanup cat >&2 <<-END ------------------------------------------------- SUCCESS: created ./$name.tar.gz. This archive contains statically-linked, hermetic, relocatable Zsh 5.8. Installation of Zsh from the archive does not require libc, terminfo, ncurses or root access. As long as the target machine has a compatible CPU and kernel, it will work. To install Zsh from the archive, copy it along with 'install' script to the home directory on the target machine and run: sh ~/install -f ~/$name.tar.gz END END )" image= docker= ZSH_BIN_ARCH= ZSH_BIN_CPU= while getopts ':m:c:i:d:h' opt "$@"; do case "$opt" in h) printf '%s\n' "$usage" exit ;; m) if [ -n "$ZSH_BIN_ARCH" ]; then >&2 echo "[error] duplicate option: -$opt" exit 1 fi if [ -z "$OPTARG" ]; then >&2 echo "[error] incorrect value of -$opt: $OPTARG" exit 1 fi ZSH_BIN_ARCH="$OPTARG" ;; c) if [ -n "$ZSH_BIN_CPU" ]; then >&2 echo "[error] duplicate option: -$opt" exit 1 fi if [ -z "$OPTARG" ]; then >&2 echo "[error] incorrect value of -$opt: $OPTARG" exit 1 fi ZSH_BIN_CPU="$OPTARG" ;; i) if [ -n "$image" ]; then >&2 echo "[error] duplicate option: -$opt" exit 1 fi if [ -z "$OPTARG" ]; then >&2 echo "[error] incorrect value of -$opt: $OPTARG" exit 1 fi image="$OPTARG" ;; d) if [ -n "$docker" ]; then >&2 echo "[error] duplicate option: -$opt" exit 1 fi if [ -z "$OPTARG" ]; then >&2 echo "[error] incorrect value of -$opt: $OPTARG" exit 1 fi docker="$OPTARG" ;; \?) >&2 echo "[error] invalid option: -$OPTARG" ; exit 1;; :) >&2 echo "[error] missing required argument: -$OPTARG"; exit 1;; *) >&2 echo "[internal error] unhandled option: -$opt" ; exit 1;; esac done if [ "$OPTIND" -le $# ]; then >&2 echo "[error] unexpected positional argument" exit 1 fi if [ -z "$ZSH_BIN_ARCH" ]; then ZSH_BIN_ARCH="$(uname -m)" ZSH_BIN_ARCH="$(printf '%s' "$ZSH_BIN_ARCH" | tr '[A-Z]' '[a-z]')" fi if [ -z "$ZSH_BIN_CPU" ]; then case "$ZSH_BIN_ARCH" in armv6l) ZSH_BIN_CPU=armv6;; armv7l) ZSH_BIN_CPU=armv7;; arm64) ZSH_BIN_CPU=armv8;; aarch64) ZSH_BIN_CPU=armv8-a;; ppc64le) ZSH_BIN_CPU=powerpc64le;; x86_64|amd64) ZSH_BIN_CPU=x86-64;; i386|i586|i686) ZSH_BIN_CPU="$ZSH_BIN_ARCH";; *) >&2 echo '[error] unable to infer target CPU architecture' >&2 echo 'Please specify explicitly with `-c CPU`.' exit 1 ;; esac fi ZSH_BIN_KERNEL="$(uname -s)" ZSH_BIN_KERNEL="$(printf '%s' "$ZSH_BIN_KERNEL" | tr '[A-Z]' '[a-z]')" case "$ZSH_BIN_KERNEL" in linux) if [ -z "$image" ]; then case "$ZSH_BIN_ARCH" in x86_64) image=alpine:3.9.5;; i386|i586|i686) image=i386/alpine:3.9.5;; armv6l) image=arm32v6/alpine:3.9.5;; armv7l) image=arm32v7/alpine:3.9.5;; aarch64) image=arm64v8/alpine:3.9.5;; ppc64le) image=ppc64le/alpine:3.9.5;; *) >&2 echo '[error] unable to infer docker image' >&2 echo 'Please specify explicitly with `-i IMAGE`.' exit 1 ;; esac fi : "${docker:=docker}" if [ -z "${docker##*/*}" ]; then if [ ! -x "$docker" ]; then >&2 echo "[error] not an executable file: $docker" exit 1 fi else if ! command -v "$docker" >/dev/null 2>&1; then >&2 echo "[error] command not found: $docker" exit 1 fi fi ;; freebsd) if [ -n "$image" ]; then >&2 echo '[error] docker image (-i) is not supported on freebsd' exit 1 fi if [ -n "$docker" ]; then >&2 echo '[error] docker (-d) is not supported on freebsd' exit 1 fi ;; darwin) if [ -n "$image" ]; then >&2 echo '[error] docker image (-i) is not supported on macOS' exit 1 fi if [ -n "$docker" ]; then >&2 echo '[error] docker (-d) is not supported on macOS' exit 1 fi ;; msys_nt-*|mingw32_nt-*|mingw64_nt-*|cygwin_nt-*) if ! printf '%s' "$ZSH_BIN_KERNEL" | grep -Eqx '[^-]+-[0-9]+\.[0-9]+(-.*)?'; then >&2 echo '[error] unsupported kernel, sorry!' exit 1 fi ZSH_BIN_KERNEL="$(printf '%s' "$ZSH_BIN_KERNEL" | sed 's/^\([^-]*-[0-9]*\.[0-9]*\).*/\1/')" if [ -n "$image" ]; then >&2 echo '[error] docker image (-i) is not supported on windows' exit 1 fi if [ -n "$docker" ]; then >&2 echo '[error] docker (-d) is not supported on windows' exit 1 fi ;; *) >&2 echo '[error] unsupported kernel, sorry!' exit 1 ;; esac >&2 echo "Building zsh..." >&2 echo "" [ -z "$docker" ] || >&2 echo " DOCKER=$docker" [ -z "$image" ] || >&2 echo " IMAGE=$image" >&2 echo " KERNEL=$ZSH_BIN_KERNEL" >&2 echo " ARCH=$ZSH_BIN_ARCH" >&2 echo " CPU=$ZSH_BIN_CPU" case "$ZSH_BIN_KERNEL" in linux) dir="$(pwd)" "$docker" run \ -e ZSH_BIN_KERNEL="$ZSH_BIN_KERNEL" \ -e ZSH_BIN_ARCH="$ZSH_BIN_ARCH" \ -e ZSH_BIN_CPU="$ZSH_BIN_CPU" \ -v "$dir":/out \ -w /out \ --rm \ -- "$image" /bin/sh -uexc "$build" ;; freebsd|darwin|msys*|mingw*|cygwin*) eval "$build" ;; *) >&2 echo "[internal error] unhandled kernel: $ZSH_BIN_KERNEL"; exit 1 ;; esac