2
0
mirror of https://github.com/xcat2/xcat-dep.git synced 2026-09-30 23:05:17 +00:00
Files
xcat-dep/goconserver/sbuild.pl
T
Daniel Hilst 2a4551f05c fix(xcat-dep): the riscv64 goconserver build never finishes
xcat-dep-ubuntu-cd build 83 failed in its riscv64 branch. The goconserver build for
jammy, noble and resolute each ran the full 9000s budget and produced no deb. The stall
report found the Go processes parked in futex_wait, two of the three cells with no CPU
ticks at all during the sample.

riscv64 has no build host, so its chroot runs on the amd64 agent under qemu-user.
goconserver/sbuild.pl installed the Go toolchain for the chroot architecture, so the
compiler itself ran emulated, and `go build` did not return. The EL builder already avoids
this: goconserver/mockbuild.pl cross-compiles for a forcearch target rather than running
the toolchain in the emulated chroot.

sbuild.pl now stamps the build host architecture into the build script and fetches the
toolchain for that architecture, then sets GOARCH to the chroot architecture. A Go
toolchain is statically linked, so the host one runs inside the foreign chroot, and
goconserver is CGO-free, so it cross-compiles. The toolchain also unpacks into the build
tree instead of overwriting /usr/local/go. A native cell is unchanged: host and target
agree, and GOARCH names the architecture it already used.

t/goconserver_cross_build.t covers this and fails without the change: the toolchain was
fetched for riscv64 and `go build` ran with GOARCH unset.

Measured on xcat-master-ub, jammy riscv64: 380s, against a 9000s budget the emulated
build exhausted. The deb carries statically linked RISC-V binaries that run under binfmt.

Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
2026-09-15 07:09:48 -03:00

133 lines
7.0 KiB
Prolog
Executable File

#!/usr/bin/env perl
# goconserver/sbuild.pl -- per-package Ubuntu/Debian builder for goconserver; the apt/sbuild analogue
# of goconserver/mockbuild.pl. Builds the package inside the matching <codename>-<arch>-sbuild chroot
# from its MAINTAINED debian/ packaging and collects the .deb(s) into --result-dir. Invoked by
# sbuild-all.pl per (codename,arch); also runnable standalone. Out-of-tree: the build runs on a COPY of
# the package tree (the checkout is never mutated). The common chroot orchestration lives in
# BuildUtils::build_deb_in_chroot; goconserver's source clone + Go toolchain + build is below.
use strict;
use warnings;
use Cwd qw(abs_path);
use File::Basename qw(basename);
use Getopt::Long qw(GetOptions);
use FindBin qw($RealBin);
use lib "$RealBin/..";
use BuildUtils qw(chroot_name build_deb_in_chroot);
my $pkg_dir = abs_path($RealBin);
my $pkg = basename($pkg_dir);
my ($codename, $arch, $chroot, $result_dir, $log_dir) = ('', '', '', '', '');
my ($build_timestamp, $build_number, $skip_install) = (undef, undef, 0);
# --log-dir / --build-number / --skip-install are accepted for CLI-compat with sbuild-all.pl (which
# passes them uniformly to every per-package builder) but are intentionally UNUSED here: sbuild-all
# does its own per-package logging and there is no deb install-smoke. They are parsed and ignored.
GetOptions(
'codename=s' => \$codename, 'arch=s' => \$arch, 'chroot=s' => \$chroot,
'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir,
'build-timestamp=i' => \$build_timestamp, 'build-number=i' => \$build_number,
'skip-install!' => \$skip_install,
) or die "bad options\n";
$arch ||= `dpkg --print-architecture 2>/dev/null`; chomp $arch; $arch ||= 'amd64';
die "FATAL: --codename required\n" unless $codename;
$chroot ||= chroot_name($codename, $arch);
$result_dir ||= "$pkg_dir/../build-output/sbuild/$codename/$arch";
$build_timestamp = time() unless defined $build_timestamp;
# goconserver's pinned SHA + Go toolchain (see the shell below). goconserver 0.3.3 is UNRELEASED so it
# lives only on master -- pin an immutable SHA so every matrix cell builds the SAME source (reproducible,
# matching the EL GOCONSERVER_REF in mockbuild-all.pl). Its modules require Go >= 1.25, but the Ubuntu
# codenames ship older toolchains (focal=go1.13 ... noble=go1.22) and the pre-1.21 ones cannot even
# auto-switch toolchains; goconserver is a static CGO-free binary, so a pinned Go downloaded into the
# build works in ANY codename chroot and keeps the compiler reproducible. Bump these two deliberately.
# ---- package-specific build (absorbed from the former make_deb.sh); CWD = the copied package dir ----
# The maintained debian/ is at ./debian in the copied package dir; the upstream source is cloned fresh
# at the pinned SHA into ./gcsrc, the maintained debian/ copied in, and dpkg-buildpackage run there
# (its .deb(s) land in the copied package dir, which the collector picks up).
# The build script below is lifted by t/goconserver_cross_build.t and run with the commands it
# calls shadowed. Keep the marker: the test dies when it can no longer find this region.
my $host_deb_arch = `dpkg --print-architecture 2>/dev/null`;
chomp $host_deb_arch;
die "FATAL: cannot read the build host architecture from dpkg\n"
unless $host_deb_arch =~ /^[a-z0-9]+$/;
my $build = "HOST_DEB_ARCH=$host_deb_arch\n" . <<'BUILD';
set -e
VERSION=0.3.3
REPO=https://github.com/xcat2/goconserver.git
REF=6166fe5ec1c5b3c20475e322a9f0e8e93c87e45f
GO_PIN=1.25.12
# pinned modern Go toolchain (static CGO-free build, portable across codenames; reproducible compiler)
# The toolchain is the BUILD HOST's and the target comes from GOARCH, because riscv64 has no build
# host: its chroot runs under qemu-user, and a riscv64 `go build` there parks in futex_wait and never
# returns. Go cross-compiles a CGO-free binary, and a Go toolchain is statically linked, so the host
# one runs inside the foreign chroot at native speed. HOST_DEB_ARCH is stamped in by sbuild.pl: it
# cannot be read here, because qemu makes the chroot's dpkg and uname both answer for the target.
deb_to_goarch() { case "$1" in ppc64el) echo ppc64le;; *) echo "$1";; esac; }
go_host_arch=$(deb_to_goarch "$HOST_DEB_ARCH")
go_target_arch=$(deb_to_goarch "$(dpkg --print-architecture)")
echo "installing pinned go${GO_PIN} (${go_host_arch}) to compile for ${go_target_arch}"
gotoolchain="$PWD/.gotoolchain"
mkdir -p "$gotoolchain"
curl -fsSL "https://go.dev/dl/go${GO_PIN}.linux-${go_host_arch}.tar.gz" | tar -C "$gotoolchain" --strip-components=1 -xz
export PATH="$gotoolchain/bin:$PATH"
export GOTOOLCHAIN=local # use exactly the pinned toolchain; never auto-download another
export GOOS=linux GOARCH="$go_target_arch"
go version
go env GOHOSTARCH GOARCH
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
SNAP_TS=$(date -d "@$SOURCE_DATE_EPOCH" --utc '+%Y%m%d%H%M')
else
SNAP_TS=$(date '+%Y%m%d%H%M')
fi
FULL_VERSION="${VERSION}-snap${SNAP_TS}"
# shallow-fetch the pinned commit by object id (GitHub allows reachable-SHA fetches)
gc=gcsrc
git init -q "$gc"
git -C "$gc" remote add origin "$REPO"
git -C "$gc" fetch -q --depth 1 origin "$REF"
git -C "$gc" checkout -q FETCH_HEAD
# etcd storage backend has broken deps with modern Go modules
rm -rf "$gc/storage/etcd.go" "$gc/storage/etcd/"
cp -rL debian "$gc/debian"
cd "$gc"
export GOPATH="$PWD/.gopath" GOCACHE="$PWD/.gocache" GOMODCACHE="$PWD/.gomodcache" CGO_ENABLED=0
# Overlay the committed, PINNED go.mod/go.sum for this exact upstream SHA (generated with this same
# GO_PIN toolchain -- see ../gomod/README.md). go.mod replaces the abandoned github.com/kr/pty with
# creack/pty (pty.Start sets Ctty in a way Go >=1.15 rejects); go.sum integrity-checks every module.
# Build with -mod=mod: modules are downloaded from the proxy but PINNED + verified by go.sum, so the
# build is reproducible -- NO `go mod tidy` floating transitive versions from the network at build time.
# (The etcd backend removed above is why the pinned graph omits github.com/coreos/bbolt, which now
# declares its path as go.etcd.io/bbolt and breaks a fresh `go mod tidy`.)
if [ ! -f ../gomod/go.mod ] || [ ! -f ../gomod/go.sum ]; then
echo "FATAL: pinned ../gomod/go.{mod,sum} missing -- regenerate per goconserver/gomod/README.md" >&2
exit 1
fi
cp ../gomod/go.mod go.mod
cp ../gomod/go.sum go.sum
export GOFLAGS=-mod=mod
# stamp the maintained debian/ to the snapshot version, OUT-OF-TREE (this is the cloned copy)
sed -i "s/Version=${VERSION}/Version=${FULL_VERSION}/g" debian/rules
export DEBEMAIL="${DEBEMAIL:-xcat-build@xcat.org}" DEBFULLNAME="${DEBFULLNAME:-xCAT Build}"
if [ -n "${SOURCE_DATE_EPOCH:-}" ]; then
deterministic_date=$(date -R -d "@$SOURCE_DATE_EPOCH" --utc)
sed -i "1s/(.*)/(${FULL_VERSION})/" debian/changelog
sed -i "s/^ -- .*/ -- $DEBFULLNAME <$DEBEMAIL> $deterministic_date/" debian/changelog
else
dch -v "$FULL_VERSION" -b -D unstable "Snap build for xCAT"
fi
dpkg-buildpackage -uc -us
BUILD
build_deb_in_chroot(
pkg => $pkg, chroot => $chroot, pkg_dir => $pkg_dir, result_dir => $result_dir,
build_timestamp => $build_timestamp, build => $build,
);