From f26a6a7115e505e1cae54cb5d3341778b6909318 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Sun, 23 Aug 2026 18:38:46 -0400 Subject: [PATCH] [FIX] stage 2 could not clone, its pacman had no user, and it installed conflicts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- patches/pkgbuild/git.sh | 32 ++++++++++++++ patches/pkgbuild/p11-kit.sh | 22 ++++++++++ scripts/build-stage2.sh | 86 +++++++++++++++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/patches/pkgbuild/git.sh b/patches/pkgbuild/git.sh index 185588a..69c1460 100755 --- a/patches/pkgbuild/git.sh +++ b/patches/pkgbuild/git.sh @@ -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)" diff --git a/patches/pkgbuild/p11-kit.sh b/patches/pkgbuild/p11-kit.sh index 93ff281..e3db4c1 100755 --- a/patches/pkgbuild/p11-kit.sh +++ b/patches/pkgbuild/p11-kit.sh @@ -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)" diff --git a/scripts/build-stage2.sh b/scripts/build-stage2.sh index 54af1e3..487bf06 100755 --- a/scripts/build-stage2.sh +++ b/scripts/build-stage2.sh @@ -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[@]}"