SPS · log · files · refs · tags · releases · people · languages · GitHub · commit
461b1ff
mkpkg: add --build-root controlled build environment
Parents
Stat
bin/mkpkg | 43 +++++++++++++++++++++++++++++++++++++++++-- man/mkpkg.1 | 33 +++++++++++++++++++++++++++++++++ tests/test_build.sh | 44 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 3 deletions(-)
Diff
diff --git a/bin/mkpkg b/bin/mkpkg
index ca04b67..b83e655 100755
--- a/bin/mkpkg
+++ b/bin/mkpkg
@@ -12,6 +12,7 @@ output_dir=.
recipe=
build_dir=
stage_dir=
+build_root=
usage()
{
@@ -23,6 +24,8 @@ Build the given recipe (default: ./recipe) into an SPS package archive.
options:
-o, --output DIR write the package to DIR
-C, --compression TYPE auto, none, gzip, xz, or zstd
+ -R, --build-root DIR run build phases with DIR/usr/bin and DIR/bin
+ first on PATH and expose it as \$SYSROOT
--no-download use only local sources and cached downloads
--keep-build retain build and staging directories
-h, --help show this help
@@ -95,6 +98,16 @@ while [ "$#" -gt 0 ]; do
no_download=1
shift
;;
+ -R|--build-root)
+ [ "$#" -ge 2 ] || die_status 2 "$1 requires a directory"
+ build_root=$2
+ shift 2
+ ;;
+ --build-root=*)
+ build_root=${1#*=}
+ [ -n "$build_root" ] || die_status 2 "--build-root requires a directory"
+ shift
+ ;;
--keep-build)
keep_build=1
shift
@@ -171,6 +184,15 @@ esac
: "${SPS_CACHE:=$SPS_ROOT/var/cache/sps}"
: "${SPS_BUILD:=${TMPDIR:-/tmp}/sps}"
+if [ -n "$build_root" ]; then
+ case $build_root in
+ /*) ;;
+ *) die_status 2 "--build-root must be an absolute path: $build_root" ;;
+ esac
+ build_root=${build_root%/}
+ [ -d "$build_root" ] || die_status 8 "build root does not exist: $build_root"
+fi
+
makejobs=${MAKEJOBS:-${SPS_MAKEJOBS:-1}}
case $makejobs in
''|*[!0-9]*|0) die_status 2 "MAKEJOBS must be a positive integer" ;;
@@ -616,14 +638,31 @@ WORK=$work_dir
MAKEJOBS=$makejobs
export PKG SRC WORK MAKEJOBS
+# A controlled build root runs each phase with the root's /usr/bin and /bin
+# ahead of the host PATH and exposes the root as $SYSROOT. Recipes building a
+# staged toolchain use this to ensure their compiler, linker, and headers are
+# taken from the target root rather than the host, mirroring an LFS sysroot.
+if [ -n "$build_root" ]; then
+ run_env="PATH=$build_root/usr/bin:$build_root/bin:$PATH SYSROOT=$build_root"
+ say "using build root $build_root"
+else
+ run_env=
+fi
+
run_phase()
{
phase_name=$1
phase_file=$build_dir/$phase_name.sh
[ -s "$phase_file" ] || return 0
say "running $phase_name phase"
- if ! (CDPATH= cd "$WORK" && sh -eu "$phase_file"); then
- die_status 10 "$phase_name phase failed"
+ if [ -n "$run_env" ]; then
+ if ! (CDPATH= cd "$WORK" && env $run_env sh -eu "$phase_file"); then
+ die_status 10 "$phase_name phase failed"
+ fi
+ else
+ if ! (CDPATH= cd "$WORK" && sh -eu "$phase_file"); then
+ die_status 10 "$phase_name phase failed"
+ fi
fi
}
diff --git a/man/mkpkg.1 b/man/mkpkg.1
index fc5a9b0..51eb049 100644
--- a/man/mkpkg.1
+++ b/man/mkpkg.1
@@ -15,6 +15,21 @@ The default recipe is
A directory argument means its
.B recipe
file.
+.PP
+Build phases normally run with the host environment.
+.BR --build-root ,
+when given, runs every build phase with the root's
+.B /usr/bin
+and
+.B /bin
+first on
+.B PATH
+and exposes the root as
+.BR SYSROOT .
+Recipes that stage a toolchain inside a root (an LFS-style sysroot) use this to
+ensure their compiler, linker, and headers resolve from the target root rather
+than the host. It does not use a chroot, does not require root privileges, and
+never writes into the build root itself.
.SH OPTIONS
.TP
.BI "-o, --output " dir
@@ -29,6 +44,20 @@ Select
or
.BR zstd .
.TP
+.BI "-R, --build-root " dir
+Run build phases with
+.B DIR/usr/bin
+and
+.B DIR/bin
+first on
+.B PATH
+and expose the absolute
+.B DIR
+as
+.BR SYSROOT .
+.B DIR
+must already exist.
+.TP
.B --no-download
Require remote sources to already exist in the source cache.
.TP
@@ -109,6 +138,10 @@ provides per-package parallelism and is exposed to build commands as
contains downloaded sources;
.B SPS_BUILD
contains temporary build/staging trees.
+With
+.BR --build-root ,
+.B SYSROOT
+is the absolute controlled root directory.
.SH OUTPUT
On success the final stdout line is the absolute or requested path of the
created package artifact, suitable for consumption by
diff --git a/tests/test_build.sh b/tests/test_build.sh
index 6aeb1a4..042b30e 100755
--- a/tests/test_build.sh
+++ b/tests/test_build.sh
@@ -156,4 +156,46 @@ tar -xOf "$tar_artifact" ./usr/bin/archive-hello >"$tmp/archive-hello.out" 2>/de
assert_equal "hello from SPS" "$(sh "$tmp/archive-hello.out")" \
"archive-source payload returned unexpected output"
-printf '%s\n' 'test_build: ok'
+# --build-root must expose the root as SYSROOT and put its bin dirs first on
+# PATH for every build phase.
+mkdir -p "$tmp/sysroot-recipe" "$tmp/sysroot-out" "$tmp/buildroot/usr/bin" \
+ "$tmp/buildroot/bin"
+touch "$tmp/buildroot/usr/bin/sysroot-marker" "$tmp/buildroot/bin/bin-marker"
+cat >"$tmp/sysroot-recipe/recipe" <<'EOF'
+name sysroot-probe
+version 1.0
+release 1
+arch any
+description Confirms --build-root env isolation
+install mkdir -p "$PKG/usr/share/sysroot-probe"
+install printf '%s\n' "${SYSROOT-}" >"$PKG/usr/share/sysroot-probe/sysroot"
+install printf '%s\n' "$PATH" >"$PKG/usr/share/sysroot-probe/path"
+EOF
+sysroot_artifact=$(
+ SPS_ROOT=$tmp/root \
+ SPS_DB=$tmp/db \
+ SPS_CACHE=$tmp/cache \
+ SPS_BUILD=$tmp/build \
+ SPS_CONFIG=/dev/null \
+ SPS_REPOS_CONFIG=/dev/null \
+ SPS_COMPRESSION=none \
+ "$mkpkg" --no-download --build-root "$tmp/buildroot" \
+ --output "$tmp/sysroot-out" "$tmp/sysroot-recipe/recipe"
+) || fail "sysroot build failed"
+mkdir "$tmp/sysroot-extract"
+tar -xf "$sysroot_artifact" -C "$tmp/sysroot-extract"
+assert_equal "$tmp/buildroot" \
+ "$(cat "$tmp/sysroot-extract/usr/share/sysroot-probe/sysroot")" \
+ "SYSROOT was not set to the build root"
+case "$(cat "$tmp/sysroot-extract/usr/share/sysroot-probe/path")" in
+ "$tmp/buildroot/usr/bin:$tmp/buildroot/bin:"*) : ;;
+ *) fail "build root bin dirs were not first on PATH" ;;
+esac
+
+[ ! -e "$tmp/buildroot/usr/share/sysroot-probe" ] ||
+ fail "build root was modified by the build"
+
+[ ! -e "$tmp/root/usr/share/sysroot-probe" ] ||
+ fail "builder modified the target root with a build root"
+
+printf '%s\n' 'test_build: ok'
\ No newline at end of file