[FIX] stage 2 could not clone, its pacman had no user, and it installed conflicts

Three gaps in the driver, all found by packages advancing to their next obstacle.

  no checkout for python-jinja

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

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

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

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

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

--- FR ---

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

  no checkout for python-jinja

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

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

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

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

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

Assisted-by: Claude Opus 5
This commit is contained in:
Mathieu Benoit 2026-08-23 18:38:46 -04:00
parent 5418dace22
commit f26a6a7115
3 changed files with 137 additions and 3 deletions

View file

@ -90,3 +90,35 @@ ZZPY
grep -qE "\ball man\b|\binstall-man\b" PKGBUILD && {
echo "git: a man target survived" >&2; exit 1; }
echo "git: man pages dropped (no asciidoc, four places)"
# --- no Rust ------------------------------------------------------------------
#
# cargo: command not found
#
# git's PKGBUILD passes WITH_RUST=1 and lists rust in makedepends. This port does
# not package Rust, and packaging it is not a package: rustc bootstraps from a
# previous rustc, which is the one thing a from-source distribution cannot start
# from -- Arch ships a binary to break that cycle, and this port has no such
# escape on s390x.
#
# WHY IT SURFACED ONLY NOW. Stage 1 built git happily: the HOST has rustc, from
# apt. So this is another entry in the longest-running theme of the port -- a
# dependency the host satisfied invisibly, appearing the first time the chroot is
# the one being asked. And it appeared only after the man-page section above
# started running at stage 2, which it had not been doing.
#
# WITH_RUST=1 selects Rust reimplementations of some internals; git builds and
# behaves the same without it. Both stages, so the two agree: stage 1 was
# producing a git that stage 2 could not reproduce.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*WITH_RUST=1[ \t]*$", l)]
assert len(hit) == 1, "git: expected one WITH_RUST=1 line, got %d" % len(hit)
del lines[hit[0]]
out = [l.replace(" 'rust'", "") if "makedepends" in l else l for l in lines]
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(out))
ZZPY
grep -qE "^[[:space:]]*WITH_RUST=1" PKGBUILD && { echo "git: WITH_RUST survived" >&2; exit 1; }
grep -q "'rust'" PKGBUILD && { echo "git: rust still in makedepends" >&2; exit 1; }
echo "git: built without Rust (no rustc to bootstrap from on s390x)"

View file

@ -31,3 +31,25 @@ io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-D gtk_doc=false" PKGBUILD || { echo "p11-kit: gtk_doc not disabled" >&2; exit 1; }
echo "p11-kit: gtk-doc manual disabled (glib was only locating its xrefs)"
# --- and no man pages either --------------------------------------------------
#
# With gtk_doc off, the next line was
#
# meson.build:94:4: ERROR: Problem encountered: Docbook stylesheet for man
# pages not found
#
# The stylesheets, not xsltproc -- which our libxslt supplies. Third package to
# stop on exactly this: pam validates DocBook sources it cannot fetch, shadow
# renders man pages from them. Shipping docbook-xsl would be a package that only
# documentation needs; -D man is already in the option list.
python3 - <<'ZZPY'
import io, re
lines = io.open("PKGBUILD", encoding="utf-8").read().split("\n")
hit = [i for i, l in enumerate(lines) if re.match(r"^[ \t]*-D man=true[ \t]*$", l)]
assert len(hit) == 1, "p11-kit: expected one -D man=true, got %d" % len(hit)
lines[hit[0]] = lines[hit[0]].replace("man=true", "man=false")
io.open("PKGBUILD", "w", encoding="utf-8").write("\n".join(lines))
ZZPY
grep -q -- "-D man=false" PKGBUILD || { echo "p11-kit: man not disabled" >&2; exit 1; }
echo "p11-kit: man pages disabled (DocBook stylesheets absent, not xsltproc)"

View file

@ -362,6 +362,24 @@ configure_chroot() {
# zz- so it sorts last: the drop-ins are read in glob order, and this has to
# be the final word. Owned by no package, and remade on every run since
# make_rootfs wipes the tree.
# DownloadUser, which the rebuilt pacman brings with it. nss failed on
#
# error: problem setting DownloadUser 'alpm' (user does not exist)
# ==> ERROR: 'pacman' returned a fatal error (1)
#
# Arch's pacman.conf drops privileges to `alpm` for downloads, and that user
# exists on a real Arch system because the package creates it via sysusers.d
# -- which `pacman --root` does not run, the same reason the CA bundle was
# never generated. Nothing in this chroot downloads anything (every source is
# already in /build, every package installed with -U from a local file), so
# the setting has no purpose here and its absence is fatal only because
# pacman checks it at startup.
if [ -f "$ROOT/etc/pacman.conf" ]; then
sudo sed -i 's/^\s*DownloadUser/#DownloadUser/' "$ROOT/etc/pacman.conf"
sudo grep -q '^#DownloadUser' "$ROOT/etc/pacman.conf" &&
printf ' pacman: DownloadUser disabled (no such user in a chroot)\n'
fi
sudo install -d -m0755 "$ROOT/etc/makepkg.conf.d"
sudo tee "$ROOT/etc/makepkg.conf.d/zz-s390x.conf" > /dev/null <<'EOC'
# stage 2: every flag variable, because Arch's are x86_64's and the drop-ins
@ -816,7 +834,17 @@ CHROOT_STAGE2_PKGS=(
# Five more packages -- coreutils, sed, grep, findutils, diffutils -- fetch PO
# files the same way and are unblocked by this one rebuild, which is why wget is
# also hoisted into STAGE2_FIRST.
STAGE2_HOST_EXTRACT=(libxml2 wget)
# nss joins them, for the cheapest of the three reasons: its source is
#
# hg+https://hg.mozilla.org/projects/nss#tag=NSS_..._RTM
#
# a MERCURIAL checkout, and the chroot has no hg -- it stopped on
# `hg: command not found`. Packaging Mercurial would work and is not needed: the
# host has it, and this mechanism exists precisely to let the host do the
# fetching. Considered and rejected: mercurial itself is cheap (a Python
# application), but it drags python-docutils in for its man pages and would sit
# in the chroot forever to serve one package's source URL.
STAGE2_HOST_EXTRACT=(libxml2 wget nss)
host_extract() {
local n="$1" h
@ -832,7 +860,36 @@ host_extract() {
stage2_build() {
local name="$1"
local dir="$WORK/pkg/$name"
[ -d "$dir" ] || { echo " no checkout for $name" >&2; return 1; }
# CLONE IF ABSENT. Stage 2 used to refuse:
#
# no checkout for python-jinja
#
# It could only rebuild what stage 1 had cloned -- which is wrong for every
# package stage 2 builds and stage 1 cannot. libxslt was the first (Ubuntu's
# libxml2 is too old to configure it), and the Python set followed (their
# package() reads the running python's paths, so the host bakes in
# dist-packages). Those are named in CHROOT_STAGE2_PKGS precisely because
# only this stage can produce them, and then this stage would not fetch them.
#
# Pinned to upstream.lock like stage 1's clone, so a package first built here
# is as reproducible as one built there.
if [ ! -d "$dir/.git" ]; then
local url="https://gitlab.archlinux.org/archlinux/packaging/packages/$name.git"
local sha=""
[ -f "$HERE/upstream.lock" ] &&
sha=$(awk -v n="$name" '$1 == n {print $2}' "$HERE/upstream.lock")
echo " cloning $name"
rm -rf "$dir"
GIT_TERMINAL_PROMPT=0 git clone -q --depth 1 "$url" "$dir" || {
rm -rf "$dir"; echo " clone failed: $url" >&2; return 1; }
if [ -n "$sha" ]; then
git -C "$dir" fetch -q --depth 1 origin "$sha" 2>/dev/null &&
git -C "$dir" checkout -q --detach FETCH_HEAD 2>/dev/null &&
echo " pinned to ${sha:0:9} (upstream.lock)"
else
echo " NOTE: $name is not in upstream.lock; built against HEAD" >&2
fi
fi
( cd "$dir" && git checkout -- PKGBUILD 2>/dev/null ) || true
if [ -f "$PATCH_DIR/$name.sh" ]; then
@ -930,8 +987,31 @@ stage2_build() {
# against the finished repository with --nodeps OFF. The relaxation is one
# install command wide, and the check that would catch its consequences is
# still there.
# Install what this package produced, MINUS the alternatives. zlib-ng
# produces zlib-ng-compat, and installing it fails:
#
# :: zlib-ng-compat-2.3.3-1 and zlib-1:1.3.2-3 are in conflict
#
# The package is correct and belongs in repo2; it just cannot be installed
# beside the zlib this chroot was built with. CHROOT_EXCLUDE already says so
# for the populate step, and the same list applies here -- one statement of
# which packages cannot coexist, used in both places.
local keep=() f2 pn2
for f2 in "${produced[@]}"; do
pn2=$(bsdtar -xOf "$f2" .PKGINFO 2>/dev/null | sed -n 's/^pkgname = //p')
if printf ' %s ' "${CHROOT_EXCLUDE[*]}" | grep -q " ${pn2:-_} "; then
printf ' not installed here: %s (alternative to an installed package)\n' "$pn2"
continue
fi
keep+=("$f2")
done
if [ "${#keep[@]}" -eq 0 ]; then
printf ' nothing to install\n'
( cd "$dir" && rm -rf src pkg ) || true
return 0
fi
sudo pacman --root "$ROOT" --config "$CONF2" --cachedir "$CACHE" \
--noconfirm --nodeps -U "${produced[@]}" > "$WORK/stage2-inst-$name.txt" 2>&1 || {
--noconfirm --nodeps -U "${keep[@]}" > "$WORK/stage2-inst-$name.txt" 2>&1 || {
tail -10 "$WORK/stage2-inst-$name.txt" >&2; return 1; }
printf ' installed %s package(s)\n' "${#produced[@]}"