Compare commits

...

10 commits

Author SHA1 Message Date
ff65121058 [FIX] systemd without AppArmor, gpgme without its test harness, groff without a logo
gpgme is the one worth reading. It failed while BUILDING, not testing:

  make[3]: Entering directory '.../gpgme/tests/gpg'
  gpgconf: error running '/usr/bin/gpg-connect-agent': exit status 1

EL_NOCHECK skips check(), but `make all` still descends into tests/gpg and sets
up a throwaway GNUPGHOME, which means starting an agent -- and the chroot has no
dbus, no session and no tty. Third time this port has found that "no tests" and
"no test harness" are different requests.

groff needs xpmtoppm from netpbm to draw the GNU logo for its own manual. There
is no configure switch: groff has neither --without-doc nor --disable-doc, so
inventing a flag would have failed identically while the hook's check passed --
autoconf only warns about options it does not know. The generated Makefile's
DOC_GNU_EPS is emptied instead, and the verification runs inside build(), where
the generated file actually exists.

--- FR ---

gpgme mérite lecture. Il échouait en CONSTRUISANT, pas en testant :

  make[3]: Entering directory '.../gpgme/tests/gpg'
  gpgconf: error running '/usr/bin/gpg-connect-agent': exit status 1

EL_NOCHECK saute check(), mais `make all` descend quand même dans tests/gpg et
met en place un GNUPGHOME jetable, donc démarre un agent — or le chroot n'a ni
dbus, ni session, ni terminal. Troisième fois que ce portage constate que « pas
de tests » et « pas de harnais de test » sont deux demandes différentes.

groff a besoin de xpmtoppm, de netpbm, pour dessiner le logo GNU de son propre
manuel. Aucun interrupteur n'existe : ni --without-doc ni --disable-doc, donc
inventer un drapeau aurait échoué à l'identique pendant que le contrôle du hook
passait — autoconf ne fait qu'avertir des options qu'il ignore. La variable
DOC_GNU_EPS du Makefile généré est vidée, et la vérification tourne dans build(),
là où le fichier généré existe.

Assisted-by: Claude Opus 5
2026-08-22 22:02:56 -04:00
2fd675ce6b [FIX] libaio, and two packages that installed man pages nobody wrote
libaio is a real closure gap: lvm2 stops on "libaio.h: No such file or
directory". This port had already met that library from the other side, noting
Ubuntu's time64 rename to libaio.so.1t64 long before anything needed its headers.

fakeroot is the interesting one. The first hook removed the po4a call and nothing
else, so build() ended on a bare `cd doc` and package() failed on

  install: cannot stat './faked.1': No such file or directory

The English man pages ARE shipped. What is not shipped are the translations:
doc/Makefile carries SUBDIRS = de es fr nl pt ro sv, each holding only Makefiles
whose man_MANS name files po4a was supposed to generate. Removing the generator
and leaving the recursion is the same mistake as removing a build target and
leaving its install target.

shadow needs distinguishing: xsltproc exists here now, from our libxslt. What is
missing is the DocBook XML data its stylesheets read -- the same as pam.

--- FR ---

libaio est un vrai trou de fermeture : lvm2 s'arrête sur « libaio.h: No such file
or directory ». Ce portage avait déjà croisé cette bibliothèque par l'autre bout,
en notant le renommage time64 d'Ubuntu en libaio.so.1t64 bien avant que ses
en-têtes servent.

fakeroot est le cas intéressant. Le premier hook retirait l'appel à po4a et rien
d'autre : build() finissait sur un `cd doc` nu et package() échouait sur

  install: cannot stat './faked.1': No such file or directory

Les pages anglaises SONT livrées. Ce qui ne l'est pas, ce sont les traductions :
doc/Makefile porte SUBDIRS = de es fr nl pt ro sv, chacun ne contenant que des
Makefiles dont man_MANS nomme des fichiers que po4a devait générer. Retirer le
générateur en laissant la récursion est la même erreur que retirer une cible de
construction en laissant celle d'installation.

shadow mérite une distinction : xsltproc existe désormais, via notre libxslt. Ce
qui manque, ce sont les données DocBook que lisent ses feuilles de style — comme
pour pam.

Assisted-by: Claude Opus 5
2026-08-22 22:00:00 -04:00
ca355190c0 [FIX] binutils' extra targets were x86_64's, and gcc only wanted man pages
gold failed to link with

  s390.cc: undefined reference to `void gold::gold_error_at_location<32, true>'

Its s390 target file compiles code for both s390 and s390x and references
<32, true> instantiations, which exist only when a 32-bit s390 target is
configured. The PKGBUILD asks for --enable-targets=x86_64-pep,bpf-unknown-none.
x86_64-pep is the PE+ target for x86_64: it means nothing here, and on x86_64 it
is what happens to bring in the 32-bit instantiations gold's target files want.
So this was never a gold bug -- it is the -march=x86-64 story one layer further
in. s390-linux-gnu is the honest translation of that line.

gcc now compiles, Fortran front end included, and stopped in package_gcc() on
libstdc++'s doxygen man pages -- two places, build and install, the eighth time
this port has met that shape.

--- FR ---

gold ne se liait pas :

  s390.cc: undefined reference to `void gold::gold_error_at_location<32, true>'

Son fichier de cible s390 compile pour s390 et s390x et référence des
instanciations <32, true>, qui n'existent que si une cible s390 32 bits est
configurée. Le PKGBUILD demande --enable-targets=x86_64-pep,bpf-unknown-none.
x86_64-pep est la cible PE+ d'x86_64 : elle ne veut rien dire ici, et sur x86_64
c'est elle qui amène par hasard les instanciations 32 bits que gold réclame. Ce
n'était donc pas un défaut de gold — c'est l'histoire du -march=x86-64 une couche
plus loin. s390-linux-gnu est la traduction honnête de cette ligne.

gcc compile désormais, front-end Fortran compris, et s'arrêtait dans
package_gcc() sur les pages de manuel doxygen de libstdc++ — deux endroits,
construction et installation, huitième fois que ce portage rencontre cette forme.

Assisted-by: Claude Opus 5
2026-08-22 21:52:39 -04:00
f67796d04c [FIX] sqlite, libpsl and util-linux
sqlite is the deferred item this port had written down long ago -- "libtcl8.6
versus our tcl 9.0" -- arriving as a missing directory:

  mv: cannot stat '<pkgdir>/usr/lib/tcl8.6/sqlite*': No such file or directory

The version is read from the tree rather than written in, because the tcl package
decides it and hardcoding 9.0 puts the same trap back one release later.

The first attempt guarded with a blanket `grep tcl8.6` and condemned correct
code: the existing section of that hook names tcl8.6 on purpose, because it is
about the HOST's tcl. It also substituted $_tcldir into that older section,
before the variable was defined. The guard caught both.

util-linux is the seventh package to fail on a path at the end of package() after
a compile that worked. libpsl's error names its own fix.

--- FR ---

sqlite est l'élément différé que ce portage avait noté depuis longtemps —
« libtcl8.6 contre notre tcl 9.0 » — arrivant sous la forme d'un répertoire
absent :

  mv: cannot stat '<pkgdir>/usr/lib/tcl8.6/sqlite*': No such file or directory

La version est lue dans l'arbre plutôt qu'écrite en dur, parce que c'est le
paquet tcl qui la décide et qu'inscrire 9.0 remettrait le même piège une version
plus tard.

La première tentative gardait par un `grep tcl8.6` global et condamnait du code
juste : la section existante de ce hook nomme tcl8.6 exprès, car elle traite du
tcl de l'HÔTE. Elle avait aussi injecté $_tcldir dans cette section antérieure,
avant que la variable soit définie. Le garde a attrapé les deux.

util-linux est le septième paquet à échouer sur un chemin en fin de package()
après une compilation réussie. L'erreur de libpsl nomme son propre correctif.

Assisted-by: Claude Opus 5
2026-08-22 07:33:10 -04:00
7c7a1260d7 [FIX] the z13 baseline was only half applied
gcc failed at stage 2 with

  gfortran: error: unrecognized argument in option '-march=x86-64'
  configure: error: GNU Fortran is not working; please report a bug ...

Worth reading twice: gcc reported that its OWN freshly-built Fortran compiler was
not working, when the compiler was fine and had been handed another
architecture's flags. Everything about the message points at the wrong thing.

Arch splits makepkg.conf into /etc/makepkg.conf.d/*.conf, read AFTER the main
file. fortran.conf sets FFLAGS to the x86_64 list and FCFLAGS from it; rust.conf
does the same for RUSTFLAGS. So the override appended to makepkg.conf was itself
overridden -- silently, and only for the variables it did not mention. CFLAGS
survived by luck: no drop-in sets it.

Every flag variable now lives in a drop-in named to sort last, and the run prints
which drop-ins are still read after it. A drop-in that does not sort last does
nothing and looks like it worked.

--- FR ---

gcc a échoué à l'étage 2 sur

  gfortran: error: unrecognized argument in option '-march=x86-64'
  configure: error: GNU Fortran is not working; please report a bug ...

À lire deux fois : gcc annonçait que SON PROPRE compilateur Fortran fraîchement
bâti ne fonctionnait pas, alors qu'il allait bien et qu'on lui avait passé les
drapeaux d'une autre architecture. Tout dans le message désigne la mauvaise chose.

Arch éclate makepkg.conf en /etc/makepkg.conf.d/*.conf, lus APRÈS le fichier
principal. fortran.conf met FFLAGS à la liste x86_64 et FCFLAGS d'après lui ;
rust.conf fait de même pour RUSTFLAGS. Le remplacement ajouté en fin de
makepkg.conf était donc lui-même écrasé — silencieusement, et seulement pour les
variables qu'il ne nommait pas. CFLAGS a survécu par chance : aucun appoint ne le
définit.

Toutes les variables de drapeaux vivent maintenant dans un appoint nommé pour
passer en dernier, et la construction affiche quels appoints sont encore lus
après lui. Un appoint qui ne passe pas en dernier ne fait rien et en a l'air.

Assisted-by: Claude Opus 5
2026-08-22 07:29:43 -04:00
4eca3612f9 [ADD] nlohmann-json, and three more packages without their docs
nlohmann-json is a real closure gap, not a documentation cut: both cmake and
cppdap stop on "Could NOT find nlohmann_json". It is a header-only C++ library,
one of the cheapest packages in the list, and cmake is not optional -- the
cmake/cppdap/jsoncpp/libuv group behind it is what several other packages build
with.

The other three are doxygen and sphinx again. jsoncpp's is the least readable
failure in the port so far: doxybuild.py is handed an empty doxygen path and
raises Exception('path is empty.'), so a missing tool arrives as a Python
traceback about a path.

libuv needed two places -- `make man -C docs` and the install of
docs/build/man/libuv.1 -- the same shape as libxml2's docs split and pam's PDFs:
a failure on a path at the end of package(), after a compile that worked.

--- FR ---

nlohmann-json est un vrai trou de fermeture, pas une coupe de documentation :
cmake et cppdap s'arrêtent tous deux sur « Could NOT find nlohmann_json ». C'est
une bibliothèque C++ d'en-têtes seuls, l'un des paquets les moins chers de la
liste, et cmake n'est pas optionnel — le groupe cmake/cppdap/jsoncpp/libuv
derrière lui est ce avec quoi plusieurs autres se bâtissent.

Les trois autres sont encore doxygen et sphinx. Celui de jsoncpp est l'échec le
moins lisible du portage jusqu'ici : doxybuild.py reçoit un chemin de doxygen
vide et lève Exception('path is empty.'), donc un outil manquant arrive sous la
forme d'une trace Python parlant d'un chemin.

libuv demandait deux endroits — `make man -C docs` et l'installation de
docs/build/man/libuv.1 — même forme que le découpage de libxml2 et les PDF de
pam : un échec sur un chemin en fin de package(), après une compilation réussie.

Assisted-by: Claude Opus 5
2026-08-22 02:32:55 -04:00
e5c6835234 [FIX] libxcrypt: a compiler newer than the release expects
lib/crypt-gost-yescrypt.c:134:16: error: initialization discards 'const'
    qualifier from pointer target type

A warning, promoted by libxcrypt's own -Werror. Our gcc is 16.2.1 and libxcrypt
4.5.2 was released against considerably older ones; the same code warns the same
way on x86_64 with the same compiler, so this says nothing about s390x.

--disable-werror is libxcrypt's own switch. Patching the source would mean
carrying a diff against upstream C for a const-correctness nit in an
implementation of a Russian hash standard nothing here uses. Declining to treat
warnings as errors is what a distribution does when its compiler is ahead of a
release.

Both configure calls: libxcrypt is built twice, once for the obsolete-API compat
library. Fixing only the first leaves the second failing identically, which reads
as if the flag had done nothing.

--- FR ---

  lib/crypt-gost-yescrypt.c:134:16: error: initialization discards 'const'
    qualifier from pointer target type

Un avertissement, promu par le -Werror de libxcrypt lui-même. Notre gcc est en
16.2.1 et libxcrypt 4.5.2 est sorti face à bien plus anciens ; le même code
avertit de la même façon sur x86_64 avec le même compilateur — cela ne dit rien
de s390x.

--disable-werror est l'interrupteur de libxcrypt. Corriger la source
supposerait de porter un correctif contre du C amont pour une vétille de
const-correction dans une implémentation d'une norme de hachage russe dont rien
ici ne se sert. Refuser de traiter les avertissements en erreurs est ce que fait
une distribution quand son compilateur devance une version.

Les deux appels à configure : libxcrypt est bâti deux fois, dont une pour la
bibliothèque de compatibilité. Ne corriger que le premier laisse le second
échouer à l'identique, ce qui se lit comme un drapeau sans effet.

Assisted-by: Claude Opus 5
2026-08-22 00:08:00 -04:00
adb6c4f21d [FIX] four more packages that build without their documentation
git, xz, meson and pam. Each uses the project's own switch where one exists.

git is the one worth reading: `man` appears as a make target twice and
`install-man` twice, once each for git proper and once for contrib/subtree.
Removing the build targets and leaving the install targets gives "No rule to
make target 'install-man'", which reads like a broken Makefile. Four places,
again.

pam is different in kind from the others: xmllint EXISTS here, from our own
libxml2. It fails because pam validates its DocBook sources against DTDs that
are not installed and --nonet forbids fetching them. Shipping the DocBook XML
data would be a package; disabling the documentation is a flag.

--- FR ---

git, xz, meson et pam. Chacun par l'interrupteur du projet lui-même, quand il
existe.

git mérite lecture : `man` figure deux fois comme cible make et `install-man`
deux fois, une paire pour git et une pour contrib/subtree. Retirer les cibles de
construction en laissant celles d'installation donne « No rule to make target
'install-man' », ce qui se lit comme un Makefile cassé. Quatre endroits, encore.

pam diffère en nature : xmllint EXISTE ici, il vient de notre libxml2. Il échoue
parce que pam valide ses sources DocBook contre des DTD non installées, et que
--nonet interdit de les chercher. Livrer les données DocBook serait un paquet ;
désactiver la documentation est un drapeau.

Assisted-by: Claude Opus 5
2026-08-22 00:07:14 -04:00
0602a2b64c [FIX] the chroot was missing 35 packages we had already built
CHROOT_PKGS grew one name at a time, each added because a build asked for it. It
reached 151 of the 190 packages stage 1 had produced, and the absent ones broke
builds without ever being named:

  openldap: configure: error: --enable_argon2=yes requires --with-argon2

while the PKGBUILD already passes --with-argon2=libsodium and libsodium sat in
repo/s390x, simply not installed. configure looked for a library, did not find
it, and reported a missing OPTION. krb5's "libldap not found", libsasl's "Could
not locate OpenLDAP", lvm2's "libudev >= 143" and audit's undefined SASL symbol
are the same sentence in different words.

Stage 2 builds with --nodeps, so nothing installs a build dependency for it.
Dropping --nodeps would need a working in-chroot pacman, and that pacman is one
of the packages being rebuilt. So: install the distribution we have. Four names
out of 190 cannot coexist -- two of them only visible to a real install, since
-Syp says nothing about file conflicts. 187 packages installed, no fallback.

--- FR ---

CHROOT_PKGS a grandi un nom à la fois, chacun ajouté parce qu'une construction le
demandait. Il atteignait 151 des 190 paquets produits par l'étage 1, et les
absents cassaient des constructions sans jamais être nommés :

  openldap: configure: error: --enable_argon2=yes requires --with-argon2

alors que le PKGBUILD passe déjà --with-argon2=libsodium et que libsodium était
dans repo/s390x, simplement pas installé. configure cherchait une bibliothèque,
ne la trouvait pas, et signalait une OPTION manquante. Le « libldap not found »
de krb5, le « Could not locate OpenLDAP » de libsasl, le « libudev >= 143 » de
lvm2 et le symbole SASL indéfini d'audit sont la même phrase autrement dite.

L'étage 2 bâtit avec --nodeps : rien n'installe de dépendance de compilation pour
lui. Y renoncer exigerait un pacman fonctionnel dans le chroot, et ce pacman est
l'un des paquets à rebâtir. Donc : installer la distribution que nous avons.
Quatre noms sur 190 ne peuvent coexister — dont deux visibles seulement à
l'installation réelle, -Syp ne disant rien des conflits de fichiers. 187 paquets
installés, aucun repli.

Assisted-by: Claude Opus 5
2026-08-21 21:37:26 -04:00
07486af2fa [FIX] systemd without BPF, pacman without asciidoc
Two more documentation-and-toolchain cuts, both using the project's own switch.

systemd asked for libbpf, which this port does not package -- and packaging it
means clang and llvm, an entire second compiler toolchain for unit features
nothing here uses yet. pacman asked for asciidoc, the same Python documentation
stack already declined for doxygen, asciidoctor and a2x.

pacman is worth naming out loud: it is the package manager, so its man pages are
the ones a user reaches for first. This is the most visible of the documentation
cuts and the strongest single argument for restoring them at stage 3.

--- FR ---

Deux coupes de plus, documentation et chaîne d'outils, chacune par l'interrupteur
prévu par le projet lui-même.

systemd réclamait libbpf, que ce portage ne paquette pas — et le paqueter suppose
clang et llvm, soit une seconde chaîne de compilation entière pour des fonctions
d'unité dont rien ici ne se sert encore. pacman réclamait asciidoc, la même pile
Python déjà écartée pour doxygen, asciidoctor et a2x.

pacman mérite d'être nommé : c'est le gestionnaire de paquets, donc ses pages de
manuel sont les premières qu'un utilisateur cherche. C'est la plus visible de ces
coupes, et le meilleur argument pour les restaurer à l'étage 3.

Assisted-by: Claude Opus 5
2026-08-21 21:25:20 -04:00
21 changed files with 757 additions and 98 deletions

View file

@ -97,3 +97,37 @@ ZZPY
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"
# --- the extra targets are x86_64's --------------------------------------------
#
# s390.cc:(.text+0x2d76): undefined reference to
# `void gold::gold_error_at_location<32, true>(...)'
#
# 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
#
# --enable-targets=x86_64-pep,bpf-unknown-none
#
# 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.
#
# 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
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 -- "--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,29 +1,50 @@
#!/usr/bin/env bash
# fakeroot: no translated man pages.
# fakeroot: no translated man pages, and no attempt to install them.
#
# po4a: command not found
# First attempt removed the po4a call and nothing else. build() then ended on a
# bare `cd doc`, and package() failed with
#
# build() ends on a po4a call that renders the man page into every language it
# has a catalogue for. po4a is Perl, and packaging it means packaging its module
# stack -- perl-yaml-tiny, perl-syntax-keyword-try, perl-term-readkey,
# perl-locale-gettext and more -- so that fakeroot's man page exists in Polish.
# install: cannot stat './faked.1': No such file or directory
#
# fakeroot is not optional the way its man pages are: makepkg cannot build a
# package without it. Same arbitration as --auto-features auto in
# build-stage2.sh, and the same note for stage 3.
# The English man pages are shipped -- doc/faked.1 and doc/fakeroot.1 are in the
# tree. What is NOT shipped is their translations: doc/Makefile carries
#
# SUBDIRS = de es fr nl pt ro sv
#
# and each of those directories holds only Makefiles, with man_MANS naming
# fakeroot.1 and faked.1 that po4a was supposed to generate. So `make install`
# recursed into doc/fr and tried to install a file nobody had written. Removing
# the generator without removing the recursion left the second half of the job
# undone, which is the same mistake as removing a build target and leaving its
# install target -- eight packages in this port have now failed that way.
#
# WHY NOT po4a. It is Perl, and packaging it means packaging its module stack --
# perl-yaml-tiny, perl-syntax-keyword-try, perl-term-readkey and more -- so that
# fakeroot's man page exists in Polish. fakeroot itself is not optional: makepkg
# cannot build a package without it.
#
# The English man pages ARE still installed. Only the translations are dropped.
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]*po4a\b", l)]
assert len(hit) == 1, "fakeroot: expected one po4a call, got %d" % len(hit)
lines[hit[0]] = " # po4a call removed: no Perl module stack at either stage."
out = []
for l in lines:
# makedepends too, or --nodeps is the only thing hiding the gap.
out.append(l.replace(" 'po4a'", "") if "makedepends" in l else l)
po = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*po4a\b", l)]
assert len(po) == 1, "fakeroot: expected one po4a call, got %d" % len(po)
i = po[0]
# The `cd doc` immediately above exists only to run po4a.
assert re.match(r"^[ \t]*cd doc[ \t]*$", lines[i - 1]), \
"fakeroot: expected `cd doc` above the po4a call, found %r" % lines[i - 1]
ind = re.match(r"^[ \t]*", lines[i - 1]).group(0)
lines[i - 1:i + 1] = [
ind + "# po4a is not available at either stage, so the translated man pages",
ind + "# are never generated -- and doc/<lang>/Makefile would still try to",
ind + "# install them. Cut the recursion instead of leaving it to fail.",
ind + 'sed -i "s/^SUBDIRS = de es fr nl pt ro sv$/SUBDIRS =/" doc/Makefile',
]
out = [l.replace(" 'po4a'", "") if "makedepends" in l else l for l in lines]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(out))
ZZPY
grep -qE "^[ \t]*po4a " PKGBUILD && { echo "fakeroot: a po4a call survived" >&2; exit 1; }
grep -q "po4a call removed" PKGBUILD || { echo "fakeroot: nothing was removed" >&2; exit 1; }
echo "fakeroot: translated man pages dropped"
grep -qE "^[[:space:]]*po4a " PKGBUILD && { echo "fakeroot: a po4a call survived" >&2; exit 1; }
grep -q 'SUBDIRS =/' PKGBUILD || { echo "fakeroot: the doc recursion was not cut" >&2; exit 1; }
grep -q "'po4a'" PKGBUILD && { echo "fakeroot: po4a still in makedepends" >&2; exit 1; }
echo "fakeroot: translated man pages dropped, recursion cut"

View file

@ -445,3 +445,31 @@ grep -q -- '--with-arch=z13' PKGBUILD || { echo "gcc: baseline not set" >&2; exi
grep -A1 -E -- '--enable-languages=' PKGBUILD | grep -q -- '--with-arch=z13' || {
echo "gcc: --with-arch is in the file but not on the configure line" >&2; exit 1; }
echo "gcc: baseline z13, tune z16"
# --- no libstdc++ man pages ----------------------------------------------------
#
# make: *** [Makefile:890: doc-install-man] Error 1
# ==> ERROR: A failure occurred in package_gcc()
#
# gcc now COMPILES here -- what remained was libstdc++'s API documentation, built
# with doxygen. TWO PLACES: doc-man-doxygen in build() and doc-install-man in
# package(). Removing only the first fails on the second, which is the shape this
# port has now hit eight times.
#
# Worth noting how close this was to the end: the whole of gcc, including the
# Fortran front end that had failed for six passes, built successfully and then
# stopped on man pages.
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"doc-man-doxygen|doc-install-man", l)]
assert len(hit) == 2, "gcc: expected two libstdc++ doc lines, got %d" % len(hit)
for i in reversed(hit):
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i] = ind + ": # libstdc++ man pages need doxygen, which this port lacks."
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "doc-man-doxygen|doc-install-man" PKGBUILD && {
echo "gcc: a libstdc++ doc target survived" >&2; exit 1; }
echo "gcc: libstdc++ man pages dropped"

View file

@ -45,3 +45,34 @@ 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"
# --- no man pages -------------------------------------------------------------
#
# asciidoc: command not found
#
# git's man pages come from asciidoc and xmlto, the Python documentation stack
# this port has declined four times now. FOUR PLACES, which is what makes this
# worth a comment: `man` appears as a make target twice and `install-man` twice,
# once each for git proper and once each for contrib/subtree. Removing the build
# targets and leaving the install targets gives
#
# make: *** No rule to make target 'install-man'
#
# which reads like a broken Makefile.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
b = [i for i, l in enumerate(lines) if re.search(r"_make .*\ball man\b", l)]
i2 = [i for i, l in enumerate(lines) if re.search(r"\binstall install-man\b", l)]
assert len(b) == 2, "git: expected two `all man` targets, got %d" % len(b)
assert len(i2) == 2, "git: expected two `install install-man` targets, got %d" % len(i2)
for i in b:
lines[i] = re.sub(r"\ball man\b", "all", lines[i])
for i in i2:
lines[i] = re.sub(r"\binstall install-man\b", "install", lines[i])
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
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)"

35
patches/pkgbuild/gpgme.sh Executable file
View file

@ -0,0 +1,35 @@
#!/usr/bin/env bash
# gpgme: do not build the gpg test harness.
#
# make[3]: Entering directory '.../gpgme/tests/gpg'
# gpgconf: error running '/usr/bin/gpg-connect-agent': exit status 1
# gpgconf: error running '/usr/bin/gpg-connect-agent KILLDIRMNGR': General error
#
# This is not check() -- EL_NOCHECK skips that. gpgme's `make all` descends into
# tests/gpg and SETS UP a throwaway GNUPGHOME, which means starting an agent.
# Inside the chroot there is no dbus, no session and no tty, so the agent cannot
# come up and the setup fails while merely building.
#
# --disable-gpg-test is gpgme's own switch, and the PKGBUILD already passes its
# sibling --disable-gpgsm-test for the same kind of reason. This is the third
# time this port has found that "no tests" and "no test harness" are different
# requests: skipping check() does not stop `make all` from building what check()
# would have run.
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]*--disable-gpgsm-test", l)]
assert len(hit) == 1, "gpgme: expected one --disable-gpgsm-test, got %d" % len(hit)
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
cont = lines[i].rstrip().endswith("\\")
# Insert BEFORE, so the continuation state of the last option is untouched --
# adding a line after a final option that has no backslash would end the command.
lines.insert(i, ind + "--disable-gpg-test \\")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "--disable-gpg-test" PKGBUILD || { echo "gpgme: the flag was not added" >&2; exit 1; }
grep -B1 -- "--disable-gpgsm-test" PKGBUILD | grep -q -- "--disable-gpg-test" || {
echo "gpgme: the flag is not adjacent to its sibling" >&2; exit 1; }
echo "gpgme: gpg test harness not built"

47
patches/pkgbuild/groff.sh Executable file
View file

@ -0,0 +1,47 @@
#!/usr/bin/env bash
# groff: one decorative figure, and no netpbm to draw it.
#
# program 'xpmtoppm' is missing; can't generate doc/gnu.eps
# make[2]: *** [Makefile:19801: doc/gnu.eps] Error 1
#
# xpmtoppm belongs to netpbm. What it produces is the GNU logo, as EPS, for
# groff's own manual -- and getting it would mean packaging netpbm and the image
# libraries under it (libjpeg-turbo, libpng, libtiff, jbigkit), none of which
# anything else in this bootstrap wants.
#
# NO CONFIGURE SWITCH EXISTS: groff's configure has neither --without-doc nor
# --disable-doc, so there was nothing to turn off. Adding an invented flag would
# have been worse than useless -- autoconf merely warns about options it does not
# know, the build would have failed identically, and the hook's own check would
# have passed.
#
# The Makefile is generated, so it is edited after configure and before make. The
# variable is emptied rather than the rule patched, because the INSTALL side is
# already tolerant:
#
# if test -f "$$d/gnu.eps"; then ... fi
#
# so with nothing built, nothing is installed and nothing complains. One line,
# and the only one whose absence the rest of the Makefile already handles.
set -euo pipefail
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.match(r"^[ \t]*make[ \t]*$", l)]
assert len(mk) >= 1, "groff: no bare `make` line found in build()"
i = mk[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i:i] = [
ind + "# doc/gnu.eps needs xpmtoppm from netpbm, which this port does not",
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',
]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q "DOC_GNU_EPS" PKGBUILD || { echo "groff: the figure was not dropped" >&2; exit 1; }
# The grep inside build() is the real check: it runs against the generated
# Makefile and fails the build if the variable was not what this hook expects.
grep -q 'grep -q "\^DOC_GNU_EPS =\$" Makefile' PKGBUILD || {
echo "groff: the in-build verification is missing" >&2; exit 1; }
echo "groff: GNU logo figure dropped (no netpbm)"

28
patches/pkgbuild/jsoncpp.sh Executable file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
# jsoncpp: no doxygen build.
#
# File "doxybuild.py", line 82, in assert_is_exe
# raise Exception('path is empty.')
#
# A python traceback, which is what makes this one hard to read: doxybuild.py was
# handed an empty doxygen path because there is no doxygen, and it reports that
# as a path problem. build() runs it after the compile has already finished.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
# The call spans several lines: find it and drop through its last continuation.
hit = [i for i, l in enumerate(lines) if re.search(r"python doxybuild\.py", l)]
assert len(hit) == 1, "jsoncpp: expected one doxybuild call, got %d" % len(hit)
i = hit[0]
j = i
while lines[j].rstrip().endswith("\\"):
j += 1
del lines[i:j + 1]
# The `echo "Building docs..."` above it would now announce nothing.
if i > 0 and "Building docs" in lines[i - 1]:
del lines[i - 1]
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"

20
patches/pkgbuild/libevent.sh Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# libevent: no Doxygen API reference.
#
# -- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
# CMake Error at cmake/UseDoxygen.cmake:108 (message):
#
# EVENT__DOXYGEN=ON makes it a hard requirement. doxygen is the most expensive of
# the documentation tools this port declined -- it wants clang, fmt, spdlog and
# llvm-libs, an entire compiler infrastructure to produce HTML.
set -euo pipefail
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-D EVENT__DOXYGEN=ON"
assert s.count(old) == 1, "libevent: expected one EVENT__DOXYGEN, got %d" % s.count(old)
s = s.replace(old, "-D EVENT__DOXYGEN=OFF", 1)
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"

20
patches/pkgbuild/libpsl.sh Executable file
View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# libpsl: no gtk-doc.
#
# configure: error: ... documentation by adding '--disable-gtk-doc' to
# './configure'.
#
# The error names its own fix, which is the whole of this hook. gtk-doc is a
# GNOME documentation toolchain; --enable-gtk-doc is in the PKGBUILD and the
# tool is not here.
set -euo pipefail
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "--enable-gtk-doc"
assert s.count(old) == 1, "libpsl: expected one --enable-gtk-doc, got %d" % s.count(old)
s = s.replace(old, "--disable-gtk-doc", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q -- "--disable-gtk-doc" PKGBUILD || { echo "libpsl: gtk-doc not disabled" >&2; exit 1; }
echo "libpsl: gtk-doc disabled"

32
patches/pkgbuild/libuv.sh Executable file
View file

@ -0,0 +1,32 @@
#!/usr/bin/env bash
# libuv: no man page.
#
# Makefile:14: *** The 'sphinx-build' command was not found.
#
# TWO PLACES: `make man -C docs` builds it and package() installs
# docs/build/man/libuv.1. Removing only the build gives
#
# install: cannot stat 'docs/build/man/libuv.1'
#
# which is the same shape as libxml2's docs split and pam's PDFs -- a failure on
# a path, at the end of package(), after a compile that worked.
set -euo pipefail
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 man -C docs", l)]
assert len(mk) == 1, "libuv: expected one `make man -C docs`, got %d" % len(mk)
inst = [i for i, l in enumerate(lines) if "docs/build/man/libuv.1" in l]
assert len(inst) == 1, "libuv: expected one man install, got %d" % len(inst)
i = inst[0]
# The install spans two lines: the source and the destination.
j = i
while lines[j].rstrip().endswith("\\"):
j += 1
del lines[i:j + 1]
del lines[mk[0]]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -qE "make man -C docs|libuv\.1" PKGBUILD && {
echo "libuv: a man page reference survived" >&2; exit 1; }
echo "libuv: man page dropped (no sphinx)"

37
patches/pkgbuild/libxcrypt.sh Executable file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env bash
# libxcrypt: build with a compiler newer than it expects.
#
# lib/crypt-gost-yescrypt.c:134:16: error: initialization discards 'const'
# qualifier from pointer target type
#
# A warning, promoted to an error by libxcrypt's own -Werror. Our gcc is 16.2.1;
# libxcrypt 4.5.2 was released against considerably older ones, and this
# diagnostic is not about the port -- the same code warns the same way on x86_64
# with the same compiler.
#
# --disable-werror is libxcrypt's own switch. Patching the source would mean
# carrying a diff against upstream C for a const-correctness nit in an
# implementation of a Russian hash standard that nothing here uses; declining to
# treat warnings as errors is what a distribution does when its compiler is
# ahead of a release.
#
# BOTH CONFIGURE CALLS. libxcrypt is built twice -- once normally and once for
# the obsolete-API compat library -- and fixing only the first leaves the second
# failing identically, which reads as if the flag had no effect.
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"/configure[ \t]*\\\\?$", l)]
assert len(hit) == 2, "libxcrypt: expected two configure calls, got %d" % len(hit)
# Backwards, so inserting does not move the indices still to be used.
for i in reversed(hit):
ind = re.match(r"^[ \t]*", lines[i]).group(0)
assert lines[i].rstrip().endswith("\\"), \
"libxcrypt: a configure call does not continue: %r" % lines[i]
lines.insert(i + 1, ind + " --disable-werror \\")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
n=$(grep -c -- "--disable-werror" PKGBUILD)
[ "$n" = 2 ] || { echo "libxcrypt: expected 2 --disable-werror, found $n" >&2; exit 1; }
echo "libxcrypt: -Werror disabled in both builds"

View file

@ -89,3 +89,31 @@ 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"
# --- no reference manual ------------------------------------------------------
#
# meson.build:6:4: ERROR: Problem encountered: Cannot build documentation
#
# meson builds its own reference manual with hotdoc, and its docs/meson.build
# refuses outright when the tool is absent rather than degrading. FOUR LINES:
# the setup, the compile, the install, and a reference_manual.json copied
# separately. Leaving any of the last three behind fails on a path in
# docs.build/ that was never created.
set -euo pipefail
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
pats = [r"meson\.py setup docs docs\.build", r"meson\.py compile -C docs\.build",
r"meson\.py install -C docs\.build", r"docs\.build/reference_manual\.json"]
keep = []
found = 0
for l in lines:
if any(re.search(p, l) for p in pats):
found += 1
continue
keep.append(l)
assert found == 4, "meson: expected four docs lines, got %d" % found
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(keep))
ZZPY
grep -q "docs.build" PKGBUILD && { echo "meson: a docs.build line survived" >&2; exit 1; }
echo "meson: reference manual dropped (no hotdoc)"

View file

@ -53,3 +53,29 @@ PY
[ "$(grep -c -- '--libdir=lib' PKGBUILD)" = 1 ] || {
echo "pacman: libdir not forced to lib exactly once" >&2; exit 1; }
echo "pacman: libdir forced to /usr/lib (Debian host defaults it to multiarch)"
# --- no asciidoc documentation ------------------------------------------------
#
# ERROR: Program 'asciidoc' not found or not executable
#
# pacman's man pages come from asciidoc, a Python toolchain this port declined
# along with doxygen, asciidoctor and a2x -- each of them a language runtime
# whose only job is rendering documentation. -Ddoc=disabled is meson's own
# switch.
#
# Worth noting where this one lands: pacman IS the package manager, so its man
# pages are the ones a user reaches for first. This is the most visible of the
# documentation cuts and the strongest argument for restoring them at stage 3,
# where the tools can be built against a self-hosted toolchain.
set -euo pipefail
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-Ddoc=enabled"
assert s.count(old) == 1, "pacman: expected one doc option, got %d" % s.count(old)
s = s.replace(old, "-Ddoc=disabled", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q -- "-Ddoc=disabled" PKGBUILD || {
echo "pacman: doc was not disabled" >&2; exit 1; }
echo "pacman: man pages disabled (no asciidoc)"

View file

@ -1,44 +1,24 @@
#!/usr/bin/env bash
# pam: the PDF removal assumes fop, which the Ubuntu host does not have.
# pam: no XML documentation.
#
# rm: cannot remove
# '.../pkg/pam/usr/share/doc/Linux-PAM/*.pdf': No such file or directory
# meson.build:42:2: ERROR: Command `/usr/bin/xmllint --nonet --noout ...`
#
# package() ends on
#
# # remove unreproducible pdf files
# rm "${pkgdir}"/usr/share/doc/Linux-PAM/*.pdf
#
# and that line only works because Arch lists fop in makedepends. fop is a
# Java renderer; putting a JVM on the build host to produce three PDFs that
# the very next line deletes buys nothing, so the host stays without it.
#
# Without fop meson drops every PDF target in silence -- doc/adg, doc/mwg and
# doc/sag each guard theirs with `if prog_fop.found()`, and the configured
# build.ninja then holds zero .pdf rules. The DIRECTORY still exists, because
# the HTML and text manuals install beside them, so the glob is never
# expanded and rm is handed the literal string `*.pdf`. Same shape as the
# util-linux glob that started the libdir hunt: the message names a path that
# was never going to exist, and it aborts package() AFTER a successful
# compile -- so the build looks like it failed at the very last step for no
# reason at all.
#
# -f is the whole fix. With fop the PDFs are still removed; without it the
# empty glob stops being an error.
#
# Not a hook, but the other half of pam and worth finding here: libnsl and
# libtirpc are NOT optional on this host even though upstream marks NIS
# `auto`, because arch-meson passes --auto-features enabled, which promotes
# every auto feature to a hard requirement. Same mechanism turns pam's `docs`
# feature into five fatal xmlcatalog probes. All of it is host packages, and
# they are declared in install_host_deps.
# xmllint EXISTS here -- it comes from our libxml2 -- and that is what makes this
# one different from the other documentation failures. It fails because pam
# validates its DocBook sources against DTDs that are not installed, and
# --nonet forbids fetching them. Shipping the DocBook XML data would be a
# package; disabling the documentation is a flag.
set -euo pipefail
# Exactly one removal, and it must be the pdf one. Never a blanket rewrite:
# package() also runs `install` and `chmod +s` on paths under the same tree,
# and meson install itself writes there.
n=$(grep -cF 'rm "${pkgdir}"/usr/share/doc/Linux-PAM/' PKGBUILD || true)
[ "$n" = "1" ] || { echo "pam: expected 1 pdf removal, found $n" >&2; exit 1; }
sed -i 's|rm "${pkgdir}"/usr/share/doc/Linux-PAM/|rm -f "${pkgdir}"/usr/share/doc/Linux-PAM/|' PKGBUILD
grep -qF 'rm -f "${pkgdir}"/usr/share/doc/Linux-PAM/*.pdf' PKGBUILD || {
echo "pam: pdf removal not made tolerant" >&2; exit 1; }
echo "pam: pdf removal tolerates the empty glob (host has no fop)"
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"^[ \t]*-Dselinux=disabled", l)]
assert len(hit) == 1, "pam: expected one -Dselinux option line, got %d" % len(hit)
i = hit[0]
ind = re.match(r"^[ \t]*", lines[i]).group(0)
assert lines[i].rstrip().endswith("\\"), "pam: the option list does not continue"
lines.insert(i + 1, ind + "-Ddocs=disabled \\")
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"

33
patches/pkgbuild/shadow.sh Executable file
View file

@ -0,0 +1,33 @@
#!/usr/bin/env bash
set -euo pipefail
# --- no man pages -------------------------------------------------------------
#
# install: cannot stat './man5/gshadow.5': No such file or directory
# install: cannot stat './man8/groupdel.8': No such file or directory
#
# shadow renders its man pages from DocBook with xsltproc. xsltproc EXISTS here
# now -- our libxslt supplies it -- and that is what makes this one worth
# distinguishing: what is missing is the DocBook XML data the stylesheets need,
# not the tool. Exactly pam's situation.
#
# TWO PLACES: --enable-man in the configure options and an explicit
# `make -C man install` in package(). The second is the one that reported the
# failure, and removing only it would leave configure still trying to build them.
set -euo pipefail
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-man[ \t]*$", l)]
assert len(en) == 1, "shadow: expected one --enable-man, got %d" % len(en)
lines[en[0]] = lines[en[0]].replace("--enable-man", "--disable-man")
inst = [i for i, l in enumerate(lines) if re.search(r"make .*-C man install", l)]
assert len(inst) == 1, "shadow: expected one `make -C man install`, got %d" % len(inst)
ind = re.match(r"^[ \t]*", lines[inst[0]]).group(0)
lines[inst[0]] = ind + ": # man pages need DocBook XML data, which this port lacks."
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "--disable-man" PKGBUILD || { echo "shadow: man not disabled" >&2; exit 1; }
grep -qE "make .*-C man install" PKGBUILD && {
echo "shadow: the man install survived" >&2; exit 1; }
echo "shadow: man pages disabled (DocBook data absent, not xsltproc)"

View file

@ -43,3 +43,39 @@ 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)"
# --- and in the chroot, our tcl is not 8.6 -----------------------------------
#
# mv: cannot stat '<pkgdir>/usr/lib/tcl8.6/sqlite*': No such file or directory
#
# The section above solves the HOST's problem: Ubuntu's tclConfig.sh says
# share/tcltk, so the tree is relocated to usr/lib/tcl8.6 where the upstream mv
# expects it. Inside the stage-2 chroot neither half of that applies -- Arch's
# tclConfig.sh already says lib, and our tcl is 9.0, so sqlite installs into
# usr/lib/tcl9.0 and the upstream mv finds nothing. The relocation above becomes
# a no-op there, correctly, because it is guarded by [ -d ].
#
# This is the deferred item this port had already written down -- "libtcl8.6
# versus our tcl 9.0" -- arriving as a missing directory rather than as a version
# mismatch.
#
# The version is READ from the tree, not written in. Hardcoding 9.0 would put the
# same trap back one release later, and the tcl package is what decides it.
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = 'mv "$pkgdir"/usr/lib/tcl8.6/sqlite* "$srcdir"/tcl'
assert s.count(old) == 1, "sqlite: the upstream tcl move is not in the expected form"
new = ('_tcldir=$(cd "$pkgdir"/usr/lib && echo tcl[0-9]*)\n'
' [ -d "$pkgdir/usr/lib/$_tcldir" ] || { echo "sqlite: no tcl dir under usr/lib" >&2; exit 1; }\n'
' mv "$pkgdir"/usr/lib/"$_tcldir"/sqlite* "$srcdir"/tcl')
s = s.replace(old, new, 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
# Only the upstream mv had to change. The relocation above still names tcl8.6 on
# purpose -- it is about the HOST's tcl -- so a blanket grep for tcl8.6 would
# condemn correct code, and did on the first attempt.
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"

View file

@ -237,3 +237,52 @@ if left:
print("systemd: pkgname -> %s" % " ".join(names))
PYGUARD
echo "systemd: ukify disabled (guess_efi_arch raises on s390x), tests dropped"
# --- no BPF framework ---------------------------------------------------------
#
# meson.build:1052:9: ERROR: Dependency "libbpf" not found (tried pkgconfig)
#
# libbpf is not in this port's package list, and adding it is not a small step:
# systemd's BPF programs are compiled with clang and llvm, so the dependency is
# an entire second compiler toolchain for a feature that only matters to
# systemd's own resource-control and socket-binding units.
#
# -Dbpf-framework=disabled is systemd's own switch for building without it,
# which is what every distribution that does not ship BPF-based unit features
# uses. Revisit if something on the target actually needs IPAddressAllow= or
# RestrictNetworkInterfaces=.
set -euo pipefail
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-Dbpf-framework=enabled"
assert s.count(old) == 1, "systemd: expected one bpf-framework option, got %d" % s.count(old)
s = s.replace(old, "-Dbpf-framework=disabled", 1)
io.open("PKGBUILD", "w", encoding="utf-8").write(s)
ZZPY
grep -q -- "-Dbpf-framework=disabled" PKGBUILD || {
echo "systemd: bpf-framework was not disabled" >&2; exit 1; }
echo "systemd: BPF framework disabled (no libbpf, no clang)"
# --- no AppArmor ---------------------------------------------------------------
#
# meson.build:1098:14: ERROR: Dependency "libapparmor" not found (tried pkgconfig)
#
# The second dependency systemd asked for that this port does not package, after
# libbpf. AppArmor is a Debian/Ubuntu security module; Arch ships it but nothing
# in this bootstrap uses it, and systemd's support for it is a set of unit
# directives (AppArmorProfile=) rather than anything the system needs to boot.
#
# -Dapparmor=disabled is systemd's own switch. Revisit if the target ever runs
# an AppArmor policy.
set -euo pipefail
python3 - <<'ZZPY'
import io
s = io.open("PKGBUILD", encoding="utf-8").read()
old = "-Dapparmor=enabled"
assert s.count(old) == 1, "systemd: expected one apparmor option, got %d" % s.count(old)
s = s.replace(old, "-Dapparmor=disabled", 1)
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"

34
patches/pkgbuild/util-linux.sh Executable file
View file

@ -0,0 +1,34 @@
#!/usr/bin/env bash
# util-linux: the man3 pages that were never built.
#
# mv: cannot stat '<pkgdir>/usr/share/man/man3': No such file or directory
#
# util-linux renders its man pages from asciidoc, which this port does not have,
# so man3 does not exist. The util-linux-libs split moves it out of pkgdir and
# then back in -- TWO places, and removing only the first fails on the second.
#
# Both moves go. Nothing is left conditional: if the directory is never created,
# a guard around moving it is a guard around nothing, and it would read as if
# the man pages were sometimes present.
#
# Same shape as libxml2's docs split, pam's PDFs, libuv's man page and cmake's
# emacs byte-compilation: a failure on a path at the end of package(), after a
# compile that worked. Seventh time.
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"\bman3\b", l) and l.lstrip().startswith("mv ")]
assert len(hit) == 2, "util-linux: expected two man3 moves, got %d" % len(hit)
for i in reversed(hit):
ind = re.match(r"^[ \t]*", lines[i]).group(0)
lines[i] = ind + ": # man3 not built: no asciidoc at either stage."
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
if grep -qE "^[[:space:]]*mv .*\bman3\b" PKGBUILD; then
echo "util-linux: a man3 move survived" >&2; exit 1
fi
grep -c "man3 not built" PKGBUILD | grep -qx 2 || {
echo "util-linux: expected both moves replaced" >&2; exit 1; }
echo "util-linux: man3 moves removed (both)"

22
patches/pkgbuild/xz.sh Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# xz: autogen.sh without po4a.
#
# po4a/update-po: Translated man pages were not generated.
# ==> ERROR: A failure occurred in prepare().
#
# prepare() runs only `./autogen.sh`, and that script regenerates the translated
# man pages with po4a -- a Perl toolchain this port declined along with the rest
# of the documentation stack. xz's autogen.sh takes --no-po4a for exactly this,
# so the switch is upstream's, not ours.
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]*\./autogen\.sh[ \t]*$", l)]
assert len(hit) == 1, "xz: expected one bare ./autogen.sh, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].rstrip() + " --no-po4a"
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "autogen.sh --no-po4a" PKGBUILD || {
echo "xz: --no-po4a was not added" >&2; exit 1; }
echo "xz: translated man pages skipped"

View file

@ -147,9 +147,64 @@ Server = file://$REPO1
EOF
sudo rm -rf "$ROOT" "$CACHE"
sudo mkdir -p "$ROOT/var/lib/pacman" "$CACHE"
sudo pacman --root "$ROOT" --config "$CONF" --cachedir "$CACHE" \
--noconfirm -Sy "${CHROOT_PKGS[@]}" > "$WORK/stage2-install.txt" 2>&1 \
|| { tail -20 "$WORK/stage2-install.txt" >&2; die "populate failed"; }
# EVERYTHING in stage 1, not a hand-picked core.
#
# CHROOT_PKGS grew one name at a time, each added because a build said so.
# It reached 158 of the 190 packages stage 1 had produced, and the 32 that
# were missing failed builds in a way that never mentioned them:
#
# openldap: configure: error: --enable_argon2=yes requires --with-argon2
#
# while the PKGBUILD already passes --with-argon2=libsodium and libsodium
# sits in repo/s390x, simply not installed. configure had looked for a
# library, not found it, and reported a missing OPTION. krb5's "libldap not
# found", libsasl's "Could not locate OpenLDAP", lvm2's "libudev >= 143" and
# audit's undefined SASL symbol are all the same sentence in different words.
#
# Stage 2 builds with --nodeps, so nothing installs a build dependency for
# it. The alternative -- dropping --nodeps -- needs a working in-chroot
# pacman, and that pacman is one of the packages being rebuilt.
#
# So: install the distribution we have. This IS what stage 2 means -- the
# distribution rebuilt inside itself -- and every package in there is ours.
# CHROOT_PKGS stays as the documented core, and it is what the fallback
# below installs if the full set will not resolve.
local all=() f pn
shopt -s nullglob
for f in "$REPO1"/*.pkg.tar.*; do
pn=$(bsdtar -xOf "$f" .PKGINFO 2>/dev/null | sed -n 's/^pkgname = //p')
[ -n "$pn" ] || continue
# Alternatives, which a repository holds happily and a system cannot.
printf ' %s ' "${CHROOT_EXCLUDE[*]}" | grep -q " $pn " && continue
all+=("$pn")
done
shopt -u nullglob
printf ' %s packages in stage 1, %s excluded as alternatives\n' \
"${#all[@]}" "${#CHROOT_EXCLUDE[@]}"
# Two log files, not one. The first version wrote both attempts to
# stage2-install.txt, so the fallback's success overwrote the failure that
# caused it and the reason was gone.
if ! sudo pacman --root "$ROOT" --config "$CONF" --cachedir "$CACHE" \
--noconfirm -Sy "${all[@]}" > "$WORK/stage2-install-full.txt" 2>&1; then
# pacman puts the reason on a `::` line, and the first version of this
# grep matched three phrases that did not include it -- so it printed
# NOTHING between "did not resolve" and "falling back". A fallback that
# cannot say why it happened is the thing this message exists to
# prevent; it took reproducing the command by hand to learn that the
# answer was one package wanting zsh.
printf ' the full set did not resolve:\n'
# THREE kinds of refusal, because two greps in a row printed nothing
# here and each time the fallback looked unexplained: a dependency it
# cannot satisfy (`:: unable to satisfy`), a package pair in conflict,
# and a FILE owned by two packages (`exists in both`). The last one is
# invisible to -Syp, which is why the exclusion list was wrong twice.
grep -E "^error|^:: unable|in conflict|exists in both|target not found" \
"$WORK/stage2-install-full.txt" | sed 's/^/ /' | cut -c1-100 | head -6
printf ' falling back to CHROOT_PKGS (see stage2-install-full.txt)\n'
sudo pacman --root "$ROOT" --config "$CONF" --cachedir "$CACHE" \
--noconfirm -Sy "${CHROOT_PKGS[@]}" > "$WORK/stage2-install.txt" 2>&1 \
|| { tail -20 "$WORK/stage2-install.txt" >&2; die "populate failed"; }
fi
printf ' %s packages installed\n' "$(sudo ls "$ROOT/var/lib/pacman/local" | wc -l)"
# Put stage 2's own output back on top of it.
@ -286,49 +341,50 @@ configure_chroot() {
#
# An override at the end of the file needs no parsing of what came before:
# the last assignment is the one that counts.
sudo tee -a "$ROOT/etc/makepkg.conf" > /dev/null <<'EOC'
# --- stage 2: the shipped values are Arch's x86_64 ones ---
#
# THE BASELINE CPU OF THIS PORT, which was being decided by accident.
#
# zlib stopped on
#
# contrib/crc32vx/crc32_vx.c:205:10: error:
# '__builtin_s390_vec_unpackl' requires '-mvx'
#
# after its own configure had detected vector support and defined
# -DHAVE_S390X_VX. Both halves were right, which is what made it worth
# measuring instead of patching:
#
# host gcc (Ubuntu) --with-arch=z13 --with-tune=z16 default -march=arch11
# our gcc (nothing) default -march=arch5
#
# arch5 is z900, from 2000. arch11 is z13, from 2015. Arch's PKGBUILD names no
# s390x baseline because Arch has no s390x, so our gcc fell back to the oldest
# machine the port could possibly run on -- and every package using a vector
# intrinsic failed, while zlib's configure went on detecting a CPU the compiler
# had been told to forget.
#
# z13 is not a guess: it is what Ubuntu s390x, the host distribution, already
# requires, so nothing that runs here today stops running. Every distribution
# picks one of these; this port had simply never said which, and silence chose
# the year 2000.
#
# TWO PLACES, because they answer different questions. Here is how this
# distribution is COMPILED. patches/pkgbuild/gcc.sh passes --with-arch to the
# compiler we SHIP, so a build done later on the target assumes the same
# machine. Setting only this one leaves a gcc that quietly reverts to arch5.
# A DROP-IN, not an append to makepkg.conf.
#
# Appending was wrong, and gcc is what proved it:
#
# gfortran: error: unrecognized argument in option '-march=x86-64'
# configure: error: GNU Fortran is not working; please report a bug ...
#
# Arch splits makepkg.conf into /etc/makepkg.conf.d/*.conf, sourced AFTER
# the main file. fortran.conf sets FFLAGS to the x86_64 list and FCFLAGS
# from it; rust.conf does the same for RUSTFLAGS. So an override at the end
# of makepkg.conf is itself overridden, silently, for exactly the variables
# it does not mention -- CFLAGS survived only because no drop-in sets it.
#
# The error is worth noting for its shape: gcc reported that ITS OWN
# freshly-built Fortran compiler "is not working", when the compiler was
# fine and had been handed another architecture's flags. Everything about
# the message points at the wrong thing.
#
# 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.
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
# under this directory would otherwise put them back.
CFLAGS="-march=z13 -mtune=z16 -O2 -pipe -fno-plt -fexceptions"
CXXFLAGS="$CFLAGS -Wp,-D_GLIBCXX_ASSERTIONS"
FFLAGS="$CFLAGS"
FCFLAGS="$CFLAGS"
LDFLAGS="-Wl,-O1 -Wl,--sort-common -Wl,--as-needed -Wl,-z,relro -Wl,-z,now"
LTOFLAGS=""
RUSTFLAGS=""
DEBUG_CFLAGS=""
DEBUG_CXXFLAGS=""
DEBUG_FFLAGS=""
DEBUG_FCFLAGS=""
DEBUG_RUSTFLAGS=""
EOC
sudo grep -E '^(CARCH|CHOST|MAKEFLAGS|OPTIONS)=' "$ROOT/etc/makepkg.conf" | sed 's/^/ /'
printf ' baseline: -march=z13 -mtune=z16 (host Ubuntu uses the same)\n'
printf ' baseline: -march=z13 -mtune=z16, in makepkg.conf.d/zz-s390x.conf\n'
# Named, because a drop-in that does not sort last does nothing and looks
# like it worked.
printf ' drop-ins read after it: %s\n' \
"$(sudo ls "$ROOT/etc/makepkg.conf.d" | awk '$0 > "zz-s390x.conf"' | tr '\n' ' ')none"
# --auto-features auto, for this chroot only.
#
@ -485,7 +541,19 @@ in_chroot() {
# of host artefact the ARTEFACT check exists to find.
generate_ca_bundle() {
log "Generating the CA bundle"
if in_chroot "update-ca-trust" > "$WORK/stage2-ca.txt" 2>&1; then
# AS ROOT, not through in_chroot.
#
# in_chroot runs as the build user, and the first version of this used it.
# update-ca-trust exited non-zero with
#
# p11-kit: couldn't create file:
# /etc/ca-certificates/extracted/tls-ca-bundle.pem
#
# which is a permission error wearing the words of a broken tool. Writing
# under /etc is root's job; makepkg is the only thing here that must not be
# root.
if sudo chroot "$ROOT" /usr/bin/env -i PATH=/usr/bin update-ca-trust \
> "$WORK/stage2-ca.txt" 2>&1; then
local n
n=$(sudo grep -c "BEGIN CERTIFICATE" \
"$ROOT/etc/ca-certificates/extracted/tls-ca-bundle.pem" 2>/dev/null || echo 0)
@ -649,7 +717,8 @@ NOTE
# the host, whose python would bake dist-packages into every one of them.
STAGE2_FIRST=(texinfo libxml2 binutils pkgconf wget libxslt
python-packaging python-pyproject-hooks python-build
python-installer python-setuptools python-wheel)
python-installer python-setuptools python-wheel
python-setuptools-scm)
# Packages the chroot needs that can only come from repo2.
#
@ -669,6 +738,27 @@ STAGE2_FIRST=(texinfo libxml2 binutils pkgconf wget libxslt
# conflicts with the zlib stage 1 chose for this chroot. A list says which
# packages are build tools and why; a conflict heuristic would have to be
# extended every time a package like that appeared.
# Packages a REPOSITORY can hold and a SYSTEM cannot: alternatives to something
# else in there. zlib-ng-compat replaces zlib, and stage 1 chose zlib for this
# chroot. Excluded by name rather than discovered by conflict, so populate stays
# deterministic -- and if a new one appears, pacman names the pair and the
# fallback says so out loud.
# What a REPOSITORY can hold and a SYSTEM cannot. Found by installing the whole
# repository into a probe root and excluding whatever pacman objected to, until
# it stopped objecting -- four names out of 190, and 186 packages install.
#
# `-Syp` was not enough to find them. It resolves dependencies and says nothing
# about FILE conflicts, so the first list came back clean and the real install
# still failed. Two of these were only visible to an actual install.
CHROOT_EXCLUDE=(
zlib-ng-compat # replaces zlib; stage 1 chose zlib for this chroot
git-zsh-completion # requires zsh, which this port does not build. A shell
# completion file, not a build tool.
libcurl-compat # both ship /usr/lib/libcurl.la, so they collide with
libcurl-gnutls # curl itself. ABI-compatibility builds of libcurl; a
# build chroot needs neither.
)
CHROOT_STAGE2_PKGS=(
libxslt
# The Python packaging set, for the same reason and a sharper one: their
@ -688,6 +778,7 @@ CHROOT_STAGE2_PKGS=(
# dist-packages so it can say so next time.
python-packaging python-pyproject-hooks python-build
python-installer python-setuptools python-wheel
python-setuptools-scm
)
STAGE2_SKIP_HOOKS=(libgcrypt git meson libarchive)

View file

@ -261,6 +261,33 @@ STAGE1_PACKAGES=(
# runtimes behind doxygen or asciidoctor, so it is cheaper to build than to
# hook around.
scdoc
# nlohmann-json: a real closure gap, not a documentation cut. BOTH cmake and
# cppdap stop on
#
# Could NOT find nlohmann_json (missing: nlohmann_json_DIR)
#
# It is a header-only C++ library -- one of the cheapest packages in this
# list -- and cmake is not optional here: the whole cmake/cppdap/jsoncpp/
# libuv group behind it is what several other packages build with.
nlohmann-json
# python-setuptools-scm: python-tqdm's build stops on
#
# setuptools-scm[toml]>=3.4
#
# which is a requirements line, not an error message -- pip printing what it
# could not find. Built by stage 2 with the rest of the Python set, for the
# same reason: its package() would take Debian's dist-packages from the
# host's python.
python-setuptools-scm
# libaio: a real closure gap. lvm2 stops on
#
# device/bcache.c:30:10: fatal error: libaio.h: No such file or directory
#
# A small C library for asynchronous I/O, and one this port had already met
# from the other side: Ubuntu renamed its runtime to libaio.so.1t64 for
# time64, which was noted as a divergence long before anything needed the
# headers.
libaio
# libxslt stays in the list because it is part of the distribution, but
# STAGE 1 CANNOT BUILD IT:
#