diff --git a/patches/pkgbuild/cmake.sh b/patches/pkgbuild/cmake.sh index 557f110..55cb074 100755 --- a/patches/pkgbuild/cmake.sh +++ b/patches/pkgbuild/cmake.sh @@ -43,3 +43,42 @@ sed -n '/^makedepends=(/,/)/p' PKGBUILD | grep -q 'qt6-base' && { grep -q -- '--sphinx-man' PKGBUILD || { echo "cmake: man pages lost with the GUI" >&2; exit 1; } echo "cmake: --qt-gui dropped (no Qt, no display)" + +# emacs, for one lisp file, at the very last line of package(). +# +# PKGBUILD: line 64: emacs: command not found +# +# The build compiled everything, generated the sphinx documentation, installed +# the whole tree, and then died on +# +# emacs -batch -f batch-byte-compile .../site-lisp/cmake-mode.el +# +# make install already put cmake-mode.el in place; this only produces the +# byte-compiled .elc beside it. Emacs loads the .el perfectly well without it, +# a little slower on first load. Installing emacs -- forty megabytes and its +# own dependency tree -- to pre-compile one editor mode for a bootstrap that +# has no editor is not a trade worth making. +# +# THE SHAPE IS WORTH NOTING because it is the third time: pam's PDF removal, +# e2fsprogs' fuse2fs removal, and now this. A failure on the LAST line of +# package(), after a completely successful build, always looks like something +# broke badly. It never is -- it is a tool the host does not have, doing +# something optional. +set -euo pipefail +python3 - <<'ZZPY' +import io +s = io.open("PKGBUILD", encoding="utf-8").read() +old = ' emacs -batch -f batch-byte-compile "${pkgdir}"/usr/share/emacs/site-lisp/cmake-mode.el\n' +assert s.count(old) == 1, "cmake: the emacs byte-compile line is not in the expected form" +s = s.replace(old, "", 1) +old = "makedepends=(emacs\n" +assert s.count(old) == 1, "cmake: makedepends does not start with emacs" +s = s.replace(old, "makedepends=(\n", 1) +io.open("PKGBUILD", "w", encoding="utf-8").write(s) +ZZPY +grep -q 'emacs -batch' PKGBUILD && { echo "cmake: the emacs step survived" >&2; exit 1; } +sed -n '/^makedepends=(/,/)/p' PKGBUILD | grep -qw emacs && { + echo "cmake: emacs still in makedepends" >&2; exit 1; } +grep -q 'cmake-mode' PKGBUILD && { + echo "cmake: a cmake-mode reference remains -- check it is harmless" >&2; } +echo "cmake: emacs byte-compilation dropped (the .el is installed regardless)" diff --git a/patches/pkgbuild/readline.sh b/patches/pkgbuild/readline.sh new file mode 100755 index 0000000..a30ccac --- /dev/null +++ b/patches/pkgbuild/readline.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# readline: its .pc file requires a module nothing in the repository provides. +# +# Package 'termcap', required by 'readline', not found +# +# readline's configure writes `Requires.private: termcap` into readline.pc, +# because that is the historical name of the terminal library it links. Our +# ncurses package ships tinfo.pc and ncurses.pc -- there is no termcap.pc, and +# nothing else in the repository supplies one. +# +# Nothing fails at build time. The .pc is data, and pkg-config only follows +# Requires.private when a CONSUMER asks. The first consumer to ask was libxml2, +# inside the stage-2 chroot, and it stopped at meson configure -- three +# packages and one stage away from the cause. +# +# tinfo is the right name: it is what ncurses actually installs the terminfo +# routines into (`--with-termlib`), and readline links -lncurses which pulls it. +# This is the same substitution the host needed for history.pc, made for the +# same reason, in the other direction: there the file was missing and had to be +# generated, here it exists and names the wrong dependency. +# +# The package's .pc is patched in package(), after make install writes it, so +# nothing in the build sees a different file than what ships. +set -euo pipefail +python3 - <<'ZZPY' +import io, re +s = io.open("PKGBUILD", encoding="utf-8").read() +# AFTER make install, not at the top of package(). The first version anchored +# on `package() {` and ran the sed before the file existed: +# sed: can't read .../usr/lib/pkgconfig/readline.pc: No such file or directory +m = re.search(r'^ make -C \$pkgname-\$_basever DESTDIR="\$pkgdir" install\n', s, re.M) +assert m, "readline: the install line in package() is not in the expected form" +fix = """ # readline.pc says `Requires.private: termcap`; this repository ships + # tinfo.pc, not termcap.pc, so any consumer asking pkg-config about readline + # fails on a module nobody provides. + sed -i 's/^Requires.private: termcap$/Requires.private: tinfo/' \\ + "$pkgdir"/usr/lib/pkgconfig/readline.pc + grep -q '^Requires.private: tinfo$' "$pkgdir"/usr/lib/pkgconfig/readline.pc +""" +s = s[:m.end()] + fix + s[m.end():] +io.open("PKGBUILD", "w", encoding="utf-8").write(s) +ZZPY +grep -q 'Requires.private: tinfo' PKGBUILD || { + echo "readline: the .pc substitution was not inserted" >&2; exit 1; } +echo "readline: readline.pc now requires tinfo (termcap.pc does not exist here)" diff --git a/scripts/build-stage2.sh b/scripts/build-stage2.sh index a817980..9938f4d 100755 --- a/scripts/build-stage2.sh +++ b/scripts/build-stage2.sh @@ -123,7 +123,47 @@ configure_chroot() { # stages, and comparing them is the whole point. sudo sed -i 's|^OPTIONS=.*|OPTIONS=(strip docs !libtool !staticlibs emptydirs zipman purge !debug !lto)|' \ "$ROOT/etc/makepkg.conf" + # CFLAGS and friends, which arrive from the same place and are just as + # wrong. Our pacman package ships Arch's makepkg.conf verbatim, so the + # chroot inherits + # + # CFLAGS="-march=x86-64 -mtune=generic -O2 ... -fcf-protection ..." + # + # On s390x cc rejects that, and the failure surfaces nowhere near the + # cause: meson simply cannot start. + # + # libxml2/meson.build:1:0: ERROR: Unable to detect linker for compiler + # `cc -Wl,--version ... -march=x86-64 -mtune=generic ...` + # + # They are emptied rather than translated, because "no flags" is what stage + # 1 used -- the host's makepkg.conf carries no CFLAGS line at all -- and + # 179 working packages are the evidence that it builds. Choosing s390x + # tuning (-march=z13, and only the hardening flags that exist on Z) is a + # deliberate later step, not something to guess at inside a bootstrap. + # TODO.md records it. + # + # APPENDED, not commented. The first attempt here put a # in front of each + # assignment, and CFLAGS is a MULTI-LINE assignment: commenting its first + # line left the continuations active and the quote unbalanced, so makepkg + # would not start at all -- + # + # /etc/makepkg.conf: line 109: unexpected EOF while looking for matching `"' + # + # An override at the end of the file needs no parsing of what came before: + # the last assignment is the one that counts. + sudo tee -a "$ROOT/etc/makepkg.conf" > /dev/null <<'EOC' + +# --- stage 2: the shipped values are Arch's x86_64 ones --- +CFLAGS="" +CXXFLAGS="" +LDFLAGS="" +LTOFLAGS="" +RUSTFLAGS="" +DEBUG_CFLAGS="" +DEBUG_CXXFLAGS="" +EOC sudo grep -E '^(CARCH|CHOST|MAKEFLAGS|OPTIONS)=' "$ROOT/etc/makepkg.conf" | sed 's/^/ /' + printf ' compiler flags: commented out (stage 1 used none)\n' # makepkg refuses to run as root, so the chroot needs the SAME uid as the # user who owns the bind-mounted sources. A bind mount carries the host's @@ -251,7 +291,9 @@ NOTE # git.sh joins it for the same reason: it drops ZLIB_NG=1 because Ubuntu has no # zlib-ng headers, and our own zlib-ng package ships them, so inside the chroot # the flag is correct and the hook would be a downgrade. -STAGE2_SKIP_HOOKS=(libgcrypt git) +# meson.sh joins them: it moves a wheel out of /usr/local, which only the +# host's Debian-patched python puts there. +STAGE2_SKIP_HOOKS=(libgcrypt git meson) # Packages whose sources must be extracted on the HOST, because the chroot # cannot extract anything until they are rebuilt.