[FIX] the build host answered questions the target should
Five packages, one shape: a default computed from this machine rather than from Arch, and arch-meson's --auto-features enabled turning what was optional into a requirement. dbus taught the method. It stopped three times in a row on a different documentation tool -- ducktype, then yelp-build, then qhelpgenerator. The first two were installed. The third needs Qt, so the chain had no end. All three are `auto` features promoted to mandatory; pinned off, they cost nothing. A single missing library is a host dependency. A queue of them is a feature that should be disabled. pinentry wanted Qt6 for a passphrase prompt; tty and curses remain. krb5 asked for a system ss library Ubuntu ships no headers for, and said "cannot run test program" instead of saying so. sqlite found tcl, then installed into share/tcltk because Ubuntu's tclConfig.sh says so and Arch's says lib. gettext linked the host's libselinux, silently -- the sixth package to do it, and the first the audit caught rather than a build. --- FR --- Cinq paquets, une seule forme : une valeur par défaut calculée depuis cette machine plutôt que depuis Arch, et le --auto-features enabled d'arch-meson qui transforme l'optionnel en exigence. dbus a enseigné la méthode. Il s'est arrêté trois fois de suite sur un outil de documentation différent — ducktype, puis yelp-build, puis qhelpgenerator. Les deux premiers ont été installés. Le troisième exige Qt : la chaîne ne terminait nulle part. Les trois sont des features `auto` promues en obligations ; épinglées à disabled, elles ne coûtent rien. Une bibliothèque manquante est une dépendance hôte. Une file d'attente en est une fonctionnalité à désactiver. pinentry réclamait Qt6 pour une invite de mot de passe ; tty et curses suffisent. krb5 demandait une bibliothèque ss système dont Ubuntu ne livre aucun en-tête, et annonçait « cannot run test program » plutôt que de le dire. sqlite trouvait tcl, puis installait dans share/tcltk parce que le tclConfig.sh d'Ubuntu le dit là où celui d'Arch dit lib. gettext liait le libselinux de l'hôte, en silence — sixième paquet à le faire, et le premier que l'audit a pris plutôt qu'une compilation. Assisted-by: Claude Opus 5
This commit is contained in:
parent
3c926f10a9
commit
09ef26798a
5 changed files with 279 additions and 0 deletions
84
patches/pkgbuild/dbus.sh
Executable file
84
patches/pkgbuild/dbus.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/env bash
|
||||
# dbus: three documentation generators, made mandatory by arch-meson.
|
||||
#
|
||||
# The build stopped three times in a row, on a different tool each time:
|
||||
#
|
||||
# ERROR: Program 'ducktype' not found or not executable
|
||||
# ERROR: Program 'yelp-build' not found or not executable
|
||||
# ERROR: Program 'qhelpgenerator qhelpgenerator-qt5' not found
|
||||
#
|
||||
# Each looked like its own missing host package, and the first two were duly
|
||||
# installed. THE TRAP is that chasing them one at a time is the wrong shape:
|
||||
# the third needs Qt, so the chain does not terminate anywhere reasonable, and
|
||||
# none of the three was ever really required.
|
||||
#
|
||||
# meson.build asks for them like this:
|
||||
#
|
||||
# doxygen = find_program('doxygen', required: get_option('doxygen_docs'))
|
||||
# ducktype = find_program('ducktype', required: get_option('ducktype_docs'))
|
||||
# yelpbuild = find_program('yelp-build', required: get_option('ducktype_docs'))
|
||||
# qhelpgen = find_program('qhelpgenerator', ..., required: get_option('qt_help'))
|
||||
#
|
||||
# and all three options are features declared with no value -- 'auto'.
|
||||
# arch-meson passes --auto-features enabled, which promotes every one of them
|
||||
# to a hard requirement. On Arch that is harmless: the doc tools are in the
|
||||
# build chroot. Here it turned optional documentation into three consecutive
|
||||
# build failures. Same mechanism as util-linux's translated man pages and
|
||||
# expat's docbook converter; dbus is just the clearest instance, because it
|
||||
# repeated three times in one afternoon.
|
||||
#
|
||||
# xml_docs is deliberately LEFT ALONE: it generates the man pages through
|
||||
# xsltproc, which is installed, and man pages are worth having.
|
||||
#
|
||||
# The dbus-docs sub-package goes with the options. Its `_pick docs
|
||||
# usr/share/doc` is a mv, so with nothing generated it would fail the way
|
||||
# every other _pick on an absent path in this port has failed -- after a
|
||||
# successful compile, at the last step, naming a directory rather than a
|
||||
# reason. Deferred, not architectural: TODO.md records the way back.
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
import io, re
|
||||
s = io.open("PKGBUILD", encoding="utf-8").read()
|
||||
|
||||
# (a) pin the three features off, beside the options already being pinned
|
||||
anchor = " -D apparmor=disabled\n"
|
||||
assert s.count(anchor) == 1, "dbus: meson_options array not in the expected form"
|
||||
s = s.replace(anchor,
|
||||
anchor +
|
||||
" -D doxygen_docs=disabled\n"
|
||||
" -D ducktype_docs=disabled\n"
|
||||
" -D qt_help=disabled\n", 1)
|
||||
|
||||
# (b) the sub-package that would have nothing to package
|
||||
m = re.search(r"pkgname=\((.*?)\)\n", s, re.S)
|
||||
assert m, "dbus: pkgname array not found"
|
||||
names = [n for n in re.findall(r"[A-Za-z0-9._+-]+", m.group(1))]
|
||||
assert "dbus-docs" in names, "dbus: dbus-docs not in pkgname"
|
||||
kept = [n for n in names if n != "dbus-docs"]
|
||||
s = s[:m.start()] + "pkgname=(\n" + "".join(" %s\n" % n for n in kept) + ")\n" + s[m.end():]
|
||||
|
||||
# (c) and the _pick that feeds it
|
||||
old = ' _pick docs "$pkgdir"/usr/share/doc\n'
|
||||
assert s.count(old) == 1, "dbus: docs _pick not in the expected form"
|
||||
s = s.replace(old, "", 1)
|
||||
|
||||
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
|
||||
PY
|
||||
for o in doxygen_docs ducktype_docs qt_help; do
|
||||
grep -qF -- "-D $o=disabled" PKGBUILD || {
|
||||
echo "dbus: $o not pinned off" >&2; exit 1; }
|
||||
done
|
||||
grep -qF '_pick docs' PKGBUILD && {
|
||||
echo "dbus: docs _pick survived and would fail on an absent path" >&2; exit 1; }
|
||||
python3 - <<'PY'
|
||||
import io, re, sys
|
||||
s = io.open("PKGBUILD", encoding="utf-8").read()
|
||||
m = re.search(r"pkgname=\((.*?)\)\n", s, re.S)
|
||||
if not m:
|
||||
sys.exit("dbus: pkgname array unreadable after patching")
|
||||
names = re.findall(r"[A-Za-z0-9._+-]+", m.group(1))
|
||||
if "dbus-docs" in names:
|
||||
sys.exit("dbus: dbus-docs still declared in pkgname")
|
||||
print("dbus: pkgname -> %s" % " ".join(names))
|
||||
PY
|
||||
echo "dbus: doc generators pinned off (--auto-features made three of them fatal)"
|
||||
36
patches/pkgbuild/gettext.sh
Executable file
36
patches/pkgbuild/gettext.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/usr/bin/env bash
|
||||
# gettext: --without-selinux. The sixth package to pick up the host's
|
||||
# libselinux, and the first one nothing warned about.
|
||||
#
|
||||
# $ readelf -d usr/lib/libgettextlib-1.0.so | grep NEEDED
|
||||
# (NEEDED) Shared library: [libselinux.so.1]
|
||||
#
|
||||
# Nothing failed. gettext built, passed, and went into the repository with a
|
||||
# library linked against something Arch has no package for -- the artefact
|
||||
# audit is the only thing that found it, exactly as it found coreutils,
|
||||
# findutils, sed and tar before it.
|
||||
#
|
||||
# WHY IT CAME BACK. Those four were fixed with per-package hooks, and the
|
||||
# reasoning at the time was that the same gnulib probe "catches findutils, sed
|
||||
# and tar" -- a list of the packages then in the build. gettext was not in the
|
||||
# port yet. It arrived with the dependency closure, uses gnulib too, probes
|
||||
# selinux/selinux.h unconditionally, and found Ubuntu's libselinux1-dev
|
||||
# sitting where it always was.
|
||||
#
|
||||
# So this is the fifth copy of one three-line fix, and that is the real
|
||||
# finding: the per-package hook does not scale to a probe that any gnulib
|
||||
# package can trip. The alternative -- exporting
|
||||
# ac_cv_header_selinux_selinux_h=no once in build_package -- was considered
|
||||
# and rejected when coreutils was fixed, on the grounds that a global cache
|
||||
# override is invisible at the point where it acts. That trade has now been
|
||||
# paid for five times. TODO.md records it as a decision to revisit rather than
|
||||
# leaving the next person to rediscover the arithmetic.
|
||||
#
|
||||
# Arch's gettext links no libselinux, so this converges with Arch.
|
||||
set -euo pipefail
|
||||
grep -q '^ --without-included-libunistring$' PKGBUILD || {
|
||||
echo "gettext: configure block not in the expected form" >&2; exit 1; }
|
||||
sed -i 's|^\( --without-included-libunistring\)$|\1 \\\n --without-selinux|' PKGBUILD
|
||||
[ "$(grep -c -- '--without-selinux' PKGBUILD)" = 1 ] || {
|
||||
echo "gettext: --without-selinux not inserted exactly once" >&2; exit 1; }
|
||||
echo "gettext: --without-selinux (gnulib found the host's libselinux)"
|
||||
30
patches/pkgbuild/krb5.sh
Executable file
30
patches/pkgbuild/krb5.sh
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env bash
|
||||
# krb5: --with-system-ss, and Ubuntu ships no ss headers.
|
||||
#
|
||||
# checking whether system ss package works... configure: error: cannot run
|
||||
# test program
|
||||
#
|
||||
# THE TRAP is that message. "cannot run test program" reads like a broken
|
||||
# cross-compile or a missing loader -- the kind of thing that sends you into
|
||||
# config.log looking for a link failure on this architecture. It is neither.
|
||||
# The subsystem library (ss, from e2fsprogs) has no development package on
|
||||
# Ubuntu at all: libss2 is runtime-only, libss-dev does not exist, and
|
||||
# e2fslibs-dev has no candidate. There is no ss.h to compile against, so the
|
||||
# test program cannot be built, let alone run.
|
||||
#
|
||||
# Note what IS present, so the two are not confused: --with-system-et works,
|
||||
# because comerr-dev ships /usr/include/et/com_err.h. Only ss is missing.
|
||||
#
|
||||
# krb5 carries its own copy of the subsystem library and uses it when not told
|
||||
# otherwise, so dropping the flag costs nothing but a slightly larger binary.
|
||||
# Building e2fsprogs to supply one header would be the other answer; it is not
|
||||
# worth a package in the closure, and TODO.md records the choice.
|
||||
set -euo pipefail
|
||||
grep -qE '^\s+--with-system-ss \\$' PKGBUILD || {
|
||||
echo "krb5: --with-system-ss line not in the expected form" >&2; exit 1; }
|
||||
[ "$(grep -c -- '--with-system-ss' PKGBUILD)" = 1 ] || {
|
||||
echo "krb5: expected exactly one --with-system-ss" >&2; exit 1; }
|
||||
sed -i '/^\s\+--with-system-ss \\$/d' PKGBUILD
|
||||
grep -q -- '--with-system-ss' PKGBUILD && {
|
||||
echo "krb5: --with-system-ss survived" >&2; exit 1; }
|
||||
echo "krb5: uses its bundled ss (Ubuntu has no ss development package)"
|
||||
84
patches/pkgbuild/pinentry.sh
Executable file
84
patches/pkgbuild/pinentry.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
|||
#!/usr/bin/env bash
|
||||
# pinentry: three graphical front ends, on a machine with no display stack.
|
||||
#
|
||||
# configure: error:
|
||||
# ***
|
||||
# *** Qt6 (Qt6Core, Qt6Gui, Qt6Widgets) is required.
|
||||
# ***
|
||||
#
|
||||
# Arch builds every front end pinentry has -- tty, curses, emacs, gnome3 and
|
||||
# qt -- and declares qt6-base, gcr, kguiaddons and kwindowsystem as
|
||||
# makedepends. None of that exists on this build host, and none of it belongs
|
||||
# in a bootstrap: pacman needs a passphrase prompt on a terminal, nothing more.
|
||||
#
|
||||
# THE HONEST FRAMING. This is not architectural -- Qt runs on s390x perfectly
|
||||
# well. It is deferred, and TODO.md says so: a desktop on Z would want these
|
||||
# back, and the way back is to build qt6-base and the KDE pieces first. What
|
||||
# is not defensible is dragging a GUI toolkit into the closure of a package
|
||||
# manager's passphrase prompt.
|
||||
#
|
||||
# THE SECOND PLACE, and then a THIRD. libsecret and glib2 are in the top-level
|
||||
# depends= and exist only to serve the front ends being switched off. --nodeps
|
||||
# means makepkg never checks them, so leaving them would ship a pinentry that
|
||||
# cannot install -- the same trap as gcc-libs/libhwasan, make/guile and
|
||||
# gnutls/leancrypto.
|
||||
#
|
||||
# The third place is package(), which APPENDS more:
|
||||
#
|
||||
# depends+=( libglib-2.0.so libncursesw.so libsecret-1.so )
|
||||
#
|
||||
# The first version of this hook fixed the array at the top of the file, ran
|
||||
# its guard against that array, passed, and shipped the append untouched. The
|
||||
# guard checked the place I was thinking about rather than every place the
|
||||
# name occurs -- which is exactly the mistake the note above warns against.
|
||||
# Only libncursesw.so survives; the other two name libraries this build no
|
||||
# longer links.
|
||||
#
|
||||
# tty, curses and fallback-curses stay, and so does emacs: it is a protocol
|
||||
# over a pipe, not a toolkit, and it links nothing extra.
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
import io
|
||||
s = io.open("PKGBUILD", encoding="utf-8").read()
|
||||
|
||||
for flag in ("--enable-pinentry-gnome3", "--enable-pinentry-qt", "--enable-libsecret"):
|
||||
off = flag.replace("--enable-", "--disable-", 1)
|
||||
assert s.count(flag) == 1, "pinentry: expected exactly one %s" % flag
|
||||
s = s.replace(flag, off, 1)
|
||||
|
||||
old = " 'glibc' 'ncurses' 'libassuan' 'libsecret' 'glib2'\n"
|
||||
assert s.count(old) == 1, "pinentry: depends block not in the expected form"
|
||||
s = s.replace(old, " 'glibc' 'ncurses' 'libassuan'\n", 1)
|
||||
|
||||
# package() appends three sonames; two of them are gone with the front ends.
|
||||
old = " depends+=(\n libglib-2.0.so\n libncursesw.so\n libsecret-1.so\n )\n"
|
||||
assert s.count(old) == 1, "pinentry: package() depends+= not in the expected form"
|
||||
s = s.replace(old, " depends+=(\n libncursesw.so\n )\n", 1)
|
||||
|
||||
# package() renames the GTK binary, which is no longer built.
|
||||
old = ' # The -gtk backend has been built to be used with GTK3.\n mv "${pkgdir}/usr/bin/pinentry-gtk"{-2,}\n'
|
||||
assert s.count(old) == 1, "pinentry: gtk rename not in the expected form"
|
||||
s = s.replace(old, "", 1)
|
||||
|
||||
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
|
||||
PY
|
||||
for f in gnome3 qt libsecret; do
|
||||
grep -q -- "--disable-pinentry-$f\|--disable-$f" PKGBUILD || {
|
||||
echo "pinentry: $f front end still enabled" >&2; exit 1; }
|
||||
done
|
||||
grep -qE "^ 'glibc' 'ncurses' 'libassuan'$" PKGBUILD || {
|
||||
echo "pinentry: depends not reduced" >&2; exit 1; }
|
||||
# EVERY place the names occur -- but as they are actually WRITTEN, not as bare
|
||||
# substrings. The first version of this loop grepped for `libsecret` and
|
||||
# matched the `--disable-libsecret` it had just inserted, so a correct patch
|
||||
# reported failure. Third guard in this port to check something adjacent to
|
||||
# what it meant; the lesson is that a guard needs the same care as the edit.
|
||||
for n in "'libsecret'" "'glib2'" "libglib-2.0.so" "libsecret-1.so" \
|
||||
"--enable-libsecret" "--enable-pinentry-qt" "--enable-pinentry-gnome3" \
|
||||
'pinentry-gtk"{-2,}'; do
|
||||
grep -qF -- "$n" PKGBUILD && {
|
||||
echo "pinentry: $n still present" >&2; exit 1; }
|
||||
done
|
||||
grep -q -- "--enable-pinentry-curses" PKGBUILD || {
|
||||
echo "pinentry: curses front end lost -- nothing would prompt" >&2; exit 1; }
|
||||
echo "pinentry: tty/curses/emacs only (no Qt, GNOME or libsecret on this host)"
|
||||
45
patches/pkgbuild/sqlite.sh
Executable file
45
patches/pkgbuild/sqlite.sh
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env bash
|
||||
# sqlite: the tcl extension installs where Ubuntu's tclConfig.sh says, not
|
||||
# where Arch's does.
|
||||
#
|
||||
# mv: cannot stat '<pkgdir>/usr/lib/tcl8.6/sqlite*': No such file or directory
|
||||
#
|
||||
# THE TRAP is that tcl was found. The log says so, plainly:
|
||||
#
|
||||
# Checking for tclsh8.6.../usr/bin/tclsh8.6
|
||||
# Using tclConfig.sh: /usr/lib/s390x-linux-gnu/tclConfig.sh
|
||||
#
|
||||
# so "install tcl-dev" looks like it worked, and the failure looks like a
|
||||
# separate problem. It is the same problem one layer down: tclConfig.sh
|
||||
# carries TCL_PACKAGE_PATH, and Ubuntu's says share/tcltk while Arch's says
|
||||
# lib. Measured in the pkgdir:
|
||||
#
|
||||
# usr/share/tcltk/tcl8.6/sqlite3.53.4/libsqlite3.53.4.so
|
||||
#
|
||||
# and usr/lib/tcl8.6 does not exist at all. This is the multiarch libdir theme
|
||||
# again -- a path the BUILD HOST decides -- but one layer out from the library
|
||||
# directory itself, which is why --libdir does not reach it.
|
||||
#
|
||||
# The tree is relocated to Arch's path before package_sqlite() splits it out,
|
||||
# so the upstream mv keeps working unchanged. Same shape as the lib64 merge in
|
||||
# gcc.sh: move the tree, do not rewrite every path that names it.
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
import io
|
||||
s = io.open("PKGBUILD", encoding="utf-8").read()
|
||||
anchor = " # split out tcl extension\n"
|
||||
assert s.count(anchor) == 1, "sqlite: expected one tcl split block"
|
||||
fix = """ # Ubuntu's tclConfig.sh puts the tcl package tree under share/tcltk;
|
||||
# Arch's puts it under lib. Move it before the split below reads it.
|
||||
if [ -d "$pkgdir"/usr/share/tcltk/tcl8.6 ]; then
|
||||
mkdir -p "$pkgdir"/usr/lib
|
||||
mv "$pkgdir"/usr/share/tcltk/tcl8.6 "$pkgdir"/usr/lib/tcl8.6
|
||||
rmdir --ignore-fail-on-non-empty "$pkgdir"/usr/share/tcltk
|
||||
fi
|
||||
"""
|
||||
s = s.replace(anchor, fix + anchor, 1)
|
||||
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
|
||||
PY
|
||||
grep -q "tclConfig.sh puts the tcl package tree" PKGBUILD || {
|
||||
echo "sqlite: tcl relocation not inserted" >&2; exit 1; }
|
||||
echo "sqlite: tcl extension relocated from share/tcltk to lib (host TCL_PACKAGE_PATH)"
|
||||
Loading…
Reference in a new issue