Commit graph

49 commits

Author SHA1 Message Date
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
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
f2aff788ec [FIX] the chroot could not resolve a name, and wget could not fix itself
Four packages -- m4, libtool, groff, libnghttp2 -- failed in prepare() on

  fatal: unable to access 'https://github.com/...': Could not resolve host
  Failed to clone 'gl-mod/bootstrap' a second time, aborting

Arch takes its sources from git and these fetch gnulib as a submodule, so a
chroot with no resolv.conf cannot start the build at all. The retry line is what
makepkg prints; the reason sits one line above it.

Six more stopped on wget, which links libnettle.so.8 -- Ubuntu's soname, where
ours is .9. Our gnutls is already a stage-2 package and correctly wants .9; wget
was the last thing holding the old one, and gnulib's bootstrap fetches
translation catalogues WITH wget, so the tool it needed to fix itself was itself.
It joins libxml2 in STAGE2_HOST_EXTRACT: the host, which has a working wget, runs
prepare(); the chroot compiles.

Also fixed: my own ca-certificates hook left build() containing nothing but a
comment, which bash reports as a syntax error on the closing brace.

--- FR ---

Quatre paquets — m4, libtool, groff, libnghttp2 — ont échoué dans prepare() sur

  fatal: unable to access 'https://github.com/...': Could not resolve host
  Failed to clone 'gl-mod/bootstrap' a second time, aborting

Arch tire ses sources de git et ceux-là récupèrent gnulib en sous-module : un
chroot sans resolv.conf ne peut pas même commencer. La ligne de reprise est ce
qu'affiche makepkg ; la raison est juste au-dessus.

Six autres se sont arrêtés sur wget, qui lie libnettle.so.8 — le soname
d'Ubuntu, quand le nôtre est .9. Notre gnutls est déjà un paquet d'étage 2 et
demande bien .9 ; wget était le dernier à retenir l'ancien, et le bootstrap de
gnulib récupère les catalogues de traduction AVEC wget : l'outil dont il avait
besoin pour se réparer était lui-même. Il rejoint libxml2 dans
STAGE2_HOST_EXTRACT — l'hôte, qui a un wget valide, exécute prepare() ; le chroot
compile.

Corrigé aussi : mon propre hook ca-certificates laissait build() avec un seul
commentaire, ce que bash signale comme une erreur de syntaxe sur l'accolade.

Assisted-by: Claude Opus 5
2026-08-21 16:29:10 -04:00
e137999f8b [FIX] the xar cycle reformed inside stage 2
39 of 39 packages failed on the same line:

  bsdtar: error while loading shared libraries: libicuuc.so.76

libarchive is in STAGE2_SKIP_HOOKS, so stage 2 rebuilt it as Arch does -- with
xar, linking whatever libxml2 was installed at that moment: the stage-1 one,
which wants the host's ICU 76 while ours ships 78. bsdtar is what makepkg uses
to extract sources and write packages, so nothing could come out, including
libxml2 itself. The stage-1 fix does not reach in here.

No new mechanism was needed. libarchive is dropped from stage2.state, so the
restore falls back to the stage-1 build that has no xar and therefore no libxml2
at all; libxml2 is hoisted and rebuilt against our icu; libarchive is rebuilt
later with xar, against a libxml2 whose ICU now agrees. Verified: bsdtar 3.8.9
runs, and libxml2 is the first OK of the pass.

The libgpgme.so.11 errors in five logs were noise -- makepkg carries on past a
failed .BUILDINFO. Reading the FIRST error in a log had attributed five failures
to the wrong cause; the last error before the abort is the one that matters.

--- FR ---

39 paquets sur 39 ont échoué sur la même ligne :

  bsdtar: error while loading shared libraries: libicuuc.so.76

libarchive figure dans STAGE2_SKIP_HOOKS : l'étage 2 l'a donc rebâti comme le
fait Arch — avec xar, contre le libxml2 installé à cet instant, celui de
l'étage 1, qui veut l'ICU 76 de l'hôte quand le nôtre livre la 78. bsdtar est ce
dont makepkg se sert pour extraire et pour écrire les paquets : plus rien ne
sortait, libxml2 compris. Le correctif d'étage 1 n'atteint pas l'intérieur.

Aucun mécanisme nouveau. libarchive quitte stage2.state, donc la restauration
retombe sur la construction d'étage 1, sans xar et donc sans libxml2 ; libxml2
est hissé et rebâti contre notre icu ; libarchive est rebâti plus tard avec xar,
contre un libxml2 dont l'ICU concorde. Vérifié : bsdtar 3.8.9 démarre, et
libxml2 est le premier OK de la passe.

Les erreurs libgpgme.so.11 de cinq journaux étaient du bruit — makepkg poursuit
après un .BUILDINFO manqué. Lire la PREMIÈRE erreur d'un journal avait attribué
cinq échecs à la mauvaise cause ; c'est la dernière avant l'abandon qui compte.

Assisted-by: Claude Opus 5
2026-08-21 02:18:25 -04:00
ed541dacc4 [FIX] stage 2: do not restore what it no longer trusts
Removing a name from stage2.state means its stage-2 artefact is not trusted --
that is the whole meaning of removing it. The restore ignored that and put the
package back anyway, undoing the fix that motivated the removal before the
rebuild could run.

It happened twice. binutils had to be moved out of repo2 by hand. filesystem
was worse because it was invisible: its pass-one build predates the hook adding
s390x's lib64 symlinks, so restoring it REMOVED the symlink stage 1 had just
installed. /usr/lib64 was absent again and the fix looked like it had failed.

pkgbase, not pkgname: stage2.state records what was built, and libxml2-docs is
not in it under that name.

The diagnosis also took a wrong turn worth recording. `test -L` failing was
reported as "a real directory", but it fails just as readily on a path that does
not exist -- which was the actual state. The check now says what the path IS.

--- FR ---

Retirer un nom de stage2.state signifie que son artefact d'étage 2 n'est plus de
confiance — c'est tout le sens du retrait. La restauration l'ignorait et le
réinstallait quand même, défaisant le correctif qui avait motivé le retrait avant
que la reconstruction puisse tourner.

Deux fois. binutils a dû être écarté de repo2 à la main. filesystem était pire
car invisible : sa construction de la passe 1 précède le hook ajoutant les liens
lib64 de s390x, donc le restaurer SUPPRIMAIT le lien que l'étage 1 venait de
poser. /usr/lib64 disparaissait et le correctif semblait inopérant.

pkgbase, pas pkgname : stage2.state enregistre ce qui a été bâti, et
libxml2-docs n'y figure pas sous ce nom.

Le diagnostic a aussi pris un mauvais chemin qui mérite d'être noté. L'échec de
`test -L` était rapporté comme « vrai répertoire », alors qu'il échoue tout
autant sur un chemin absent — l'état réel. Le test dit maintenant ce que le
chemin EST.

Assisted-by: Claude Opus 5
2026-08-21 00:58:44 -04:00
f380f3fd66 [FIX] one stray .a file broke every pkg-config lookup
libxslt reported

  configure: error: Package requirements (python-3.14) were not met:
  Package 'python-3.14' not found

with python 3.14.7 installed and /usr/lib/pkgconfig/python-3.14.pc present. The
chain, none of whose links resembles the others:

  binutils installs libiberty.a into /usr/lib64, because s390x's default
  MULTILIB_OSDIRNAME is lib64, creating that path as a REAL directory. meson
  chooses its default libdir by inspecting the system, and 64-bit plus a real
  /usr/lib64 means lib64 -- a symlink does not count, which is why Arch never
  sees this and arch-meson passes no --libdir at all. pkgconf is a meson
  package, so it installed there and COMPILED IN /usr/lib64/pkgconfig as its
  search path. Every .pc in the distribution is in /usr/lib/pkgconfig.

Fixed at all three levels: binutils pinned to --libdir=/usr/lib, the chroot's
arch-meson wrapper pins --libdir lib as a backstop, and the ARTEFACT check --
which said "no Debian multiarch libdir anywhere", truthfully and uselessly --
now also counts paths under usr/lib64.

--- FR ---

libxslt annonçait

  configure: error: Package requirements (python-3.14) were not met:
  Package 'python-3.14' not found

avec python 3.14.7 installé et /usr/lib/pkgconfig/python-3.14.pc bien présent.
La chaîne, dont aucun maillon ne ressemble aux autres :

  binutils dépose libiberty.a dans /usr/lib64, le MULTILIB_OSDIRNAME par défaut
  de s390x, et crée donc ce chemin comme VRAI répertoire. meson choisit son
  libdir par défaut en inspectant le système : 64 bits plus un vrai /usr/lib64
  donne lib64 — un lien ne compte pas, d'où le fait qu'Arch ne voit jamais cela
  et qu'arch-meson ne passe aucun --libdir. pkgconf étant un paquet meson, il
  s'y est installé et a COMPILÉ /usr/lib64/pkgconfig comme chemin de recherche.
  Or tous les .pc de la distribution sont dans /usr/lib/pkgconfig.

Corrigé aux trois niveaux : binutils épinglé à --libdir=/usr/lib, l'enveloppe
arch-meson du chroot épingle --libdir lib en filet, et le test ARTEFACT — qui
répondait « aucun libdir multiarch Debian », véridiquement et inutilement —
compte désormais aussi les chemins sous usr/lib64.

Assisted-by: Claude Opus 5
2026-08-21 00:47:09 -04:00
c164b59ca6 [FIX] help2man was uninstallable, which stopped the whole pass
Stage-2 pass two never reached its first package:

  unable to satisfy dependency 'perl-locale-gettext' required by help2man
  stage2: populate failed

help2man had built cleanly the moment before. Stage 1 builds with --nodeps, so
a successful build says nothing about whether the repository is complete -- and
this is the failure that says it out loud, by refusing to assemble the chroot.

Asked the resolver for the whole closure rather than fixing one name and trying
again: pacman stops at the first unsatisfiable dependency, so the count matters
more than the first line. One gap, then 148 packages resolved with --nodeps off,
up from 101 before the tools went in.

--- FR ---

La deuxième passe de l'étage 2 n'a jamais atteint son premier paquet :

  unable to satisfy dependency 'perl-locale-gettext' required by help2man
  stage2: populate failed

help2man venait de se bâtir sans une erreur. L'étage 1 bâtit avec --nodeps :
une construction réussie ne dit rien de la complétude du dépôt — et voici
l'échec qui le dit tout haut, en refusant d'assembler le chroot.

Le résolveur a été interrogé sur toute la fermeture, plutôt que de corriger un
nom et de réessayer : pacman s'arrête au premier manque, donc le compte importe
plus que la première ligne. Un seul trou, puis 148 paquets résolus avec --nodeps
éteint, contre 101 avant l'ajout des outils.

Assisted-by: Claude Opus 5
2026-08-21 00:33:07 -04:00
390f3fabda [ADD] the seven tools stage 2 asked for by name
Six built on the host. libxslt will not: its configure demands libxml2 2.15.1
and Ubuntu ships 2.14.5. Ours is 2.15.3 and exists only inside the stage-2
chroot, so that chroot is the only place libxslt can come from -- built BY
stage 2 rather than installed into it, and hoisted so it precedes shadow, which
died on `xsltproc is missing`.

That is a shape worth naming: a package the host cannot build because the host
is behind. Not contamination and not a closure gap -- the wrong machine.

Three more tools were asked for and refused: po4a for fakeroot, a2x for
ca-certificates, asciidoctor for cryptsetup. Perl, Python and Ruby respectively,
each to render a man page. Hooks instead.

--- FR ---

Six se bâtissent sur l'hôte. Pas libxslt : son configure exige libxml2 2.15.1 et
Ubuntu livre 2.14.5. Le nôtre est en 2.15.3 et n'existe que dans le chroot de
l'étage 2 : ce chroot est donc le seul endroit d'où libxslt puisse venir — bâti
PAR l'étage 2 plutôt qu'installé dedans, et hissé pour précéder shadow, mort sur
`xsltproc is missing`.

La forme mérite un nom : un paquet que l'hôte ne peut pas bâtir parce que l'hôte
est en retard. Ni contamination ni trou de fermeture — la mauvaise machine.

Trois autres outils réclamés et refusés : po4a pour fakeroot, a2x pour
ca-certificates, asciidoctor pour cryptsetup. Perl, Python et Ruby
respectivement, chacun pour rendre une page de manuel. Des hooks à la place.

Assisted-by: Claude Opus 5
2026-08-21 00:29:40 -04:00
ef143aac67 [FIX] s390x: say which machine this port targets
zlib stopped on '__builtin_s390_vec_unpackl' requires '-mvx', after its own
configure had detected vector support and defined -DHAVE_S390X_VX. Both halves
were right, so it was worth measuring rather than patching:

  host gcc (Ubuntu)  --with-arch=z13 --with-tune=z16   default -march=arch11
  our  gcc           nothing                           default -march=arch5

arch5 is z900, from 2000. Arch's PKGBUILD names no s390x baseline because Arch
has no s390x, so ours fell back to the oldest machine imaginable while zlib went
on detecting a CPU the compiler had been told to forget. Every distribution
picks one of these; this port had never said which, and silence chose 2000.

z13 is what Ubuntu s390x already requires, so nothing that runs today stops.
Set in two places: makepkg.conf, how this distribution is compiled, and gcc's
--with-arch, what the compiler we ship assumes with no flags at all.

--- FR ---

zlib s'est arrêté sur '__builtin_s390_vec_unpackl' requires '-mvx', après que
son propre configure avait détecté le support vectoriel et défini
-DHAVE_S390X_VX. Les deux moitiés avaient raison : il fallait mesurer, pas
rustiner.

  gcc de l'hôte  --with-arch=z13 --with-tune=z16   défaut -march=arch11
  notre gcc      rien                              défaut -march=arch5

arch5, c'est z900, de l'an 2000. Le PKGBUILD d'Arch ne nomme aucune base s390x
puisque Arch n'a pas de s390x : le nôtre retombait sur la machine la plus
ancienne imaginable pendant que zlib détectait un processeur qu'on avait dit au
compilateur d'oublier. Toute distribution en choisit une ; ce portage ne l'avait
jamais dit, et le silence a choisi 2000.

z13 est ce qu'Ubuntu s390x exige déjà : rien qui tourne aujourd'hui ne cesse de
tourner. Posé aux deux endroits : makepkg.conf, comment cette distribution est
compilée, et le --with-arch de gcc, ce que suppose le compilateur qu'on livre.

Assisted-by: Claude Opus 5
2026-08-21 00:23:26 -04:00
eafa3355aa [FIX] stage 2: the build tools the host was providing silently
glibc rebuilt inside the chroot; binutils died on `make info-recursive`, and
makeinfo said why: its XS module was compiled against the host's perl 5.40 and
our perl is 5.42. Stage-1 texinfo cannot run in there. Everything that builds
.info documentation needs it, and it sat near the end of the list because that
is where its own dependencies are, so stage 2 hoists it.

linux-api-headers stopped earlier on `rsync: command not found` -- the kernel's
headers_install runs it. apt had put it there and a build that finds its tool
says nothing, so stage 1 never mentioned it. Its man pages come from Markdown
the git tag does not pre-render, hence --disable-md2man.

An inventory of the 110 apt packages against the chroot found 84 more such
binaries. Most are documentation; the rest wait until something needs them.

--- FR ---

glibc a été rebâti dans le chroot ; binutils est mort sur `make info-recursive`,
et makeinfo en donne la raison : son module XS a été compilé contre le perl 5.40
de l'hôte, or le nôtre est en 5.42. Le texinfo de l'étage 1 ne peut pas tourner
là-dedans. Tout ce qui bâtit de la documentation .info en dépend, et il figurait
en fin de liste, là où sont ses propres dépendances : l'étage 2 le remonte.

linux-api-headers s'était arrêté avant sur `rsync: command not found` — le
headers_install du noyau l'appelle. apt l'avait posé, et une construction qui
trouve son outil n'en dit rien : l'étage 1 ne l'a jamais mentionné. Ses pages de
manuel viennent d'un Markdown que l'étiquette git ne rend pas, d'où
--disable-md2man.

Un inventaire des 110 paquets apt face au chroot a trouvé 84 autres binaires du
même genre. La plupart sont de la documentation ; le reste attendra qu'un paquet
les réclame.

Assisted-by: Claude Opus 5
2026-08-20 01:34:41 -04:00
dd202a3a54 [IMP] stage 2: resumable, guarded, driven by the shared list
Stage 2 could rebuild one named package. It could not rebuild a hundred and
thirty-three, for reasons that were all about the driver.

The order is not re-derived: it is the closure stage 1 arrived at over four
rounds of resolver output and then confirmed by 179 builds, so it moves to
packages.sh and both stages read it. make_rootfs wipes the chroot every run,
which is what makes it reproducible and what made stage 2 unresumable --
stage2.state outlived the packages it named. Its output is now put back.

The stall guard is shared rather than copied: stage 2 needs it more, since a
test suite is the likeliest thing in a build to wait forever. Build trees are
removed after a package installs, never after it fails.

--- FR ---

L'étage 2 savait rebâtir un paquet nommé. Il ne savait pas en rebâtir cent
trente-trois, pour des raisons qui tenaient toutes au pilote.

L'ordre n'est pas réinventé : c'est la fermeture obtenue à l'étage 1 en quatre
tours de sortie du résolveur, puis confirmée par 179 constructions. Il passe
donc dans packages.sh, que les deux étages lisent. make_rootfs efface le chroot
à chaque passage — ce qui le rend reproductible et rendait l'étage 2
irreprenable, stage2.state survivant aux paquets qu'il nommait. Sa production y
est désormais réinstallée.

La garde d'immobilité est partagée plutôt que recopiée : l'étage 2 en a plus
besoin, une suite de tests étant ce qui attend le plus volontiers pour
toujours. Les arbres de compilation sont effacés après installation, jamais
après un échec.

Assisted-by: Claude Opus 5
2026-08-20 01:16:49 -04:00
f6199bdb16 [FIX] stage 2: produce its first package
Stage 2 could build and could not deliver. Four obstacles, all in the tail of
package(), after a compile that had already succeeded.

Our meson ships arch-meson and it passes --auto-features enabled, so each of
the nine documentation tools this chroot lacks was a hard error, not a skipped
feature. A wrapper appends --auto-features auto; meson honours the last one.
Building those tools was the alternative and it is not close -- doxygen alone
wants clang, fmt, spdlog, llvm-libs.

Then bsdtar would not start: stage-1 libxml2 asks for the host's
libicuuc.so.76, our icu ships 78, and bsdtar is what writes the package. The
package that would fix it was the one being built. libarchive only links
libxml2 for xar, which nothing here reads, so stage 1 drops it.

Verified: libxml2 rebuilt against libicuuc.so.78, provides libxml2.so=16-64,
zero multiarch paths.

--- FR ---

L'étage 2 savait bâtir et ne savait pas livrer. Quatre obstacles, tous dans la
queue de package(), après une compilation déjà réussie.

Notre meson livre arch-meson, qui passe --auto-features enabled : chacun des
neuf outils de documentation absents de ce chroot devenait une erreur franche
au lieu d'une option écartée. Une enveloppe ajoute --auto-features auto, meson
retenant la dernière occurrence. Bâtir ces outils était l'autre voie et l'écart
est net -- doxygen seul réclame clang, fmt, spdlog, llvm-libs.

Puis bsdtar ne démarrait plus : le libxml2 de l'étage 1 réclame le
libicuuc.so.76 de l'hôte, notre icu livre le 78, et bsdtar est ce qui écrit le
paquet. Le paquet qui corrigeait cela était celui qu'on bâtissait. libarchive
ne lie libxml2 que pour xar, que rien ici ne lit : l'étage 1 l'abandonne.

Vérifié : libxml2 rebâti sur libicuuc.so.78, fournit libxml2.so=16-64, aucun
chemin multiarch.

Assisted-by: Claude Opus 5
2026-08-20 01:12:14 -04:00
4d8856b82d [FIX] stage 2: x86_64 compiler flags, and a .pc naming nothing
Two failures three packages apart, both from the same place: our pacman
package ships Arch's configuration verbatim, and Arch's is for x86_64.

  libxml2/meson.build:1:0: ERROR: Unable to detect linker for compiler
  `cc ... -march=x86-64 -mtune=generic ...`

configure_chroot fixed CARCH, CHOST, MAKEFLAGS and OPTIONS and left CFLAGS
alone. They are emptied rather than translated, because that is what stage 1
used -- the host's makepkg.conf has no CFLAGS line at all -- and 179 working
packages are the evidence. s390x tuning is a deliberate later choice.

Emptied by APPENDING, not commenting. The first attempt put a # in front of
each assignment, and CFLAGS spans several lines: commenting the first left the
continuations active and the quote unbalanced, so makepkg would not start.

Then readline. Its .pc says `Requires.private: termcap` and this repository
ships tinfo.pc; nothing provides termcap.pc. Nothing failed at build time --
a .pc is data, and pkg-config only follows Requires.private when a consumer
asks. The first consumer to ask was libxml2, in the chroot, one stage and
three packages from the cause.

--- FR ---

Deux échecs à trois paquets d'écart, de la même origine : notre paquet pacman
livre la configuration d'Arch telle quelle, et celle d'Arch vise x86_64.

  libxml2/meson.build:1:0: ERROR: Unable to detect linker for compiler
  `cc ... -march=x86-64 -mtune=generic ...`

configure_chroot corrigeait CARCH, CHOST, MAKEFLAGS et OPTIONS, et laissait
CFLAGS. Ils sont vidés plutôt que traduits, car c'est ce qu'a utilisé l'étage
1 — le makepkg.conf de l'hôte n'a aucune ligne CFLAGS — et 179 paquets
fonctionnels en sont la preuve. Le réglage pour s390x est un choix ultérieur
délibéré.

Vidés par AJOUT, non par commentaire. La première tentative mettait un # devant
chaque affectation, et CFLAGS s'étend sur plusieurs lignes : commenter la
première laissait les continuations actives et le guillemet déséquilibré, si
bien que makepkg ne démarrait plus.

Puis readline. Son .pc dit « Requires.private: termcap » alors que ce dépôt
livre tinfo.pc ; personne ne fournit termcap.pc. Rien n'échouait à la
compilation — un .pc est une donnée, et pkg-config ne suit Requires.private
que si un consommateur le demande. Le premier à demander fut libxml2, dans le
chroot, à une étape et trois paquets de la cause.

Assisted-by: Claude Opus 5
2026-08-19 20:19:37 -04:00
adc508d8f6 [FIX] python packages: /usr/local, once, for all of them
Debian patches sysconfig to prefer the "posix_local" scheme, so every
wheel-installing PKGBUILD ships /usr/local -- a tree Arch reserves and whose
every path the filesystem package owns. pacman refuses the transaction:

  error: failed to commit transaction (conflicting files)
  /usr/local/share/man exists in both 'filesystem' and 'meson'

Four packages hit it. Two were dropped for other reasons. Rather than write a
third and fourth relocation hook, build_package now exports
DEB_PYTHON_INSTALL_LAYOUT=deb_system, which moves the default from
/usr/local/lib/python3.13/dist-packages to /usr/lib/python3/dist-packages and
scripts from /usr/local/bin to /usr/bin. One line, every python package,
including ones not built yet.

It does not finish the job: deb_system still says dist-packages and Arch reads
site-packages. That move stays per-package, because only modules actually
imported on the target need it. meson does, and its hook does it -- targeting
the version of the python PACKAGE in repo/s390x, not the interpreter running
the build. Installed under the host's 3.13, /usr/bin/meson runs in the chroot
and then says ModuleNotFoundError, because the chroot's python is our 3.14 and
never looks in 3.13.

--- FR ---

Debian modifie sysconfig pour préférer le schéma « posix_local » : tout
PKGBUILD installant une roue livre donc /usr/local, arbre qu'Arch réserve et
dont le paquet filesystem possède chaque chemin. pacman refuse la transaction :

  error: failed to commit transaction (conflicting files)
  /usr/local/share/man exists in both 'filesystem' et 'meson'

Quatre paquets l'ont rencontré. Deux ont été écartés pour d'autres raisons.
Plutôt que d'écrire un troisième et un quatrième crochet de relocalisation,
build_package exporte désormais DEB_PYTHON_INSTALL_LAYOUT=deb_system, qui
déplace le défaut de /usr/local/lib/python3.13/dist-packages vers
/usr/lib/python3/dist-packages, et les scripts de /usr/local/bin vers /usr/bin.
Une ligne, tous les paquets python, y compris ceux pas encore bâtis.

Cela ne termine pas le travail : deb_system dit encore dist-packages quand
Arch lit site-packages. Ce déplacement reste par paquet, car seuls les modules
réellement importés sur la cible en ont besoin. meson en fait partie, et son
crochet le fait — en visant la version du PAQUET python de repo/s390x, non
l'interpréteur qui exécute la compilation. Installé sous le 3.13 de l'hôte,
/usr/bin/meson démarre dans le chroot puis annonce ModuleNotFoundError, le
python du chroot étant notre 3.14, qui ne regarde jamais dans 3.13.

Assisted-by: Claude Opus 5
2026-08-19 20:19:37 -04:00
c78c263312 [ADD] the build systems stage 2 rebuilds with
Ten of the 159 PKGBUILDs call arch-meson and four call cmake, so a chroot
without them could rebuild most of the repository and then stop. Neither is a
dependency of anything in the repository, which is why no closure round ever
named them -- the same shape as fakeroot and bison, one layer up.

python returns with meson, and that is not the earlier decision being
reversed. Dropping python at stage 1 was about what the REPOSITORY must
supply: it was wanted only by two wheels Arch's own python could not load.
This is about what the BUILD ENVIRONMENT must contain, and meson is written in
Python. Different question, different answer.

python needed two fixes of its own. Its xvfb loop is in build() AND in
check(); stage 1 never ran the second because of --nocheck, but stage 2 drops
--nocheck on purpose, so it would have spun there too. And Python 3.13 removed
the vendored libmpdec, so --with-system-libmpdec is the only way to build and
needs the host headers -- reported nine hundred lines in as a missing make
rule for a file that used to be vendored.

cmake wanted rhash, then jsoncpp, then cppdap, one at a time. That is a queue,
and the rule in install_host_deps says a queue is a feature to disable. Not
applied here, deliberately: each exists as an Ubuntu package, so the queue
ends. The rule is for queues that do not, like dbus reaching a documentation
tool that needed Qt. Its Qt GUI is dropped -- a dialog box on a headless
mainframe.

--- FR ---

Dix des 159 PKGBUILD appellent arch-meson et quatre appellent cmake : un
chroot sans eux pourrait reconstruire l'essentiel du dépôt puis s'arrêter.
Aucun des deux n'est une dépendance de quoi que ce soit dans le dépôt, ce qui
explique qu'aucun tour de fermeture ne les ait nommés — même forme que
fakeroot et bison, une couche plus haut.

python revient avec meson, et ce n'est pas un revirement. L'écarter à l'étage
1 portait sur ce que le DÉPÔT doit fournir : il n'était voulu que par deux
roues que le python d'Arch ne pouvait pas charger. Ici il s'agit de ce que
l'ENVIRONNEMENT DE BUILD doit contenir, et meson est écrit en Python. Autre
question, autre réponse.

python a demandé deux correctifs propres. Sa boucle xvfb est dans build() ET
dans check() ; l'étage 1 n'a jamais exécuté la seconde grâce à --nocheck, mais
l'étage 2 l'abandonne exprès — elle y aurait tourné aussi. Et Python 3.13 a
retiré le libmpdec embarqué : --with-system-libmpdec est la seule voie et
réclame les en-têtes de l'hôte, signalé neuf cents lignes plus loin comme une
règle make manquante pour un fichier autrefois embarqué.

cmake a réclamé rhash, puis jsoncpp, puis cppdap, un par un. C'est une file, et
la règle d'install_host_deps dit qu'une file est une fonctionnalité à
désactiver. Non appliquée ici, délibérément : chacun existe en paquet Ubuntu,
donc la file se termine. La règle vise celles qui ne terminent pas, comme dbus
atteignant un outil de documentation qui exigeait Qt. Son interface Qt est
retirée — une boîte de dialogue sur un mainframe sans écran.

Assisted-by: Claude Opus 5
2026-08-19 19:53:46 -04:00
92c5850e53 [FIX] driver: kill a build that has stopped producing output
python's PKGBUILD hunts for a free X display before running make:

  export servernum=99
  while ! xvfb-run -a -n "$servernum" /bin/true 2>/dev/null; do
      servernum=$((servernum+1)); done

xvfb-run is not installed here. It exits 127, `!` inverts that to true, and
the loop tries the next number. There is no exit condition for "the command
does not exist", and the 2>/dev/null swallows the one line that would have
said so.

Measured before anyone noticed: ten hours of wall clock, four million PIDs,
and a log frozen at `configure: creating Makefile`. Every signal said the
build was healthy -- alive, 40% CPU, sitting in a plausible source directory.
A process list cannot tell work from spinning. A stalled log next to a live
makepkg can, and that is all this guard does.

Forty-five minutes of COMPLETE silence, because real builds do go quiet: a
long link, a test suite that prints nothing, gcc between bootstrap stages.
The case this was written for ran two hundred times longer. EL_STALL_MIN=0
disables it.

Tested against a real spin loop: detected, whole process tree killed, rc=2 so
STALL reads differently from FAIL in the summary.

--- FR ---

Le PKGBUILD de python cherche un numéro d'affichage X libre avant make :

  export servernum=99
  while ! xvfb-run -a -n "$servernum" /bin/true 2>/dev/null; do
      servernum=$((servernum+1)); done

xvfb-run n'est pas installé ici. Il sort en 127, le `!` inverse, et la boucle
essaie le numéro suivant. Il n'existe aucune condition de sortie pour « la
commande n'existe pas », et le 2>/dev/null avale la seule ligne qui l'aurait
dit.

Mesuré avant que quiconque s'en aperçoive : dix heures d'horloge, quatre
millions de PID, un journal figé à `configure: creating Makefile`. Tous les
signaux disaient que le build allait bien — vivant, 40 % de CPU, dans un
répertoire source plausible. Une liste de processus ne distingue pas le
travail du sur-place. Un journal figé à côté d'un makepkg vivant, si — et
c'est tout ce que fait ce garde-fou.

Quarante-cinq minutes de silence COMPLET, car de vrais builds se taisent :
une longue édition de liens, une suite de tests muette, gcc entre deux étages.
Le cas qui l'a motivé a tourné deux cents fois plus longtemps. EL_STALL_MIN=0
le désactive.

Éprouvé contre une vraie boucle infinie : détecté, arbre de processus tué en
entier, rc=2 pour que STALL se lise autrement que FAIL dans le bilan.

Assisted-by: Claude Opus 5
2026-08-19 19:53:46 -04:00
d5b8158e55 [ADD] stage 2: the rebuild loop, and git to feed it
The loop applies each hook on the HOST -- /build is the same directory from
both sides, so makepkg in the chroot reads the patched PKGBUILD and nothing is
duplicated inside. It drops --nocheck: stage 1 skipped the test suites because
they ran against the host's libraries, and here they test what was built.
Each rebuilt package is installed into the chroot before the next one, with
the host's pacman and --root, because the chroot's own pacman will not start
until stage 2 has rebuilt it.

Two hooks must NOT run there, and both for the same satisfying reason: the
condition they work around does not exist in the chroot. libgcrypt.sh points
at a host prefix holding our libgpg-error, which the chroot has installed
properly. git.sh drops ZLIB_NG=1 because Ubuntu ships no zlib-ng headers,
while our own zlib-ng package ships them.

git is here because STAGE 2 needs it, not the repository: 51 of the 159
PKGBUILDs take their sources from git+https, and makepkg validates that clone
even under --noextract. Nothing depends on git. Its three -- perl-error,
perl-mailtools with perl-timedate, zlib-ng -- were read from the depends array
rather than from my own tool, which had reported `zsh` as a dependency of git.
It is not; the tool's regex was catching a neighbouring array.

--- FR ---

La boucle applique chaque crochet sur l'HÔTE — /build est le même répertoire
des deux côtés, donc makepkg dans le chroot lit le PKGBUILD corrigé et rien
n'est dupliqué dedans. Elle abandonne --nocheck : l'étage 1 sautait les suites
de tests parce qu'elles s'exécutaient contre les bibliothèques de l'hôte ; ici
elles éprouvent ce qui a été bâti. Chaque paquet reconstruit est installé dans
le chroot avant le suivant, avec le pacman de l'hôte et --root, celui du
chroot ne démarrant pas avant que l'étage 2 ne l'ait reconstruit.

Deux crochets ne doivent PAS y tourner, et pour la même raison satisfaisante :
la condition qu'ils contournent n'existe pas dans le chroot. libgcrypt.sh
pointe sur un préfixe hôte contenant notre libgpg-error, que le chroot a
installé correctement. git.sh retire ZLIB_NG=1 parce qu'Ubuntu ne livre pas
les en-têtes zlib-ng, alors que notre propre paquet zlib-ng les livre.

git est là parce que l'ÉTAGE 2 en a besoin, pas le dépôt : 51 des 159
PKGBUILD prennent leurs sources en git+https, et makepkg valide ce clone même
sous --noextract. Rien ne dépend de git. Ses trois dépendances — perl-error,
perl-mailtools avec perl-timedate, zlib-ng — ont été lues dans le tableau
depends plutôt que dans mon propre outil, qui annonçait `zsh` comme dépendance
de git. Elle ne l'est pas : la regex de l'outil attrapait un tableau voisin.

Assisted-by: Claude Opus 5
2026-08-19 08:56:41 -04:00
94c74eb691 [ADD] stage 2: a chroot that builds
Stage 1 is done and its output is provably wrong in nine places: binaries
asking for libgpgme.so.11, libnettle.so.8, libicuuc.so.76 and six more at the
HOST's soname versions. Stage 2 dissolves all nine by rebuilding each package
against what the repository actually ships.

The constraint that shapes it: pacman cannot run inside the stage-1 rootfs,
because libalpm was linked against the host's gpgme. So the rootfs is
populated from OUTSIDE, with the host's pacman and --root, and the chroot is
used only to build. That is not a workaround, it is the order the problem has
-- stage 2's own output is the first pacman that will run on the target.

Five packages were added to stage 1 for this, and the resolver could never
have named them: nothing DEPENDS on fakeroot or bison, they are simply what a
build needs to happen. makepkg refuses to run as root, so the chroot carries a
user with the host's uid -- a bind mount keeps the numeric owner, and a
different uid inside could not write $srcdir.

The smoke test separates hard failures from known drift. makeinfo fails
because texinfo was built against the host's perl; that is what stage 2
repairs, so refusing to start over it would refuse to run the fix.

--- FR ---

L'étage 1 est terminé et sa sortie est démontrablement fausse en neuf points :
des binaires réclamant libgpgme.so.11, libnettle.so.8, libicuuc.so.76 et six
autres, aux versions de soname de l'HÔTE. L'étage 2 les dissout tous les neuf
en reconstruisant chaque paquet contre ce que le dépôt livre réellement.

La contrainte qui le façonne : pacman ne peut pas tourner dans le rootfs
d'étage 1, libalpm ayant été lié contre le gpgme de l'hôte. Le rootfs est donc
peuplé depuis l'EXTÉRIEUR, avec le pacman de l'hôte et --root, et le chroot ne
sert qu'à bâtir. Ce n'est pas un contournement mais l'ordre qu'a le problème :
la sortie de l'étage 2 est le premier pacman qui tournera sur la cible.

Cinq paquets ont rejoint l'étage 1 pour cela, et le résolveur n'aurait jamais
pu les nommer : rien ne DÉPEND de fakeroot ni de bison, ils sont simplement ce
qu'il faut pour qu'une compilation ait lieu. makepkg refuse de tourner en
root, le chroot porte donc un utilisateur avec l'uid de l'hôte — un bind mount
conserve le propriétaire numérique, et un uid différent ne pourrait pas
écrire $srcdir.

Le smoke test sépare les échecs durs des dérives connues. makeinfo échoue
parce que texinfo a été bâti contre le perl de l'hôte ; c'est précisément ce
que l'étage 2 répare, donc refuser de démarrer pour cela serait refuser de
lancer le correctif.

Assisted-by: Claude Opus 5
2026-08-19 08:30:49 -04:00
9a6c11e2ab [FIX] makepkg.conf: .la files in a third of the repository
This host was configured with `libtool staticlibs`, which KEEPS what Arch
strips. Fifty-six of a hundred and fifty-nine packages carried .la files, and
nothing reported it until two sub-packages of one source both claimed the
same file:

  /usr/lib/libcrypt.la exists in both 'libxcrypt' and 'libxcrypt-compat'

That is what .la files are for -- they record a link line -- so a split
library produces two descriptions of itself. Arch removes them because
nothing in a modern toolchain reads them and the paths they record are wrong
as soon as a package moves. OPTIONS now matches Arch, minus debug and lto:
debug wants a source-package pipeline this bootstrap has no use for, and lto
doubles a stage whose output stage 2 discards.

libnspr4-dev and mercurial came with nss. The nspr distinction is worth
keeping: the host had NOTHING, which the ordinary stage-1 rule fixes with a
host package. libgcrypt needed a hook because the host had something too OLD
-- 1.51 against a floor of 1.56 -- and no installation fixes that.

--- FR ---

Cet hôte était configuré avec `libtool staticlibs`, qui CONSERVE ce qu'Arch
retire. Cinquante-six paquets sur cent cinquante-neuf portaient des fichiers
.la, et rien ne l'avait signalé jusqu'à ce que deux sous-paquets d'une même
source réclament le même fichier :

  /usr/lib/libcrypt.la exists in both 'libxcrypt' et 'libxcrypt-compat'

C'est à cela que servent les .la — ils consignent une ligne de liaison — donc
une bibliothèque scindée produit deux descriptions d'elle-même. Arch les
supprime parce que rien dans une chaîne moderne ne les lit et que les chemins
qu'ils consignent sont faux dès qu'un paquet se déplace. OPTIONS suit
désormais Arch, sauf debug et lto : debug réclame un pipeline de paquets
sources dont cet amorçage n'a que faire, et lto double une étape dont l'étage
2 jette la sortie.

libnspr4-dev et mercurial sont venus avec nss. La distinction sur nspr mérite
d'être gardée : l'hôte n'avait RIEN, ce que la règle ordinaire de l'étage 1
corrige par un paquet hôte. libgcrypt a exigé un crochet parce que l'hôte
avait quelque chose de trop VIEUX — 1.51 contre un plancher de 1.56 — et
qu'aucune installation ne corrige cela.

Assisted-by: Claude Opus 5
2026-08-19 08:30:49 -04:00
7ec3d14851 [FIX] test-chroot: an audit that classifies, and one that was wrong
The soname check reported seventeen missing libraries. Four were false: it
built its "shipped" set from DT_SONAME alone, and tcl's libtcl9.0.so records
no SONAME at all. The file sits in usr/lib and ld.so resolves it by name, so
the check was calling a correct package broken -- which would have sent the
next reader hunting for a packaging bug that does not exist. Shipped filenames
and symlinks now count as supplied.

The remaining twelve needed separating, because an unclassified list is only
alarming. A missing soname whose library exists at a DIFFERENT version is
stage 1 working as designed: it linked the host, and stage 2 dissolves it. A
missing soname with no provider at any version is a closure gap. Nine and
three.

The three are each understood: pylibmount's cp313 extension (inert, no python
is shipped), makedb's libselinux (deliberate), and libtcl8.6 against our tcl
9.0 -- which is the honest cost of a decision made an hour before on closure
size without looking at ABI.

TODO.md records all twelve, with the note that stage 2 must install using the
host's pacman, because pacman inside the stage-1 rootfs cannot start.

--- FR ---

La vérification de sonames annonçait dix-sept bibliothèques manquantes. Quatre
étaient fausses : elle construisait son ensemble « livré » à partir du seul
DT_SONAME, et le libtcl9.0.so de tcl n'enregistre aucun SONAME. Le fichier est
dans usr/lib et ld.so le résout par son nom : la vérification déclarait donc
défectueux un paquet correct — de quoi envoyer le lecteur suivant chasser un
défaut d'empaquetage inexistant. Les noms de fichiers et les liens livrés
comptent désormais comme fournis.

Les douze restants demandaient d'être séparés, une liste non classée n'étant
qu'alarmante. Un soname manquant dont la bibliothèque existe à une AUTRE
version, c'est l'étage 1 fonctionnant comme prévu : il a lié l'hôte, et
l'étage 2 le dissout. Un soname sans aucun fournisseur est un trou de
fermeture. Neuf et trois.

Les trois sont compris : l'extension cp313 de pylibmount (inerte, aucun python
livré), le libselinux de makedb (délibéré), et libtcl8.6 contre notre tcl 9.0
— coût honnête d'une décision prise une heure plus tôt sur la taille de la
fermeture, sans regarder l'ABI.

TODO.md consigne les douze, avec la note que l'étage 2 devra installer avec le
pacman de l'hôte, celui du rootfs d'étage 1 ne pouvant pas démarrer.

Assisted-by: Claude Opus 5
2026-08-19 06:40:50 -04:00
6f44fdb572 [FIX] lvm2: only device-mapper, and the closure closes
The fourth closure round asked for libaio and thin-provisioning-tools, both
wanted by lvm2 alone. thin-provisioning-tools is Rust now, so a language
runtime was arriving through a fourth-order dependency.

Nothing in the repository wants `lvm2`. Checked across every .PKGINFO:
cryptsetup wants device-mapper, and device-mapper is the other package this
same source produces. Dropping the sub-package removed both entries and the
Rust question with them.

The measurement is the argument, and it went the other way an hour earlier.
tcl, unixodbc and libmicrohttpd each cost zero new packages, so building them
beat dropping sub-packages. This one costs a language runtime, so dropping
wins. The rule is not to prefer either -- it is to measure each time.

device-mapper had also picked up the host's libselinux, silently. Seventh
package to do it, second found by reading binaries rather than by a build.

35 packages, then 19, then 2, then 0.

--- FR ---

Le quatrième tour de fermeture réclamait libaio et thin-provisioning-tools,
tous deux voulus par lvm2 seul. thin-provisioning-tools est en Rust
désormais : un environnement de langage arrivait par une dépendance de
quatrième ordre.

Rien dans le dépôt ne veut `lvm2`. Vérifié sur chaque .PKGINFO : cryptsetup
veut device-mapper, et device-mapper est l'autre paquet que cette même source
produit. Écarter le sous-paquet a retiré les deux entrées et la question Rust
avec elles.

La mesure est l'argument, et elle avait tranché dans l'autre sens une heure
plus tôt. tcl, unixodbc et libmicrohttpd coûtaient zéro paquet nouveau : les
bâtir valait mieux qu'écarter des sous-paquets. Celui-ci coûte un
environnement de langage : écarter gagne. La règle n'est de préférer ni l'un
ni l'autre — c'est de mesurer chaque fois.

device-mapper avait aussi ramassé le libselinux de l'hôte, en silence.
Septième paquet à le faire, deuxième trouvé en lisant des binaires plutôt que
par une compilation.

35 paquets, puis 19, puis 2, puis 0.

Assisted-by: Claude Opus 5
2026-08-19 06:40:50 -04:00
82a231e5f9 [ADD] test-chroot: read the binaries, not the declarations
The resolver reported satisfied. The rootfs installed 102 packages. bash,
coreutils, tar, sed and find all ran inside the chroot, on s390x, against
glibc 2.44. Every check passed.

Then a fourth check, reading ELF headers instead of metadata, found seventeen
libraries that some shipped binary asks for and no shipped package provides.
Most are the ordinary stage-1 artefact -- the host's soname where Arch's
differs, libgpgme.so.11 against our .45 -- and stage 2 dissolves those by
rebuilding inside the chroot.

One was not. kbd's loadkeys needed libxkbcommon.so.0 while kbd declared
glibc, gzip and pam. An UNDER-DECLARED dependency: invisible to pacman's
resolver and to this port's own closure computation, because both read
declarations. And the cause was mine -- libxkbcommon-dev went into
install_host_deps for systemd, and kbd, built later, probed for it and linked
it. Arch declares it nowhere, so its chroot fails the same probe;
--disable-xkb converges rather than diverges.

Also here: the test now uses its own package cache. The shared one held a
coreutils from before its selinux fix, at the same pkgver-pkgrel, and pacman
called it corrupted.

--- FR ---

Le résolveur se déclarait satisfait. Le rootfs installait 102 paquets. bash,
coreutils, tar, sed et find tournaient tous dans le chroot, sur s390x, contre
la glibc 2.44. Toutes les vérifications passaient.

Puis une quatrième, lisant les en-têtes ELF au lieu des métadonnées, a trouvé
dix-sept bibliothèques qu'un binaire livré réclame et qu'aucun paquet livré ne
fournit. La plupart sont l'artefact ordinaire de l'étage 1 — le soname de
l'hôte là où celui d'Arch diffère, libgpgme.so.11 contre notre .45 — et
l'étage 2 les dissout en reconstruisant dans le chroot.

Une ne l'était pas. Le loadkeys de kbd réclamait libxkbcommon.so.0 quand kbd
déclarait glibc, gzip et pam. Une dépendance SOUS-DÉCLARÉE : invisible au
résolveur de pacman comme au calcul de fermeture de ce portage, puisque tous
deux lisent des déclarations. Et la cause était mienne — libxkbcommon-dev est
entré dans install_host_deps pour systemd, et kbd, bâti plus tard, l'a sondé
et lié. Arch ne le déclare nulle part, son chroot échoue donc à la même
sonde ; --disable-xkb converge au lieu de diverger.

Aussi ici : le test utilise désormais son propre cache de paquets. Le cache
partagé gardait un coreutils d'avant son correctif selinux, au même
pkgver-pkgrel, et pacman le déclarait corrompu.

Assisted-by: Claude Opus 5
2026-08-19 06:24:11 -04:00
de7a4c70a2 [ADD] closure rounds two and three, and the CA bundle
Thirty-five packages pulled in nineteen more, and those two. The resolver
named each round as precisely as the first, and it now reports satisfied:
101 packages, --nodeps off.

THE MEASUREMENT THAT CHANGED ITS ANSWER. Four of round two were going to be
avoided by dropping sub-packages -- sqlite-tcl, sqlite-analyzer, the openldap
server, debuginfod -- reasoning that had been right for python-brotli.
Measured instead: tcl, unixodbc and libmicrohttpd each cost ZERO new
packages, because the closure had filled in around them. Building them beats
four hooks, and it does not leave sqlite shipping an sqltclsh that cannot
start. The arithmetic that was right at forty packages was wrong at a hundred
and thirty.

ca-certificates-mozilla is the only entry here that is not a library: it is
the root certificate list itself, and ca-certificates is only the machinery
around it. Without it the target trusts nothing and every HTTPS verification
fails. It has no packaging repo -- nss produces it -- so nss and nspr came
too, measured first: nspr free, nss needing only mercurial on the host, and
hg.mozilla.org answering in 0.3s. 172 certificates shipped.

--- FR ---

Trente-cinq paquets en ont tiré dix-neuf autres, puis ces deux-là. Le
résolveur a nommé chaque tour aussi précisément que le premier, et il se
déclare maintenant satisfait : 101 paquets, --nodeps désactivé.

LA MESURE QUI A CHANGÉ SA RÉPONSE. Quatre paquets du deuxième tour allaient
être évités en écartant des sous-paquets — sqlite-tcl, sqlite-analyzer, le
serveur openldap, debuginfod — par un raisonnement juste pour python-brotli.
Mesuré plutôt que supposé : tcl, unixodbc et libmicrohttpd coûtent ZÉRO
paquet nouveau, la fermeture s'étant refermée autour d'eux. Les bâtir vaut
mieux que quatre crochets, et évite de livrer un sqlite contenant un sqltclsh
incapable de démarrer. L'arithmétique juste à quarante paquets était fausse à
cent trente.

ca-certificates-mozilla est la seule entrée ici qui ne soit pas une
bibliothèque : c'est la liste des certificats racine elle-même, et
ca-certificates n'en est que la mécanique. Sans lui la cible ne fait
confiance à rien et toute vérification HTTPS échoue. Il n'a pas de dépôt de
packaging — nss le produit — donc nss et nspr ont suivi, mesurés d'abord :
nspr gratuit, nss ne réclamant que mercurial sur l'hôte, et hg.mozilla.org
répondant en 0,3 s. 172 certificats livrés.

Assisted-by: Claude Opus 5
2026-08-19 06:24:11 -04:00
1ef18efeec [ADD] the closure pacman's resolver named
Everything in stage 1 until now was added because a BUILD stopped. These were
added because test-chroot.sh ran the resolver with --nodeps OFF -- the check
the whole bootstrap skips -- and it listed exactly what the repository owed.
Nothing here is speculative.

Thirty-five packages, then five rounds of failures that each got further than
the last: 12 failed, then 7, then 4, then 0. The pattern was almost always a
host tool nobody had declared, and the fix for one uncovered the next --
ducktype then yelp-build, ss then lmdb, autoconf-archive then cmocka.

Two corrections worth keeping. libargon2-dev was the wrong package: openldap
passes --with-argon2=libsodium, so the error named argon2 and the answer was
sodium. And apt-cache reported NONE for all eight candidates because this host
answers `Candidat :`, not `Candidate:` -- the same locale trap that had broken
util-linux hours earlier, met again inside the script written to verify its
fix. Query apt under LC_ALL=C.

--- FR ---

Tout ce qui composait l'étage 1 jusqu'ici avait été ajouté parce qu'une
COMPILATION s'arrêtait. Ceux-ci l'ont été parce que test-chroot.sh a lancé le
résolveur avec --nodeps DÉSACTIVÉ — le contrôle que tout l'amorçage saute — et
qu'il a listé exactement ce que le dépôt devait. Rien ici n'est spéculatif.

Trente-cinq paquets, puis cinq tours d'échecs allant chacun plus loin que le
précédent : 12, puis 7, puis 4, puis 0. Le motif était presque toujours un
outil hôte que personne n'avait déclaré, et corriger l'un dévoilait le
suivant — ducktype puis yelp-build, ss puis lmdb, autoconf-archive puis
cmocka.

Deux corrections à garder. libargon2-dev était le mauvais paquet : openldap
passe --with-argon2=libsodium, l'erreur nommait donc argon2 quand la réponse
était sodium. Et apt-cache annonçait NONE pour les huit candidats parce que
cet hôte répond « Candidat : » et non « Candidate: » — le piège de locale même
qui avait cassé util-linux quelques heures plus tôt, retrouvé dans le script
écrit pour en vérifier le correctif. Interroger apt sous LC_ALL=C.

Assisted-by: Claude Opus 5
2026-08-19 05:28:32 -04:00
549b5650f5 [ADD] driver: refuse to build below 8 GiB free
A full disk does not say so. gcc's bootstrap reported

  genattrtab: cannot close file tmp-attrtab.cc: No space left on device

seventeen thousand lines into its log, behind two hundred lines of make
recursion. Everything visible pointed at gcc, and the first grep for
"error:" matched cpp_error() -- a function name in gcc's own source, the
documented trap of grepping a whole file instead of the right part.

df would have said it in one line, before the forty minutes.

This host is shared, and the free margin is not stable, so it is checked per
package rather than assumed once. 8 GiB clears gcc, the largest build here;
smaller packages never trip it. The message names the reclaim command,
because the answer is not obvious either: the src trees are regenerable,
makepkg -C wipes them anyway.

--- FR ---

Un disque plein ne le dit pas. L'amorçage de gcc a signalé

  genattrtab: cannot close file tmp-attrtab.cc: No space left on device

dix-sept mille lignes plus bas dans son journal, derrière deux cents lignes
de récursion make. Tout ce qui était visible accusait gcc, et le premier
grep sur « error: » est tombé sur cpp_error() — un nom de fonction dans les
sources de gcc, précisément le piège documenté qui consiste à grep un
fichier entier plutôt que le bon endroit.

df l'aurait dit en une ligne, avant les quarante minutes.

Cet hôte est partagé et la marge libre n'est pas stable : la vérification se
fait donc par paquet, sans rien supposer. 8 GiB suffisent à gcc, le plus
gros build ici ; les petits paquets ne la déclencheront jamais. Le message
nomme la commande de récupération, car la réponse n'est pas évidente non
plus : les arbres src se régénèrent, makepkg -C les efface de toute façon.

Assisted-by: Claude Opus 5
2026-08-17 03:14:40 -04:00
f7f94f3160 [ADD] test-chroot: the test that proved the port, made repeatable
Sixty-eight successful builds said nothing true about whether the port
worked. One chroot found the missing dynamic linker and the missing
packages in a single run -- and it was done by hand, so "is it still true?"
had no cheap answer.

Three checks, kept apart because they fail for different reasons. RESOLVE
runs pacman's own resolver with --nodeps OFF, the check the whole bootstrap
skips. ARTEFACT greps every package for host contamination that raises no
error. RUN installs for real and executes the binaries -- the only one that
can catch an absent ld.so, since a package whose interpreter is missing
installs perfectly.

It earned itself immediately. ARTEFACT is clean across all 86 packages, and
RESOLVE named the next milestone precisely: about forty packages are still
missing, from pcre2 and libxcrypt to python and perl.

--- FR ---

Soixante-huit compilations réussies n'ont rien dit de vrai sur le
fonctionnement du portage. Un seul chroot a trouvé l'interpréteur dynamique
absent et les paquets manquants — et il avait été fait à la main, si bien
que « est-ce toujours vrai ? » n'avait pas de réponse bon marché.

Trois vérifications, tenues séparées parce qu'elles échouent pour des
raisons différentes. RESOLVE lance le résolveur de pacman avec --nodeps
DÉSACTIVÉ, le contrôle que tout l'amorçage saute. ARTEFACT inspecte chaque
paquet pour les contaminations de l'hôte qui ne lèvent aucune erreur. RUN
installe pour de vrai et exécute les binaires — seul capable de détecter un
ld.so absent, puisqu'un paquet dont l'interpréteur manque s'installe
parfaitement.

Il s'est rentabilisé aussitôt. ARTEFACT est propre sur les 86 paquets, et
RESOLVE a nommé l'étape suivante avec précision : une quarantaine de paquets
manquent encore, de pcre2 et libxcrypt jusqu'à python et perl.

Assisted-by: Claude Opus 5
2026-08-17 02:37:46 -04:00
9a696af012 [FIX] host defaults: apt was deciding what got built
--auto-features enabled turns every auto feature into a requirement, so
installing a tool switches on code that never compiled here. Three of the
fifty host packages did exactly that, and only one failed for the reason
it named.

expat had built clean the day before. asciidoc pulled docbook-utils in as
an automatic dependency; it owns docbook2man, the third name in expat's
find_program list, so docs defaulted ON twenty-nine hours before anything
rebuilt. That tool is the SGML pipeline: on DocBook XML it writes nothing
and still exits 0, and the mv it feeds is what reported the failure. Arch
ships no xmlwf.1 either, so OFF is parity.

systemd's split-bin probes the BUILD host's /usr/sbin. Ubuntu's is a real
directory, so six binaries went where package() does not look -- and
arch-meson's --sbindir is ignored, systemd computes its own.

util-linux needed no option: poman-translate.sh greps po4a for the English
"Discard" and this host answers "Rejet de". The locale belongs to the host,
so it is pinned once on the makepkg line.

--- FR ---

--auto-features enabled fait de chaque fonction « auto » une exigence :
installer un outil active donc du code jamais compilé ici. Trois des
cinquante paquets hôte l'ont fait, et un seul a échoué pour la raison
qu'il annonçait.

expat compilait proprement la veille. asciidoc avait tiré docbook-utils en
dépendance automatique ; il fournit docbook2man, troisième nom de la liste
find_program d'expat, et la doc est passée à ON vingt-neuf heures avant
toute reconstruction. Cet outil est le pipeline SGML : sur du DocBook XML
il n'écrit rien et sort quand même 0, et le mv qu'il alimente est ce qui a
signalé la panne. Arch ne livre pas xmlwf.1 non plus : OFF, c'est la parité.

Le split-bin de systemd sonde le /usr/sbin de la machine de BUILD. Celui
d'Ubuntu est un vrai répertoire, donc six binaires sont partis là où
package() ne regarde pas — et le --sbindir d'arch-meson est ignoré, systemd
calcule le sien.

util-linux n'avait besoin d'aucune option : poman-translate.sh cherche le
« Discard » anglais de po4a, et cet hôte répond « Rejet de ». La locale
appartient à l'hôte : elle est épinglée une fois, sur la ligne makepkg.

Assisted-by: Claude Opus 5
2026-08-17 02:37:46 -04:00
dcaa55b19a [ADD] host deps: the tools six packages needed and nobody declared
--nodeps means every makedepend must be named here by hand. Seven builds
stopped on one, each naming a different tool: itstool, convert, fig2dev,
asciidoctor, libnsl, python -m build, libbpf, history.

The list was also a lie by omission. libreadline-dev, libncurses-dev,
zlib1g-dev, python3-dev, doxygen, xsltproc, docbook-xsl, elinks and
libcap2-bin were on this VM by hand and never declared. They made the
builds pass here and would fail on a fresh Ubuntu, for reasons that have
nothing to do with s390x.

history.pc is generated rather than copied: our own readline package
ships a correct one, but its libdir names /usr/lib, which on a multiarch
host holds no libhistory. The .pc has to describe THIS host.

Three hooks for what apt cannot buy. systemd asks for an EFI arch s390x
does not have -- and says "python3 is missing modules: elftools", which
sends you to apt for four lines before admitting the real reason.

--- FR ---

--nodeps veut dire que chaque makedepend doit être nommé ici à la main.
Sept compilations se sont arrêtées sur l'une d'elles, chacune désignant
un outil différent : itstool, convert, fig2dev, asciidoctor, libnsl,
python -m build, libbpf, history.

La liste mentait aussi par omission. libreadline-dev, libncurses-dev,
zlib1g-dev, python3-dev, doxygen, xsltproc, docbook-xsl, elinks et
libcap2-bin étaient posés à la main sur cette VM, jamais déclarés. Ils
faisaient passer les compilations ici et auraient échoué sur un Ubuntu
neuf, pour des raisons étrangères à s390x.

history.pc est généré et non copié : notre propre paquet readline en
livre un correct, mais son libdir nomme /usr/lib, qui sur un hôte
multiarch ne contient aucun libhistory. Le .pc doit décrire CET hôte-ci.

Trois crochets pour ce qu'apt n'achète pas. systemd réclame une
architecture EFI que s390x n'a pas — et annonce « python3 is missing
modules: elftools », ce qui envoie chez apt pour quatre lignes avant
d'avouer la vraie raison.

Assisted-by: Claude Opus 5
2026-08-17 01:33:41 -04:00
04ee9be5cc [FIX] selinux: the chroot named a host artefact, not a dependency
The previous commit read the chroot's "coreutils needs libselinux.so.1"
as a missing package. It is not one. Arch has no libselinux at all -- the
clone 404s -- and Arch's coreutils declares no selinux dependency,
because its build chroot has no selinux/selinux.h to find.

Ours found one. Ubuntu carries libselinux1-dev, gnulib probes for the
header unconditionally, and the audit names every victim:

  coreutils 13 binaries   findutils find   sed   tar   glibc makedb

--without-selinux per package, which is what Arch gets for free. Arch
ships neither chcon nor runcon either, so this converges with Arch rather
than diverging. tar and find matter most: stage 2 runs makepkg inside
this rootfs, and makepkg calls both.

glibc is deliberately left alone. Nothing runs makedb, and rebuilding
glibc would relink the foundation under sixty-nine other packages; stage
2 does it in a chroot where the header cannot be found.

--- FR ---

Le commit précédent a lu le « coreutils réclame libselinux.so.1 » du
chroot comme un paquet manquant. Ce n'en est pas un. Arch n'a aucun
libselinux — le clone rend un 404 — et son coreutils ne déclare aucune
dépendance selinux, faute de selinux/selinux.h dans son chroot de
construction.

Le nôtre en a trouvé un. Ubuntu embarque libselinux1-dev, gnulib sonde
l'en-tête sans condition, et l'audit nomme chaque victime :

  coreutils 13 binaires   findutils find   sed   tar   glibc makedb

--without-selinux par paquet, ce qu'Arch obtient gratuitement. Arch ne
livre ni chcon ni runcon non plus : on converge donc vers Arch au lieu de
s'en écarter. tar et find sont les plus critiques — l'étage 2 lance
makepkg dans ce rootfs, et makepkg les appelle tous deux.

glibc est laissé tel quel, délibérément. Rien n'exécute makedb, et le
reconstruire relierait la fondation sous soixante-neuf autres paquets ;
l'étage 2 s'en charge dans un chroot où l'en-tête est introuvable.

Assisted-by: Claude Opus 5
2026-08-17 01:33:41 -04:00
ddbf91108f [FIX] libdir: the Debian host hid the libraries from Arch
meson and cmake both ask the HOST where libraries go. On Ubuntu the
answer is lib/s390x-linux-gnu, so five packages already in the repository
ship theirs where Arch's ld.so and pkgconf never look. Measured:

  expat 12  lz4 6  pacman 6  pkgconf 6  zstd 12   entries

Nothing failed. util-linux is where it finally shouted, and even there
the rm was the first victim, not the cause.

pacman is the one that matters: libalpm.so.16 landed where pacman's own
binary cannot load it, and a target installed from that package has no
working package manager left to repair itself with.

arch-meson now states the libdir devtools has no need to state, and is
reinstalled on every run -- editing the copy here changed nothing while
/usr/local/bin held a stale one. Four packages call bare meson or cmake
and get their own hook. util-linux's old hook chased lib64, which never
existed here; it is deleted.

--- FR ---

meson et cmake demandent tous deux à l'HÔTE où vont les bibliothèques.
Sous Ubuntu la réponse est lib/s390x-linux-gnu : cinq paquets déjà dans
le dépôt livrent donc les leurs là où ld.so et pkgconf d'Arch ne
regarderont jamais. Mesuré :

  expat 12  lz4 6  pacman 6  pkgconf 6  zstd 12   entrées

Rien n'a échoué. util-linux est l'endroit où cela a fini par crier, et
même là le rm était la première victime, pas la cause.

pacman est celui qui compte : libalpm.so.16 atterrissait là où le binaire
de pacman ne peut pas la charger, et une cible installée depuis ce paquet
n'a plus de gestionnaire de paquets pour se réparer.

arch-meson énonce désormais le libdir que devtools n'a pas besoin
d'énoncer, et se réinstalle à chaque exécution : éditer la copie du dépôt
ne changeait rien tant que /usr/local/bin en gardait une périmée. Quatre
paquets appellent meson ou cmake nu et reçoivent leur crochet. L'ancien
crochet util-linux poursuivait un lib64 qui n'a jamais existé ici : il
est supprimé.

Assisted-by: Claude Opus 5
2026-08-17 01:33:41 -04:00
ea9f9831cc [FIX] driver: a failed clone rebuilt the package before it
libselinux does not exist in Arch, so its clone 404'd. GitLab answers a
404 by asking for credentials, which is why the log read "could not read
Username" rather than "no such project" -- the first wrong culprit.

The second was mine. Neither the clone nor the cd that follows it was
guarded, so makepkg ran in whatever directory the previous package had
left. It rebuilt util-linux, wrote 119 KB of util-linux output into
log-libselinux.txt, and copied the artefact back into the repository.

The lesson is the repository's oldest one, one level up: an exit code
proves nothing, and neither does a log file's name. Verified by checking
every log against the "==> Making package:" line inside it.

--- FR ---

libselinux n'existe pas dans Arch, son clone a donc rendu un 404. GitLab
répond à un 404 en réclamant des identifiants : d'où le « could not read
Username » du journal, plutôt qu'un « projet inconnu ». Premier faux
coupable.

Le second était de mon fait. Ni le clone ni le cd qui le suit n'étaient
gardés, alors makepkg tournait dans le répertoire laissé par le paquet
précédent. Il a reconstruit util-linux, versé 119 ko de sortie util-linux
dans log-libselinux.txt, puis recopié l'artefact dans le dépôt.

La leçon est la plus ancienne du dépôt, d'un cran plus haut : un code de
sortie ne prouve rien, et le NOM d'un journal non plus. Vérifié en
confrontant chaque journal à la ligne « ==> Making package: » qu'il porte.

Assisted-by: Claude Opus 5
2026-08-17 01:33:41 -04:00
f81d2dc315 [ADD] the packages the chroot named, and grep builds
Arch Linux runs on s390x. Installing the repository into a rootfs and
entering it:

  bash      : 5.3.15(1)-release
  uname -m  : s390x
  glibc     : ldd (GNU libc) 2.44

Two things the test taught that sixty-eight successful builds had not.

bash would not start at all: "chroot: No such file or directory" on a
binary that was plainly there. That is the dynamic linker being absent
-- bash asks for /lib/ld64.so.1, and that path exists only through the
usr-merge symlinks the filesystem package installs. Without it an Arch
rootfs starts nothing.

coreutils then failed on libselinux.so.1. Not a port defect: a genuine
dependency that was never built. pacman had in fact listed all of them
before I passed --nodeps -- brotli, libxml2, pam, systemd,
pacman-mirrorlist, libmakepkg-dropins. The resolver works; the
repository was incomplete.

They are added to stage 1 by name, with the reason recorded. A build
that succeeds proves the compiler accepted the source. Only running the
binary proves the package.

--- FR ---

Arch Linux tourne sur s390x. Le depot installe dans un rootfs, puis on
y entre :

  bash      : 5.3.15(1)-release
  uname -m  : s390x
  glibc     : ldd (GNU libc) 2.44

Deux enseignements que soixante-huit compilations reussies n avaient
pas donnes.

bash ne demarrait pas du tout : « chroot: No such file or directory »
sur un binaire pourtant present. C est l interpreteur dynamique qui
manque -- bash reclame /lib/ld64.so.1, chemin qui n existe que par les
liens usr-merge du paquet filesystem. Sans lui, un rootfs Arch ne lance
rien.

coreutils echouait ensuite sur libselinux.so.1. Non pas un defaut du
portage : une dependance reelle jamais batie. pacman les avait
d ailleurs toutes nommees avant que je passe --nodeps -- brotli,
libxml2, pam, systemd, pacman-mirrorlist, libmakepkg-dropins. Le
resolveur fonctionne ; c est le depot qui etait incomplet.

Elles rejoignent l etage 1, avec la raison consignee. Une compilation
reussie prouve que le compilateur a accepte la source. Seul le binaire
qui s execute prouve le paquet.

Assisted-by: Claude Opus 5
2026-08-17 00:23:36 -04:00
8fe9fc3d9d [FIX] give the build host Arch's group ids
install: invalid group: '11'

The filesystem package creates directories with `install -g 11`, and
GNU coreutils rejects a gid that resolves to nothing. gid 11 is `ftp`
on Arch; Ubuntu leaves it free.

This is the circular corner of any bootstrap: the package that DEFINES
/etc/group needs groups that do not exist yet. The way out is to give
the build host the target's id map, not to work around the check --
rewriting the install call to force a numeric gid would produce
directories owned by a group the target does not have.

Only the ids Arch uses and Ubuntu lacks are created, and only when
missing, so a host that is already correct is left alone. Checked one
by one: of 1, 2, 10, 11, 12 and 50, only 11 was absent.

--- FR ---

  install: invalid group: '11'

Le paquet filesystem cree des repertoires avec « install -g 11 », et
GNU coreutils refuse un gid qui ne resout vers rien. Le gid 11 est
« ftp » sur Arch ; Ubuntu le laisse libre.

C est le coin circulaire de tout amorcage : le paquet qui DEFINIT
/etc/group reclame des groupes qui n existent pas encore. La sortie est
de donner a l hote de compilation la table d identifiants de la cible,
non de contourner la verification -- forcer l interpretation numerique
produirait des repertoires appartenant a un groupe que la cible n a
pas.

Seuls les identifiants qu Arch utilise et qu Ubuntu n a pas sont crees,
et seulement s ils manquent : un hote deja correct n est pas touche.
Verifie un a un : sur 1, 2, 10, 11, 12 et 50, seul le 11 etait absent.

Assisted-by: Claude Opus 5
2026-08-16 22:41:50 -04:00
d72673a3db [FIX] clean the source tree between attempts; strip gcc _pick lines
Four packages -- grep, gnupg, pacman and curl -- were failing on my own
driver, not on the port. makepkg -f rebuilds but does not wipe $srcdir,
so a re-run re-entered the previous attempt's tree:

  patch: ... already exists!  Skipping patch.
  1 out of 1 hunk ignored
  mkdir: build-curl-compat: File exists

makepkg -C now cleans first. Worth stating plainly: those four looked
like port problems for two full rounds, and were not.

gcc: dropping sub-packages from pkgname was not enough. package_gcc()
_picks files out for every front end regardless of what was requested,
and _pick is a mv, so it fails on what was never built:

  mv: cannot stat 'usr/bin/gnat': No such file or directory

The _pick lines for the disabled front ends are removed too.

shadow needed libaudit-dev on the host -- one more makedepend that
--nodeps hides until a build stops on it.

--- FR ---

Quatre paquets -- grep, gnupg, pacman et curl -- echouaient a cause de
mon propre pilote, pas du portage. makepkg -f reconstruit mais ne vide
pas $srcdir, si bien qu une reprise revenait dans l arbre de la
tentative precedente :

  patch: ... already exists!  Skipping patch.
  1 out of 1 hunk ignored
  mkdir: build-curl-compat: File exists

makepkg -C nettoie desormais d abord. A dire clairement : ces quatre-la
ont eu l air de problemes de portage pendant deux tours entiers, et ne
l etaient pas.

gcc : retirer les sous-paquets de pkgname ne suffisait pas.
package_gcc() extrait des fichiers pour chaque frontal quoi qu on ait
demande, et _pick est un mv, donc il echoue sur ce qui n a jamais ete
bati :

  mv: cannot stat 'usr/bin/gnat': No such file or directory

Les lignes _pick des frontaux desactives sont retirees aussi.

shadow reclamait libaudit-dev sur l hote -- un makedepend de plus que
--nodeps masque jusqu a ce qu une compilation s y arrete.

Assisted-by: Claude Opus 5
2026-08-16 02:23:01 -04:00
a61890d83a [FIX] canonical CHOST, and a reachable libassuan source
CHOST was the Debian-style triplet. gcc -dumpmachine reports
s390x-linux-gnu on this host, but config.sub canonicalises that to
s390x-ibm-linux-gnu and GCC builds its tree under the canonical name.

Arch PKGBUILDs assume the canonical form -- theirs is
x86_64-pc-linux-gnu, vendor field present -- so gcc's own PKGBUILD
looked for $CHOST/libstdc++-v3/doc and found nothing:

  make: *** s390x-linux-gnu/libstdc++-v3/doc: No such file or directory

while the build had created s390x-ibm-linux-gnu/libstdc++-v3/doc. This
is a port-wide setting, not a gcc quirk: every PKGBUILD that derives a
path from CHOST was pointing one directory sideways.

libassuan fetches from dev.gnupg.org, which is unreachable from this
build host -- measured HTTP 000, while github.com/gpg/libassuan.git and
gnupg.org/ftp both answer. That is a network fact, not a port problem,
so only the transport changes: the pinned tag is untouched and what
gets built is what Arch specifies.

Host dependencies again: doxygen for xz, libunistring for libpsl,
systemd-dev for util-linux. Every one of them was invisible until a
build stopped on it, because --nodeps means pacman never checks
makedepends.

--- FR ---

CHOST portait le triplet de style Debian. Sur cet hote, gcc
-dumpmachine rend s390x-linux-gnu, mais config.sub le canonise en
s390x-ibm-linux-gnu, et GCC batit son arbre sous le nom canonique.

Les PKGBUILD d Arch supposent la forme canonique -- la leur est
x86_64-pc-linux-gnu, champ constructeur present -- si bien que le
PKGBUILD de gcc cherchait $CHOST/libstdc++-v3/doc sans rien trouver :

  make: *** s390x-linux-gnu/libstdc++-v3/doc: No such file or directory

alors que la compilation avait cree s390x-ibm-linux-gnu/libstdc++-v3/doc.
C est un reglage de portage, pas une bizarrerie de gcc : tout PKGBUILD
derivant un chemin de CHOST visait un repertoire a cote.

libassuan se telecharge depuis dev.gnupg.org, injoignable depuis cet
hote -- mesure : HTTP 000, quand github.com/gpg/libassuan.git et
gnupg.org/ftp repondent tous deux. C est un fait de reseau, pas un
probleme de portage : seul le transport change, l etiquette epinglee
reste intacte et ce qui se batit est ce qu Arch specifie.

Dependances d hote, encore : doxygen pour xz, libunistring pour libpsl,
systemd-dev pour util-linux. Chacune est restee invisible jusqu a ce
qu une compilation s y arrete, parce que --nodeps interdit a pacman de
verifier les makedepends.

Assisted-by: Claude Opus 5
2026-08-16 00:45:50 -04:00
c9cc17bd01 [ADD] devtools stand-ins, and lib32 removal for glibc and gcc
The same x86 assumption keeps returning in a different disguise: a
32-bit multilib split package. libtool had it, and so do glibc and gcc.
On x86_64 multilib means i686 and Arch wants it; on Z it means the
31-bit ESA/390 ABI that GCC is actively removing:

  configure: error: Support for -m31 is deprecated and will be removed.

glibc is the clearest case. The whole compile SUCCEEDS and the glibc
package is written -- then makepkg aborts in package_lib32-glibc() with
"No rule to make target install", because the 32-bit tree was never
configured. The work was done; only the packaging of a tree nobody
built failed.

gcc needed three cuts: front ends written in themselves (Ada, D,
Modula-2, COBOL have no bootstrap compiler here), multilib, and every
lib32 reference including the _pick lines that split out usr/lib32.

arch-meson and arch-cmake are provided as stand-ins. Several PKGBUILDs
call them, they ship in devtools, and devtools is itself an Arch
package -- so during a bootstrap they cannot exist yet and every
PKGBUILD using them stops on "command not found".

A verification lesson, learned twice: grepping a whole PKGBUILD for
"lib32" declares false failures, because the string survives in
comments and in functions that are never called. Only the pkgname array
matters, since that is what makepkg iterates.

--- FR ---

La meme hypothese x86 revient sans cesse sous un autre deguisement : un
paquet scinde multilib 32 bits. libtool l avait, glibc et gcc aussi.
Sur x86_64 le multilib signifie i686 et Arch le veut ; sur Z il designe
l ABI 31 bits ESA/390 que GCC est en train de retirer.

glibc est le cas le plus net. Toute la compilation REUSSIT et le paquet
glibc est ecrit -- puis makepkg abandonne dans package_lib32-glibc()
sur « No rule to make target install », parce que l arbre 32 bits n a
jamais ete configure. Le travail etait fait ; seul l empaquetage d un
arbre que personne n avait bati a echoue.

gcc demandait trois coupes : les frontaux ecrits en eux-memes (Ada, D,
Modula-2 et COBOL n ont ici aucun compilateur d amorcage), le multilib,
et toute reference a lib32 y compris les lignes _pick qui extraient
usr/lib32.

arch-meson et arch-cmake sont fournis en substituts. Plusieurs PKGBUILD
les appellent, ils vivent dans devtools, et devtools est lui-meme un
paquet Arch -- pendant un amorcage ils ne peuvent donc pas exister, et
chaque PKGBUILD qui les utilise s arrete sur « command not found ».

Une lecon de verification, apprise deux fois : chercher « lib32 » dans
tout un PKGBUILD declare de faux echecs, car la chaine survit dans les
commentaires et dans des fonctions jamais appelees. Seul le tableau
pkgname compte, puisque c est lui que makepkg parcourt.

Assisted-by: Claude Opus 5
2026-08-15 21:15:01 -04:00
b29ac89e7a [FIX] trust artefacts, not exit codes; add a PKGBUILD patch mechanism
A run reported "51 built, 0 failed" while glibc, gcc, coreutils and
pacman had all failed. The cause is a bash rule that is easy to forget:
callers invoke build_package inside an "if", and "if" DISABLES set -e
for the whole function body. A failing makepkg fell through to
repo-add, whose success became the function exit status.

build_package now returns explicitly on failure and, more importantly,
verifies that a package file actually exists. An exit code is not
proof; only the artefact is. Measured before the fix: 23 files in the
repository where a hundred were claimed.

First real port patch, applied through a per-package hook rather than
by hand: the glibc PKGBUILD passes --enable-sframe, and s390x has no
SFrame at all -- configure refuses outright. Hooks re-clone cleanly, so
an upstream change is never silently discarded.

--nocheck for stage 1: these test suites run against the HOST
libraries, not the Arch ones, so their verdict says nothing about the
port. acl failed its check step on a sound build. Stage 2 runs them for
real.

The remaining failures were host makedepends that --nodeps hides: po4a,
gnat, debuginfod and jansson are now installed up front.

--- FR ---

Une execution annoncait « 51 built, 0 failed » alors que glibc, gcc,
coreutils et pacman avaient tous echoue. La cause est une regle de bash
qu on oublie : build_package est appelee dans un « if », et « if »
DESACTIVE set -e pour tout le corps de la fonction. Un makepkg en echec
poursuivait jusqu a repo-add, dont la reussite devenait le code de
sortie.

build_package rend desormais la main explicitement en cas d echec et,
surtout, verifie qu un fichier de paquet existe vraiment. Un code de
sortie ne prouve rien ; seul l artefact prouve. Mesure avant
correction : 23 fichiers dans le depot la ou cent etaient annonces.

Premier vrai correctif de portage, applique par crochet et non a la
main : le PKGBUILD de glibc passe --enable-sframe, et s390x n a aucun
SFrame -- configure refuse net. Les crochets survivent a un reclonage,
donc une evolution amont n est jamais perdue en silence.

--nocheck pour l etage 1 : ces suites s executent contre les
bibliotheques de l HOTE, pas celles d Arch, et leur verdict ne dit rien
du portage. acl echouait a son etape check sur une compilation saine.
L etage 2 les executera pour de vrai.

Les autres echecs etaient des makedepends d hote que --nodeps masque :
po4a, gnat, debuginfod et jansson sont poses d entree.

Assisted-by: Claude Opus 5
2026-08-15 20:41:10 -04:00
1a0e19a368 [ADD] stage 1: build a self-hosting core for s390x
Forty-odd packages in link-time order, from linux-api-headers to
pacman itself. The order follows what a compiler actually needs, not
pacman metadata: --nodeps means nothing is ever checked, so anything
required at link time has to exist already.

A failure does not stop the run. One missing package must not hide the
state of the forty that follow, so failures are collected and reported
at the end, each with its own log. A state file makes the run
resumable, which matters when a single gcc build is measured in tens
of minutes.

The header states why stage 1 is not the port: everything here links
against the host glibc, because Arch glibc needs an Arch gcc which
needs an Arch glibc. Stages 2 and 3 break that circle.

bootstrap-pacman.sh now guards its own main so it can be sourced for
build_package without re-running the whole bootstrap.

--- FR ---

Une quarantaine de paquets dans l ordre des dependances de lien, de
linux-api-headers a pacman lui-meme. L ordre suit ce dont un
compilateur a reellement besoin, pas les metadonnees de pacman :
--nodeps ne verifie jamais rien, donc tout ce qui sert au lien doit
deja exister.

Un echec n arrete pas la course. Un paquet manquant ne doit pas
masquer l etat des quarante suivants : les echecs sont collectes et
rapportes a la fin, chacun avec son journal. Un fichier d etat rend la
reprise possible, ce qui compte quand un seul gcc se compte en
dizaines de minutes.

L en-tete dit pourquoi l etage 1 n est pas le portage : tout s y lie a
la glibc de l hote, parce que la glibc d Arch reclame un gcc d Arch
qui reclame une glibc d Arch. Les etages 2 et 3 brisent ce cercle.

bootstrap-pacman.sh garde desormais son main pour etre sourcable et
fournir build_package sans relancer tout l amorcage.

Assisted-by: Claude Opus 5
2026-08-15 15:48:30 -04:00
057b1b471c [ADD] bootstrap pacman, makepkg and repo-add natively on s390x
The README lists pacman, bash and coreutils as three missing pieces.
They are not three tasks: pacman is the only one that matters, because
its source tree also ships makepkg and repo-add. Once those run, every
remaining package stops being hand-work and becomes makepkg on the
upstream PKGBUILD.

pacman 7.1.0 builds unpatched on s390x. No cross-compilation is
involved: on native hardware the whole userspace builds at full speed,
which removes the z/VM round-trip the other scripts need.

Measured end to end: zlib built from the official PKGBUILD yields
zlib, zlib-static and minizip, all tagged arch = s390x, the library
reporting "ELF 64-bit MSB shared object, IBM S/390", and repo-add
indexing them into a usable core.db.

--- FR ---

Le README annonce pacman, bash et coreutils comme trois pieces
manquantes. Ce ne sont pas trois travaux : seul pacman compte, parce
que son arbre source livre aussi makepkg et repo-add. Des qu ils
tournent, chaque paquet restant cesse d etre du travail manuel et
devient un makepkg sur le PKGBUILD amont.

pacman 7.1.0 se compile sans correctif sur s390x. Aucune compilation
croisee n intervient : sur du materiel natif tout l espace utilisateur
se batit a pleine vitesse, ce qui supprime l aller-retour z/VM dont
dependent les autres scripts.

Mesure de bout en bout : zlib bati depuis le PKGBUILD officiel produit
zlib, zlib-static et minizip, tous marques arch = s390x, la
bibliotheque se declarant « ELF 64-bit MSB shared object, IBM S/390 »,
et repo-add les indexant dans un core.db utilisable.

Assisted-by: Claude Opus 5
2026-08-15 03:18:47 -04:00
Josephine Pfeiffer
5026da6f22
making the house a home
Signed-off-by: Josephine Pfeiffer <hi@josie.lol>
2026-01-27 23:26:31 +01:00
Josephine Pfeiffer
8032b83e6a
cleaned up scripts to be more DRY 2025-06-05 20:48:16 +02:00
Josephine Pfeiffer
0e3a4c4dd5
full boot with systemd 2025-06-05 12:32:34 +02:00
Josephine Pfeiffer
7b50875291
systemd mvp 2025-06-02 22:58:34 +02:00
Josephine Pfeiffer
a4a2684756
use arch kernel patches 2025-06-02 21:53:33 +02:00
Josephine Pfeiffer
8b704b3189
rearchitechted things 2025-06-02 19:01:23 +02:00
Josephine Pfeiffer
a7a7a6160b
fix ci & cleanup mkintcpio patching 2025-06-01 19:22:10 +02:00
Josephine Pfeiffer
d7690f4c1d
fix ci 2025-06-01 14:03:19 +02:00
Josephine Pfeiffer
79d84414e5
change scripts to use upstream mkincpio instead of local copy 2025-06-01 12:35:33 +02:00
Josephine Pfeiffer
6831b3fa5c
mvp bootable system 2025-06-01 11:53:38 +02:00