Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/probe/lib.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Shared helpers for the probe scripts. Every script prints what it observes
# and never asserts: a failing command is recorded with its exit code.
XV="${XV:-2026.10.15.4}" # the xlings that installs the "upstream" home
MV="${MV:-2026.10.11.1}" # the published mcpp under test
T="${T:-$RUNNER_TEMP/p}"
mkdir -p "$T" "$T/home" "$T/logs"
SUMMARY="${GITHUB_STEP_SUMMARY:-/dev/null}"

say() { echo "$*"; echo "$*" >> "$SUMMARY"; }
section() { say ""; say "### $*"; }
# Run a command, print its (tail of) output into the summary as a code block.
obs() {
local title="$1"; shift
local out rc
out="$("$@" 2>&1)"; rc=$?
echo "== $title (exit $rc)"; echo "$out"
{
echo ""; echo "**$title** (exit $rc)"; echo '```'
echo "$out" | tail -n "${OBS_LINES:-25}"
echo '```'
} >> "$SUMMARY"
return 0
}
clean_env() {
env -u XLINGS_ACTIVE_SUBOS -u XLINGS_SUBOS_MODE -u XLINGS_SUBOS_LIB -u XLINGS_BIN \
-u XLINGS_PROJECT_DIR -u XLINGS_SHIM_DEPTH -u XLINGS_HOME -u MCPP_VENDORED_XLINGS "$@"
}
# An ELF's loader and run path, one line each.
elf() {
local f="$1"
[ -e "$f" ] || { echo "$f: absent"; return; }
echo "$f"
readelf -l "$f" 2>/dev/null | grep -o 'interpreter: [^]]*' | sed 's/^/ /'
readelf -d "$f" 2>/dev/null | grep -E 'RUNPATH|RPATH' | sed 's/.*\[/ runpath [/'
}
# A Mach-O's dylibs and rpaths.
macho() {
local f="$1"
[ -e "$f" ] || { echo "$f: absent"; return; }
echo "$f"
otool -L "$f" 2>/dev/null | tail -n +2 | sed 's/^/ dylib/'
otool -l "$f" 2>/dev/null | awk '/LC_RPATH/{r=1} r&&/path /{print " rpath " $2; r=0}'
}
# Digest of every regular file under a directory (to show it was not written).
tree_digest() {
(cd "$1" && find . -type f -print0 | sort -z | xargs -0 sha256sum 2>/dev/null | sha256sum | cut -c1-16)
}
hello_cpp() {
cat > "$1" <<'EOF'
#include <cstdio>
#include <string>
int main() { std::string s = "hello from the probe"; std::puts(s.c_str()); return 0; }
EOF
}
104 changes: 104 additions & 0 deletions .github/probe/linux.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
#!/usr/bin/env bash
# O1: toolchains installed by xlings $XV into a fresh home -- do they run, and
# run what they build, with no modification from mcpp?
# O2: mcpp $MV with its managed gcc/llvm replaced by a symbolic link to the
# upstream home's payload (what a borrowed entry is) -- does the build
# work, what loader does the product get, is the upstream written?
# Prints observations; never asserts.
set -uo pipefail
. "$(dirname "$0")/lib.sh"
ARCH="$(uname -m)"
cd "$T"

section "host"
obs "uname / glibc" bash -c 'uname -a; ldd --version | head -1'

curl -fsSL -o xl.tgz "https://github.com/openxlings/xlings/releases/download/v$XV/xlings-$XV-linux-$ARCH.tar.gz"
tar -xzf xl.tgz
UP="$T/xlings-$XV-linux-$ARCH"
x() { clean_env XLINGS_HOME="$UP" HOME="$T/home" "$UP/bin/xlings" "$@"; }

section "O1: xlings $XV, a fresh home, gcc 16.1.0"
obs "xlings --version" x --version
obs "self init" x self init
obs "update" x update
OBS_LINES=15 obs "install xim:gcc@16.1.0" x install -y xim:gcc@16.1.0
G="$UP/data/xpkgs/xim-x-gcc/16.1.0"
obs "abi" bash -c "ls -la '$UP/abi' '$UP/abi/'* 2>&1 | head -20; cat '$UP/abi.json' 2>&1 | head -30"
obs "installed xim payloads" bash -c "ls '$UP/data/xpkgs'; for d in '$UP'/data/xpkgs/xim-x-{gcc,binutils,glibc}; do echo \$d: \$(ls \$d 2>&1); done"
CC1="$(find "$G/libexec" -name cc1plus 2>/dev/null | head -1)"
LD="$(ls -d "$UP"/data/xpkgs/xim-x-binutils/*/bin/ld 2>/dev/null | head -1)"
obs "gcc ELF loader/runpath" bash -c "$(declare -f elf); for f in '$G/bin/gcc' '$G/bin/g++' '$CC1' '$LD'; do elf \"\$f\"; done"
obs "gcc specs: dynamic linker and rpath" bash -c "'$G/bin/g++' -dumpspecs | grep -nE 'dynamic-linker|rpath' | head -10; ls '$G'/lib/gcc/*/16.1.0/specs 2>&1; grep -nE 'dynamic-linker|rpath' '$G'/lib/gcc/*/16.1.0/specs 2>/dev/null | head"
hello_cpp "$T/h.cpp"
obs "g++ (upstream) builds and runs hello, no flags" bash -c "cd '$T' && '$G/bin/g++' -std=c++23 h.cpp -o h-gcc && ./h-gcc"
obs "the product's loader/runpath" bash -c "$(declare -f elf); elf '$T/h-gcc'"

section "O1: xlings $XV, llvm 23.1.3"
OBS_LINES=15 obs "install xim:llvm@23.1.3" x install -y xim:llvm@23.1.3
L="$UP/data/xpkgs/xim-x-llvm/23.1.3"
obs "llvm ELF loader/runpath" bash -c "$(declare -f elf); elf '$L/bin/clang++'; elf \$(readlink -f '$L/bin/clang++'); for f in \$(find '$L/lib' -maxdepth 2 -name 'libc++.so.1*' -o -maxdepth 2 -name 'libunwind.so.1*' | head -4); do elf \$f; done"
obs "clang cfg files" bash -c "for c in '$L'/bin/*.cfg; do echo \"== \$c\"; cat \"\$c\"; done"
obs "clang++ (upstream) builds and runs hello, no flags" bash -c "cd '$T' && '$L/bin/clang++' -std=c++23 h.cpp -o h-clang && ./h-clang"
obs "the product's loader/runpath" bash -c "$(declare -f elf); elf '$T/h-clang'"

section "O5 setup: the upstream store becomes root-owned and read-only (a system layer)"
# Round 2: the port's answer "installed" is simulated with mcpp's own marker,
# so that mcpp uses the linked payload instead of reinstalling over the link.
obs "simulate the port's answer (.mcpp_ok)" bash -c "for d in '$UP'/data/xpkgs/xim-x-gcc/16.1.0 '$UP'/data/xpkgs/xim-x-binutils/* '$UP'/data/xpkgs/xim-x-llvm/23.1.3; do touch \$d/.mcpp_ok && echo \$d; done"
obs "chown root, remove write" bash -c "sudo chown -R root:root '$UP/data/xpkgs' && sudo chmod -R a-w '$UP/data/xpkgs' && stat -c '%U %A %n' '$UP/data/xpkgs/xim-x-gcc/16.1.0' '$UP/data/xpkgs/xim-x-llvm/23.1.3'"

section "O2: mcpp $MV, managed gcc replaced by a link to the upstream payload"
curl -fsSL -o mc.tgz "https://github.com/mcpp-community/mcpp/releases/download/v$MV/mcpp-$MV-linux-$ARCH.tar.gz"
tar -xzf mc.tgz
M="$T/mcpp-$MV-linux-$ARCH/bin/mcpp"
export MCPP_HOME="$T/mh"
m() { clean_env MCPP_HOME="$MCPP_HOME" HOME="$T/home" "$M" "$@"; }
OBS_LINES=10 obs "mcpp toolchain install gcc 16.1.0" m toolchain install gcc 16.1.0
obs "mcpp toolchain default gcc@16.1.0" m toolchain default gcc@16.1.0
mkdir -p "$T/w" && cd "$T/w"
obs "mcpp new demo" m new demo
cd "$T/w/demo"
product() { find "$T/w/demo/target" -type f -name demo -perm -u+x 2>/dev/null | head -1; }
OBS_LINES=10 obs "baseline: build" m build
obs "baseline: run" m run
obs "baseline: product" bash -c "$(declare -f elf); elf \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
R="$MCPP_HOME/registry/data/xpkgs/xim-x-gcc/16.1.0"
obs "mcpp-modified gcc (for comparison)" bash -c "$(declare -f elf); elf '$R/bin/gcc'"
obs "binutils versions: mcpp vs upstream" bash -c "ls '$MCPP_HOME/registry/data/xpkgs/xim-x-binutils' '$UP/data/xpkgs/xim-x-binutils'"
UPD_BEFORE="$(tree_digest "$G")"
mv "$R" "$R.own" && ln -s "$G" "$R"
rm -rf "$T/w/demo/target"
OBS_LINES=30 obs "variant A (gcc linked): build -v" m build -v
obs "variant A: is the entry still a link?" ls -ld "$R"
obs "variant A: run" m run
obs "variant A: product" bash -c "$(declare -f elf); elf \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
obs "variant A: compile/link commands (loader, rpath, -B, specs)" bash -c "grep -rhoE -- '(--dynamic-linker[= ][^ ]+|-rpath[, ][^ ]+|-B[^ ]+|-specs=[^ ]+|--sysroot[= ][^ ]+)' '$T/w/demo/target' 2>/dev/null | sort | uniq -c | head -20"
for b in "$UP"/data/xpkgs/xim-x-binutils/*; do
v="$(basename "$b")"; RB="$MCPP_HOME/registry/data/xpkgs/xim-x-binutils/$v"
if [ -d "$RB" ] && [ ! -L "$RB" ]; then mv "$RB" "$RB.own" && ln -s "$b" "$RB"; say "linked binutils $v"; fi
done
rm -rf "$T/w/demo/target"
OBS_LINES=15 obs "variant B (gcc + binutils linked): build" m build
obs "variant B: are the entries still links?" bash -c "ls -ld '$R' '$MCPP_HOME'/registry/data/xpkgs/xim-x-binutils/*"
obs "variant B: run" m run
obs "variant B: product" bash -c "$(declare -f elf); elf \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
obs "was the upstream gcc written?" bash -c "echo before=$UPD_BEFORE after=\$( cd '$G' && find . -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | cut -c1-16)"

section "O2: mcpp $MV, managed llvm 23.1.3 replaced by a link"
cd "$T/w/demo"
OBS_LINES=10 obs "mcpp toolchain install llvm 23.1.3" m toolchain install llvm 23.1.3
rm -rf "$T/w/demo/target"
OBS_LINES=10 obs "baseline llvm: build" m build --toolchain llvm@23.1.3
obs "baseline llvm: product" bash -c "$(declare -f elf); elf \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
RL="$MCPP_HOME/registry/data/xpkgs/xim-x-llvm/23.1.3"
obs "mcpp-modified llvm libc++ runpath / cfg (comparison)" bash -c "$(declare -f elf); for f in \$(find '$RL/lib' -maxdepth 2 -name 'libc++.so.1*' | head -2); do elf \$f; done; cat '$RL/bin/clang++.cfg' 2>&1 | head -20"
LUP_BEFORE="$(tree_digest "$L")"
mv "$RL" "$RL.own" && ln -s "$L" "$RL"
rm -rf "$T/w/demo/target"
OBS_LINES=30 obs "llvm linked: build -v" m build -v --toolchain llvm@23.1.3
obs "llvm linked: is the entry still a link?" ls -ld "$RL"
obs "llvm linked: run" bash -c "\$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
obs "llvm linked: product" bash -c "$(declare -f elf); elf \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
obs "was the upstream llvm written?" bash -c "echo before=$LUP_BEFORE after=\$( cd '$L' && find . -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum | cut -c1-16)"
cp -r "$T/w/demo/target" "$T/logs/target-llvm" 2>/dev/null || true
60 changes: 60 additions & 0 deletions .github/probe/mac.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# O1 (macOS): llvm 23.1.3 installed by xlings $XV into a fresh home.
# O2 (macOS): mcpp $MV with its managed llvm replaced by a symbolic link to
# the upstream payload: the build, the resource directory, the product.
# Prints observations; never asserts.
set -uo pipefail
. "$(dirname "$0")/lib.sh"
cd "$T"
tree_digest() { (cd "$1" && find . -type f -print0 | sort -z | xargs -0 shasum -a 256 2>/dev/null | shasum -a 256 | cut -c1-16); }

section "host"
obs "sw_vers / xcode" bash -c 'sw_vers; xcode-select -p; xcrun --show-sdk-path'

curl -fsSL -o xl.tgz "https://github.com/openxlings/xlings/releases/download/v$XV/xlings-$XV-macosx-arm64.tar.gz"
tar -xzf xl.tgz
UP="$T/xlings-$XV-macosx-arm64"
x() { clean_env XLINGS_HOME="$UP" HOME="$T/home" "$UP/bin/xlings" "$@"; }

section "O1: xlings $XV, a fresh home, llvm 23.1.3"
obs "xlings --version" x --version
obs "self init" x self init
obs "update" x update
OBS_LINES=15 obs "install xim:llvm@23.1.3" x install -y xim:llvm@23.1.3
L="$UP/data/xpkgs/xim-x-llvm/23.1.3"
obs "clang++ and libc++ (dylibs, rpaths)" bash -c "$(declare -f macho); macho '$L/bin/clang++'; macho \$(python3 -c 'import os,sys;print(os.path.realpath(sys.argv[1]))' '$L/bin/clang++'); for f in \$(find '$L/lib' -maxdepth 1 -name 'libc++*.dylib' | head -3); do macho \$f; done"
obs "clang cfg files" bash -c "for c in '$L'/bin/*.cfg; do echo \"== \$c\"; cat \"\$c\"; done"
hello_cpp "$T/h.cpp"
obs "clang++ (upstream) builds and runs hello, no flags" bash -c "cd '$T' && '$L/bin/clang++' -std=c++23 h.cpp -o h-clang && ./h-clang"
obs "the product" bash -c "$(declare -f macho); macho '$T/h-clang'"

section "O2: mcpp $MV, managed llvm replaced by a link to the upstream payload"
curl -fsSL -o mc.tgz "https://github.com/mcpp-community/mcpp/releases/download/v$MV/mcpp-$MV-macosx-arm64.tar.gz"
tar -xzf mc.tgz
M="$T/mcpp-$MV-macosx-arm64/bin/mcpp"
export MCPP_HOME="$T/mh"
m() { clean_env MCPP_HOME="$MCPP_HOME" HOME="$T/home" "$M" "$@"; }
OBS_LINES=10 obs "mcpp toolchain install llvm 23.1.3" m toolchain install llvm 23.1.3
obs "mcpp toolchain default llvm@23.1.3" m toolchain default llvm@23.1.3
mkdir -p "$T/w" && cd "$T/w"
obs "mcpp new demo" m new demo
cd "$T/w/demo"
OBS_LINES=10 obs "baseline: build" m build
obs "baseline: run" m run
obs "baseline: product" bash -c "$(declare -f macho); macho \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
RL="$MCPP_HOME/registry/data/xpkgs/xim-x-llvm/23.1.3"
obs "resource dir (mcpp's own payload)" "$RL/bin/clang++" -print-resource-dir
obs "mcpp-modified cfg (comparison)" bash -c "cat '$RL/bin/clang++.cfg' 2>&1 | head -20"
# Round 2: simulate the port's answer "installed" with mcpp's own marker, then
# make the upstream read-only so that any write is visible.
touch "$L/.mcpp_ok"; chmod -R a-w "$L"
LUP_BEFORE="$(tree_digest "$L")"
mv "$RL" "$RL.own" && ln -s "$L" "$RL"
obs "resource dir through the link" "$RL/bin/clang++" -print-resource-dir
rm -rf "$T/w/demo/target"
OBS_LINES=30 obs "llvm linked: build -v" m -v build
obs "llvm linked: is the entry still a link?" ls -ld "$RL"
obs "llvm linked: run" m run
obs "llvm linked: product" bash -c "$(declare -f macho); macho \$(find '$T/w/demo/target' -type f -name demo -perm -u+x | head -1)"
obs "llvm linked: flags in build.ninja (sysroot, -isystem, -L, rpath)" bash -c "grep -rhoE -- '(--sysroot[= ][^ ]+|-isystem [^ ]+|-L[^ ]+|-rpath[, ][^ ]+|-resource-dir[= ][^ ]+)' '$T/w/demo/target' 2>/dev/null | sort | uniq -c | head -20"
obs "was the upstream llvm written?" bash -c "echo before=$LUP_BEFORE after=\$( cd '$L' && find . -type f -print0 | sort -z | xargs -0 shasum -a 256 | shasum -a 256 | cut -c1-16)"
53 changes: 53 additions & 0 deletions .github/probe/nfs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# F2 / #809 under mcpp: on an NFSv3 mount,
# - `mcpp new` (mcpp's own renameat2(RENAME_NOREPLACE) in scaffold_fs),
# - `mcpp toolchain install gcc` with the bundled xlings (2026.10.10.2),
# - the same with MCPP_VENDORED_XLINGS = xlings $XV,
# - then build and run a project that lives on NFS.
# Prints observations; never asserts.
set -uo pipefail
. "$(dirname "$0")/lib.sh"
N="${1:?usage: nfs.sh <NFSv3 mount>}"
cd "$T"
obs "mount" bash -c "grep ' $N ' /proc/mounts"
obs "renameat2(RENAME_NOREPLACE) here" python3 - "$N" <<'PY'
import ctypes, errno, os, sys
libc = ctypes.CDLL(None, use_errno=True)
d = sys.argv[1]; a = os.path.join(d, "probe-a"); b = os.path.join(d, "probe-b")
open(a, "w").close()
rc = libc.syscall(316, -100, a.encode(), -100, b.encode(), 1)
print("rc", rc, "errno", errno.errorcode.get(ctypes.get_errno(), ctypes.get_errno()))
for p in (a, b):
try: os.remove(p)
except FileNotFoundError: pass
PY

curl -fsSL -o mc.tgz "https://github.com/mcpp-community/mcpp/releases/download/v$MV/mcpp-$MV-linux-x86_64.tar.gz"
tar -xzf mc.tgz
M="$T/mcpp-$MV-linux-x86_64/bin/mcpp"
curl -fsSL -o xl.tgz "https://github.com/openxlings/xlings/releases/download/v$XV/xlings-$XV-linux-x86_64.tar.gz"
tar -xzf xl.tgz
XB="$T/xlings-$XV-linux-x86_64/bin/xlings"

section "mcpp new on NFS (local MCPP_HOME)"
export MCPP_HOME="$T/mh-local"
m() { clean_env MCPP_HOME="$MCPP_HOME" HOME="$T/home" "$M" "$@"; }
mkdir -p "$N/w1" && cd "$N/w1"
obs "mcpp new demo (cwd on NFS)" m new demo
obs "what was made" bash -c "ls -la '$N/w1' '$N/w1/demo' 2>&1 | head -20"

section "MCPP_HOME on NFS, bundled xlings"
export MCPP_HOME="$N/mh-bundled"
OBS_LINES=20 obs "mcpp toolchain install gcc 16.1.0" m toolchain install gcc 16.1.0

section "MCPP_HOME on NFS, xlings $XV"
export MCPP_HOME="$N/mh-new"
mv_() { clean_env MCPP_HOME="$MCPP_HOME" HOME="$T/home" MCPP_VENDORED_XLINGS="$XB" "$M" "$@"; }
OBS_LINES=20 obs "mcpp toolchain install gcc 16.1.0" mv_ toolchain install gcc 16.1.0
obs "mcpp toolchain default gcc@16.1.0" mv_ toolchain default gcc@16.1.0
obs "xlings in that home" bash -c "'$N/mh-new/registry/bin/xlings' --version"
mkdir -p "$T/local" && cd "$T/local" && mv_ new demo >/dev/null 2>&1
cp -r "$T/local/demo" "$N/w2" 2>/dev/null || { mkdir -p "$N/w2"; cp -r "$T/local/demo/." "$N/w2/"; }
cd "$N/w2"
OBS_LINES=20 obs "build a project on NFS" mv_ build
obs "run" mv_ run
Loading
Loading