Compare commits

..

No commits in common. "327ff55665877866a0d2772e7ff33057027aa6bf" and "4101d18f72a53c4695a7f9fedf107155347ccab2" have entirely different histories.

27 changed files with 60 additions and 1379 deletions

View file

@ -1,47 +0,0 @@
#!/usr/bin/env bash
# autoconf-archive: take the patch from a mirror savannah's cgit will serve.
#
# curl: (52) Empty reply from server
# ==> ERROR: Failure while downloading
# https://cgit.git.savannah.gnu.org/cgit/autoconf-archive.git/patch/?id=241f7ae...
#
# The tarball comes from ftpmirror.gnu.org and arrives fine. What fails is a
# single patch fetched from savannah's cgit /patch/?id= endpoint, which returns
# nothing at all -- measured, not assumed:
#
# savannah /patch/?id= (no response)
# github /commit/.patch 200, 2463 bytes
#
# savannah itself is up (its root answers 302); it is that endpoint, which cgit
# generates on demand and rate-limits.
#
# The mirror is asked for THE SAME COMMIT, by its full hash, so this changes
# where the bytes come from and not which bytes they are -- the substantive
# difference from replacing a source with a different version of itself. GitHub
# renders `git format-patch` output where cgit renders its own; both are what
# `patch` expects, and the build fails loudly if that were untrue.
#
# WHY NOT DROP THE PATCH. It is 0001_fix_AX_CXX_COMPILE_STDCXX.patch, a fix to a
# macro this port needs -- tpm2-tss stopped on a missing AX_* macro, which is why
# autoconf-archive is in the list at all.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "https://cgit.git.savannah.gnu.org/cgit/autoconf-archive.git/patch/?id="
assert s.count(old) == 1, "autoconf-archive: expected one cgit patch URL, got %d" % s.count(old)
# The commit hash follows the URL, up to the closing quote. Reused verbatim: the
# point of the substitution is that the content is identical.
m = re.search(re.escape(old) + r"([0-9a-f]{40})", s)
assert m, "autoconf-archive: no 40-character commit hash after the cgit URL"
sha = m.group(1)
s = s.replace(old + sha,
"https://github.com/autoconf-archive/autoconf-archive/commit/%s.patch" % sha, 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
print(" patch commit: %s" % sha[:12])
ZZPY
grep -q "cgit.git.savannah.gnu.org" PKGBUILD && {
echo "autoconf-archive: a cgit URL survived" >&2; exit 1; }
grep -qE "github.com/autoconf-archive/autoconf-archive/commit/[0-9a-f]{40}\.patch" PKGBUILD || {
echo "autoconf-archive: the mirror URL is malformed" >&2; exit 1; }
echo "autoconf-archive: patch taken from the git mirror (same commit)"

View file

@ -98,105 +98,36 @@ grep -qE "^[[:space:]]*--enable-pgo-build" PKGBUILD && {
echo "binutils: an active --enable-pgo-build line survived" >&2; exit 1; }
echo "binutils: PGO+LTO build dropped"
# --- no gold, and no s390-specific extra target -------------------------------
# --- the extra targets are x86_64's --------------------------------------------
#
# This section was written twice, and the first version was wrong in an
# instructive way.
# s390.cc:(.text+0x2d76): undefined reference to
# `void gold::gold_error_at_location<32, true>(...)'
#
# gold would not link:
# gold's s390 target file compiles code for BOTH s390 (32-bit) and s390x, and
# references template instantiations for <32, true>. Those exist only if a
# 32-bit s390 target is configured -- and the PKGBUILD asks for
#
# s390.cc: undefined reference to `gold::gold_error_at_location<32, true>'
# --enable-targets=x86_64-pep,bpf-unknown-none
#
# gold's s390 target file compiles for both s390 and s390x and needs <32, true>
# template instantiations, which exist only when a 32-bit s390 target is
# configured. The PKGBUILD asked for --enable-targets=x86_64-pep, so the first
# fix translated that to s390-linux-gnu -- the honest-looking equivalent. bfd's
# configure answered:
# x86_64-pep is the PE+ target for x86_64. It means nothing on this machine, and
# its presence is why gold linked on Arch and not here: on x86_64 that line
# happens to bring in the 32-bit instantiations gold's target files want.
#
# *** Specify --enable-obsolete to build it anyway.
# *** Support will be REMOVED in the next major release of BINUTILS,
# *** unless a maintainer comes forward.
#
# 32-bit s390 is OBSOLETE in binutils. So gold on s390x cannot be built without
# enabling a target upstream has announced it is deleting -- and gold itself is
# deprecated, with bfd ld the default here already (--enable-ld=default). Turning
# on one deprecated thing to build another is not a trade worth making.
#
# So: gold is dropped, and the extra target list keeps only bpf-unknown-none,
# which is architecture-neutral. x86_64-pep goes because it names a format for a
# machine this is not.
#
# WHAT IS LOST: /usr/bin/ld.gold. Nothing in this port invokes it, ld is the
# default, and upstream is removing gold. Recorded rather than hidden -- the
# stage-2 binutils will not match Arch's file list, and this is why.
# So this is not a workaround for a gold bug -- it is the same x86_64 assumption
# as the -march=x86-64 in makepkg.conf, one layer further in. s390-linux-gnu is
# the honest translation of that line, and it is what makes the templates exist.
# bpf-unknown-none stays: it is architecture-neutral.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
t = [i for i, l in enumerate(lines) if "--enable-targets=" in l]
assert len(t) == 1, "binutils: expected one --enable-targets, got %d" % len(t)
assert "x86_64-pep" in lines[t[0]], "binutils: --enable-targets is not the x86 one: %r" % lines[t[0]]
lines[t[0]] = lines[t[0]].replace("x86_64-pep,", "")
g = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*--enable-gold[ \t]*\\?$", l)]
assert len(g) == 1, "binutils: expected one --enable-gold line, got %d" % len(g)
lines[g[0]] = lines[g[0]].replace("--enable-gold", "--disable-gold")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "--enable-targets=x86_64-pep,bpf-unknown-none"
assert s.count(old) == 1, "binutils: expected one --enable-targets, got %d" % s.count(old)
s = s.replace(old, "--enable-targets=s390-linux-gnu,bpf-unknown-none", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q "x86_64-pep" PKGBUILD && { echo "binutils: an x86_64 target survived" >&2; exit 1; }
grep -q -- "--disable-gold" PKGBUILD || { echo "binutils: gold not disabled" >&2; exit 1; }
grep -q -- "--enable-gold" PKGBUILD && { echo "binutils: gold still enabled" >&2; exit 1; }
echo "binutils: gold dropped (its s390 support needs an obsolete target)"
# --- libiberty still records a usr/lib64 path ---------------------------------
#
# --libdir=/usr/lib above is not enough. pacman refused to install the result:
#
# binutils: <root>/usr/lib64 exists in filesystem (owned by filesystem)
# binutils: <root>/usr/lib64/libiberty.a exists in filesystem
#
# libiberty installs into $(libdir)$(MULTIOSSUBDIR), and on s390x gcc reports
# ../lib64 for -print-multi-os-directory. In the INSTALLED system that resolves
# through the symlink the filesystem package now provides, so the file lands in
# /usr/lib -- but pkgdir has no such symlink, so make creates a real
# pkg/usr/lib64/ and makepkg records `usr/lib64/libiberty.a` as the package's
# own path. Installing it then collides with the symlink.
#
# The two fixes are not alternatives: filesystem's symlink is what keeps meson
# and cmake choosing lib, and this moves the one file that still writes through
# the multi-os subdirectory. Done in package(), on pkgdir, so nothing depends on
# the order the two packages are installed in.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
# The last package function is where pkgdir is complete.
# package(), not package_binutils(). binutils is NOT a split package here --
# pkgname=binutils, one function -- and the first version of this assumed the
# split form and asserted its way out. Both shapes are in this port; a hook has
# to look.
hit = [i for i, l in enumerate(lines) if re.match(r"^package(_binutils)?\(\)", l)]
assert len(hit) == 1, "binutils: expected one package function, got %d" % len(hit)
# Find that function's closing brace.
i = hit[0] + 1
while i < len(lines) and lines[i] != "}":
i += 1
assert i < len(lines), "binutils: package_binutils() has no closing brace"
lines[i:i] = [
"",
" # libiberty writes through gcc's multi-os subdirectory (../lib64 on",
" # s390x). In the installed system usr/lib64 is a symlink to lib, but pkgdir",
" # has no symlink, so the path is recorded literally and collides with the",
" # filesystem package. Move it to where it resolves to anyway.",
' if [ -d "$pkgdir/usr/lib64" ]; then',
' install -d "$pkgdir/usr/lib"',
' mv "$pkgdir"/usr/lib64/* "$pkgdir/usr/lib/"',
' rmdir "$pkgdir/usr/lib64"',
' fi',
]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q 'rmdir "\$pkgdir/usr/lib64"' PKGBUILD || {
echo "binutils: the lib64 move was not inserted" >&2; exit 1; }
echo "binutils: libiberty moved out of usr/lib64"
grep -q -- "--enable-targets=s390-linux-gnu" PKGBUILD || {
echo "binutils: the target list was not translated" >&2; exit 1; }
grep -q "x86_64-pep" PKGBUILD && {
echo "binutils: an x86_64 target survived" >&2; exit 1; }
echo "binutils: extra targets translated to s390 (gold needs the 32-bit one)"

View file

@ -1,39 +0,0 @@
#!/usr/bin/env bash
# clang: no Sphinx documentation.
#
# -DLLVM_ENABLE_SPHINX=ON pulls in python-sphinx and python-myst-parser, and
# behind those sit docutils, babel, pygments, snowballstemmer, imagesize,
# alabaster, the sphinxcontrib-* family, markdown-it-py and mdit-py-plugins --
# fifteen or so packages whose entire purpose here would be to render HTML for a
# compiler nothing in this port reads the manual of.
#
# MEASURED BEFORE CHOOSING, the way glib2 and iproute2 were: llvm and clang need
# only libedit and python-psutil beyond what this port already has. Sphinx is the
# whole of the rest of the bill, and one flag removes it.
#
# AND THE CLEANUP, found before it failed. package() ends on
#
# rm -r "$pkgdir"/usr/share/doc/.../html/{_sources,.buildinfo}
#
# which Sphinx creates and which will not exist. This is the seventeenth
# appearance of that shape in this port; the procedure written in jsoncpp's hook
# says to grep package() for the OUTPUT paths after disabling a documentation
# build, and this is the first time it was done before the build rather than
# after. rm -rf rather than deleted: on a machine with Sphinx the directory is
# real and still has to go.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-DLLVM_ENABLE_SPHINX=ON"
assert s.count(old) == 1, "clang: expected one LLVM_ENABLE_SPHINX, got %d" % s.count(old)
s = s.replace(old, "-DLLVM_ENABLE_SPHINX=OFF", 1)
lines = s.split("\n")
hit = [i for i, l in enumerate(lines) if "_sources" in l and l.lstrip().startswith("rm ")]
assert len(hit) == 1, "clang: expected one _sources removal, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("rm -r ", "rm -rf ", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-DLLVM_ENABLE_SPHINX=OFF" PKGBUILD || { echo "clang: sphinx not disabled" >&2; exit 1; }
grep -qE "^[[:space:]]*rm -rf .*_sources" PKGBUILD || { echo "clang: the cleanup is not tolerant" >&2; exit 1; }
echo "clang: Sphinx documentation disabled, cleanup made tolerant"

View file

@ -82,54 +82,3 @@ sed -n '/^makedepends=(/,/)/p' PKGBUILD | grep -qw emacs && {
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)"
# --- no Sphinx documentation ---------------------------------------------------
#
# CMake Error at Utilities/Sphinx/CMakeLists.txt:52 (message):
#
# cmake builds its own manual and man pages with Sphinx, requested by
# --sphinx-man and --sphinx-html in the bootstrap arguments. python-sphinx is not
# in this port: it is a large Python documentation stack, and cmake is wanted here
# as a build tool.
#
# BOTH FLAGS, and they are bootstrap arguments rather than -D options -- cmake
# configures itself with its own script before it can run itself. Dropping only
# one leaves the other pulling Sphinx in, and the error is identical, which is how
# a half-fix looks like no fix.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*--sphinx-(man|html)[ \t]*\\\\?$", l)]
assert len(hit) == 2, "cmake: expected two --sphinx flags, got %d" % len(hit)
# Backwards: deleting shifts everything after it.
for i in reversed(hit):
del lines[i]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*--sphinx-" PKGBUILD && {
echo "cmake: a --sphinx flag survived" >&2; exit 1; }
echo "cmake: Sphinx manual and man pages dropped"
# --- and the html it no longer generates --------------------------------------
#
# rm: cannot remove '<pkgdir>/usr/share/doc/cmake/html/_sources': No such file
#
# ELEVENTH time in this port. With Sphinx off, cmake generates no html, and the
# PKGBUILD deletes _sources from it -- Sphinx leaves the reStructuredText inputs
# beside the rendered pages and they are not wanted in a package.
#
# rm -rf rather than deleted: on a machine with Sphinx the directory is real and
# still has to go.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if "_sources" in l and l.lstrip().startswith("rm ")]
assert len(hit) == 1, "cmake: expected one _sources removal, got %d" % len(hit)
i = hit[0]
lines[i] = lines[i].replace("rm -r ", "rm -rf ", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*rm -rf .*_sources" PKGBUILD || {
echo "cmake: the _sources removal was not made tolerant" >&2; exit 1; }
echo "cmake: _sources cleanup tolerates its absence"

View file

@ -24,17 +24,6 @@
# choice will have to be made then: build Rust, or drop the flag. Recorded now
# rather than discovered twice.
set -euo pipefail
# STAGE 1 ONLY -- this section, not this file.
#
# It used to be expressed by naming this hook in STAGE2_SKIP_HOOKS, which skips
# the whole FILE. Then a section was appended that both stages need, and it
# silently never ran: the package kept failing in stage 2 on the very thing the
# new section fixes, and the log said "hook skipped (stage-1 only)" about a hook
# that was now two thirds relevant.
#
# The scope belongs next to the reason, so each section decides for itself.
if [ "${EL_STAGE:-1}" = 1 ]; then
python3 - <<'PY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
@ -56,9 +45,6 @@ grep -q "'zlib-ng'" PKGBUILD && {
grep -qF "'grep' 'shadow' 'zlib')" PKGBUILD || {
echo "git: zlib not substituted in depends" >&2; exit 1; }
echo "git: plain zlib (no zlib-ng headers on the host); STAGE-1 ONLY"
else
echo "git: zlib-ng available in the chroot; keeping Arch's choice"
fi
# --- no man pages -------------------------------------------------------------
#
@ -90,110 +76,3 @@ ZZPY
grep -qE "\ball man\b|\binstall-man\b" PKGBUILD && {
echo "git: a man target survived" >&2; exit 1; }
echo "git: man pages dropped (no asciidoc, four places)"
# --- no Rust ------------------------------------------------------------------
#
# cargo: command not found
#
# git's PKGBUILD passes WITH_RUST=1 and lists rust in makedepends. This port does
# not package Rust, and packaging it is not a package: rustc bootstraps from a
# previous rustc, which is the one thing a from-source distribution cannot start
# from -- Arch ships a binary to break that cycle, and this port has no such
# escape on s390x.
#
# WHY IT SURFACED ONLY NOW. Stage 1 built git happily: the HOST has rustc, from
# apt. So this is another entry in the longest-running theme of the port -- a
# dependency the host satisfied invisibly, appearing the first time the chroot is
# the one being asked. And it appeared only after the man-page section above
# started running at stage 2, which it had not been doing.
#
# WITH_RUST=1 selects Rust reimplementations of some internals; git builds and
# behaves the same without it. Both stages, so the two agree: stage 1 was
# producing a git that stage 2 could not reproduce.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
# NO_RUST=1, not merely dropping WITH_RUST=1.
#
# Removing WITH_RUST=1 changed nothing -- cargo was still invoked:
#
# CARGO target/release/libgitcore.a
# /bin/sh: line 1: cargo: command not found
#
# The Makefile reads
#
# ifndef NO_RUST
# BASIC_CFLAGS += -DWITH_RUST
# GITLIBS += $(RUST_LIB)
#
# so Rust is built BY DEFAULT and the opt-out is NO_RUST. WITH_RUST=1 in the
# PKGBUILD sets a variable the Makefile never reads; deleting it was correct and
# insufficient, which is the worst combination -- the hook reported success and
# the build failed identically.
#
# Worth recording as an upstream change: git now needs a Rust toolchain unless
# told otherwise. For a distribution bootstrapped from source that is a real
# obstacle, because rustc bootstraps from a previous rustc.
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*WITH_RUST=1[ \t]*$", l)]
assert len(hit) == 1, "git: expected one WITH_RUST=1 line, got %d" % len(hit)
lines[hit[0]] = re.match(r"^[ \t]*", lines[hit[0]]).group(0) + "NO_RUST=1"
out = [l.replace(" 'rust'", "") if "makedepends" in l else l for l in lines]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(out))
ZZPY
grep -qE "^[[:space:]]*WITH_RUST=1" PKGBUILD && { echo "git: WITH_RUST survived" >&2; exit 1; }
grep -qE "^[[:space:]]*NO_RUST=1" PKGBUILD || { echo "git: NO_RUST=1 was not set" >&2; exit 1; }
grep -q "'rust'" PKGBUILD && { echo "git: rust still in makedepends" >&2; exit 1; }
echo "git: built without Rust (no rustc to bootstrap from on s390x)"
# --- no libsecret credential helper -------------------------------------------
#
# make: *** [Makefile:20: git-credential-libsecret.o] Error 1
#
# contrib/credential/libsecret needs libsecret's headers, and libsecret is not in
# this port -- it is a GNOME keyring client, and behind it sit glib2 and
# gobject-introspection, the sub-project already declined for p11-kit.
#
# THREE PLACES, and one of them spans two lines:
#
# 52 _make -C contrib/credential/libsecret
# 106 install -m 0755 contrib/credential/libsecret/git-credential-libsecret \
# 107 "$pkgdir"/usr/lib/git-core/git-credential-libsecret
# 108 _make -C contrib/credential/libsecret clean
#
# The first version of this section asserted "expected one _make, got 2" and
# stopped, which is the guard doing its job. Replacing line 106 alone would leave
# 107 as a command of its own -- the binutils continuation trap again, and the
# reason the install is deleted as a range rather than commented.
#
# The optdepend line stays: it describes what the helper would need if someone
# built it, and it is a sentence, not a dependency.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
mk = [i for i, l in enumerate(lines)
if re.search(r"_make -C contrib/credential/libsecret", l)]
assert len(mk) == 2, "git: expected two libsecret _make lines, got %d" % len(mk)
ins = [i for i, l in enumerate(lines)
if re.match(r"^[ \t]*install ", l) and "credential/libsecret" in l]
assert len(ins) == 1, "git: expected one libsecret install, got %d" % len(ins)
i = ins[0]
j = i
while lines[j].rstrip().endswith("\\"):
j += 1
# Delete the install and its continuations, then neutralise both _make lines.
# Backwards, so earlier indices stay valid.
for k in reversed(mk):
ind = re.match(r"^[ \t]*", lines[k]).group(0)
lines[k] = ind + ": # libsecret is not packaged here (glib2, gobject-introspection)."
del lines[i:j + 1]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*_make -C contrib/credential/libsecret" PKGBUILD && {
echo "git: a libsecret build survived" >&2; exit 1; }
grep -qE "^[[:space:]]*install .*credential/libsecret" PKGBUILD && {
echo "git: the libsecret install survived" >&2; exit 1; }
grep -q "git-credential-libsecret" PKGBUILD && {
echo "git: a reference to the helper binary survived" >&2; exit 1; }
echo "git: libsecret credential helper dropped"

View file

@ -1,41 +0,0 @@
#!/usr/bin/env bash
# gnupg: no manual, because two diagrams in it need two image toolchains.
#
# /bin/sh: line 1: convert: command not found
# make[2]: *** [Makefile:1199: gnupg-module-overview.pdf] Error 127
# /bin/sh: line 1: fig2dev: command not found
# make[2]: *** [Makefile:1211: gnupg-card-architecture.pdf] Error 127
#
# `convert` is ImageMagick, `fig2dev` is transfig. Two separate toolchains, for
# two architecture diagrams in gnupg's own documentation.
#
# --disable-doc is gnupg's own switch, and it was CHECKED before being used:
# `grep -oE '\-\-(disable|enable)-doc' configure` finds both, so autoconf knows
# the option. groff had no such switch and needed its generated Makefile edited
# instead -- the difference matters, because an invented flag draws only a
# warning from autoconf and the build fails exactly as before, with the hook's
# own check passing.
set -euo pipefail
# AN ARRAY, not a continued command. The first version of this hook looked for a
# ./configure line ending in a backslash and asserted its way out:
#
# AssertionError: gnupg: ./configure does not continue
#
# gnupg collects its options in `local configure_options=( ... )` and then calls
# `./configure "${configure_options[@]}"`. Both shapes appear in this port --
# binutils, cryptsetup and libxcrypt use continued commands; gnupg and shadow use
# arrays -- so a hook has to look at the file rather than assume the house style.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*local configure_options=\($", l)]
assert len(hit) == 1, "gnupg: expected one configure_options array, got %d" % len(hit)
i = hit[0] + 1
ind = re.match(r"^[ \t]*", lines[i]).group(0)
# The array is kept in alphabetical order upstream; --disable-doc sorts first.
lines.insert(i, ind + "--disable-doc # no ImageMagick, no transfig for the diagrams")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -A2 "local configure_options=(" PKGBUILD | grep -q -- "--disable-doc" || {
echo "gnupg: the flag is not inside the options array" >&2; exit 1; }
echo "gnupg: manual disabled (no ImageMagick, no transfig)"

View file

@ -36,53 +36,6 @@ lines[i:i] = [
ind + "# package. The install side already skips the file when it is absent.",
ind + 'sed -i "s|^DOC_GNU_EPS = doc/gnu.eps$|DOC_GNU_EPS =|" Makefile',
ind + 'grep -q "^DOC_GNU_EPS =$" Makefile',
ind + "# And its CONSUMER. Emptying DOC_GNU_EPS removed the rule that",
ind + "# builds the figure and not the document that includes it:",
ind + "#",
ind + "# doc/webpage.ms:45: error: cannot open 'gnu.eps'",
ind + "# doc/webpage.ms:45: fatal error: PSPIC failed to include 'gnu.eps'",
ind + "#",
ind + "# PROCESSEDEXAMPLEFILES_PS lists webpage.ps and grnexmpl.ps. The",
ind + "# HTML variant right above it is ALREADY empty, with its real value",
ind + "# left as a comment -- groff's own configure empties that one when",
ind + "# the tools are missing. This does the same for PS, which is the",
ind + "# decision groff would have made had it checked.",
ind + 'sed -i "/^PROCESSEDEXAMPLEFILES_PS = /,/^$/ s|^.*$||" Makefile',
ind + 'grep -q "^PROCESSEDEXAMPLEFILES_PS" Makefile && { echo "groff: PS example list not emptied" >&2; exit 1; }',
ind + "# THE SECOND consumer. Two files include the logo, and only two --",
ind + "# counted rather than guessed:",
ind + "#",
ind + "# doc/webpage.ms (above)",
ind + "# contrib/hdtbl/examples/mixed_pickles.roff (this)",
ind + "#",
ind + "# hdtbl renders its example tables to PostScript, and one of them shows",
ind + "# the GNU logo. HDTBLPROCESSEDEXAMPLEFILES lists all twelve; the whole",
ind + "# list goes, because there is no reason to render eleven examples of a",
ind + "# table macro package into a documentation set nothing here reads.",
ind + "# ALL of them, not one per failure. This is the third round on the",
ind + "# same package: DOC_GNU_EPS, then PROCESSEDEXAMPLEFILES_PS, then",
ind + "# HDTBL, and mom's examples were waiting behind those --",
ind + "#",
ind + "# pdfmom: fatal error: test-groff exited with status 4",
ind + "# contrib/mom/examples/typesetting.pdf",
ind + "#",
ind + "# groff renders its own manuals and every contrib package's examples",
ind + "# to PostScript and PDF using the groff being built. Each list is a",
ind + "# separate variable, and finding them one failure at a time is how",
ind + "# three passes went by. They are emptied together.",
ind + 'for _v in HDTBLPROCESSEDEXAMPLEFILES MOMPROCESSEDEXAMPLEFILES \\',
ind + ' PROCESSEDDOCFILES_PDF PROCESSEDDOCFILES_PS \\',
ind + ' PROCESSEDDOCFILES_TXT PROCESSEDDOCFILES \\',
ind + ' GENERATEDDOCFILES; do',
ind + ' sed -i "/^$_v = /,/^$/ s|^.*$||" Makefile',
ind + ' grep -q "^$_v" Makefile && { echo "groff: $_v not emptied" >&2; exit 1; }',
ind + 'done',
ind + "# And say it out loud: if a THIRD consumer appears, this is the pattern",
ind + "# to look for, not a new kind of problem.",
ind + 'if grep -rl "gnu\\.eps" --include="*.roff" --include="*.ms" . 2>/dev/null | grep -qv "^\\./doc/webpage.ms$\\|mixed_pickles"; then',
ind + ' echo "groff: a third file includes gnu.eps; see patches/pkgbuild/groff.sh" >&2',
ind + ' exit 1',
ind + 'fi',
]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY

View file

@ -26,34 +26,3 @@ io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q "doxybuild" PKGBUILD && { echo "jsoncpp: doxybuild survived" >&2; exit 1; }
echo "jsoncpp: doxygen build removed"
# --- and the html it no longer generates --------------------------------------
#
# cp: cannot stat 'dist/doxygen/jsoncpp-api-html-1.9.6': No such file or dir
#
# FOURTEENTH time, and the sixth of my own making. Removing the doxybuild.py call
# above stopped the API html being generated, and package() still copies the
# directory it would have produced. The name carries the version, so the path is
# not even greppable without knowing pkgver.
#
# By now the rule is mechanical and worth stating as a procedure rather than a
# lesson: after removing a documentation build, grep package() for the OUTPUT
# directory name, not for the tool. The tool appears in build(); the path appears
# in package(), and it is the path that fails.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if "doxygen" in l and re.match(r"^[ \t]*(cp|mv|install) ", l)]
assert len(hit) >= 1, "jsoncpp: no doxygen output copy found in package()"
for i in reversed(hit):
j = i
while lines[j].rstrip().endswith("\\"):
j += 1
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i:j + 1] = [ind + ": # the doxygen html is not generated (no doxygen here)."]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*(cp|mv|install) .*doxygen" PKGBUILD && {
echo "jsoncpp: a doxygen output copy survived" >&2; exit 1; }
echo "jsoncpp: doxygen html copy removed"

View file

@ -37,41 +37,16 @@
# It also means these build on the HOST, where they had failed, so they need no
# stage-2 special handling at all.
set -euo pipefail
# A PROBE, not a constant, and it runs in the BUILD ENVIRONMENT.
#
# The first version set _bootstrap=1 unconditionally. That is right on a machine
# with no Python packaging stack and wrong the moment there is one, because the
# vendored builder carries its OWN copy of each project -- older than the
# PKGBUILD's pkgver. python-packaging was labelled 26.3 and installed 25.0, and
# the mismatch surfaced two packages later:
#
# python-vcs-versioning: packaging>=26.2
#
# a floor our own package met on paper and not in fact. Nothing about the
# installed package looked wrong; only `import packaging; packaging.__version__`
# disagreed with its name.
#
# So the PKGBUILD decides at build time, where the answer is knowable: if `build`
# and `installer` import, the real path works and the vendored copies are not
# wanted. Hooks run on the HOST even for stage 2, so a probe written here could
# not answer for the chroot -- this one is evaluated by makepkg, inside it.
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "_bootstrap=0"
assert s.count(old) == 1, "python bootstrap: expected one _bootstrap=0, got %d" % s.count(old)
new = ("# Bootstrap only when the real path is unavailable -- see\n"
"# patches/pkgbuild/lib-python-bootstrap.sh. Evaluated by makepkg, in the\n"
"# build environment, because that is where the answer lives.\n"
"_bootstrap=1\n"
"python -c 'import build, installer' >/dev/null 2>&1 && _bootstrap=0")
s = s.replace(old, new, 1)
s = s.replace(old, "_bootstrap=1 # see patches/pkgbuild/lib-python-bootstrap.sh", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q "^_bootstrap=1$" PKGBUILD || {
echo "python bootstrap: the default was not set to 1" >&2; exit 1; }
grep -q "import build, installer" PKGBUILD || {
echo "python bootstrap: the probe was not inserted" >&2; exit 1; }
grep -q "^_bootstrap=1" PKGBUILD || {
echo "python bootstrap: _bootstrap was not switched" >&2; exit 1; }
# The branch it selects must actually exist, or the switch silently builds
# nothing -- these PKGBUILDs are not required to have both halves.
grep -q "bootstrap.build" PKGBUILD || {

View file

@ -37,15 +37,6 @@
# leaves the package uninstallable for a reason the diff appears to fix -- the
# fifth place, and the one that has cost the most time in this port.
set -euo pipefail
# STAGE 1 ONLY, the whole file. Expressed here rather than in a list kept by the
# driver: a list of file names cannot say WHY, and when a hook later grows a
# section that both stages need, the list silently skips that too. That happened
# to git and meson.
if [ "${EL_STAGE:-1}" != 1 ]; then
echo "libarchive: nothing to do at stage 2 -- our libxml2 and icu agree in the chroot"
exit 0
fi
python3 - <<'ZZPY'
import io, re
s = io.open("PKGBUILD", encoding="utf-8").read()

View file

@ -18,32 +18,3 @@ io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q "EVENT__DOXYGEN=OFF" PKGBUILD || { echo "libevent: doxygen not disabled" >&2; exit 1; }
echo "libevent: Doxygen reference disabled"
# --- and the docs split, which I left behind ----------------------------------
#
# mv: cannot stat '<pkgdir>/usr/share/doc': No such file or directory
#
# Turning EVENT__DOXYGEN off above stopped the documentation being BUILT and left
# package() moving it into the libevent-doc split:
#
# mv {"$pkgdir",doc}/usr/share/doc
#
# NINTH time in this port, and the second time I have made it myself rather than
# inherited it: fakeroot was the other. Removing a documentation build without
# removing its install is apparently the easiest mistake available here, so it is
# worth stating the rule plainly -- after disabling documentation anywhere,
# search package() for share/doc, man, and the split arrays before believing the
# hook is finished.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.search(r"share/doc", l) and l.lstrip().startswith("mv ")]
assert len(hit) == 1, "libevent: expected one share/doc move, got %d" % len(hit)
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i] = ind + ': # doxygen is off above, so usr/share/doc is never created.'
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*mv .*share/doc" PKGBUILD && {
echo "libevent: the docs move survived" >&2; exit 1; }
echo "libevent: docs split removed (nothing builds them now)"

View file

@ -53,15 +53,6 @@
# MAKEFLAGS bound inside build() rather than at hook level.
set -euo pipefail
# STAGE 1 ONLY, the whole file. Expressed here rather than in a list kept by the
# driver: a list of file names cannot say WHY, and when a hook later grows a
# section that both stages need, the list silently skips that too. That happened
# to git and meson.
if [ "${EL_STAGE:-1}" != 1 ]; then
echo "libgcrypt: nothing to do at stage 2 -- our libgpg-error is installed in the chroot"
exit 0
fi
WORK="${WORK:-$HOME/work/arch-s390x}"
PREFIX="$WORK/stage1-prefix"

View file

@ -1,39 +0,0 @@
#!/usr/bin/env bash
# llvm: no Sphinx documentation.
#
# -DLLVM_ENABLE_SPHINX=ON pulls in python-sphinx and python-myst-parser, and
# behind those sit docutils, babel, pygments, snowballstemmer, imagesize,
# alabaster, the sphinxcontrib-* family, markdown-it-py and mdit-py-plugins --
# fifteen or so packages whose entire purpose here would be to render HTML for a
# compiler nothing in this port reads the manual of.
#
# MEASURED BEFORE CHOOSING, the way glib2 and iproute2 were: llvm and clang need
# only libedit and python-psutil beyond what this port already has. Sphinx is the
# whole of the rest of the bill, and one flag removes it.
#
# AND THE CLEANUP, found before it failed. package() ends on
#
# rm -r "$pkgdir"/usr/share/doc/.../html/{_sources,.buildinfo}
#
# which Sphinx creates and which will not exist. This is the seventeenth
# appearance of that shape in this port; the procedure written in jsoncpp's hook
# says to grep package() for the OUTPUT paths after disabling a documentation
# build, and this is the first time it was done before the build rather than
# after. rm -rf rather than deleted: on a machine with Sphinx the directory is
# real and still has to go.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-DLLVM_ENABLE_SPHINX=ON"
assert s.count(old) == 1, "llvm: expected one LLVM_ENABLE_SPHINX, got %d" % s.count(old)
s = s.replace(old, "-DLLVM_ENABLE_SPHINX=OFF", 1)
lines = s.split("\n")
hit = [i for i, l in enumerate(lines) if "_sources" in l and l.lstrip().startswith("rm ")]
assert len(hit) == 1, "llvm: expected one _sources removal, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("rm -r ", "rm -rf ", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-DLLVM_ENABLE_SPHINX=OFF" PKGBUILD || { echo "llvm: sphinx not disabled" >&2; exit 1; }
grep -qE "^[[:space:]]*rm -rf .*_sources" PKGBUILD || { echo "llvm: the cleanup is not tolerant" >&2; exit 1; }
echo "llvm: Sphinx documentation disabled, cleanup made tolerant"

View file

@ -40,17 +40,6 @@
# will actually import this. Deriving it from the interpreter running the build
# is the intuitive thing and the wrong one.
set -euo pipefail
# STAGE 1 ONLY -- this section, not this file.
#
# It used to be expressed by naming this hook in STAGE2_SKIP_HOOKS, which skips
# the whole FILE. Then a section was appended that both stages need, and it
# silently never ran: the package kept failing in stage 2 on the very thing the
# new section fixes, and the log said "hook skipped (stage-1 only)" about a hook
# that was now two thirds relevant.
#
# The scope belongs next to the reason, so each section decides for itself.
if [ "${EL_STAGE:-1}" = 1 ]; then
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
@ -100,9 +89,6 @@ ZZPY
grep -q 'posix_local scheme installs' PKGBUILD || {
echo "meson: the relocation was not inserted" >&2; exit 1; }
echo "meson: wheel relocated out of /usr/local; STAGE-1 ONLY"
else
echo "meson: chroot python has no posix_local scheme; no relocation needed"
fi
# --- no reference manual ------------------------------------------------------
#

View file

@ -1,106 +0,0 @@
#!/usr/bin/env bash
# p11-kit: no gtk-doc manual.
#
# meson.build:65:18: ERROR: Dependency "glib-2.0" not found (tried pkgconfig)
#
# THIS ONE WAS NEARLY A SUB-PROJECT. glib-2.0 is not in this port, and adding it
# is not one package: Arch's glib2 wants gobject-introspection (which itself
# needs glib2 -- a cycle to bootstrap), libsysprof-capture, dconf and gi-docgen.
# Several packages and a circular dependency, for one entry in one build.
#
# Reading where the requirement actually comes from changed the answer entirely.
# The file is doc/manual/meson.build, and the line is
#
# glib_prefix = dependency('glib-2.0').get_variable(pkgconfig : 'prefix')
#
# glib is consulted to find where gtk-doc keeps its cross-reference files. It is
# not linked, not called, and nothing in p11-kit's code touches it. -D gtk_doc
# is already in the PKGBUILD's option list, set to true; turning it false drops
# the whole doc/manual subdirectory and the dependency with it.
#
# The lesson is the one this port keeps relearning: the error names glib, and
# glib is not the subject. Six lines of context said so.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*-D gtk_doc=true[ \t]*$", l)]
assert len(hit) == 1, "p11-kit: expected one -D gtk_doc=true, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("gtk_doc=true", "gtk_doc=false")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-D gtk_doc=false" PKGBUILD || { echo "p11-kit: gtk_doc not disabled" >&2; exit 1; }
echo "p11-kit: gtk-doc manual disabled (glib was only locating its xrefs)"
# --- and no man pages either --------------------------------------------------
#
# With gtk_doc off, the next line was
#
# meson.build:94:4: ERROR: Problem encountered: Docbook stylesheet for man
# pages not found
#
# The stylesheets, not xsltproc -- which our libxslt supplies. Third package to
# stop on exactly this: pam validates DocBook sources it cannot fetch, shadow
# renders man pages from them. Shipping docbook-xsl would be a package that only
# documentation needs; -D man is already in the option list.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*-D man=true[ \t]*$", l)]
assert len(hit) == 1, "p11-kit: expected one -D man=true, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("man=true", "man=false")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-D man=false" PKGBUILD || { echo "p11-kit: man not disabled" >&2; exit 1; }
echo "p11-kit: man pages disabled (DocBook stylesheets absent, not xsltproc)"
# --- and the gtk-doc split ----------------------------------------------------
#
# mv: cannot stat '<pkgdir>/usr/share/gtk-doc': No such file or directory
#
# TWELFTH time. -D gtk_doc=false above stopped the documentation being built and
# left package() splitting it out:
#
# _pick doc "$pkgdir"/usr/share/gtk-doc
#
# _pick is Arch's helper for moving a path into a sub-package, and it is as fatal
# on a missing path as a bare mv.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if "gtk-doc" in l and "_pick" in l]
assert len(hit) == 1, "p11-kit: expected one gtk-doc _pick, got %d" % len(hit)
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i] = ind + ": # gtk_doc is false above, so usr/share/gtk-doc is never created."
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "_pick doc .*gtk-doc" PKGBUILD && {
echo "p11-kit: the gtk-doc pick survived" >&2; exit 1; }
echo "p11-kit: gtk-doc split removed (nothing builds it now)"
# --- and the doc sub-package that now stages nothing --------------------------
#
# mv: cannot stat 'doc/*': No such file or directory
#
# THIRTEENTH time, and the third layer of the same package. Neutralising the
# _pick above stopped anything being staged into doc/, and package_p11-kit-doc()
# still runs `mv doc/* "$pkgdir"`. _pick, mv, and now the sub-package function
# that consumes what _pick staged: three places for one decision.
#
# The lesson is stated plainly because five of these have been mine: disabling a
# documentation build means following it through the ENTIRE package() chain --
# the build flag, the staging, and the sub-package that reads the staging area.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*mv doc/\* ", l)]
assert len(hit) == 1, "p11-kit: expected one `mv doc/*`, got %d" % len(hit)
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i] = ind + ": # nothing is staged into doc/: gtk_doc and man are both off."
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*mv doc/\*" PKGBUILD && {
echo "p11-kit: the doc move survived" >&2; exit 1; }
echo "p11-kit: doc sub-package stages nothing, and no longer tries"

View file

@ -22,31 +22,3 @@ io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-Ddocs=disabled" PKGBUILD || { echo "pam: docs not disabled" >&2; exit 1; }
echo "pam: DocBook documentation disabled"
# --- and the PDFs it no longer produces ---------------------------------------
#
# rm: cannot remove '<pkgdir>/usr/share/doc/Linux-PAM/*.pdf': No such file
#
# The line above it says why it exists:
#
# # remove unreproducible pdf files
#
# pam builds PDFs whose bytes differ between runs, so the PKGBUILD deletes them
# to keep the package reproducible. With -Ddocs=disabled they are never built,
# and a cleanup step that finds nothing to clean is fatal under set -e.
#
# The rm becomes tolerant rather than being deleted: on a machine that does have
# the DocBook stack, the PDFs WILL be there and they still have to go.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.search(r"\*\.pdf", l) and l.lstrip().startswith("rm ")]
assert len(hit) == 1, "pam: expected one pdf removal, got %d" % len(hit)
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i] = ind + "rm -f " + lines[i].lstrip()[3:].lstrip()
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "^[[:space:]]*rm -f .*\*\.pdf" PKGBUILD || {
echo "pam: the pdf removal was not made tolerant" >&2; exit 1; }
echo "pam: PDF cleanup tolerates their absence"

View file

@ -1,38 +0,0 @@
#!/usr/bin/env bash
# python-jinja: our flit_core is NEWER than jinja will accept.
#
# * Getting build dependencies for wheel...
# ERROR Missing dependencies:
# flit_core<4
#
# flit_core IS installed, and importable: 4.0.2. jinja's pyproject.toml pins
# `flit_core<4`, so `python -m build` refuses before compiling anything.
#
# A NEW SHAPE for this port, and the mirror image of the one already recorded.
# libxslt could not be built on the host because Ubuntu's libxml2 was too OLD.
# Here our own package is too NEW for another package's declared ceiling. Both
# read as a missing dependency; neither is one.
#
# --skip-dependency-check tells build to compile anyway, and it is not an
# invention: Arch's own python-packaging PKGBUILD passes `-wn
# --skip-dependency-check` for the same class of reason. The pin is upstream
# caution -- flit_core 4 changed no API jinja uses, and jinja builds and imports
# fine against it.
#
# The honest alternative was pinning flit-core to 3.x for the whole
# distribution, which would trade one package's ceiling for every other
# package's floor.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.search(r"python -m build\b", l)]
assert len(hit) == 1, "python-jinja: expected one `python -m build`, got %d" % len(hit)
i = hit[0]
assert "--skip-dependency-check" not in lines[i], "python-jinja: already skipping"
lines[i] = lines[i].rstrip() + " --skip-dependency-check"
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "python -m build .*--skip-dependency-check" PKGBUILD || {
echo "python-jinja: the flag is not on the build line" >&2; exit 1; }
echo "python-jinja: dependency check skipped (our flit_core is 4.x, the pin says <4)"

View file

@ -23,34 +23,10 @@ import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if "LICENSE*" in l and "$_name-$pkgver" in l]
assert len(hit) == 1, "python-packaging: expected one license install, got %d" % len(hit)
# BOTH LAYOUTS, because _bootstrap is now a probe rather than a constant
# (see lib-python-bootstrap.sh). With the vendored builder the sources are git
# clones under $srcdir/$pkgname; without it they are the release tarball under
# $srcdir/$_name-$pkgver. Hardcoding either one breaks the other, and the first
# version of this hook hardcoded the clone -- correct only while _bootstrap was
# always 1.
lines[hit[0]] = re.sub(
r"\$srcdir/\$_name-\$pkgver/LICENSE\*",
'"$srcdir/$pkgname"/LICENSE* "$srcdir/$_name-$pkgver"/LICENSE*',
lines[hit[0]])
# install -t with two globs would fail on whichever does not exist, so the
# whole line becomes a loop over what is actually there.
ind = re.match(r"^[ \t]*", lines[hit[0]]).group(0)
lines[hit[0]:hit[0] + 1] = [
ind + '# The sources are a git clone under $pkgname when the vendored builder',
ind + '# is used, and the release tarball otherwise. Take whichever exists.',
ind + 'for _lic in "$srcdir/$pkgname"/LICENSE* "$srcdir/$_name-$pkgver"/LICENSE*; do',
ind + ' [ -f "$_lic" ] && install -vDm644 "$_lic" -t "$pkgdir/usr/share/licenses/$pkgname"',
ind + 'done',
]
lines[hit[0]] = lines[hit[0]].replace("$srcdir/$_name-$pkgver/LICENSE*",
"$srcdir/$pkgname/LICENSE*")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q 'Take whichever exists' PKGBUILD || {
echo "python-packaging: the license loop was not inserted" >&2; exit 1; }
# Matched against what is actually written -- the inserted line quotes the
# directory, `"$srcdir/$_name-$pkgver"/LICENSE*`, so a pattern expecting the
# slash to follow $pkgver directly finds nothing. The first version of this guard
# did exactly that and failed on a correct file.
grep -q '_name-\$pkgver"/LICENSE\*' PKGBUILD || {
echo "python-packaging: the tarball layout was dropped" >&2; exit 1; }
grep -q 'srcdir/\$pkgname/LICENSE\*' PKGBUILD || {
echo "python-packaging: the license path was not redirected" >&2; exit 1; }
echo "python-packaging: license taken from the bootstrap clone"

View file

@ -79,56 +79,3 @@ grep -qE '^[[:space:]]*mv .*usr/lib/tcl8\.6/sqlite' PKGBUILD && {
echo "sqlite: the hardcoded upstream move survived" >&2; exit 1; }
grep -q '_tcldir=' PKGBUILD || { echo "sqlite: the tcl dir is not discovered" >&2; exit 1; }
echo "sqlite: upstream tcl move reads the version from the tree"
# --- tcl 9 keeps its library inside the binary -------------------------------
#
# The section above discovers whatever tcl directory exists under usr/lib. In
# the chroot there was none, and its guard said so:
#
# sqlite: no tcl dir under usr/lib
#
# What sqlite had created instead was a directory literally named `zipfs:`:
#
# <pkgdir>/sqlite/zipfs:/lib/tcl/tcl_library/
#
# The generated Makefile explains itself:
#
# # $(TCLLIBDIR) = where to install the tcl plugin. If this is empty, it
# TCLLIBDIR = //zipfs:/lib/tcl/tcl_library/sqlite3.53.4
#
# sqlite asks tcl where its library lives and installs the plugin beside it. In
# tcl 8.6 that was a real directory. TCL 9 SHIPS ITS LIBRARY INSIDE THE
# EXECUTABLE, in a zipfs mount, so the answer is a path that exists only to tcl
# -- and sqlite used it as a filesystem path, creating a directory whose name
# contains a colon.
#
# This is the deferred "libtcl8.6 versus our tcl 9.0" item in its final form: not
# a version number to update but a change in what tcl considers a path.
#
# TCLLIBDIR is set to a real directory, with the version READ from tclsh so the
# next tcl release does not need this hook edited again. The section above then
# finds it, because usr/lib/tcl<version> is exactly the shape it looks for.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*make\b.*\bsqltclsh\b", l)]
if not hit:
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*make[ \t]*$", l)]
assert hit, "sqlite: no make line found in build()"
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i:i] = [
ind + "# tcl 9 answers with a zipfs path that only tcl can open, and sqlite",
ind + "# would install its plugin into a directory named `zipfs:`. Give it a",
ind + "# real one, versioned from tclsh rather than written in here.",
ind + '_tclver=$(echo "puts [info tclversion]" | tclsh)',
ind + '[ -n "$_tclver" ] || { echo "sqlite: tclsh gave no version" >&2; exit 1; }',
ind + 'sed -i "s|^TCLLIBDIR = .*|TCLLIBDIR = /usr/lib/tcl${_tclver}/sqlite${pkgver}|" Makefile',
ind + 'grep -q "^TCLLIBDIR = /usr/lib/tcl" Makefile || { echo "sqlite: TCLLIBDIR not redirected" >&2; exit 1; }',
]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q "TCLLIBDIR = /usr/lib/tcl" PKGBUILD || {
echo "sqlite: the TCLLIBDIR redirection was not inserted" >&2; exit 1; }
echo "sqlite: tcl plugin directed to a real directory (tcl 9 uses zipfs)"

View file

@ -286,95 +286,3 @@ io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q -- "-Dapparmor=disabled" PKGBUILD || { echo "systemd: apparmor not disabled" >&2; exit 1; }
echo "systemd: AppArmor disabled"
# --- no man pages -------------------------------------------------------------
#
# compilation error: file ../systemd/man/custom-man.xsl line 12 element import
#
# An XSLT import that cannot resolve: systemd renders its man pages from DocBook,
# and the stylesheets its own custom-man.xsl imports are not installed. xsltproc
# exists -- our libxslt supplies it -- so this is the FOURTH package to stop on
# missing DocBook DATA rather than a missing tool, after pam, shadow and p11-kit.
#
# systemd has by far the largest man page set of any package here, which is the
# strongest argument in the port for shipping docbook-xsl eventually. Not now.
set -euo pipefail
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-Dman=enabled"
assert s.count(old) == 1, "systemd: expected one man option, got %d" % s.count(old)
s = s.replace(old, "-Dman=disabled", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q -- "-Dman=disabled" PKGBUILD || { echo "systemd: man not disabled" >&2; exit 1; }
echo "systemd: man pages disabled (DocBook stylesheets absent, not xsltproc)"
# --- and every man page operation, in every sub-package -----------------------
#
# -Dman=disabled means NO man page exists, and package() touches them SIXTEEN
# times across five sub-package functions. This section was written three times,
# each catching more:
#
# 1st man3 only -> stopped on man8/*nss*
# 2nd mv and rm of share/man paths -> stopped on `ln -s ... installkernel.8.gz`
# 3rd mv, ln and rm -> stopped on
#
# install -D -m0644 -t "$pkgdir"/usr/share/man/man1 \
# build/man/init.1
#
# in package_systemd-sysvcompat(), whose SOURCE is under build/ and whose
# path contains no "man<digit>" component at all.
#
# So the pattern is a man path ANYWHERE on the line -- source or destination,
# build tree or pkgdir -- and the verbs are mv, ln, rm and install. Treated by
# kind:
#
# mv, ln, install neutralised. Nothing exists to move, link or install.
# rm made tolerant with -f. Their intent -- do not ship these --
# still holds where the DocBook stack exists, and one of them
# also removes a binary that must go.
# install -d LEFT ALONE. Creating an empty directory harms nothing, and
# other lines in the same functions install real files into
# the same trees.
#
# Written out because three rounds on one package is the cost of guessing the
# shape of "all of them" instead of listing them.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
MAN = re.compile(r"(share/man|build/man|\bman[1-8]\b|\bman[1-8]/)")
neutral = tolerant = 0
for i in range(len(lines) - 1, -1, -1):
l = lines[i]
if not MAN.search(l):
continue
# install -d only makes a directory; leave it.
if re.match(r"^[ \t]*install +-d", l):
continue
if re.match(r"^[ \t]*(mv|ln|install) ", l):
j = i
while lines[j].rstrip().endswith("\\"):
j += 1
ind = re.match(r"^[ \t]*", l).group(0)
lines[i:j + 1] = [ind + ": # no man pages are built: -Dman=disabled above."]
neutral += 1
elif re.match(r"^[ \t]*rm ", l) and not re.match(r"^[ \t]*rm -[a-z]*f", l):
lines[i] = re.sub(r"^([ \t]*)rm ", r"\1rm -f ", l)
tolerant += 1
assert neutral >= 5, "systemd: expected at least five man operations, got %d" % neutral
assert tolerant >= 1, "systemd: no man page removals found"
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
print(" %d neutralised, %d made tolerant" % (neutral, tolerant))
ZZPY
# Nothing may still act on a man path except `install -d` and a tolerant rm.
if grep -nE "^[[:space:]]*(mv|ln|install) .*(share/man|build/man|man[1-8])" PKGBUILD |
grep -v "install +-d" | grep -qE "install -d" ; then :; fi
grep -nE "^[[:space:]]*(mv|ln) .*(share/man|build/man|man[1-8])" PKGBUILD && {
echo "systemd: a man move or link survived" >&2; exit 1; }
grep -nE "^[[:space:]]*install +-[^d].*(share/man|build/man)" PKGBUILD && {
echo "systemd: a man install survived" >&2; exit 1; }
grep -nE "^[[:space:]]*rm [^-].*(share/man|man[1-8])" PKGBUILD && {
echo "systemd: an intolerant rm of a man path survived" >&2; exit 1; }
echo "systemd: all man page operations handled, in every sub-package"

View file

@ -1,66 +0,0 @@
#!/usr/bin/env bash
# tpm2-tss: no unit tests, because their framework is not here.
#
# configure: error: Unit test enabled, but cmocka missing or version
#
# This is the second obstacle in this package, and the first one hid it: before
# autoconf-archive existed, configure died on
#
# configure.ac:139: error: undefined or overquoted macro: AC_MSG_WARN
#
# which named a macro that exists. An AX_* macro was missing, m4 quoting came
# apart, and autoconf blamed the next thing it recognised. With the archive
# installed, configure gets far enough to ask for cmocka.
#
# cmocka is a C unit-test framework, wanted only to build tests that this
# bootstrap does not run -- makepkg is invoked with --nocheck for the whole first
# pass. --disable-unit is tpm2-tss's own switch; the PKGBUILD passes --enable-unit
# in its options array.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*--enable-unit[ \t]*$", l)]
assert len(hit) == 1, "tpm2-tss: expected one --enable-unit, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("--enable-unit", "--disable-unit")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "--disable-unit" PKGBUILD || { echo "tpm2-tss: unit tests not disabled" >&2; exit 1; }
grep -qE "^[[:space:]]*--enable-unit[[:space:]]*$" PKGBUILD && {
echo "tpm2-tss: --enable-unit survived" >&2; exit 1; }
echo "tpm2-tss: unit tests disabled (no cmocka)"
# --- and no integration tests -------------------------------------------------
#
# configure: error: Missing required program 'ss': ensure it is installed
#
# The THIRD obstacle in this package, each hidden by the one before it: a missing
# AX_* macro, then cmocka, now `ss`. configure.ac has
#
# [ERROR_IF_NO_PROG([ss])]
#
# inside the integration-test branch. `ss` comes from iproute2, and adding
# iproute2 was the first idea -- it is in Arch's base group and belongs in this
# distribution eventually. It was MEASURED first: iproute2 depends on libbpf,
# iptables and linux-atm, none of which are here, and libbpf is the dependency
# systemd was already spared for the same reason.
#
# Three packages to satisfy a test-suite prerequisite, or one flag. The tests are
# not run: makepkg gets --nocheck for the whole first pass.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
en = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*--enable-integration[ \t]*$", l)]
if en:
lines[en[0]] = lines[en[0]].replace("--enable-integration", "--disable-integration")
else:
# Put it beside the unit-test switch this hook already set.
u = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*--disable-unit[ \t]*$", l)]
assert len(u) == 1, "tpm2-tss: expected the --disable-unit line from the section above"
ind = re.match(r"^[ \t]*", lines[u[0]]).group(0)
lines.insert(u[0] + 1, ind + "--disable-integration")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "--disable-integration" PKGBUILD || {
echo "tpm2-tss: integration tests not disabled" >&2; exit 1; }
echo "tpm2-tss: integration tests disabled (ss would cost three packages)"

View file

@ -20,44 +20,3 @@ ZZPY
grep -q -- "autogen.sh --no-po4a" PKGBUILD || {
echo "xz: --no-po4a was not added" >&2; exit 1; }
echo "xz: translated man pages skipped"
# --- and no doxygen API reference ---------------------------------------------
#
# Two documentation toolchains in one package: po4a above for the translated man
# pages, doxygen for the liblzma API reference.
#
# ../../../doxygen/update-doxygen: 'doxygen' command not found
# make[4]: *** [Makefile:642: ../../../doc/api/index.html] Error 1
#
# THE FLAG WAS ALREADY THERE, AND LOST. The first version of this section
# inserted --disable-doxygen right after ./configure, and config.log recorded what
# actually happened:
#
# $ ./configure --disable-doc --disable-doxygen --prefix=/usr \
# --disable-rpath --enable-doxygen --enable-werror
#
# The PKGBUILD passes --enable-doxygen further along the same line, and autoconf
# takes the LAST occurrence. The port had already learned this from the other
# side -- the arch-meson wrapper APPENDS --auto-features auto precisely so it
# wins -- and this got it backwards.
#
# The existing option is flipped in place rather than a second one added. One
# statement per decision: two contradictory flags on one command line is how the
# next reader spends an hour in config.log.
#
# Note for later: --enable-werror is on that same line. It has not bitten yet.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.search(r"--enable-doxygen\b", l)]
assert len(hit) == 1, "xz: expected one --enable-doxygen, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("--enable-doxygen", "--disable-doxygen")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "--enable-doxygen" PKGBUILD && { echo "xz: --enable-doxygen survived" >&2; exit 1; }
grep -q -- "--disable-doxygen" PKGBUILD || { echo "xz: doxygen not disabled" >&2; exit 1; }
# One occurrence only: a line carrying both would be decided by position.
n=$(grep -c -- "--disable-doxygen" PKGBUILD)
[ "$n" = 1 ] || { echo "xz: expected 1 --disable-doxygen, found $n" >&2; exit 1; }
echo "xz: doxygen disabled by flipping the flag the PKGBUILD already passes"

View file

@ -403,11 +403,7 @@ build_package() {
local hook="$PATCH_DIR/$name.sh"
if [ -f "$hook" ]; then
git checkout -- PKGBUILD 2>/dev/null || true
# EL_STAGE tells the hook which stage is asking. Some hooks -- git,
# meson, libgcrypt, libarchive -- exist partly or wholly to work around
# something about the HOST, and that part must not run in the chroot
# where the problem does not exist.
EL_STAGE=1 bash "$hook" || return 1
bash "$hook" || return 1
fi
rm -f ./*.pkg.tar.*
# Explicit `|| return 1`. Relying on `set -e` here does NOT work: callers

View file

@ -145,24 +145,6 @@ SigLevel = Never
[core]
Server = file://$REPO1
EOF
# NOTHING MAY BE MOUNTED UNDER $ROOT WHEN THIS RUNS.
#
# `rm -rf` follows a bind mount and deletes what is on the other side. This
# function had no such guard, and the cost was the host's /dev: a diagnostic
# session left `mount --rbind /dev "$ROOT/dev"` in place, the next run wiped
# the rootfs, and the delete went through the mount into the real /dev. Every
# device node went -- zero, full, random, urandom, tty, console -- and
# /dev/null came back later as a regular file created by a redirection, which
# broke every `> /dev/null` on the machine.
#
# Two guards, because the first one is not enough: unmount, then CHECK, then
# refuse. A stale mount is a reason to stop, never a thing to delete through.
umount_chroot
if findmnt -rno TARGET | grep -qF "$ROOT/"; then
printf 'still mounted under %s:\n' "$ROOT" >&2
findmnt -rno TARGET | grep -F "$ROOT/" | sed 's/^/ /' >&2
die "refusing to rm -rf through a mount"
fi
sudo rm -rf "$ROOT" "$CACHE"
sudo mkdir -p "$ROOT/var/lib/pacman" "$CACHE"
# EVERYTHING in stage 1, not a hand-picked core.
@ -239,19 +221,6 @@ EOF
# invocation, so resuming has to be correct rather than approximately
# correct.
#
# --nodeps TWICE, which is not a typo. pacman documents it: `-d` skips
# dependency VERSION checks, `-dd` skips dependency checks entirely. With
# one, cython could not be installed --
#
# warning: cannot resolve "python-pygments", a dependency of "cython"
# :: unable to satisfy dependency 'python-numpy' required by cython
#
# -- and the numbers matter: cython is wanted here only as a BUILD tool for
# libseccomp's Python binding, and its declared runtime dependencies are
# numpy and pygments. numpy brings BLAS and LAPACK. A single -d looked like
# it was skipping dependencies and was only skipping their versions, which
# nearly bought an entire numerical stack to compile one .pyx file.
#
# --nodeps for the same reason the per-package install uses it: a chroot
# halfway through stage 2 is a mixed population, some packages declaring
# versioned soname dependencies and some declaring names. -U is fed the
@ -315,7 +284,7 @@ EOF
fi
if [ "${#back[@]}" -gt 0 ]; then
sudo pacman --root "$ROOT" --config "$CONF" --cachedir "$CACHE" \
--noconfirm --nodeps --nodeps -U "${back[@]}" \
--noconfirm --nodeps -U "${back[@]}" \
> "$WORK/stage2-restore.txt" 2>&1 \
|| { tail -20 "$WORK/stage2-restore.txt" >&2; die "restoring stage-2 output failed"; }
printf ' %s stage-2 package(s) restored\n' "${#back[@]}"
@ -393,24 +362,6 @@ configure_chroot() {
# zz- so it sorts last: the drop-ins are read in glob order, and this has to
# be the final word. Owned by no package, and remade on every run since
# make_rootfs wipes the tree.
# DownloadUser, which the rebuilt pacman brings with it. nss failed on
#
# error: problem setting DownloadUser 'alpm' (user does not exist)
# ==> ERROR: 'pacman' returned a fatal error (1)
#
# Arch's pacman.conf drops privileges to `alpm` for downloads, and that user
# exists on a real Arch system because the package creates it via sysusers.d
# -- which `pacman --root` does not run, the same reason the CA bundle was
# never generated. Nothing in this chroot downloads anything (every source is
# already in /build, every package installed with -U from a local file), so
# the setting has no purpose here and its absence is fatal only because
# pacman checks it at startup.
if [ -f "$ROOT/etc/pacman.conf" ]; then
sudo sed -i 's/^\s*DownloadUser/#DownloadUser/' "$ROOT/etc/pacman.conf"
sudo grep -q '^#DownloadUser' "$ROOT/etc/pacman.conf" &&
printf ' pacman: DownloadUser disabled (no such user in a chroot)\n'
fi
sudo install -d -m0755 "$ROOT/etc/makepkg.conf.d"
sudo tee "$ROOT/etc/makepkg.conf.d/zz-s390x.conf" > /dev/null <<'EOC'
# stage 2: every flag variable, because Arch's are x86_64's and the drop-ins
@ -534,42 +485,13 @@ mount_chroot() {
# /dev/pts is not optional: without it any build step that opens a pty --
# and gcc's testsuite driver does -- fails in a way that names the pty and
# not the missing mount.
for m in proc sys dev; do
for m in proc sys dev dev/pts; do
sudo mkdir -p "$ROOT/$m"
done
mountpoint -q "$ROOT/proc" || sudo mount -t proc proc "$ROOT/proc"
mountpoint -q "$ROOT/sys" || sudo mount -t sysfs sys "$ROOT/sys"
# /dev by --rbind, then --make-rslave. Not a plain --bind, and nothing under
# it is ever modified.
#
# A plain --bind does NOT carry submounts, so the chroot saw /dev/shm as the
# empty 0755 stub inside devtmpfs rather than the host's tmpfs. That cost
# three passes: CPython runs sem_open at configure time, got EACCES, decided
# the platform has no POSIX semaphores, and compiled _multiprocessing without
# SemLock -- after which every consumer failed with a message blaming the
# platform, days after the cause.
#
# The first fix mounted a separate tmpfs at $ROOT/dev/shm and chmod'ed it.
# That was wrong in a way worth recording: $ROOT/dev was a bind of the HOST's
# /dev, so writes under it reach the real /dev -- and the host's /dev/null
# ended up replaced by a regular file, which broke every redirection on the
# machine until it was recreated with mknod.
#
# --rbind brings the host's /dev/shm along as it is: already a tmpfs, already
# drwxrwxrwt. Nothing to create, nothing to chmod, nothing of the host's to
# touch. --make-rslave then stops any mount this chroot makes from
# propagating back out.
mountpoint -q "$ROOT/dev" || {
sudo mount --rbind /dev "$ROOT/dev"
sudo mount --make-rslave "$ROOT/dev"
}
# PROVED from inside, as the build user. Every earlier check of this was made
# as root and passed while the build kept failing.
if ! sudo chroot --userspec="$BUILD_UID:$BUILD_GID" "$ROOT" \
/usr/bin/env -i PATH=/usr/bin sh -c \
'f=/dev/shm/.el-probe.$$; : > "$f" && rm -f "$f"'; then
die "/dev/shm is not writable by the build user; python would build without POSIX semaphores"
fi
mountpoint -q "$ROOT/dev" || sudo mount --bind /dev "$ROOT/dev"
mountpoint -q "$ROOT/dev/pts" || sudo mount -t devpts devpts "$ROOT/dev/pts"
# Sources and PKGBUILDs, already fetched by stage 1. Bind-mounting them
# means the chroot needs no network at all, which is worth having: this
# host cannot reach dev.gnupg.org, and a build that silently re-fetches
@ -579,9 +501,7 @@ mount_chroot() {
}
umount_chroot() {
# -R for dev, because --rbind brought its submounts with it.
mountpoint -q "$ROOT/dev" && sudo umount -R -l "$ROOT/dev"
for m in repo2 build sys proc; do
for m in repo2 build dev/pts dev sys proc; do
mountpoint -q "$ROOT/$m" && sudo umount -l "$ROOT/$m"
done
return 0
@ -767,7 +687,7 @@ NOTE
# backstop.
#
# libxml2 -- because the xar cycle reformed INSIDE stage 2. libarchive is in
# a hook that runs at stage 1 only, so stage 2 rebuilt it as Arch does, with
# STAGE2_SKIP_HOOKS, so stage 2 rebuilt it as Arch does, with xar, linking the
# libxml2 that happened to be installed at that moment: the stage-1 one, which
# wants the host's libicuuc.so.76. bsdtar then could not start, and bsdtar is
# what makepkg uses to extract sources and write packages -- so 39 of 39
@ -798,10 +718,7 @@ NOTE
STAGE2_FIRST=(texinfo libxml2 binutils pkgconf wget libxslt
python-packaging python-pyproject-hooks python-build
python-installer python-setuptools python-wheel
python-setuptools-scm python-markupsafe python-jinja
python-pkgconfig cython python-poetry-core
python-vcs-versioning python-fastjsonschema gyp
python-psutil)
python-setuptools-scm)
# Packages the chroot needs that can only come from repo2.
#
@ -862,11 +779,10 @@ CHROOT_STAGE2_PKGS=(
python-packaging python-pyproject-hooks python-build
python-installer python-setuptools python-wheel
python-setuptools-scm
python-markupsafe python-jinja python-pkgconfig cython
python-poetry-core python-vcs-versioning python-fastjsonschema gyp
python-psutil
)
STAGE2_SKIP_HOOKS=(libgcrypt git meson libarchive)
# Packages whose sources must be extracted on the HOST, because the chroot
# cannot extract anything until they are rebuilt.
#
@ -901,17 +817,7 @@ CHROOT_STAGE2_PKGS=(
# Five more packages -- coreutils, sed, grep, findutils, diffutils -- fetch PO
# files the same way and are unblocked by this one rebuild, which is why wget is
# also hoisted into STAGE2_FIRST.
# nss joins them, for the cheapest of the three reasons: its source is
#
# hg+https://hg.mozilla.org/projects/nss#tag=NSS_..._RTM
#
# a MERCURIAL checkout, and the chroot has no hg -- it stopped on
# `hg: command not found`. Packaging Mercurial would work and is not needed: the
# host has it, and this mechanism exists precisely to let the host do the
# fetching. Considered and rejected: mercurial itself is cheap (a Python
# application), but it drags python-docutils in for its man pages and would sit
# in the chroot forever to serve one package's source URL.
STAGE2_HOST_EXTRACT=(libxml2 wget nss)
STAGE2_HOST_EXTRACT=(libxml2 wget)
host_extract() {
local n="$1" h
@ -919,6 +825,12 @@ host_extract() {
return 1
}
skip_hook() {
local n="$1" h
for h in "${STAGE2_SKIP_HOOKS[@]}"; do [ "$n" = "$h" ] && return 0; done
return 1
}
# stage2_build <name> -- rebuild one package inside the chroot and install it.
#
# The hook is applied on the HOST, not in the chroot: hooks are seds over the
@ -927,51 +839,14 @@ host_extract() {
stage2_build() {
local name="$1"
local dir="$WORK/pkg/$name"
# CLONE IF ABSENT. Stage 2 used to refuse:
#
# no checkout for python-jinja
#
# It could only rebuild what stage 1 had cloned -- which is wrong for every
# package stage 2 builds and stage 1 cannot. libxslt was the first (Ubuntu's
# libxml2 is too old to configure it), and the Python set followed (their
# package() reads the running python's paths, so the host bakes in
# dist-packages). Those are named in CHROOT_STAGE2_PKGS precisely because
# only this stage can produce them, and then this stage would not fetch them.
#
# Pinned to upstream.lock like stage 1's clone, so a package first built here
# is as reproducible as one built there.
if [ ! -d "$dir/.git" ]; then
local url="https://gitlab.archlinux.org/archlinux/packaging/packages/$name.git"
local sha=""
[ -f "$HERE/upstream.lock" ] &&
sha=$(awk -v n="$name" '$1 == n {print $2}' "$HERE/upstream.lock")
echo " cloning $name"
rm -rf "$dir"
GIT_TERMINAL_PROMPT=0 git clone -q --depth 1 "$url" "$dir" || {
rm -rf "$dir"; echo " clone failed: $url" >&2; return 1; }
if [ -n "$sha" ]; then
git -C "$dir" fetch -q --depth 1 origin "$sha" 2>/dev/null &&
git -C "$dir" checkout -q --detach FETCH_HEAD 2>/dev/null &&
echo " pinned to ${sha:0:9} (upstream.lock)"
else
echo " NOTE: $name is not in upstream.lock; built against HEAD" >&2
fi
fi
[ -d "$dir" ] || { echo " no checkout for $name" >&2; return 1; }
( cd "$dir" && git checkout -- PKGBUILD 2>/dev/null ) || true
if [ -f "$PATCH_DIR/$name.sh" ]; then
# EL_STAGE=2, and no skip list any more.
#
# There WAS a list -- naming hooks to ignore here. It skipped the
# whole FILE, and that broke silently the moment a listed hook grew a
# section both stages need: git and meson each kept failing in stage
# 2 on exactly what their new sections fix, while the log said
# "hook skipped (stage-1 only)" about a hook that was by then two
# thirds relevant.
#
# A list of file names cannot say why. The hooks read EL_STAGE and
# decide per section, with the reason written beside the guard.
( cd "$dir" && EL_STAGE=2 bash "$PATCH_DIR/$name.sh" ) || {
if skip_hook "$name"; then
echo " hook skipped (stage-1 only)"
else
( cd "$dir" && bash "$PATCH_DIR/$name.sh" ) || {
echo " hook failed" >&2; return 1; }
# A hook can leave a PKGBUILD that no longer parses, and makepkg
# reports that far from its cause:
@ -988,6 +863,7 @@ stage2_build() {
# calls a valid file broken without it.
( cd "$dir" && bash -O extglob -n PKGBUILD ) || {
echo " hook left an unparseable PKGBUILD" >&2; return 1; }
fi
fi
( cd "$dir" && rm -f ./*.pkg.tar.* ) || true
@ -1013,32 +889,6 @@ stage2_build() {
if host_extract "$name"; then
echo " extracting on the host (chroot bsdtar not usable yet)"
( cd "$dir" && LC_ALL=C.UTF-8 makepkg $mkflags -o -C -f ) || return 1
# Symlinks that point out of the chroot.
#
# nss failed with
#
# /build/nss/PKGBUILD: line 55: ../certdata2pem.py:
# No such file or directory
#
# on a file that was plainly there. makepkg links a PKGBUILD's local
# source files into srcdir, and having extracted on the HOST it wrote the
# host's absolute path -- $WORK/pkg/nss/certdata2pem.py. Inside the
# chroot that path does not exist; only /build does. "No such file or
# directory" for an executable is usually its interpreter, which sent the
# first guess to the shebang; here it was the script itself, reached
# through a link to nowhere.
#
# Rewritten to the same file as the chroot sees it. Only links under
# $WORK/pkg are touched -- a link to anywhere else was not made by this
# mechanism and is not ours to redirect.
local _l _t _n=0
while IFS= read -r _l; do
_t=$(readlink "$_l")
case "$_t" in
"$WORK/pkg"/*) ln -sfn "/build/${_t#"$WORK/pkg/"}" "$_l"; _n=$((_n + 1)) ;;
esac
done < <(find "$dir/src" -maxdepth 2 -type l 2>/dev/null)
[ "$_n" -gt 0 ] && printf ' %s source link(s) repointed at /build\n' "$_n"
# -e: build in the tree already there. -C would wipe it again.
in_chroot "cd /build/$name && makepkg $mkflags -e -f" || return 1
else
@ -1080,31 +930,8 @@ stage2_build() {
# against the finished repository with --nodeps OFF. The relaxation is one
# install command wide, and the check that would catch its consequences is
# still there.
# Install what this package produced, MINUS the alternatives. zlib-ng
# produces zlib-ng-compat, and installing it fails:
#
# :: zlib-ng-compat-2.3.3-1 and zlib-1:1.3.2-3 are in conflict
#
# The package is correct and belongs in repo2; it just cannot be installed
# beside the zlib this chroot was built with. CHROOT_EXCLUDE already says so
# for the populate step, and the same list applies here -- one statement of
# which packages cannot coexist, used in both places.
local keep=() f2 pn2
for f2 in "${produced[@]}"; do
pn2=$(bsdtar -xOf "$f2" .PKGINFO 2>/dev/null | sed -n 's/^pkgname = //p')
if printf ' %s ' "${CHROOT_EXCLUDE[*]}" | grep -q " ${pn2:-_} "; then
printf ' not installed here: %s (alternative to an installed package)\n' "$pn2"
continue
fi
keep+=("$f2")
done
if [ "${#keep[@]}" -eq 0 ]; then
printf ' nothing to install\n'
( cd "$dir" && rm -rf src pkg ) || true
return 0
fi
sudo pacman --root "$ROOT" --config "$CONF2" --cachedir "$CACHE" \
--noconfirm --nodeps --nodeps -U "${keep[@]}" > "$WORK/stage2-inst-$name.txt" 2>&1 || {
--noconfirm --nodeps -U "${produced[@]}" > "$WORK/stage2-inst-$name.txt" 2>&1 || {
tail -10 "$WORK/stage2-inst-$name.txt" >&2; return 1; }
printf ' installed %s package(s)\n' "${#produced[@]}"

View file

@ -279,90 +279,6 @@ STAGE1_PACKAGES=(
# same reason: its package() would take Debian's dist-packages from the
# host's python.
python-setuptools-scm
# systemd generates part of its source with jinja2 templates:
#
# meson.build:1620:15: ERROR: python3 is missing modules: jinja2
#
# Not documentation -- the build cannot proceed without it. python-markupsafe
# comes with it, as jinja's only dependency.
python-markupsafe
python-jinja
# Three more the chroot asked for by name, each a build tool:
#
# python-pkgconfig brotli: ERROR Missing dependencies: pkgconfig
# -- the python MODULE, not pkg-config the program,
# which has been there since the lib64 fix.
# cython libseccomp: ERROR Backend subprocess exited when
# trying to invoke get_requires_for_build_wheel
# -- its python bindings are Cython.
# autoconf-archive tpm2-tss: configure.ac:139: error: undefined or
# overquoted macro: AC_MSG_WARN
# -- an AX_* macro is missing, so autoconf misreads the
# line and blames one it does know.
python-pkgconfig
cython
autoconf-archive
# publicsuffix-list: DATA, not a tool. libpsl stops on
#
# make: *** No rule to make target
# '/usr/share/publicsuffix/effective_tld_names.dat', needed by ...
#
# It compiles the Public Suffix List into a lookup table, so the list itself
# is an input to the build. First entry in this port whose absence is neither
# a program nor a library.
publicsuffix-list
# python-poetry-core: the fourth Python build backend this port has needed.
# python-pkgconfig stops on
#
# ERROR Backend 'poetry.core.masonry.api' is not available.
#
# flit_core, setuptools, hatchling and now poetry-core -- each package picks
# its own, and each one has to exist before that package can be built.
python-poetry-core
# python-vcs-versioning: setuptools-scm was split, and its own build now
# imports the half that left:
#
# from vcs_versioning import Configuration
# ModuleNotFoundError: No module named 'vcs_versioning'
#
# The traceback runs through setuptools' build_meta, so the visible error is
# "Backend subprocess exited when trying to invoke build_wheel" -- a backend
# complaint about a package that split upstream.
python-vcs-versioning
# python-fastjsonschema: poetry-core validates pyproject.toml against a JSON
# schema, so it needs a schema validator to build anything -- including
# itself. ModuleNotFoundError: No module named 'fastjsonschema'.
python-fastjsonschema
# gyp: nss will not configure without it --
#
# Building NSS requires an installation of gyp: https://gyp.gsrc.io
#
# Google's build-file generator, which nss uses to produce its ninja files.
# A Python program, so it goes with the rest of the Python set.
gyp
# --- Rust, and what it needs ---------------------------------------------
#
# NOT optional, and that was measured rather than assumed. ERPLibre's
# poetry.lock requires cryptography 46.0.5 with optional = false, plus jiter,
# orjson, pydantic-core and rpds-py -- all of which build Rust from source
# where no wheel exists, and none exists for s390x. Without Rust,
# `pip install cryptography` fails and ERPLibre does not start.
#
# The bootstrap cycle is not the obstacle it looks like: rust-1.90.0 ships an
# official s390x-unknown-linux-gnu tarball (verified, HTTP 200) and the host
# already carries rustc 1.85.1. What costs is llvm and clang, which Arch's
# rust lists as makedepends.
#
# libedit and python-psutil are all llvm and clang need beyond what is
# already here -- Sphinx is disabled by their hooks, which removes some
# fifteen documentation packages from the bill.
libedit
# python-psutil is a stage-2 package: built on the host it shipped 41
# dist-packages paths, like every Python package whose install path comes
# from the running interpreter. The ARTEFACT check caught it.
python-psutil
llvm
clang
# libaio: a real closure gap. lvm2 stops on
#
# device/bcache.c:30:10: fatal error: libaio.h: No such file or directory

View file

@ -110,37 +110,11 @@ note "2. ARTEFACT -- wrong library directories, which raise no error"
# as its default libdir, which changed where pkgconf installed, which changed
# pkgconf's compiled-in search path, which broke every pkg-config lookup in the
# chroot. One stray .a file at the bottom of that.
# TWO EXEMPTIONS, each named with its reason, because this check condemned
# correct packages on its first run against stage 2's output:
#
# python 17 x s390x-linux-gnu/ usr/lib/python3.14/config-3.14-s390x-linux-gnu/
# usr/lib/python3.14/_sysconfigdata__linux_s390x-linux-gnu.py
# filesystem 11 x usr/local/ usr/local/{bin,etc,games,include,...}
#
# CPython names those two after the build triplet on EVERY platform -- the Arch
# x86_64 package ships _sysconfigdata__linux_x86_64-linux-gnu.py -- so the
# triplet there is CPython's convention, not Debian's layout leaking in. And
# creating /usr/local's skeleton is what the filesystem package is for; the FHS
# requires it.
#
# Exempted by (package, pattern) pair rather than by package, so python is still
# checked for lib64 and dist-packages, and filesystem for multiarch. A blanket
# exemption is how a real leak gets waved through.
_exempt() { # _exempt <pkgfile> <pattern>
case "$(basename "$1")|$2" in
python-3*'|s390x-linux-gnu/') return 0 ;;
filesystem-*'|^usr/local/') return 0 ;;
esac
return 1
}
n=0
for f in "$REPO"/*.pkg.tar.*; do
_l=$(bsdtar -tf "$f" 2>/dev/null)
if ! _exempt "$f" 's390x-linux-gnu/'; then
c=$(grep -c 's390x-linux-gnu/' <<< "$_l")
[ "$c" -gt 0 ] && { bad "$(basename "$f"): $c multiarch paths"; n=$((n + 1)); }
fi
c=$(grep -c 's390x-linux-gnu/' <<< "$_l")
[ "$c" -gt 0 ] && { bad "$(basename "$f"): $c multiarch paths"; n=$((n + 1)); }
c=$(grep -c '^usr/lib64/' <<< "$_l")
[ "$c" -gt 0 ] && { bad "$(basename "$f"): $c paths under usr/lib64"; n=$((n + 1)); }
# dist-packages: Debian's name for site-packages.
@ -160,10 +134,8 @@ for f in "$REPO"/*.pkg.tar.*; do
[ "$c" -gt 0 ] && { bad "$(basename "$f"): $c paths under dist-packages"; n=$((n + 1)); }
# usr/local: python's posix_local scheme, and anything else that took the
# host's idea of where a local install goes.
if ! _exempt "$f" '^usr/local/'; then
c=$(grep -c '^usr/local/' <<< "$_l")
[ "$c" -gt 0 ] && { bad "$(basename "$f"): $c paths under usr/local"; n=$((n + 1)); }
fi
c=$(grep -c '^usr/local/' <<< "$_l")
[ "$c" -gt 0 ] && { bad "$(basename "$f"): $c paths under usr/local"; n=$((n + 1)); }
done
[ "$n" -eq 0 ] && good "no multiarch, no lib64, no dist-packages, no usr/local"

View file

@ -7,7 +7,6 @@ acl c51beb032d03a50cbd8dae96de23986d21558875 https://gitlab.archlinux.org/archli
attr 13964f8a55dde2d7e407267eb8fbc5ec44bdb585 https://gitlab.archlinux.org/archlinux/packaging/packages/attr.git
audit 8db272a0138b530ab71f98763c808a5713f859a4 https://gitlab.archlinux.org/archlinux/packaging/packages/audit.git
autoconf c69f433ef07dc732887e22cd5d5a7501cd944fef https://gitlab.archlinux.org/archlinux/packaging/packages/autoconf.git
autoconf-archive e5002e5c706204565fb7bb02a923226e8c53131b https://gitlab.archlinux.org/archlinux/packaging/packages/autoconf-archive.git
automake 9697f63e766a66b5da9ccba3fc1a46a798875154 https://gitlab.archlinux.org/archlinux/packaging/packages/automake.git
bash ac765bd0cb9b4c27f75c94bc320bf3c8b57304c4 https://gitlab.archlinux.org/archlinux/packaging/packages/bash.git
binutils 5b5ba27ac801a1e7d11819e02a80605ea33434dd https://gitlab.archlinux.org/archlinux/packaging/packages/binutils.git
@ -20,7 +19,6 @@ coreutils f33db1312f616064d4730f92e793d6a761a69e5a https://gitlab.archlinux.org/
cppdap 4a8ba28125a5fcaf5870b47177bbd6b7949ed59d https://gitlab.archlinux.org/archlinux/packaging/packages/cppdap.git
cryptsetup 5fd56afd0d44fad6129716d89103a48d29aa7ef1 https://gitlab.archlinux.org/archlinux/packaging/packages/cryptsetup.git
curl 45e9c1b535b60dd96c05f0c5dc7c07a8ea5947ac https://gitlab.archlinux.org/archlinux/packaging/packages/curl.git
cython 803d705dd505efa0e6c649164c2d609bb91121d9 https://gitlab.archlinux.org/archlinux/packaging/packages/cython.git
db5.3 ef842314637f28c5ae1a5e803ad3799965c0f27e https://gitlab.archlinux.org/archlinux/packaging/packages/db5.3.git
dbus f9960c26b0014b2b0b750425010feacda1b42be8 https://gitlab.archlinux.org/archlinux/packaging/packages/dbus.git
diffutils 63d25ebd4b3fc2850529955960643c07221bc233 https://gitlab.archlinux.org/archlinux/packaging/packages/diffutils.git
@ -46,7 +44,6 @@ gperf 1ad3213d13d12c8507fab53ec15ec0559be99ffc https://gitlab.archlinux.org/arch
gpgme ce68a023c3cd8e348f87c83666e0a2713cfca47b https://gitlab.archlinux.org/archlinux/packaging/packages/gpgme.git
grep b6557325ffa4abe40e4785823939a42093218acd https://gitlab.archlinux.org/archlinux/packaging/packages/grep.git
groff f84578039481409d2c880adc5bd5dcc4b3285dbb https://gitlab.archlinux.org/archlinux/packaging/packages/groff.git
gyp 602ee84a4028d468431a12684a0bea63c7c37bc2 https://gitlab.archlinux.org/archlinux/packaging/packages/gyp.git
gzip 4b347e7230151d6980db3251f1c65a7b27956c10 https://gitlab.archlinux.org/archlinux/packaging/packages/gzip.git
help2man e2c0cff5c9566eb76b322950e8ad85b6dd15e9cb https://gitlab.archlinux.org/archlinux/packaging/packages/help2man.git
hicolor-icon-theme 671ceecae2fa1f55a7fef9ead1d4cecfb3010d62 https://gitlab.archlinux.org/archlinux/packaging/packages/hicolor-icon-theme.git
@ -128,22 +125,14 @@ perl-timedate 0c4bac17183489ece9370298f45faf8fef586cb4 https://gitlab.archlinux.
pinentry 960a245f317f34fce281246f46d66f0d9b9d5eae https://gitlab.archlinux.org/archlinux/packaging/packages/pinentry.git
pkgconf fd5cdd4c656e1d7db763518d55a95f085e80a6f1 https://gitlab.archlinux.org/archlinux/packaging/packages/pkgconf.git
popt a40d1ac24230f1aab1034cf67423694fcbe3be4c https://gitlab.archlinux.org/archlinux/packaging/packages/popt.git
publicsuffix-list 3d200a288c966353b3638eb2e0aeabcbf8c2ba88 https://gitlab.archlinux.org/archlinux/packaging/packages/publicsuffix-list.git
python 3254b074f643d1fe29c72516f4ccb5dc4eeba2ac https://gitlab.archlinux.org/archlinux/packaging/packages/python.git
python-build 4d3e49b21fdb9e3648ca89bae4f27cb9a3681b78 https://gitlab.archlinux.org/archlinux/packaging/packages/python-build.git
python-fastjsonschema 5160e0bd608c23fb9f8cef53392a47d7f8156747 https://gitlab.archlinux.org/archlinux/packaging/packages/python-fastjsonschema.git
python-flit-core 85b6eb1e07f482cd748b30e6feee8b3d09c443d1 https://gitlab.archlinux.org/archlinux/packaging/packages/python-flit-core.git
python-installer eac45dc1cca1a3e08192ded2f206b9882ba62559 https://gitlab.archlinux.org/archlinux/packaging/packages/python-installer.git
python-jinja d39adbed48638fb2aeec1fa3c14a829e6498ffaf https://gitlab.archlinux.org/archlinux/packaging/packages/python-jinja.git
python-markupsafe bf271fe95f2b5706e30bd2278848ed344854b2f8 https://gitlab.archlinux.org/archlinux/packaging/packages/python-markupsafe.git
python-packaging a61fb6392a3d58655c46d6fba09f8edb978bb562 https://gitlab.archlinux.org/archlinux/packaging/packages/python-packaging.git
python-pkgconfig 3f70bf174624c01d2ac1431430f9f5ddd0aca302 https://gitlab.archlinux.org/archlinux/packaging/packages/python-pkgconfig.git
python-poetry-core 89a9484a143dbd201c625e54a46a9902f40b6fe4 https://gitlab.archlinux.org/archlinux/packaging/packages/python-poetry-core.git
python-pyproject-hooks 3f43005e004dd15d6aa2dde0cd3f34886f954b2c https://gitlab.archlinux.org/archlinux/packaging/packages/python-pyproject-hooks.git
python-setuptools 8b56f5626b15d6b62ea6653e34e93d3a7e6dbb3e https://gitlab.archlinux.org/archlinux/packaging/packages/python-setuptools.git
python-setuptools-scm d58e8364e8bc47b9f01ee459f31212f5c7c00d2e https://gitlab.archlinux.org/archlinux/packaging/packages/python-setuptools-scm.git
python-tqdm e0b7ec5522cc742812ba067fab0c0db660333063 https://gitlab.archlinux.org/archlinux/packaging/packages/python-tqdm.git
python-vcs-versioning 86fac49f3111ee3a77cc438d134c2d4fd794a556 https://gitlab.archlinux.org/archlinux/packaging/packages/python-vcs-versioning.git
python-wheel fa134ae45e7a48fc26dbb5c7aa4a78c16f9f61dd https://gitlab.archlinux.org/archlinux/packaging/packages/python-wheel.git
readline 562fc3dc105d29b06ad9bfae84e27387d1d50b6a https://gitlab.archlinux.org/archlinux/packaging/packages/readline.git
rhash 61d85647a7448bab282f716abed0cdfb7709cdce https://gitlab.archlinux.org/archlinux/packaging/packages/rhash.git