Commit graph

125 commits

Author SHA1 Message Date
327ff55665 [ADD] llvm and clang, with Sphinx off, for Rust
Rust is not optional. 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 with HTTP 200, and the host
already carries rustc 1.85.1. What costs is llvm and clang.

Measured before choosing, the way glib2 and iproute2 were: beyond what this port
already has, llvm and clang want libedit, python-psutil, python-sphinx and
python-myst-parser. The last two are documentation only, and behind them sit
docutils, babel, pygments, snowballstemmer, imagesize, alabaster, the
sphinxcontrib family, markdown-it-py and mdit-py-plugins -- some fifteen packages
to render HTML for a compiler whose manual nothing here reads.
-DLLVM_ENABLE_SPHINX=OFF removes all of it.

Their package() ends on `rm -r .../html/{_sources,.buildinfo}`, which Sphinx
creates. Seventeenth appearance of that shape, and the FIRST found before the
build rather than after -- the procedure written in jsoncpp's hook, applied
forwards.

python-psutil is a stage-2 package: built on the host it shipped 41 dist-packages
paths, and the ARTEFACT check caught it.

--- FR ---

Rust n'est pas optionnel. Le poetry.lock d'ERPLibre exige cryptography 46.0.5 en
optional = false, plus jiter, orjson, pydantic-core et rpds-py, qui tous
compilent du Rust depuis les sources là où aucune roue n'existe — et il n'en
existe pas pour s390x. Sans Rust, `pip install cryptography` échoue et ERPLibre ne
démarre pas.

Le cycle d'amorçage n'est pas l'obstacle qu'il paraît : rust-1.90.0 publie une
archive officielle s390x-unknown-linux-gnu, vérifiée en HTTP 200, et l'hôte porte
déjà rustc 1.85.1. Ce qui coûte, c'est llvm et clang.

Mesuré avant de choisir, comme glib2 et iproute2 : au-delà de ce que ce portage a
déjà, llvm et clang veulent libedit, python-psutil, python-sphinx et
python-myst-parser. Les deux derniers ne servent qu'à la documentation, et
derrière eux viennent docutils, babel, pygments, snowballstemmer, imagesize,
alabaster, la famille sphinxcontrib, markdown-it-py et mdit-py-plugins — une
quinzaine de paquets pour produire du HTML sur un compilateur dont personne ici ne
lit le manuel. -DLLVM_ENABLE_SPHINX=OFF supprime tout cela.

Leur package() finit sur `rm -r .../html/{_sources,.buildinfo}`, que Sphinx crée.
Dix-septième apparition de cette forme, et la PREMIÈRE trouvée avant la
construction plutôt qu'après — la procédure inscrite dans le hook de jsoncpp,
appliquée à l'endroit.

python-psutil est un paquet d'étage 2 : bâti sur l'hôte, il livrait 41 chemins
dist-packages, et le test ARTEFACT l'a attrapé.

Assisted-by: Claude Opus 5
2026-08-24 01:57:19 -04:00
51384a64af [ADD] stage 2 is complete: 164 of 164, and it resolves, is clean, and runs
Every package in the list has been rebuilt inside the chroot by the compiler,
linker and libc it produced. The verification, run against repo2 rather than
asserted:

  RESOLVE   resolver satisfied (101 packages), --nodeps OFF
  ARTEFACT  no multiarch, no lib64, no dist-packages, no usr/local
  SONAME    every requested soname has a provider in the repository
  RUN       102 packages installed; bash 5.3.15, uname s390x, glibc 2.44,
            ls, tar, find, sed and pacman all execute

SONAME is the one that matters most. Stage 1 shipped nine sonames at the host's
version and three with no provider at all, and the whole premise of a second
stage was that rebuilding inside the result dissolves them. It did: zero of
either.

python was the last holdout and the most instructive: POSIX_SEMAPHORES_NOT_ENABLED
is now 0. It had been 1 for days because `mount --bind /dev` does not carry
submounts, so the chroot saw an empty 0755 /dev/shm, CPython's configure got
EACCES from sem_open, and _multiprocessing was compiled without SemLock -- a
package that installed cleanly, imported cleanly, and was wrong.

--- FR ---

Chaque paquet de la liste a été rebâti dans le chroot par le compilateur, le lieur
et la libc qu'il a produits. La vérification, passée sur repo2 plutôt
qu'affirmée :

  RESOLVE   résolveur satisfait (101 paquets), --nodeps éteint
  ARTEFACT  ni multiarch, ni lib64, ni dist-packages, ni usr/local
  SONAME    chaque soname demandé a un fournisseur dans le dépôt
  RUN       102 paquets installés ; bash 5.3.15, uname s390x, glibc 2.44,
            ls, tar, find, sed et pacman s'exécutent tous

SONAME est le contrôle qui compte. L'étage 1 livrait neuf sonames à la version de
l'hôte et trois sans aucun fournisseur, et toute la raison d'être d'un second
étage était que rebâtir dans le résultat les dissout. C'est fait : zéro des deux.

python fut le dernier et le plus instructif : POSIX_SEMAPHORES_NOT_ENABLED vaut
désormais 0. Il valait 1 depuis des jours parce que `mount --bind /dev` ne porte
pas les sous-montages : le chroot voyait un /dev/shm vide en 0755, le configure de
CPython recevait EACCES de sem_open, et _multiprocessing était compilé sans
SemLock — un paquet qui s'installait proprement, s'importait proprement, et était
faux.

Assisted-by: Claude Opus 5
2026-08-24 01:35:16 -04:00
0da64c9933 [FIX] rm -rf followed a bind mount into the host's /dev
make_rootfs wiped the rootfs with `rm -rf "$ROOT"` and no check on what was
mounted under it. A diagnostic session had left `mount --rbind /dev "$ROOT/dev"`
in place; the next run deleted straight through it 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 and stopped the pass at its first pacman call.

Two guards now, because unmounting alone is not enough: umount_chroot, then
findmnt to CHECK, then refuse. A stale mount is a reason to stop, never a thing
to delete through.

/dev is also no longer written to at all. The bind became --rbind with
--make-rslave, which brings the host's /dev/shm along as the tmpfs it already is
-- so the separate tmpfs and the chmod that reached the host are both gone. That
chmod was the reason this driver could touch /dev in the first place.

The plain --bind was itself the original bug: it does not carry submounts, so the
chroot saw /dev/shm as an empty 0755 stub, CPython's configure got EACCES from
sem_open, and python was built without POSIX semaphores.

--- FR ---

make_rootfs effaçait le rootfs par `rm -rf "$ROOT"` sans vérifier ce qui était
monté dessous. Une session de diagnostic avait laissé `mount --rbind /dev
"$ROOT/dev"` en place ; l'exécution suivante a supprimé au travers, dans le /dev
réel. Tous les nœuds de périphériques ont disparu — zero, full, random, urandom,
tty, console — et /dev/null est revenu en fichier ordinaire créé par une
redirection, ce qui a cassé tout `> /dev/null` sur la machine et arrêté la passe
à son premier appel à pacman.

Deux gardes désormais, car démonter ne suffit pas : umount_chroot, puis findmnt
pour VÉRIFIER, puis refuser. Un montage résiduel est une raison de s'arrêter,
jamais une chose à traverser.

Et /dev n'est plus écrit du tout. Le bind devient --rbind avec --make-rslave, ce
qui apporte le /dev/shm de l'hôte tel qu'il est déjà — un tmpfs — donc le tmpfs
séparé et le chmod qui atteignait l'hôte disparaissent tous deux. Ce chmod était
la raison pour laquelle ce pilote pouvait toucher /dev.

Le --bind simple était lui-même le défaut d'origine : il ne porte pas les
sous-montages, le chroot voyait donc /dev/shm comme une souche vide en 0755, le
configure de CPython recevait EACCES de sem_open, et python était bâti sans
sémaphores POSIX.

Assisted-by: Claude Opus 5
2026-08-24 01:14:35 -04:00
c43289c5ca [FIX] /dev/shm was mounted and unwritable, so python lost its semaphores
sem_open: Permission denied
  checking whether POSIX semaphores are enabled... no

EACCES, not ENOSYS. This port read the failure as a missing mount, mounted the
tmpfs, and got the same result -- because `-o mode=1777` applies only when the
mount is created, and the `mountpoint -q ||` guard skips an existing one. findmnt
showed a tmpfs at /dev/shm while the directory itself was drwxr-xr-x, so the
build user could create nothing in it.

CPython runs sem_open at CONFIGURE time. It concluded the platform has no working
semaphores, set POSIX_SEMAPHORES_NOT_ENABLED, and compiled _multiprocessing
without SemLock -- and every consumer then failed with a message blaming the
platform, days after the cause.

The mode is now set with chmod rather than trusted to the mount option, and the
result is PROVED from inside the chroot as the build user. Every earlier check of
this was made as root and passed while the build kept failing.

--- FR ---

  sem_open: Permission denied
  checking whether POSIX semaphores are enabled... no

EACCES, pas ENOSYS. Ce portage a lu l'échec comme un montage absent, a monté le
tmpfs, et a obtenu le même résultat — car `-o mode=1777` ne s'applique qu'à la
création du montage, et la garde `mountpoint -q ||` saute un montage existant.
findmnt montrait un tmpfs sur /dev/shm alors que le répertoire était drwxr-xr-x :
l'utilisateur de compilation n'y pouvait rien créer.

CPython exécute sem_open au moment de CONFIGURE. Il en a conclu que la plateforme
n'a pas de sémaphores fonctionnels, a posé POSIX_SEMAPHORES_NOT_ENABLED et
compilé _multiprocessing sans SemLock — et chaque consommateur a ensuite échoué
sur un message accusant la plateforme, des jours après la cause.

Le mode est désormais posé par chmod plutôt que confié à l'option de montage, et
le résultat est PROUVÉ depuis l'intérieur du chroot, sous l'utilisateur de
compilation. Toutes les vérifications précédentes avaient été faites en root et
passaient pendant que la construction échouait.

Assisted-by: Claude Opus 5
2026-08-24 00:54:22 -04:00
cf3a4ffdf4 [FIX] ARTEFACT condemned two correct packages
Run against stage 2's output for the first time, the check reported:

  python      17 x s390x-linux-gnu/
  filesystem  11 x usr/local/

Both are correct. CPython names two paths after the build triplet on every
platform -- the Arch x86_64 package ships
_sysconfigdata__linux_x86_64-linux-gnu.py and config-3.14-x86_64-linux-gnu/ --
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 exists for; the FHS
requires those directories.

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 -- and this check has now
condemned correct code three times: the tcl8.6 grep in sqlite's hook, the
intolerant-rm guard in systemd's, and this.

With the exemptions, stage 2's 209 packages pass: no multiarch, no lib64, no
dist-packages, no usr/local.

--- FR ---

Passé pour la première fois sur la production de l'étage 2, le test signalait :

  python      17 x s390x-linux-gnu/
  filesystem  11 x usr/local/

Les deux sont justes. CPython nomme deux chemins d'après le triplet de
construction sur toute plateforme — le paquet Arch x86_64 livre
_sysconfigdata__linux_x86_64-linux-gnu.py et config-3.14-x86_64-linux-gnu/ — le
triplet y est donc une convention de CPython, non la disposition de Debian qui
s'infiltre. Et créer le squelette de /usr/local est la raison d'être du paquet
filesystem ; le FHS l'exige.

Exemptés par couple (paquet, motif) et non par paquet : python reste contrôlé pour
lib64 et dist-packages, filesystem pour le multiarch. Une exemption globale est la
façon dont une vraie fuite passe — et ce test a désormais condamné du code juste
trois fois : le grep tcl8.6 du hook sqlite, le garde rm de celui de systemd, et
ceci.

Avec les exemptions, les 209 paquets de l'étage 2 passent : ni multiarch, ni
lib64, ni dist-packages, ni usr/local.

Assisted-by: Claude Opus 5
2026-08-24 00:46:33 -04:00
f875649c96 [UPD] note why /dev/shm must exist before python is built 2026-08-24 00:35:07 -04:00
fe9c58c1a3 [FIX] systemd: sixteen man page operations, across five sub-packages
This section was written three times, each catching more:

  1st  man3 only                    -> stopped on man8/*nss*
  2nd  mv and rm of share/man       -> 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. Nine neutralised,
three made tolerant, `install -d` left alone because creating an empty directory
harms nothing and other lines put real files in the same trees.

Three rounds on one package is what guessing the shape of "all of them" costs
instead of listing them. The listing is in the hook now.

--- FR ---

Cette section a été écrite trois fois, chacune en attrapant davantage :

  1re  man3 seul                    -> arrêt sur man8/*nss*
  2e   mv et rm de share/man        -> arrêt sur `ln -s ... installkernel.8.gz`
  3e   mv, ln et rm                 -> arrêt sur

         install -D -m0644 -t "$pkgdir"/usr/share/man/man1 \
           build/man/init.1

       dans package_systemd-sysvcompat(), dont la SOURCE est sous build/ et dont
       le chemin ne contient aucun composant « man<chiffre> ».

Le motif est donc un chemin de manuel n'importe où sur la ligne — source ou
destination, arbre de construction ou pkgdir — et les verbes sont mv, ln, rm et
install. Neuf neutralisés, trois rendus tolérants, `install -d` laissé tel quel :
créer un répertoire vide ne nuit pas, et d'autres lignes y installent de vrais
fichiers.

Trois tours sur un seul paquet, voilà ce que coûte le fait de deviner la forme de
« tous » au lieu de les énumérer. L'énumération est dans le hook.

Assisted-by: Claude Opus 5
2026-08-24 00:33:37 -04:00
0e00b161c4 [FIX] the chroot had no /dev/shm, and systemd links man pages too
nss failed with

  ImportError: This platform lacks a functioning sem_open implementation.
  https://github.com/python/cpython/issues/48020

multiprocessing.Semaphore is built on POSIX named semaphores, which glibc
implements as files under /dev/shm. The chroot had no such mount, sem_open
returned ENOSYS, and CPython reported it as the PLATFORM lacking the feature --
which reads like an s390x limitation and is a missing tmpfs.

`mount --bind /dev` does not carry submounts. That is why /dev/pts already had a
line of its own, and /dev/shm was simply missing from the same list. Mounted as
its own tmpfs, mode 1777, and added to the umount order ahead of /dev.

systemd's man page hook listed mv and rm and missed ln, so package_systemd() got
one step further and stopped on a symlink between two man pages where neither
exists. Sixteenth instance, and the lesson is narrower than the last: the
procedure said grep for the output PATHS, and the paths were found -- what was
missed was a VERB. Seven moves and links now, up from five.

--- FR ---

nss échouait sur

  ImportError: This platform lacks a functioning sem_open implementation.
  https://github.com/python/cpython/issues/48020

multiprocessing.Semaphore repose sur les sémaphores nommés POSIX, que glibc
implémente en fichiers sous /dev/shm. Le chroot n'avait pas ce montage, sem_open
renvoyait ENOSYS, et CPython l'a rapporté comme une absence de la PLATEFORME — ce
qui se lit comme une limite de s390x et n'est qu'un tmpfs manquant.

`mount --bind /dev` ne porte pas les sous-montages. C'est pourquoi /dev/pts avait
déjà sa ligne, et /dev/shm manquait simplement dans la même liste. Monté en tmpfs
propre, mode 1777, et ajouté au démontage avant /dev.

Le hook des pages de manuel de systemd listait mv et rm et oubliait ln :
package_systemd() est allé un cran plus loin et s'est arrêté sur un lien entre
deux pages dont aucune n'existe. Seizième occurrence, et la leçon est plus fine
que la précédente : la procédure disait de chercher les CHEMINS de sortie, et les
chemins étaient trouvés — ce qui manquait était un VERBE. Sept déplacements et
liens désormais, contre cinq.

Assisted-by: Claude Opus 5
2026-08-24 00:29:13 -04:00
61abc5aef9 [FIX] systemd: every man page operation at once, not one per pass
mv: cannot stat '<pkgdir>/usr/share/man/man3': No such file or directory

Fifteenth instance, and the first where the whole class was handled before the
failures arrived. -Dman=disabled means NO man pages exist, and package() touches
them six times -- three moves into split packages, three removals of pages Arch
does not ship.

Treated by kind rather than by name: the moves are neutralised, because with no
pages there is nothing to move; the removals get -f, because their intent still
holds on a machine that HAS the DocBook stack, and one of them also removes a
binary that must still go. Five moves, three removals, counted and reported by
the hook.

This is the procedure written into jsoncpp's hook one commit earlier, applied
forwards: after disabling documentation, grep package() for the output paths.
Chasing man3 alone would have cost five more passes.

The first guard for it matched any `rm` without a dash and condemned correct
code -- systemd removes plenty that is not documentation. Same mistake as the
blanket `grep tcl8.6` in sqlite's hook, and caught the same way.

--- FR ---

  mv: cannot stat '<pkgdir>/usr/share/man/man3': No such file or directory

Quinzième occurrence, et la première où toute la classe est traitée avant que les
échecs n'arrivent. -Dman=disabled signifie qu'AUCUNE page n'existe, et package()
y touche six fois — trois déplacements vers des sous-paquets, trois suppressions
de pages qu'Arch ne livre pas.

Traitées par nature et non par nom : les déplacements sont neutralisés, puisqu'il
n'y a rien à déplacer ; les suppressions reçoivent -f, car leur intention tient
toujours sur une machine dotée de la pile DocBook, et l'une retire aussi un
binaire qui doit bien partir. Cinq déplacements, trois suppressions, comptés et
rapportés par le hook.

C'est la procédure inscrite dans le hook de jsoncpp un commit plus tôt, appliquée
à l'endroit : après avoir désactivé une documentation, chercher dans package() les
chemins de sortie. Poursuivre man3 seul aurait coûté cinq passes de plus.

Le premier garde écrit pour cela attrapait tout `rm` sans tiret et condamnait du
code juste — systemd supprime beaucoup qui n'est pas de la documentation. Même
erreur que le `grep tcl8.6` global du hook sqlite, attrapée de la même façon.

Assisted-by: Claude Opus 5
2026-08-24 00:21:48 -04:00
376c6d6a91 [UPD] hoist gyp with the Python set, ahead of nss 2026-08-24 00:17:11 -04:00
f6c0969929 [ADD] gyp for nss, and jsoncpp's doxygen output
nss will not configure without gyp:

  Building NSS requires an installation of gyp: https://gyp.gsrc.io

Google's build-file generator, which nss uses to produce its ninja files. It is a
real Arch package and a Python program, so it joins the Python set.

jsoncpp is the fourteenth instance of this shape and the sixth of my own making.
Removing the doxybuild.py call stopped the API html being generated; package()
still copied the directory it would have produced, and the name carries pkgver so
the path is not greppable without knowing the version.

The rule is mechanical enough now to state 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.

--- FR ---

nss ne se configure pas sans gyp :

  Building NSS requires an installation of gyp: https://gyp.gsrc.io

Le générateur de fichiers de construction de Google, dont nss se sert pour produire
ses fichiers ninja. C'est un vrai paquet Arch et un programme Python : il rejoint
l'ensemble Python.

jsoncpp est la quatorzième occurrence de cette forme, et la sixième de ma main.
Retirer l'appel à doxybuild.py a arrêté la génération du html d'API ; package()
copiait toujours le répertoire qu'il aurait produit, et son nom porte pkgver — le
chemin n'est donc pas trouvable par grep sans connaître la version.

La règle est assez mécanique pour être énoncée en procédure plutôt qu'en leçon :
après avoir retiré une construction de documentation, chercher dans package() le
nom du répertoire de SORTIE, pas celui de l'outil. L'outil est dans build() ; le
chemin est dans package(), et c'est le chemin qui échoue.

Assisted-by: Claude Opus 5
2026-08-24 00:03:26 -04:00
8d9e8a4dce [FIX] groff's remaining lists, systemd man pages, git without libsecret
groff was the third round on the same package: DOC_GNU_EPS, then
PROCESSEDEXAMPLEFILES_PS, then HDTBL -- and mom's examples were waiting behind
those. groff renders its own manuals and every contrib package's examples with
the groff being built, each list a separate variable. Finding them one failure at
a time cost three passes; all seven are emptied together now.

systemd is the fourth package to stop on missing DocBook DATA rather than a
missing tool -- xsltproc exists, our libxslt supplies it:

  compilation error: file ../systemd/man/custom-man.xsl line 12 element import

It has the largest man page set here, which is the strongest argument in this port
for shipping docbook-xsl eventually.

git's libsecret helper needed THREE places, one spanning two lines. The first
version asserted "expected one _make, got 2" and stopped -- the guard working.
Replacing the install's first line alone would have left its continuation as a
command of its own, which is the binutils trap again.

--- FR ---

groff en était au troisième tour sur le même paquet : DOC_GNU_EPS, puis
PROCESSEDEXAMPLEFILES_PS, puis HDTBL — et les exemples de mom attendaient
derrière. groff rend ses propres manuels et les exemples de chaque contrib avec le
groff en cours de construction, chaque liste étant une variable distincte. Les
trouver un échec à la fois a coûté trois passes ; les sept sont vidées ensemble.

systemd est le quatrième paquet à s'arrêter sur des DONNÉES DocBook absentes et
non sur un outil manquant — xsltproc existe, notre libxslt le fournit :

  compilation error: file ../systemd/man/custom-man.xsl line 12 element import

Il porte le plus grand ensemble de pages de manuel du portage : c'est le meilleur
argument pour livrer docbook-xsl à terme.

L'aide libsecret de git demandait TROIS endroits, dont un sur deux lignes. La
première version a affirmé « expected one _make, got 2 » et s'est arrêtée — le
garde faisant son travail. Remplacer la première ligne de l'installation aurait
laissé sa continuation comme commande à part, soit le piège de binutils à nouveau.

Assisted-by: Claude Opus 5
2026-08-23 23:58:32 -04:00
3d2a1485af [FIX] the Python bootstrap installed older code than it claimed
python-packaging's PKGBUILD says pkgver=26.3 and the installed module reported
25.0. The vendored builder selected by _bootstrap=1 carries its OWN copy of each
project, older than the release the PKGBUILD names. Nothing about the package
looked wrong; only `packaging.__version__` disagreed with its own name, 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.

_bootstrap is now a probe evaluated by makepkg, in the build environment: if
`build` and `installer` import, the real path works and the vendored copies are
not wanted. A hook could not decide this -- hooks run on the HOST even for stage
2, so a probe written there cannot answer for the chroot.

The license install then had to handle both layouts, since the source tree is a
git clone under $pkgname when bootstrapping and a release tarball otherwise.
Hardcoding either breaks the other, and this hook had hardcoded the clone -- right
only while _bootstrap was always 1.

--- FR ---

Le PKGBUILD de python-packaging annonce pkgver=26.3 et le module installé
rapportait 25.0. Le constructeur embarqué que sélectionne _bootstrap=1 porte ses
PROPRES copies de chaque projet, plus anciennes que la version nommée. Rien dans
le paquet n'avait l'air faux ; seul `packaging.__version__` contredisait son
propre nom, et l'écart est apparu deux paquets plus loin :

  python-vcs-versioning: packaging>=26.2

un plancher que notre paquet satisfaisait sur le papier et non en fait.

_bootstrap est désormais une sonde évaluée par makepkg, dans l'environnement de
construction : si `build` et `installer` s'importent, la voie normale fonctionne et
les copies embarquées ne sont pas voulues. Un hook ne pouvait pas trancher — les
hooks tournent sur l'HÔTE même pour l'étage 2, donc une sonde écrite là ne peut
pas répondre pour le chroot.

L'installation de la licence devait alors gérer les deux dispositions, l'arbre
source étant un clone git sous $pkgname en mode bootstrap et une archive sinon.
Écrire l'une en dur casse l'autre, et ce hook avait écrit le clone en dur — juste
seulement tant que _bootstrap valait toujours 1.

Assisted-by: Claude Opus 5
2026-08-23 23:50:06 -04:00
2b0139e99f [FIX] xz: the flag was already there, and lost to position
--disable-doxygen was added and doxygen still ran. config.log recorded why:

  $ ./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 first version of this section inserted its flag
right after ./configure, where it could only lose.

This port had already learned the rule from the other side: the arch-meson
wrapper APPENDS --auto-features auto precisely so it wins. Here the same fact was
applied backwards, and the symptom was indistinguishable from a flag that does
not exist -- which is what sent the search through configure's own logic first,
where the default turned out to be `no` all along.

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.

--- FR ---

--disable-doxygen avait été ajouté et doxygen tournait encore. config.log en
donnait la raison :

  $ ./configure --disable-doc --disable-doxygen --prefix=/usr \
      --disable-rpath --enable-doxygen --enable-werror

Le PKGBUILD passe --enable-doxygen plus loin sur la même ligne, et autoconf retient
la DERNIÈRE occurrence. La première version de cette section insérait son drapeau
juste après ./configure, là où il ne pouvait que perdre.

Ce portage avait déjà appris la règle par l'autre bout : l'enveloppe arch-meson
AJOUTE --auto-features auto à la fin précisément pour gagner. Ici le même fait a
été appliqué à l'envers, et le symptôme était indiscernable d'un drapeau
inexistant — ce qui a d'abord envoyé la recherche dans la logique de configure, où
le défaut s'est avéré être « no » depuis le début.

L'option existante est retournée sur place plutôt qu'une seconde ajoutée. Une
décision, une ligne : deux drapeaux contradictoires sur une même commande, c'est
une heure de config.log pour le prochain lecteur.

Assisted-by: Claude Opus 5
2026-08-23 23:44:36 -04:00
c375f994fe [FIX] tpm2-tss without integration tests, p11-kit's doc sub-package
tpm2-tss had three obstacles, each hidden by the one before: a missing AX_* macro
(fixed by autoconf-archive), then cmocka, now

  configure: error: Missing required program 'ss': ensure it is installed

`ss` comes from iproute2, and adding iproute2 was the first idea -- it is in
Arch's base group and belongs here eventually. Measured first: it depends on
libbpf, iptables and linux-atm, none of them present, and libbpf is the
dependency systemd was already spared for the same reason. Three packages to
satisfy a test-suite prerequisite, or --disable-integration. The tests are not
run; --nocheck covers the whole first pass.

p11-kit is the thirteenth instance of this shape and the third layer of the same
package: the build flag, the _pick that staged the docs, and now the sub-package
function that moves what _pick staged. Five of these thirteen have been mine, so
the rule is worth writing down: disabling a documentation build means following it
through the entire package() chain, not just switching it off.

--- FR ---

tpm2-tss avait trois obstacles, chacun masqué par le précédent : une macro AX_*
absente (réglée par autoconf-archive), puis cmocka, et maintenant

  configure: error: Missing required program 'ss': ensure it is installed

`ss` vient d'iproute2, et l'ajouter fut la première idée — il est dans le groupe
base d'Arch et y a sa place à terme. Mesuré d'abord : il dépend de libbpf,
iptables et linux-atm, aucun présent, et libbpf est la dépendance dont systemd
avait déjà été dispensé pour la même raison. Trois paquets pour satisfaire un
prérequis de suite de tests, ou --disable-integration. Les tests ne tournent pas :
--nocheck couvre toute la première passe.

p11-kit est la treizième occurrence de cette forme et la troisième couche du même
paquet : le drapeau de construction, le _pick qui préparait la documentation, et
maintenant la fonction de sous-paquet qui déplace ce que _pick préparait. Cinq de
ces treize sont de ma main : la règle mérite d'être écrite — désactiver une
documentation, c'est la suivre dans toute la chaîne package(), pas seulement
l'éteindre.

Assisted-by: Claude Opus 5
2026-08-23 23:33:38 -04:00
111d4c80b3 [FIX] git: the opt-out is NO_RUST, and Rust is now the default
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`, so Rust is built BY DEFAULT and the opt-out
is NO_RUST. WITH_RUST=1 sets a variable the Makefile never reads. Deleting it was
correct and insufficient, the worst combination: the hook reported success and the
build failed identically.

This is a stopgap, not a decision. Rust IS supportable here, and measured rather
than assumed: rust-1.90.0-s390x-unknown-linux-gnu.tar.xz answers 200, and the
host already carries rustc 1.85.1. The cost is llvm and clang, which Arch's rust
lists as makedepends -- large, not impossible.

It also cannot be avoided for long. ERPLibre's poetry.lock needs cryptography
46.0.5, jiter, orjson, pydantic-core and rpds-py, all of which build Rust from
source where no wheel exists -- and none exists for s390x.

--- FR ---

Retirer WITH_RUST=1 n'a rien changé — cargo était toujours appelé :

  CARGO target/release/libgitcore.a
  /bin/sh: line 1: cargo: command not found

Le Makefile dit `ifndef NO_RUST` : Rust est bâti PAR DÉFAUT et l'échappatoire est
NO_RUST. WITH_RUST=1 définit une variable que le Makefile ne lit jamais. La
supprimer était juste et insuffisant, la pire combinaison : le hook annonçait un
succès et la construction échouait à l'identique.

C'est un palliatif, pas une décision. Rust EST supportable ici, et mesuré plutôt
que supposé : rust-1.90.0-s390x-unknown-linux-gnu.tar.xz répond 200, et l'hôte
porte déjà rustc 1.85.1. Le coût est llvm et clang, que le rust d'Arch liste en
makedepends — lourd, pas impossible.

Et cela ne pourra pas être évité longtemps. Le poetry.lock d'ERPLibre exige
cryptography 46.0.5, jiter, orjson, pydantic-core et rpds-py, qui tous compilent
du Rust depuis les sources là où aucune roue n'existe — et il n'en existe pas pour
s390x.

Assisted-by: Claude Opus 5
2026-08-23 23:09:18 -04:00
29da076277 [FIX] host-extracted sources linked to paths the chroot cannot see
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 local source files
into srcdir, and having extracted on the HOST it wrote the host absolute path.
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 -- #!/usr/bin/python, with python, python3
and python3.14 all present. It was the script itself, reached through a link to
nowhere.

Only links under the work directory are repointed: a link to anywhere else was
not made by this mechanism and is not ours to redirect.

--- FR ---

nss echouait sur

  /build/nss/PKGBUILD: line 55: ../certdata2pem.py: No such file or directory

pour un fichier bien present. makepkg lie les sources locales d un PKGBUILD dans
srcdir, et ayant extrait sur l HOTE il a ecrit le chemin absolu de l hote. Dans le
chroot ce chemin n existe pas ; seul /build existe.

No such file or directory pour un executable designe d ordinaire son interprete,
ce qui a envoye la premiere hypothese vers le shebang -- #!/usr/bin/python, avec
python, python3 et python3.14 tous presents. C etait le script lui-meme, atteint
par un lien vers nulle part.

Seuls les liens sous le repertoire de travail sont rediriges : un lien ailleurs n a
pas ete cree par ce mecanisme et ne nous appartient pas.

Assisted-by: Claude Opus 5
2026-08-23 23:02:24 -04:00
41f0bc4897 [FIX] groff: the second consumer of a figure that is not built
Emptying PROCESSEDEXAMPLEFILES_PS was not enough:

  contrib/hdtbl/examples/mixed_pickles.roff:51: error: cannot open 'gnu.eps'
  fatal error: PSPIC failed to include 'gnu.eps'

hdtbl renders its example tables to PostScript and one of them shows the GNU
logo. Rather than chase a third, the consumers were COUNTED: exactly two files
include it, doc/webpage.ms and mixed_pickles.roff. Both lists are emptied, and
the hook now fails loudly if a third ever appears -- so the next reader gets a
sentence instead of a PSPIC error.

Considered and rejected: supplying a placeholder gnu.eps. It would have ended the
whack-a-mole in one line, and it would have put a blank box where a logo belongs
in a document this port then ships. Not rendering the document at all is honest;
rendering it wrong is not.

--- FR ---

Vider PROCESSEDEXAMPLEFILES_PS ne suffisait pas :

  contrib/hdtbl/examples/mixed_pickles.roff:51: error: cannot open 'gnu.eps'
  fatal error: PSPIC failed to include 'gnu.eps'

hdtbl rend ses tables d'exemple en PostScript, et l'une montre le logo GNU.
Plutôt que d'en poursuivre un troisième, les consommateurs ont été COMPTÉS :
exactement deux fichiers l'incluent, doc/webpage.ms et mixed_pickles.roff. Les
deux listes sont vidées, et le hook échoue bruyamment si un troisième apparaît —
le prochain lecteur aura une phrase au lieu d'une erreur PSPIC.

Envisagé puis rejeté : fournir un gnu.eps de remplacement. Cela aurait clos la
poursuite en une ligne, et aurait mis un cadre vide là où un logo doit être, dans
un document que ce portage livre ensuite. Ne pas rendre le document est honnête ;
le rendre faux ne l'est pas.

Assisted-by: Claude Opus 5
2026-08-23 22:49:24 -04:00
7fa489b5bb [FIX] p11-kit's gtk-doc split, tpm2-tss without cmocka
p11-kit is the twelfth instance of removing a documentation build and leaving its
install: -D gtk_doc=false stopped the docs being made, and package() still ran
`_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.

tpm2-tss had two obstacles and the first hid the second. Before autoconf-archive
existed it died on

  configure.ac:139: error: undefined or overquoted macro: AC_MSG_WARN

naming 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, a C unit-test framework wanted only
for tests this pass does not run.

--- FR ---

p11-kit est la douzième occurrence du retrait d'une construction de documentation
sans son installation : -D gtk_doc=false a arrêté la production, et package()
exécutait toujours `_pick doc "$pkgdir"/usr/share/gtk-doc`. _pick est l'aide
d'Arch pour déplacer un chemin vers un sous-paquet, et elle est aussi fatale
qu'un mv nu sur un chemin absent.

tpm2-tss avait deux obstacles, et le premier masquait le second. Avant l'existence
d'autoconf-archive il mourait sur

  configure.ac:139: error: undefined or overquoted macro: AC_MSG_WARN

nommant une macro qui existe — une macro AX_* manquait, la citation m4 s'est
défaite, et autoconf a accusé la chose suivante qu'il reconnaissait. L'archive
installée, configure va assez loin pour réclamer cmocka, cadre de tests unitaires
en C voulu seulement pour des tests que cette passe ne lance pas.

Assisted-by: Claude Opus 5
2026-08-23 22:33:58 -04:00
430b49aae2 [FIX] xz: two doc conditionals, and the flag that is not in the help
--disable-doc was added and doxygen still ran. The API reference sits behind its
own automake conditional, and the generated Makefile shows it plainly:

  @COND_DOXYGEN_TRUE@$(top_builddir)/doc/api/index.html: ...

with the prefix stripped, so configure had set it true. Two conditionals in one
package: --disable-doc for the man pages, --disable-doxygen for the API
reference. This is the third documentation toolchain xz has asked for, after
po4a.

--disable-doxygen is NOT in configure's help text -- only --enable-doxygen is --
and it works anyway. That was checked rather than assumed: AC_ARG_ENABLE accepts
both forms, and `enable_doxygen` is a real variable the script tests. groff
taught the same lesson from the other side, where no such option existed and an
invented flag would have drawn a warning and changed nothing.

--- FR ---

--disable-doc avait été ajouté et doxygen tournait encore. La référence d'API est
derrière sa propre condition automake, et le Makefile généré le montre :

  @COND_DOXYGEN_TRUE@$(top_builddir)/doc/api/index.html: ...

préfixe retiré, donc configure l'avait mise à vrai. Deux conditions dans un même
paquet : --disable-doc pour les pages de manuel, --disable-doxygen pour l'API.
Troisième chaîne de documentation que xz réclame, après po4a.

--disable-doxygen n'est PAS dans l'aide de configure — seul --enable-doxygen y
est — et fonctionne quand même. Cela a été vérifié plutôt que supposé :
AC_ARG_ENABLE accepte les deux formes, et `enable_doxygen` est une variable que
le script teste réellement. groff a enseigné la même leçon par l'autre bout, où
aucune option n'existait et où un drapeau inventé n'aurait tiré qu'un
avertissement sans rien changer.

Assisted-by: Claude Opus 5
2026-08-23 22:18:30 -04:00
f25a34eaed [UPD] hoist fastjsonschema and vcs-versioning with the Python set 2026-08-23 22:17:08 -04:00
75cff944a8 [FIX] binutils records a usr/lib64 path, and cmake deletes html it did not make
pacman refused the rebuilt binutils:

  binutils: <root>/usr/lib64 exists in filesystem (owned by filesystem)
  binutils: <root>/usr/lib64/libiberty.a exists in filesystem

--libdir=/usr/lib was not enough. libiberty installs into
$(libdir)$(MULTIOSSUBDIR), and gcc reports ../lib64 for
-print-multi-os-directory on s390x. In the installed system that resolves
through the symlink filesystem now provides, so the file lands in /usr/lib --
but pkgdir has no symlink, so make creates a real pkg/usr/lib64/ and makepkg
records the literal path. The two fixes are not alternatives: the symlink is what
keeps meson and cmake choosing lib, and this moves the one file that still writes
through the multi-os subdirectory.

cmake is the eleventh instance of removing a documentation build and leaving its
cleanup: with Sphinx off there is no html, and the PKGBUILD deletes _sources from
it. rm -rf rather than deleted -- on a machine with Sphinx it is real.

The binutils hook also assumed package_binutils(). binutils is not a split
package; its own assertion caught that.

--- FR ---

pacman a refusé le binutils reconstruit :

  binutils: <root>/usr/lib64 exists in filesystem (owned by filesystem)
  binutils: <root>/usr/lib64/libiberty.a exists in filesystem

--libdir=/usr/lib ne suffisait pas. libiberty s'installe dans
$(libdir)$(MULTIOSSUBDIR), et gcc annonce ../lib64 pour
-print-multi-os-directory sur s390x. Dans le système installé cela se résout par
le lien que filesystem fournit désormais, donc le fichier atterrit dans
/usr/lib — mais pkgdir n'a pas de lien : make crée un vrai pkg/usr/lib64/ et
makepkg enregistre le chemin littéral. Les deux correctifs ne sont pas des
alternatives : le lien est ce qui fait choisir lib à meson et cmake, et celui-ci
déplace le seul fichier qui écrive encore par le sous-répertoire multi-os.

cmake est la onzième occurrence du retrait d'une documentation sans son nettoyage :
sans Sphinx il n'y a pas de html, et le PKGBUILD en supprime _sources. rm -rf
plutôt que supprimé — sur une machine dotée de Sphinx, il est réel.

Le hook binutils supposait aussi package_binutils(). binutils n'est pas un paquet
scindé ; sa propre assertion l'a arrêté.

Assisted-by: Claude Opus 5
2026-08-23 22:05:26 -04:00
6656f07e2b [ADD] python-vcs-versioning, the half setuptools-scm lost
from vcs_versioning import Configuration
  ModuleNotFoundError: No module named vcs_versioning

setuptools-scm was split upstream and its own build imports the half that left.
The traceback runs through setuptools build_meta, so what the log showed for
several passes was

  ERROR Backend subprocess exited when trying to invoke build_wheel

a backend complaint about a package that had been divided in two. Fifth Python
build-time package this port has had to add, after flit_core, setuptools,
poetry-core and hatchling.

--- FR ---

  from vcs_versioning import Configuration
  ModuleNotFoundError: No module named vcs_versioning

setuptools-scm a ete scinde en amont, et sa propre construction importe la moitie
partie. La trace passe par build_meta de setuptools : ce que le journal montrait
depuis plusieurs passes etait

  ERROR Backend subprocess exited when trying to invoke build_wheel

une plainte de moteur au sujet d un paquet coupe en deux. Cinquieme paquet Python
de construction que ce portage doit ajouter, apres flit_core, setuptools,
poetry-core et hatchling.

Assisted-by: Claude Opus 5
2026-08-23 21:47:54 -04:00
04a55bc74c [FIX] sqlite: tcl 9 keeps its library inside the binary
The hook's guard reported "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 only tcl can open -- 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 now
points at a real directory, versioned from tclsh.

--- FR ---

Le garde du hook signalait « no tcl dir under usr/lib ». Ce que sqlite avait créé
était un répertoire littéralement nommé `zipfs:`

  <pkgdir>/sqlite/zipfs:/lib/tcl/tcl_library/

Le Makefile généré s'explique lui-même :

  # TCLLIBDIR = where to install the tcl plugin. If this is empty, it
  TCLLIBDIR = //zipfs:/lib/tcl/tcl_library/sqlite3.53.4

sqlite demande à tcl où vit sa bibliothèque et installe le greffon à côté. En tcl
8.6 c'était un vrai répertoire. tcl 9 livre sa bibliothèque DANS l'exécutable, en
montage zipfs : la réponse est un chemin que seul tcl sait ouvrir, et sqlite l'a
traité comme un chemin de fichiers, créant un répertoire dont le nom contient
deux points.

C'est l'élément différé « libtcl8.6 contre notre tcl 9.0 » dans sa forme finale :
non pas un numéro de version à mettre à jour, mais un changement de ce que tcl
appelle un chemin. TCLLIBDIR vise désormais un vrai répertoire, sa version lue
depuis tclsh.

Assisted-by: Claude Opus 5
2026-08-23 21:19:23 -04:00
ac5e5f3892 [FIX] groff: the figure and the document that includes it
Emptying DOC_GNU_EPS stopped the GNU logo being built and left the document that
includes it:

  doc/webpage.ms:45: error: cannot open gnu.eps
  doc/webpage.ms:45: fatal error: PSPIC failed to include gnu.eps

Tenth time in this port, fourth of my own making. PROCESSEDEXAMPLEFILES_PS lists
webpage.ps and grnexmpl.ps, and the HTML variant directly above it is ALREADY
empty with its real value left as a comment -- groff configure empties that one
when the tools are missing. This does the same for PS, which is the decision
groff would have made had it checked.

--- FR ---

Vider DOC_GNU_EPS a empeche la construction du logo GNU et laisse en place le
document qui l inclut :

  doc/webpage.ms:45: error: cannot open gnu.eps
  doc/webpage.ms:45: fatal error: PSPIC failed to include gnu.eps

Dixieme fois dans ce portage, quatrieme de ma main. PROCESSEDEXAMPLEFILES_PS
liste webpage.ps et grnexmpl.ps, et la variante HTML juste au-dessus est DEJA
vide, sa vraie valeur laissee en commentaire — le configure de groff vide
celle-la quand les outils manquent. Ceci fait de meme pour PS : la decision que
groff aurait prise s il avait verifie.

Assisted-by: Claude Opus 5
2026-08-23 21:03:23 -04:00
fceb5936a5 [FIX] --nodeps once skips versions, not dependencies
Two install commands passed one --nodeps and looked like they were skipping
dependencies. pacman documents the difference: -d skips dependency VERSION
checks, -dd skips them entirely. So 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 Python binding, while its declared RUNTIME dependencies are numpy and
pygments, and numpy brings BLAS and LAPACK. A single -d nearly bought a numerical
stack to compile one .pyx file.

The message named numpy and numpy was not the subject. The subject was a missing
letter d.

python-poetry-core joins the list: the fourth Python build backend needed here
after flit_core, setuptools and hatchling.

--- FR ---

Deux commandes d installation passaient un seul --nodeps et semblaient sauter les
dependances. pacman documente la difference : -d saute les controles de VERSION,
-dd les saute entierement. cython ne pouvait donc pas etre installe --

  warning: cannot resolve python-pygments, a dependency of cython
  :: unable to satisfy dependency python-numpy required by cython

-- et les chiffres comptent. cython n est voulu ici que comme OUTIL de
construction, pour la liaison Python de libseccomp, alors que ses dependances d
EXECUTION declarees sont numpy et pygments, et numpy tire BLAS et LAPACK. Un seul
-d a failli faire compiler une pile numerique pour un fichier .pyx.

Le message nommait numpy, et numpy n etait pas le sujet. Le sujet etait un d
manquant.

python-poetry-core rejoint la liste : quatrieme moteur de construction Python
necessaire ici, apres flit_core, setuptools et hatchling.

Assisted-by: Claude Opus 5
2026-08-23 20:47:32 -04:00
0963698d4b [FIX] xz has two doc toolchains, and libpsl needs data not a tool
xz got past po4a and stopped on

  ../../../doxygen/update-doxygen: doxygen command not found

Two documentation toolchains in one package: po4a for the translated man pages,
doxygen for the liblzma API reference. --disable-doc is xz own switch, checked
present in configure before use.

libpsl stopped on

  make: *** No rule to make target
    /usr/share/publicsuffix/effective_tld_names.dat, needed by ...

publicsuffix-list is DATA. libpsl compiles the Public Suffix List into a lookup
table, so the list is an input to the build -- the first entry in this port whose
absence is neither a program nor a library, and it reads exactly like a broken
Makefile.

--- FR ---

xz a passe po4a et s est arrete sur

  ../../../doxygen/update-doxygen: doxygen command not found

Deux chaines de documentation dans un seul paquet : po4a pour les pages de manuel
traduites, doxygen pour la reference d API de liblzma. --disable-doc est son
propre interrupteur, verifie present dans configure avant usage.

libpsl s est arrete sur

  make: *** No rule to make target
    /usr/share/publicsuffix/effective_tld_names.dat, needed by ...

publicsuffix-list est une DONNEE. libpsl compile la Public Suffix List en table
de correspondance : la liste est une entree de la construction — premiere entree
de ce portage dont l absence n est ni un programme ni une bibliotheque, et elle se
lit exactement comme un Makefile casse.

Assisted-by: Claude Opus 5
2026-08-23 20:19:45 -04:00
9a598fdd4d [FIX] cmake: no Sphinx manual
CMake Error at Utilities/Sphinx/CMakeLists.txt:52 (message):

cmake builds its own manual and man pages with Sphinx, asked for by --sphinx-man
and --sphinx-html. python-sphinx 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 one
leaves the other pulling Sphinx in and the error is identical, which is how a
half-fix looks like no fix.

--- FR ---

  CMake Error at Utilities/Sphinx/CMakeLists.txt:52 (message):

cmake batit son propre manuel et ses pages de manuel avec Sphinx, reclames par
--sphinx-man et --sphinx-html. python-sphinx est une large pile de documentation
Python, et cmake est voulu ici comme outil de construction.

Les deux drapeaux, et ce sont des arguments d amorcage plutot que des options -D :
cmake se configure avec son propre script avant de pouvoir s executer lui-meme.
N en retirer qu un laisse l autre tirer Sphinx et l erreur est identique, ce qui
donne a une demi-correction l apparence d aucune.

Assisted-by: Claude Opus 5
2026-08-23 20:02:59 -04:00
3267c6484c [FIX] binutils: gold's s390 support needs a target binutils is deleting
This section was written twice, and the first version was wrong instructively.

gold would not link: s390.cc references gold::gold_error_at_location<32, true>,
and those instantiations 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:

  *** 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 cannot be built here without
enabling a target upstream has announced it is deleting -- and gold is itself
deprecated, with bfd ld already the default (--enable-ld=default). Turning on one
deprecated thing to build another is not a trade worth making.

gold is dropped and the extra target list keeps only bpf-unknown-none. What is
lost is /usr/bin/ld.gold, which nothing here invokes. Recorded rather than
hidden: the stage-2 binutils will not match Arch's file list, and this is why.

--- FR ---

Cette section a été écrite deux fois, et la première version se trompait de façon
instructive.

gold ne se liait pas : s390.cc référence gold::gold_error_at_location<32, true>,
instanciations qui n'existent que si une cible s390 32 bits est configurée. Le
PKGBUILD demandait --enable-targets=x86_64-pep, donc le premier correctif l'a
traduit en s390-linux-gnu — l'équivalent d'apparence honnête. Le configure de bfd
a répondu :

  *** Specify --enable-obsolete to build it anyway.
  *** Support will be REMOVED in the next major release of BINUTILS,
  *** unless a maintainer comes forward.

Le s390 32 bits est obsolète dans binutils. gold ne peut donc pas être bâti ici
sans activer une cible dont l'amont annonce la suppression — et gold est lui-même
abandonné, ld de bfd étant déjà le défaut (--enable-ld=default). Activer un objet
déprécié pour en bâtir un autre n'est pas un échange qui vaille.

gold est retiré et la liste de cibles ne garde que bpf-unknown-none. Ce qui est
perdu est /usr/bin/ld.gold, que rien ici n'invoque. Consigné plutôt que masqué :
le binutils d'étage 2 ne correspondra pas à la liste de fichiers d'Arch, et voici
pourquoi.

Assisted-by: Claude Opus 5
2026-08-23 19:48:19 -04:00
8a41d00a90 [FIX] our flit_core is too new, and savannah would not serve a patch
python-jinja: ERROR Missing dependencies: flit_core<4

flit_core is installed and importable -- 4.0.2. jinja's pyproject.toml pins
flit_core<4, so build refuses before compiling anything. A new shape here, and
the mirror image of one already recorded: libxslt could not be built on the host
because Ubuntu's libxml2 was too OLD; this is our own package being too NEW for
another's ceiling. Both read as a missing dependency and neither is one.
--skip-dependency-check is what Arch's own python-packaging passes for the same
class of reason.

  autoconf-archive: curl: (52) Empty reply from server

The tarball arrives fine from ftpmirror.gnu.org. What fails is one patch from
savannah's cgit /patch/?id= endpoint, which returns nothing while savannah's root
answers 302 -- measured on both, plus 200 and 2463 bytes from a git mirror for
the same commit hash. Asking a mirror for the SAME COMMIT changes where the bytes
come from, not which bytes they are.

Also: pkill -f "[c]url.*autoconf-arc" killed the shell running it, because the
pattern matched that shell's own command line. Fourth time in this port.

--- FR ---

  python-jinja: ERROR Missing dependencies: flit_core<4

flit_core est installé et importable — 4.0.2. Le pyproject.toml de jinja épingle
flit_core<4 : build refuse avant de compiler. Forme nouvelle ici, et miroir d'une
déjà consignée : libxslt ne se bâtissait pas sur l'hôte car le libxml2 d'Ubuntu
était trop ANCIEN ; ici notre propre paquet est trop RÉCENT pour le plafond d'un
autre. Les deux se lisent comme une dépendance manquante, aucune ne l'est.
--skip-dependency-check est ce que passe le python-packaging d'Arch pour la même
raison.

  autoconf-archive: curl: (52) Empty reply from server

L'archive arrive très bien de ftpmirror.gnu.org. Ce qui échoue est un correctif
tiré du point /patch/?id= de cgit chez savannah, qui ne renvoie rien alors que la
racine de savannah répond 302 — mesuré sur les deux, plus 200 et 2463 octets
depuis un miroir git pour le même commit. Demander le MÊME COMMIT à un miroir
change d'où viennent les octets, pas lesquels.

Aussi : pkill -f "[c]url.*autoconf-arc" a tué le shell qui l'exécutait, son motif
correspondant à la ligne de commande de ce shell. Quatrième fois dans ce portage.

Assisted-by: Claude Opus 5
2026-08-23 19:27:14 -04:00
a8eef0e460 [FIX] the second half of two of my own hooks
libevent: mv: cannot stat '<pkgdir>/usr/share/doc': No such file or directory
  pam:      rm: cannot remove '.../Linux-PAM/*.pdf': No such file or directory

Both are hooks I wrote in this session, and both stopped a build in exactly the
way this port has documented eight times: the documentation build was disabled
and its install was left in place. Turning EVENT__DOXYGEN off does not stop
package() moving usr/share/doc into the libevent-doc split; -Ddocs=disabled does
not stop pam deleting PDFs it no longer produces.

fakeroot was the first one I made myself. This makes three, so the rule is worth
stating instead of rediscovering: after disabling documentation anywhere, search
package() for share/doc, man, and the split arrays before believing the hook is
finished.

pam's rm becomes tolerant rather than deleted -- on a machine with the DocBook
stack the PDFs are real, and the comment above the line says why they must go:
they are not reproducible.

--- FR ---

  libevent: mv: cannot stat '<pkgdir>/usr/share/doc': No such file or directory
  pam:      rm: cannot remove '.../Linux-PAM/*.pdf': No such file or directory

Deux hooks que j'ai écrits dans cette session, et deux arrêts de construction de
la façon exacte que ce portage a documentée huit fois : la construction de la
documentation désactivée, son installation laissée en place. Éteindre
EVENT__DOXYGEN n'empêche pas package() de déplacer usr/share/doc vers le
sous-paquet libevent-doc ; -Ddocs=disabled n'empêche pas pam de supprimer des PDF
qu'il ne produit plus.

fakeroot fut le premier de ma main. Cela en fait trois : la règle mérite d'être
énoncée plutôt que redécouverte — après avoir désactivé une documentation,
chercher dans package() share/doc, man, et les tableaux de sous-paquets avant de
croire le hook terminé.

Le rm de pam devient tolérant plutôt que supprimé : sur une machine dotée de la
pile DocBook les PDF existent, et le commentaire au-dessus dit pourquoi ils
doivent partir — ils ne sont pas reproductibles.

Assisted-by: Claude Opus 5
2026-08-23 19:09:10 -04:00
f26a6a7115 [FIX] stage 2 could not clone, its pacman had no user, and it installed conflicts
Three gaps in the driver, all found by packages advancing to their next obstacle.

  no checkout for python-jinja

Stage 2 could only rebuild what stage 1 had cloned -- wrong for every package
stage 2 builds and stage 1 cannot. libxslt was the first (Ubuntu's libxml2 is too
old to configure it), the Python set followed. They are named in
CHROOT_STAGE2_PKGS because only this stage can produce them, and this stage would
not fetch them. It clones now, pinned to upstream.lock.

  error: problem setting DownloadUser 'alpm' (user does not exist)

The rebuilt pacman drops privileges for downloads to a user created by
sysusers.d, which `pacman --root` does not run -- the same reason the CA bundle
was never generated. Nothing in the chroot downloads anything.

  :: zlib-ng-compat and zlib are in conflict

zlib-ng produces zlib-ng-compat, correct and belonging in repo2, uninstallable
beside the zlib this chroot uses. CHROOT_EXCLUDE already said so for populate;
the same list now applies to the per-package install.

--- FR ---

Trois lacunes du pilote, toutes révélées par des paquets parvenus à leur obstacle
suivant.

  no checkout for python-jinja

L'étage 2 ne savait reconstruire que ce que l'étage 1 avait cloné — faux pour
tout paquet que l'étage 2 bâtit et que l'étage 1 ne peut pas. libxslt fut le
premier (le libxml2 d'Ubuntu est trop ancien), l'ensemble Python a suivi. Ils
figurent dans CHROOT_STAGE2_PKGS parce que seul cet étage les produit, et cet
étage refusait de les chercher. Il clone désormais, épinglé sur upstream.lock.

  error: problem setting DownloadUser 'alpm' (user does not exist)

Le pacman reconstruit abaisse ses privilèges vers un utilisateur créé par
sysusers.d, que `pacman --root` n'exécute pas — la raison même pour laquelle le
faisceau de certificats n'était jamais généré. Rien ne télécharge dans ce chroot.

  :: zlib-ng-compat et zlib sont en conflit

zlib-ng produit zlib-ng-compat, juste et à sa place dans repo2, ininstallable à
côté du zlib de ce chroot. CHROOT_EXCLUDE le disait déjà pour le peuplement ; la
même liste vaut maintenant pour l'installation par paquet.

Assisted-by: Claude Opus 5
2026-08-23 18:38:46 -04:00
5418dace22 [FIX] p11-kit: the error named glib, and glib was not the subject
meson.build:65:18: ERROR: Dependency "glib-2.0" not found

This was nearly a sub-project. glib2 is not in this port and adding it is not one
package: Arch glib2 wants gobject-introspection, which itself needs glib2 -- a
cycle to bootstrap -- plus libsysprof-capture, dconf and gi-docgen.

Reading where the requirement 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 no p11-kit code touches it. -D gtk_doc=false drops
the subdirectory and the dependency with it. Six lines of context replaced
several packages and a dependency cycle.

--- FR ---

  meson.build:65:18: ERROR: Dependency "glib-2.0" not found

C etait presque un sous-projet. glib2 n est pas dans ce portage et l ajouter n est
pas un paquet : le glib2 d Arch veut gobject-introspection, qui depend lui-meme de
glib2 -- un cycle a amorcer -- plus libsysprof-capture, dconf et gi-docgen.

Lire d ou vient reellement l exigence a change la reponse. Le fichier est
doc/manual/meson.build et la ligne est

  glib_prefix = dependency("glib-2.0").get_variable(pkgconfig : "prefix")

glib est consulte pour trouver ou gtk-doc range ses references croisees. Il n est
ni lie, ni appele, et aucun code de p11-kit ne le touche. -D gtk_doc=false
supprime le sous-repertoire et la dependance avec lui. Six lignes de contexte ont
remplace plusieurs paquets et un cycle de dependances.

Assisted-by: Claude Opus 5
2026-08-23 18:03:10 -04:00
8c7d8f52a9 [UPD] hoist jinja2 ahead of systemd
systemd generates source from jinja2 templates, and the Python set sits at the
end of packages.sh while systemd is in the middle. The list order is the runtime
closure, not the build order -- so the packages that must exist before another
one builds are named in STAGE2_FIRST rather than moved.

--- FR ---

systemd genere du code depuis des gabarits jinja2, et l ensemble Python figure en
fin de packages.sh quand systemd est au milieu. L ordre de la liste est la
fermeture d execution, pas celle de construction : les paquets qui doivent
exister avant qu un autre se batisse sont nommes dans STAGE2_FIRST plutot que
deplaces.

Assisted-by: Claude Opus 5
2026-08-23 18:00:35 -04:00
22c0eca955 [ADD] gnupg without its diagrams, and jinja2 for systemd
gnupg builds two architecture diagrams into its manual, one needing ImageMagick
and one transfig -- two toolchains for two pictures. --disable-doc is gnupg own
switch, verified present in configure before use.

The hook was written twice. The first version looked for a ./configure line
ending in a backslash and asserted its way out; gnupg uses an options ARRAY.
Both shapes appear in this port, so a hook has to read the file rather than
assume the house style.

systemd generates source from jinja2 templates and stops without it. Not
documentation this time -- the build cannot proceed.

--- FR ---

gnupg construit deux diagrammes dans son manuel, l un exigeant ImageMagick et
l autre transfig -- deux chaines d outils pour deux images. --disable-doc est son
propre interrupteur, verifie present dans configure avant usage.

Le hook a ete ecrit deux fois. La premiere version cherchait une ligne
./configure terminee par une contre-oblique et s est arretee sur son assertion ;
gnupg emploie un TABLEAU d options. Les deux formes existent dans ce portage : un
hook doit lire le fichier plutot que supposer le style maison.

systemd genere du code depuis des gabarits jinja2 et s arrete sans lui. Pas de la
documentation cette fois -- la construction ne peut pas avancer.

Assisted-by: Claude Opus 5
2026-08-23 17:25:27 -04:00
cb3a32101a [FIX] a hook's scope belongs to its sections, not to its filename
git and meson kept failing in stage 2 on exactly what their own hooks fix, while
the log said

  hook skipped (stage-1 only)

Both were named in STAGE2_SKIP_HOOKS, correctly, when their whole content was a
host workaround: git dropped ZLIB_NG because the host had no zlib-ng headers,
meson moved a wheel out of /usr/local. Then each grew a second section that BOTH
stages need -- git's asciidoc man pages, meson's hotdoc reference manual -- and
the list skips the whole FILE, so the new sections never ran. Two hooks that were
by then two thirds relevant, silently ignored.

A list of filenames cannot say why a hook is listed, and cannot notice when that
reason stops covering the file. The hooks now read EL_STAGE and decide per
section, with the reason written beside each guard; the list is gone. libgcrypt
and libarchive keep a whole-file guard, which now states its reason instead of
being an entry somewhere else.

Verified at both stages: git drops its man pages in each, and keeps ZLIB_NG in
the chroot where zlib-ng exists.

--- FR ---

git et meson échouaient à l'étage 2 sur précisément ce que leurs propres hooks
corrigent, pendant que le journal disait

  hook skipped (stage-1 only)

Tous deux étaient nommés dans STAGE2_SKIP_HOOKS, à juste titre quand tout leur
contenu était un contournement de l'hôte : git abandonnait ZLIB_NG faute
d'en-têtes zlib-ng, meson déplaçait une roue hors de /usr/local. Puis chacun a
gagné une seconde section utile aux DEUX étages — les pages asciidoc de git, le
manuel hotdoc de meson — et la liste saute le FICHIER entier : ces sections n'ont
jamais tourné. Deux hooks devenus pertinents aux deux tiers, ignorés en silence.

Une liste de noms de fichiers ne peut pas dire pourquoi un hook y figure, ni
remarquer que cette raison a cessé de couvrir le fichier. Les hooks lisent
désormais EL_STAGE et tranchent par section, la raison écrite à côté de chaque
garde ; la liste disparaît. libgcrypt et libarchive gardent une garde de fichier
entier, qui énonce sa raison au lieu d'être une entrée ailleurs.

Vérifié aux deux étages : git abandonne ses pages de manuel dans les deux, et
conserve ZLIB_NG dans le chroot, où zlib-ng existe.

Assisted-by: Claude Opus 5
2026-08-23 17:23:30 -04:00
4101d18f72 [ADD] upstream.lock, and keep the machine out of the repository
Until now the honest description of this port was: it worked once, on one
machine. Every PKGBUILD comes from a `git clone --depth 1` of
gitlab.archlinux.org, and the 63 hooks assert exact strings -- deliberately, and
several have caught their own mistakes that way. But it means the port is written
against a moving target: someone starting over today gets what Arch has today,
not what these hooks were written against.

What closes that is not the 18 GB of build output -- regenerable packages, a
chroot wiped on every run, state files derived from the repository by design. It
is one commit per checkout: 155 lines. build_package now pins a fresh clone to
the locked commit, and says so when a package is NOT in the lock, because that is
how a lock quietly stops covering what it claims to.

RELAIS.md joins the repository, written without the build machine's alias,
address or account -- a successor needs the shape of the access, not its
coordinates. check-private.sh keeps it that way, and .env.example loses its
literal account name. Three of its patterns had to go on their first runs: they
flagged a systemd unit template, upstream maintainer headers, the localhost lines
of a generated /etc/hosts, and its own explanatory comment. A check that fails on
correct files is one somebody stops running.

--- FR ---

Jusqu'ici la description honnête de ce portage était : il a fonctionné une fois,
sur une machine. Chaque PKGBUILD vient d'un `git clone --depth 1` de
gitlab.archlinux.org, et les 63 hooks affirment des chaînes exactes — à dessein,
et plusieurs y ont attrapé leurs propres erreurs. Mais le portage est donc écrit
contre une cible mouvante : qui recommence aujourd'hui obtient l'Arch du jour.

Ce qui comble ce trou n'est pas les 18 Go de production — paquets régénérables,
chroot effacé à chaque passage, registres dérivés du dépôt par conception. C'est
un commit par arbre : 155 lignes. build_package épingle un nouveau clone sur le
commit verrouillé, et le DIT quand un paquet n'y figure pas, car c'est ainsi qu'un
verrou cesse discrètement de couvrir ce qu'il prétend.

RELAIS.md entre dans le dépôt, écrit sans l'alias, l'adresse ni le compte de la
machine — un successeur a besoin de la forme de l'accès, pas de ses coordonnées.
check-private.sh l'y maintient, et .env.example perd son nom de compte littéral.
Trois de ses motifs ont dû partir dès les premiers passages : ils signalaient un
gabarit d'unité systemd, des en-têtes de mainteneurs amont, les lignes localhost
d'un /etc/hosts généré, et son propre commentaire explicatif. Un contrôle qui
échoue sur des fichiers justes est un contrôle qu'on cesse de lancer.

Assisted-by: Claude Opus 5
2026-08-22 22:52:04 -04:00
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
d1ac7a94f9 [FIX] a dangling symlink and a complete install look the same
Four packages failed in prepare() on

  Failed to clone 'gl-mod/bootstrap' a second time, aborting

The first diagnosis was DNS, and it was right: the chroot had no resolv.conf.
Copying it made resolution work -- `getent hosts github.com` answers -- and the
clones still failed, now on

  error adding trust anchors from file: /etc/ssl/certs/ca-certificates.crt

Everything needed was already installed: the symlink, the Mozilla trust source,
update-ca-trust, p11-kit's trust. What was missing is that the bundle they
produce is made by an ALPM HOOK, and `pacman --root` does not run hooks. So the
target of that symlink never existed, and a package list cannot tell a dangling
symlink from a working installation.

Generated from our own trust source, not copied from the host, and counted
rather than trusted: update-ca-trust exits 0 on an empty source, and what that
hides is an https failure hours later. 121 certificates; the clone works.

--- FR ---

Quatre paquets ont échoué dans prepare() sur

  Failed to clone 'gl-mod/bootstrap' a second time, aborting

Le premier diagnostic était le DNS, et il était juste : le chroot n'avait pas de
resolv.conf. Le copier a rétabli la résolution — `getent hosts github.com`
répond — et les clones échouaient toujours, désormais sur

  error adding trust anchors from file: /etc/ssl/certs/ca-certificates.crt

Tout le nécessaire était installé : le lien, la source de confiance Mozilla,
update-ca-trust, le trust de p11-kit. Ce qui manquait, c'est que le faisceau
qu'ils produisent est fabriqué par un hook ALPM, et `pacman --root` n'exécute pas
les hooks. La cible du lien n'a donc jamais existé, et une liste de paquets ne
distingue pas un lien mort d'une installation valide.

Généré depuis notre propre source de confiance, non copié de l'hôte, et compté
plutôt que cru : update-ca-trust sort en 0 sur une source vide, et ce que cela
masque est un échec https des heures plus tard. 121 certificats ; le clone passe.

Assisted-by: Claude Opus 5
2026-08-21 19:37:10 -04:00
9936d8f873 [FIX] binutils: delete the option, do not comment it out
The PGO fix broke the build it was meant to repair:

  /build/binutils/PKGBUILD: line 107: --enable-plugins: command not found

The line it commented out ended in a backslash. A comment inside a
backslash-continued command does not comment out an option -- it breaks the
continuation, and the next option becomes a command of its own. This port had
already documented the mirror image, where commenting the FIRST line of a
multi-line assignment leaves the continuations live; same fact, other side.

The line is deleted now, and nothing is put in its place: any comment inside the
./configure invocation would break it again. The explanation belongs in the hook.

Also removed from that hook: a check ending in `|| true`, which passes always. A
guard that cannot fail is worse than no guard, because it reads like
verification.

--- FR ---

Le correctif PGO a cassé la construction qu'il devait réparer :

  /build/binutils/PKGBUILD: line 107: --enable-plugins: command not found

La ligne commentée finissait par une contre-oblique. Un commentaire dans une
commande continuée ne supprime pas une option — il brise la continuation, et
l'option suivante devient une commande. Ce portage avait déjà documenté l'image
inverse, où commenter la PREMIÈRE ligne d'une affectation multiligne laisse les
continuations actives ; même fait, autre face.

La ligne est désormais supprimée, et rien ne la remplace : tout commentaire dans
l'appel à ./configure le briserait de nouveau. L'explication appartient au hook.

Retiré aussi de ce hook : un contrôle finissant par `|| true`, donc toujours
satisfait. Une garde incapable d'échouer est pire qu'aucune garde, car elle se
lit comme une vérification.

Assisted-by: Claude Opus 5
2026-08-21 16:53:49 -04:00
09aa24c46a [FIX] three packages shipped 180 unreachable module paths
python-build, python-wheel and python-pyproject-hooks were built on the host,
reported OK, and installed everything under usr/lib/python3/dist-packages --
Debian's name for site-packages. Our python 3.14 never looks there, so every
module in them was unreachable. Nothing about them looked wrong.

Their package() computes install paths from the RUNNING python:

  local site_packages=$(python -c "import site; print(site.getsitepackages()[0])")
  rm "$pkgdir/$site_packages/$_name"/*.exe

which is why the other three in the same batch FAILED -- on `rm` finding
nothing. Those failures were protective. All six are built by stage 2 now, where
python is ours; a stage-1 run is expected to report them as failed, like libxslt.

The ARTEFACT check said the repository was clean throughout, truthfully: it only
ever asked about C library directories. It now also counts dist-packages and
usr/local paths, so the next time this happens it says so.

--- FR ---

python-build, python-wheel et python-pyproject-hooks ont été bâtis sur l'hôte,
déclarés OK, et ont tout installé sous usr/lib/python3/dist-packages — le nom
Debian de site-packages. Notre python 3.14 n'y regarde jamais : chaque module
était inatteignable. Rien en eux n'avait l'air faux.

Leur package() calcule les chemins depuis le python COURANT :

  local site_packages=$(python -c "import site; print(site.getsitepackages()[0])")
  rm "$pkgdir/$site_packages/$_name"/*.exe

d'où l'échec des trois autres du même lot — sur un `rm` qui ne trouvait rien. Ces
échecs étaient protecteurs. Les six sont désormais bâtis par l'étage 2, où le
python est le nôtre ; une passe d'étage 1 doit les déclarer en échec, comme
libxslt.

Le test ARTEFACT affirmait un dépôt propre, véridiquement : il n'interrogeait que
les répertoires de bibliothèques C. Il compte maintenant aussi dist-packages et
usr/local, pour le dire la prochaine fois.

Assisted-by: Claude Opus 5
2026-08-21 16:49:37 -04:00