#!/bin/sh set -eu repo_root=${PROJECT_ROOT:-$(pwd)} os_template=${OS_TEMPLATE:-$repo_root/tests/system/phase20-development-operating-system.scm.in} system_name=${SYSTEM_NAME:-phase20-operating-system} root_size=${ROOT_SIZE:-8g} metadata_target=${METADATA_OUT:-} root_authorized_key_file=${ROOT_AUTHORIZED_KEY_FILE:-$HOME/.ssh/id_ed25519.pub} root_ssh_private_key_file=${ROOT_SSH_PRIVATE_KEY_FILE:-$HOME/.ssh/id_ed25519} cleanup=0 if [ -n "${WORKDIR:-}" ]; then workdir=$WORKDIR mkdir -p "$workdir" else workdir=$(mktemp -d /tmp/fruix-phase20-development-xcpng.XXXXXX) cleanup=1 fi if [ "${KEEP_WORKDIR:-0}" -eq 1 ]; then cleanup=0 fi inner_metadata=$workdir/phase20-development-inner-metadata.txt metadata_file=$workdir/phase20-development-environment-xcpng-metadata.txt action_cleanup() { if [ "$cleanup" -eq 1 ]; then rm -rf "$workdir" fi } trap action_cleanup EXIT INT TERM KEEP_WORKDIR=1 WORKDIR="$workdir/inner" METADATA_OUT="$inner_metadata" \ ROOT_AUTHORIZED_KEY_FILE="$root_authorized_key_file" \ ROOT_SSH_PRIVATE_KEY_FILE="$root_ssh_private_key_file" \ OS_TEMPLATE="$os_template" SYSTEM_NAME="$system_name" ROOT_SIZE="$root_size" \ "$repo_root/tests/system/run-phase11-shepherd-pid1-xcpng.sh" phase8_metadata=$(sed -n 's/^phase8_metadata=//p' "$inner_metadata") closure_path=$(sed -n 's/^closure_path=//p' "$inner_metadata") closure_base=$(sed -n 's/^closure_base=//p' "$inner_metadata") guest_ip=$(sed -n 's/^guest_ip=//p' "$inner_metadata") vm_id=$(sed -n 's/^vm_id=//p' "$inner_metadata") vdi_id=$(sed -n 's/^vdi_id=//p' "$inner_metadata") shepherd_pid=$(sed -n 's/^shepherd_pid=//p' "$inner_metadata") sshd_status=$(sed -n 's/^sshd_status=//p' "$inner_metadata") compat_prefix_shims=$(sed -n 's/^compat_prefix_shims=//p' "$inner_metadata") guile_module_smoke=$(sed -n 's/^guile_module_smoke=//p' "$inner_metadata") activate_log=$(sed -n 's/^activate_log=//p' "$inner_metadata") development_profile_path=$closure_path/development-profile build_profile_path=$closure_path/build-profile runtime_profile_path=$closure_path/profile development_env_script=$closure_path/usr/local/bin/fruix-development-environment build_env_script=$closure_path/usr/local/bin/fruix-build-environment [ "$shepherd_pid" = 1 ] || { echo "shepherd was not PID 1" >&2; exit 1; } [ "$sshd_status" = running ] || { echo "sshd is not running" >&2; exit 1; } [ "$compat_prefix_shims" = absent ] || { echo "compatibility prefix shims reappeared" >&2; exit 1; } [ "$guile_module_smoke" = ok ] || { echo "guest Guile module smoke failed" >&2; exit 1; } case "$activate_log" in *fruix-activate:done*) : ;; *) echo "activation log does not show success" >&2; exit 1 ;; esac for path in \ "$development_profile_path/bin/cc" \ "$development_profile_path/bin/c++" \ "$development_profile_path/bin/ar" \ "$development_profile_path/usr/include/sys/param.h" \ "$development_profile_path/usr/share/mk/bsd.prog.mk" \ "$build_profile_path/bin/cc" \ "$build_profile_path/usr/include/sys/param.h" \ "$build_profile_path/usr/share/mk/bsd.prog.mk" \ "$development_env_script" \ "$build_env_script" do [ -e "$path" ] || { echo "required development environment path missing: $path" >&2 exit 1 } done [ ! -e "$runtime_profile_path/include" ] || { echo "runtime profile unexpectedly contains headers" >&2; exit 1; } [ ! -e "$runtime_profile_path/usr/share/mk" ] || { echo "runtime profile unexpectedly contains /usr/share/mk" >&2; exit 1; } [ ! -e "$runtime_profile_path/bin/cc" ] || { echo "runtime profile unexpectedly contains cc" >&2; exit 1; } ssh_guest() { ssh -i "$root_ssh_private_key_file" \ -o BatchMode=yes \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o ConnectTimeout=5 \ root@"$guest_ip" "$@" } guest_dev_metadata=$(ssh -i "$root_ssh_private_key_file" \ -o BatchMode=yes \ -o StrictHostKeyChecking=no \ -o UserKnownHostsFile=/dev/null \ -o ConnectTimeout=5 \ root@"$guest_ip" 'sh -s' <<'EOF' set -eu [ -x /usr/local/bin/fruix-development-environment ] [ -x /usr/local/bin/fruix-build-environment ] [ -L /run/current-development ] [ "$(readlink /run/current-development)" = "/run/current-system/development-profile" ] [ -L /run/current-build ] [ "$(readlink /run/current-build)" = "/run/current-system/build-profile" ] exports=$(/usr/local/bin/fruix-development-environment) printf '%s\n' "$exports" | grep '^export FRUIX_DEVELOPMENT_PROFILE="/run/current-system/development-profile"$' >/dev/null printf '%s\n' "$exports" | grep '^export MAKEFLAGS="-m /run/current-system/development-profile/usr/share/mk"$' >/dev/null eval "$exports" [ -d "$FRUIX_DEVELOPMENT_PROFILE" ] [ -x "$FRUIX_CC" ] [ -x "$FRUIX_CXX" ] [ -x "$FRUIX_AR" ] [ -f "$FRUIX_DEVELOPMENT_INCLUDE/sys/param.h" ] [ -f "$FRUIX_DEVELOPMENT_SHARE_MK/bsd.prog.mk" ] cc_version=$($FRUIX_CC --version | awk 'NR==1 { print; exit }') build_exports=$(/usr/local/bin/fruix-build-environment) printf '%s\n' "$build_exports" | grep '^unset MAKEOBJDIRPREFIX MAKEFLAGS CC CXX AR RANLIB NM CPPFLAGS CFLAGS CXXFLAGS LDFLAGS$' >/dev/null printf '%s\n' "$build_exports" | grep '^export FRUIX_BUILD_PROFILE="/run/current-system/build-profile"$' >/dev/null eval "$build_exports" [ -d "$FRUIX_BUILD_PROFILE" ] [ -f "$FRUIX_BUILD_INCLUDE/sys/param.h" ] [ -f "$FRUIX_BUILD_SHARE_MK/bsd.prog.mk" ] [ "${MAKEFLAGS-unset}" = unset ] [ "${CPPFLAGS-unset}" = unset ] [ "${CFLAGS-unset}" = unset ] [ "${LDFLAGS-unset}" = unset ] [ "$FRUIX_BMAKE" = make ] build_profile_value=$FRUIX_BUILD_PROFILE eval "$exports" tmp=/tmp/fruix-phase20-development-env rm -rf "$tmp" mkdir -p "$tmp/direct" "$tmp/mk" cat > "$tmp/direct/hello.c" <<'EOS' #include int main(void){puts("hello-from-direct-dev-env");return 0;} EOS $FRUIX_CC $CPPFLAGS $LDFLAGS "$tmp/direct/hello.c" -o "$tmp/direct/hello" hello_direct=$($tmp/direct/hello) cat > "$tmp/mk/hello.c" <<'EOS' #include int main(void){puts("hello-from-make-dev-env");return 0;} EOS cat > "$tmp/mk/Makefile" <<'EOS' PROG=hello SRCS=hello.c NO_MAN=yes .include EOS cat > "$tmp/mk/hello.1" <<'EOS' .Dd April 5, 2026 .Dt HELLO 1 .Sh NAME .Nm hello .Nd phase20 development environment smoke binary EOS cd "$tmp/mk" make clean >/dev/null 2>&1 || true make > "$tmp/mk/make.log" 2>&1 hello_make=$(./hello) make_log_tail=$(tail -n 20 "$tmp/mk/make.log" | tr '\n' ' ') exports_flat=$(printf '%s' "$exports" | tr '\n' ' ') printf 'development_profile=%s\n' "$FRUIX_DEVELOPMENT_PROFILE" printf 'build_profile=%s\n' "$build_profile_value" printf 'cc_version=%s\n' "$cc_version" printf 'hello_direct=%s\n' "$hello_direct" printf 'hello_make=%s\n' "$hello_make" build_exports_flat=$(printf '%s' "$build_exports" | tr '\n' ' ') printf 'exports=%s\n' "$exports_flat" printf 'build_exports=%s\n' "$build_exports_flat" printf 'make_log_tail=%s\n' "$make_log_tail" EOF ) development_profile=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^development_profile=//p') build_profile=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^build_profile=//p') cc_version=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^cc_version=//p') hello_direct=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^hello_direct=//p') hello_make=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^hello_make=//p') development_exports=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^exports=//p') build_exports=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^build_exports=//p') make_log_tail=$(printf '%s\n' "$guest_dev_metadata" | sed -n 's/^make_log_tail=//p') [ "$development_profile" = "/run/current-system/development-profile" ] || { echo "unexpected guest development profile path: $development_profile" >&2 exit 1 } [ "$build_profile" = "/run/current-system/build-profile" ] || { echo "unexpected guest build profile path: $build_profile" >&2 exit 1 } case "$cc_version" in *"FreeBSD clang version"*) : ;; *) echo "unexpected cc version output: $cc_version" >&2; exit 1 ;; esac [ "$hello_direct" = hello-from-direct-dev-env ] || { echo "unexpected direct compile output: $hello_direct" >&2 exit 1 } [ "$hello_make" = hello-from-make-dev-env ] || { echo "unexpected make-based compile output: $hello_make" >&2 exit 1 } case "$development_exports" in *'export FRUIX_CC="/run/current-system/development-profile/bin/cc"'*) : ;; *) echo "development environment exports do not include FRUIX_CC" >&2; exit 1 ;; esac case "$build_exports" in *'export FRUIX_BUILD_PROFILE="/run/current-system/build-profile"'*) : ;; *) echo "build environment exports do not include FRUIX_BUILD_PROFILE" >&2; exit 1 ;; esac cat >"$metadata_file" <