archlinux-s390x/scripts/build-stage1.sh

132 lines
6.1 KiB
Bash
Raw Normal View History

[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
#!/usr/bin/env bash
# Stage 1 of the port: build a self-hosting Arch `core` for s390x.
#
# THE THREE-STAGE DISCIPLINE, AND WHY IT IS NOT OPTIONAL
#
# Stage 1 builds with the HOST toolchain (Ubuntu gcc/glibc). Every package it
# produces is therefore linked against the host's glibc, not Arch's. That is
# acceptable -- and unavoidable, since Arch's glibc needs an Arch gcc which
# needs an Arch glibc -- but it is not a port yet.
#
# Stage 2 chroots into the stage-1 result and rebuilds everything with the
# stage-1 toolchain. Stage 3 repeats it, and a port is self-hosting once
# stage 3 reproduces stage 2. Skipping this leaves host artefacts baked into
# packages that will fail months later, far from their cause.
#
# This script is stage 1 only.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$HERE/bootstrap-pacman.sh"
WORK="${WORK:-$HOME/work/arch-s390x}"
REPO="${REPO:-$WORK/repo/s390x}"
STATE="$WORK/stage1.state"
# Build order. It follows link-time dependencies, not pacman metadata:
# --nodeps means pacman never checks, so anything a compiler actually needs
# must already exist. Within a group the order is free.
STAGE1_PACKAGES=(
# Foundation: headers, then the C library, then the compiler chain.
linux-api-headers glibc binutils gcc
# Compression and crypto, needed by libarchive and curl further down.
zlib bzip2 xz zstd lz4 openssl
# Terminal handling: bash links against readline, readline against ncurses.
ncurses readline
# The shell, and the coreutils prerequisites Arch declares.
attr acl gmp mpfr libcap bash coreutils
# Text and file tools the build systems themselves call.
sed grep gawk findutils diffutils file which patch
# Archivers, then the library pacman reads packages with.
tar gzip expat libarchive
# Build systems.
m4 autoconf automake libtool make pkgconf
# pacman's network and signature stack.
libnghttp2 libpsl curl libgpg-error libassuan gnupg gpgme
# System skeleton: without these a rootfs has no /etc/passwd, no zones,
# no /etc/services -- and nothing boots to a usable shell.
filesystem iana-etc tzdata licenses shadow util-linux
[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
# Named by the chroot test, not guessed. Installing the repo into a
# rootfs and entering it turned "does it work?" into a precise list:
# - libcap needs pam; openssl needs brotli; libarchive needs libxml2
# - pacman itself asks for systemd, pacman-mirrorlist and
# libmakepkg-dropins
# Sixty-eight successful builds proved none of this. One chroot did.
[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
#
# The chroot also named libselinux, and libselinux is NOT on this line,
# because that reading of it was wrong. Arch has no libselinux package at
# all -- the clone 404s. What the chroot saw was a HOST artefact: Ubuntu
# carries libselinux1-dev, coreutils probes for selinux/selinux.h
# unconditionally, and ours came out linked to a library the target will
# never contain. The answer is --without-selinux per package, which is
# what Arch's own build chroot gets for free by not having the header.
pam brotli libxml2 systemd pacman-mirrorlist libmakepkg-dropins
[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
# The closure, named by pacman's own resolver rather than guessed.
#
# Everything above was added because a BUILD stopped. These were added
# because scripts/test-chroot.sh ran `pacman -Syp` with --nodeps OFF --
# the check the whole bootstrap skips -- and the resolver listed exactly
# what the repository still owes. Nothing here is speculative.
#
# It is the MINIMAL closure, and two measurements shaped it. Dropping the
# python-brotli sub-package took the list from 70 unresolved names to 47
# and removed `python` outright, with libffi, mpdecimal and gdbm behind
# it. Dropping systemd-ukify and systemd-tests removed five more python
# packages, and ukify cannot run on s390x at all. Both are recorded in
# TODO.md rather than left implicit.
#
# An audit of the remaining names against the ARTEFACTS found two that
# were declared and never linked: guile by make, and libisl.so by gcc.
# Both get their declaration removed, because it should describe the
# binary we shipped.
#
# Building isl instead was the first plan, and it is recorded here because
# the reason it failed is the kind that wastes an afternoon: the Arch
# packaging repo for isl was last touched in 2017 and its only source URL
# is isl.gforge.inria.fr, which died with INRIA's GForge. There is nothing
# to build. That flipped the decision -- not a change of mind, new
# evidence.
#
# These will pull their own dependencies. That is expected: the resolver
# will name the next round as precisely as it named this one.
audit ca-certificates cryptsetup dbus elfutils gettext gnutls hwdata
icu jansson kbd kmod krb5 libcap-ng libgcrypt libidn2 libksba
libmpc libnsl libseccomp libssh2 libtirpc libunistring libusb libxcrypt
nettle npth openldap pambase pcre2 perl pinentry sqlite tpm2-tss
[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
# And finally the package manager itself, built as an Arch package.
pacman
)
built() { grep -qxF "$1" "$STATE" 2>/dev/null; }
mark() { echo "$1" >> "$STATE"; }
main() {
mkdir -p "$WORK/pkg" "$REPO"
touch "$STATE"
[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
# The stand-ins are read from /usr/local/bin, so a stage-1 run that never
# reinstalls them silently builds with whatever was deployed weeks ago.
# Cheap, idempotent, and it makes this repository the source of truth.
install_host_shims
[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
local ok=0 fail=0 failed=()
for p in "${STAGE1_PACKAGES[@]}"; do
if built "$p"; then
echo "== $p already built, skipping =="
continue
fi
# A failure must not stop the run: one missing package should not hide
# the state of the forty that follow. They are collected and reported.
if build_package "$p" > "$WORK/log-$p.txt" 2>&1; then
mark "$p"; ok=$((ok + 1))
echo "OK $p"
else
fail=$((fail + 1)); failed+=("$p")
echo "FAIL $p (see $WORK/log-$p.txt)"
fi
done
echo
echo "== stage 1: $ok built, $fail failed =="
[ "$fail" -eq 0 ] || printf ' failed: %s\n' "${failed[*]}"
}
main "$@"