Splux Linux

source-based Linux using SPS

SPS · log · files · refs · tags · releases · people · languages · GitHub · commit

461b1ff


mkpkg: add --build-root controlled build environment

Date
Commit461b1ffed2a643cf11f2bd196209def40c2a9edd
GitHubmirror

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