Skip to content
Merged
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
26 changes: 16 additions & 10 deletions .agents/docs/2026-09-12-622-verify.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ cat > "$d/build.mcpp" <<'CPP'
import mcpp;
#include <string>
int main() {
// An action runs with the build directory as its cwd, so the source is
// named by its absolute path; the manifest directory is the anchor.
const std::string in = std::string(mcpp::manifest_dir()) + "/gen/in.txt";
const std::string out = std::string(mcpp::out_dir()) + "/res.bin";
mcpp::action a; a.id = "gen:res"; a.role = "source";
a.arg("/bin/cp").arg("gen/in.txt").arg(out.c_str()).input("gen/in.txt").output(out.c_str()).submit();
a.arg("/bin/cp").arg(in.c_str()).arg(out.c_str()).input(in.c_str()).output(out.c_str()).submit();
mcpp::deploy(out.c_str(), "dep.resources");
return 0;
}
Expand Down Expand Up @@ -117,22 +120,25 @@ printf '\n[target.'"'"'cfg(linux)'"'"'.abi]\nthreads = true\n' >> "$d/mcpp.toml"
printf '\n[target.'"'"'cfg(linux)'"'"'.abi]\nfrobnicate = true\n' >> "$d/mcpp.toml"
(cd "$d" && "$STORE" build > b3.log 2>&1) && fail "an unknown abi member was accepted" || { has_text "$d/b3.log" "threads, exceptions" && ok "unknown member refused naming both members" || { fail "refused without naming the members"; tail -2 "$d/b3.log"; }; }

section "F. frameworks per target: a Linux build is byte-identical with and without the tables"
section "F. frameworks per target: a Linux build renders no framework and its graph is unchanged"
d=$root/f; mkprog "$d" fw ""
(cd "$d" && "$STORE" build > b1.log 2>&1) && n1=$(find "$d/target" -name build.ninja | head -1) && cp "$n1" "$d/n1"
printf '\n[runtime]\nframeworks = ["Foundation"]\n\n[target.macos.runtime]\nframeworks = ["AppKit"]\n\n[target.'"'"'cfg(os = "ios")'"'"'.runtime]\nframeworks = ["UIKit"]\n' >> "$d/mcpp.toml"
rm -rf "$d/target"; (cd "$d" && "$STORE" build > b2.log 2>&1) && n2=$(find "$d/target" -name build.ninja | head -1)
if [ -n "${n2:-}" ] && cmp -s "$d/n1" "$n2"; then ok "build.ninja identical on Linux"; else fail "build.ninja differs or build failed"; fi
# The manifest changed, so the fingerprint directory named inside build.ninja
# changes with it; the comparison is made with that segment normalised, and
# what it then asserts is that no flag, input or edge differs on Linux.
norm() { sed 's#/[0-9a-f]\{16\}/#/FP/#g' "$1"; }
if [ -n "${n2:-}" ] && diff -q <(norm "$d/n1") <(norm "$n2") >/dev/null; then ok "build.ninja identical on Linux up to the fingerprint segment"; else fail "build.ninja differs beyond the fingerprint segment, or the build failed"; diff <(norm "$d/n1") <(norm "${n2:-/dev/null}") | head -6; fi
grep -q -- '-framework' "${n2:-/dev/null}" && fail "a -framework flag rendered on an ELF target" || ok "no -framework on Linux"
grep -q "unsupported key 'frameworks'" "$d/b2.log" && fail "frameworks reported as unsupported under a target table" || ok "frameworks accepted under [target.<sel>.runtime]"

section "G. platforms names the rows that exist"
d=$root/g; mkprog "$d" pl 'platforms = ["linux", "ios", "android", "emscripten"]'
sed -i 's/^\[targets.pl\]/[targets.pl]/' "$d/mcpp.toml"; python3 - "$d/mcpp.toml" <<'PY'
import sys,re; p=sys.argv[1]; s=open(p).read()
s=s.replace('version = "0.1.0"\n','version = "0.1.0"\nplatforms = ["linux", "ios", "android", "emscripten"]\n',1).replace('\nplatforms = ["linux", "ios", "android", "emscripten"]\n\n','\n\n')
open(p,'w').write(s)
PY
(cd "$d" && "$STORE" build --strict > b1.log 2>&1) && ok "the six-word vocabulary passes --strict" || { fail "a known platform name was refused"; grep -i platform "$d/b1.log" | head -2; }
d=$root/g; rm -rf "$d"; mkdir -p "$d/src"
printf '[package]\nname = "pl"\nversion = "0.1.0"\nplatforms = ["linux", "ios", "android", "emscripten"]\n\n[targets.pl]\nkind = "bin"\nmain = "src/main.cpp"\n' > "$d/mcpp.toml"
printf '#include <cstdio>\nint main() { std::puts("1-2-3"); return 0; }\n' > "$d/src/main.cpp"
(cd "$d" && "$STORE" build > b1.log 2>&1) || { fail "a manifest naming the six platforms does not build"; tail -3 "$d/b1.log"; }
grep -q "unknown platform" "$d/b1.log" && fail "a known platform name was reported as unknown" || ok "the six-word vocabulary is accepted"
sed -i 's/platforms = \[.*\]/platforms = ["web"]/' "$d/mcpp.toml"; rm -rf "$d/target"
(cd "$d" && "$STORE" build --strict > b2.log 2>&1) && fail "'web' was accepted" || { has_text "$d/b2.log" "unknown platform 'web'" && has_text "$d/b2.log" "linux | macos | windows | ios | android | emscripten" && ok "'web' refused naming the six" || { fail "refused without the vocabulary"; tail -2 "$d/b2.log"; }; }

Expand Down
70 changes: 70 additions & 0 deletions tests/e2e/658_a_program_runs_on_an_attached_android_device.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# requires: android-ndk android-device
# 658 -- `mcpp run --target aarch64-linux-android` on a real device (#622).
#
# The Android rows were `verified` through qemu-user on a system image's
# bionic. This script is the device reading: the program is pushed, executed
# and its exit status returned by `adb-run`, the session program
# `xim:android-platform-tools` ships (37.0.1-3 and later), named as this
# target's runner and provisioned by the same manifest. The engine hands the
# runner the link output and nothing else; the session is the package's.
#
# The capability probe in run_all.sh selects an arm64 device that is not an
# emulator and exports its serial as E2E_ANDROID_SERIAL; adb-run addresses it
# through ANDROID_SERIAL, which is adb's own selector.
#
# 1. a program prints its marker on the device and mcpp run exits 0;
# 2. a program that exits 7 makes mcpp run exit non-zero -- the status
# crosses the session, which is what distinguishes a runner from a
# launcher that reports its own success.
set -e
TARGET=aarch64-linux-android
: "${E2E_ANDROID_SERIAL:?run_all.sh exports the serial of the selected device}"
export ANDROID_SERIAL="$E2E_ANDROID_SERIAL"

TMP=$(mktemp -d)
trap "rm -rf $TMP" EXIT
fail() { echo "FAIL: $1"; shift; for f in "$@"; do echo "--- $f ---"; cat "$f" 2>/dev/null; done; exit 1; }

mkdir -p "$TMP/p/src"
cat > "$TMP/p/mcpp.toml" <<TOML
[package]
name = "ondevice"
version = "0.1.0"

[targets.ondevice]
kind = "bin"
main = "src/main.cpp"

[targets.exits7]
kind = "bin"
main = "src/exits7.cpp"

[target.$TARGET]
min_api_level = 24
runner = ["adb-run"]

[target.$TARGET.xlings.workspace]
"xim:android-platform-tools" = ">=37.0.1-3"
TOML
cat > "$TMP/p/src/main.cpp" <<'CPP'
#include <cstdio>
#include <string>
int main() { std::string s = "1-2-" + std::to_string(3); std::puts(s.c_str()); return 0; }
CPP
cat > "$TMP/p/src/exits7.cpp" <<'CPP'
int main() { return 7; }
CPP

cd "$TMP/p"
"$MCPP" run ondevice --target "$TARGET" > run.log 2>&1 \
|| fail "mcpp run on the device failed" run.log
grep -qx '1-2-3' run.log || fail "the program's marker did not come back from the device" run.log
echo "1. the marker printed on device $ANDROID_SERIAL OK"

if "$MCPP" run exits7 --target "$TARGET" > run7.log 2>&1; then
fail "a program exiting 7 on the device was reported as success" run7.log
fi
echo "2. the exit status crossed the session OK"

echo "PASS: 658_a_program_runs_on_an_attached_android_device"
20 changes: 19 additions & 1 deletion tests/e2e/run_all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,24 @@ case "$OS" in
&>/dev/null; then
CAPS+=(android-ndk)
fi
# android-device: an arm64 Android device attached over USB and
# answering `adb`, taken from the platform-tools payload (the same
# store the NDK probe reads). An emulator is not a device here: the
# rows this capability serves are the aarch64 ones, and an emulator
# line is named `emulator-<port>`. The serial is exported so a test
# can address that device when more than one is attached.
for adb in "$HOME"/.xlings/data/xpkgs/xim-x-android-platform-tools/*/adb \
"${MCPP_HOME:-$HOME/.mcpp}"/registry/data/xpkgs/xim-x-android-platform-tools/*/adb; do
[[ -x "$adb" ]] || continue
serial=$("$adb" devices 2>/dev/null | awk '$2=="device" && $1 !~ /^emulator-/ {print $1; exit}')
[[ -n "$serial" ]] || continue
abi=$(ANDROID_SERIAL="$serial" "$adb" shell getprop ro.product.cpu.abi 2>/dev/null | tr -d '\r')
if [[ "$abi" == "arm64-v8a" ]]; then
CAPS+=(android-device)
export E2E_ANDROID_SERIAL="$serial"
break
fi
done
# pack capability: ELF + patchelf both required
if [[ " ${CAPS[*]} " == *" patchelf "* ]]; then
CAPS+=(pack)
Expand Down Expand Up @@ -232,7 +250,7 @@ echo "Detected capabilities: ${CAPS[*]:-<none>}"
# absent on Linux and must stay legal to declare. It is checked against the
# CAPS+=() calls above by tests/e2e/README or by reading them -- keep it in
# sync when adding a capability.
KNOWN_CAPS=(android-ndk elf fresh-sandbox gcc import-std-libcxx jq llvm macos
KNOWN_CAPS=(android-device android-ndk elf fresh-sandbox gcc import-std-libcxx jq llvm macos
mingw mingw-cross msvc musl nasm no-msvc pack patchelf python3
qemu-arm qemu-riscv scan-deps symlink unix-shell windows wine
xlings-msvc)
Expand Down
Loading