Skip to content

Commit 5260dec

Browse files
e2e: a program runs on an attached Android device through adb-run (#622) (#625)
* e2e 658: a program runs on an attached Android device through adb-run (#622) * verify script: the deploy probe names its input by an absolute path * verify script: the frameworks probe normalises the fingerprint segment; the platforms probe writes its key under [package] --------- Co-authored-by: speak-agent <248744407+speak-agent@users.noreply.github.com>
1 parent 22cedfa commit 5260dec

3 files changed

Lines changed: 105 additions & 11 deletions

File tree

.agents/docs/2026-09-12-622-verify.sh

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,12 @@ cat > "$d/build.mcpp" <<'CPP'
7474
import mcpp;
7575
#include <string>
7676
int main() {
77+
// An action runs with the build directory as its cwd, so the source is
78+
// named by its absolute path; the manifest directory is the anchor.
79+
const std::string in = std::string(mcpp::manifest_dir()) + "/gen/in.txt";
7780
const std::string out = std::string(mcpp::out_dir()) + "/res.bin";
7881
mcpp::action a; a.id = "gen:res"; a.role = "source";
79-
a.arg("/bin/cp").arg("gen/in.txt").arg(out.c_str()).input("gen/in.txt").output(out.c_str()).submit();
82+
a.arg("/bin/cp").arg(in.c_str()).arg(out.c_str()).input(in.c_str()).output(out.c_str()).submit();
8083
mcpp::deploy(out.c_str(), "dep.resources");
8184
return 0;
8285
}
@@ -117,22 +120,25 @@ printf '\n[target.'"'"'cfg(linux)'"'"'.abi]\nthreads = true\n' >> "$d/mcpp.toml"
117120
printf '\n[target.'"'"'cfg(linux)'"'"'.abi]\nfrobnicate = true\n' >> "$d/mcpp.toml"
118121
(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"; }; }
119122

120-
section "F. frameworks per target: a Linux build is byte-identical with and without the tables"
123+
section "F. frameworks per target: a Linux build renders no framework and its graph is unchanged"
121124
d=$root/f; mkprog "$d" fw ""
122125
(cd "$d" && "$STORE" build > b1.log 2>&1) && n1=$(find "$d/target" -name build.ninja | head -1) && cp "$n1" "$d/n1"
123126
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"
124127
rm -rf "$d/target"; (cd "$d" && "$STORE" build > b2.log 2>&1) && n2=$(find "$d/target" -name build.ninja | head -1)
125-
if [ -n "${n2:-}" ] && cmp -s "$d/n1" "$n2"; then ok "build.ninja identical on Linux"; else fail "build.ninja differs or build failed"; fi
128+
# The manifest changed, so the fingerprint directory named inside build.ninja
129+
# changes with it; the comparison is made with that segment normalised, and
130+
# what it then asserts is that no flag, input or edge differs on Linux.
131+
norm() { sed 's#/[0-9a-f]\{16\}/#/FP/#g' "$1"; }
132+
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
133+
grep -q -- '-framework' "${n2:-/dev/null}" && fail "a -framework flag rendered on an ELF target" || ok "no -framework on Linux"
126134
grep -q "unsupported key 'frameworks'" "$d/b2.log" && fail "frameworks reported as unsupported under a target table" || ok "frameworks accepted under [target.<sel>.runtime]"
127135

128136
section "G. platforms names the rows that exist"
129-
d=$root/g; mkprog "$d" pl 'platforms = ["linux", "ios", "android", "emscripten"]'
130-
sed -i 's/^\[targets.pl\]/[targets.pl]/' "$d/mcpp.toml"; python3 - "$d/mcpp.toml" <<'PY'
131-
import sys,re; p=sys.argv[1]; s=open(p).read()
132-
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')
133-
open(p,'w').write(s)
134-
PY
135-
(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; }
137+
d=$root/g; rm -rf "$d"; mkdir -p "$d/src"
138+
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"
139+
printf '#include <cstdio>\nint main() { std::puts("1-2-3"); return 0; }\n' > "$d/src/main.cpp"
140+
(cd "$d" && "$STORE" build > b1.log 2>&1) || { fail "a manifest naming the six platforms does not build"; tail -3 "$d/b1.log"; }
141+
grep -q "unknown platform" "$d/b1.log" && fail "a known platform name was reported as unknown" || ok "the six-word vocabulary is accepted"
136142
sed -i 's/platforms = \[.*\]/platforms = ["web"]/' "$d/mcpp.toml"; rm -rf "$d/target"
137143
(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"; }; }
138144

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#!/usr/bin/env bash
2+
# requires: android-ndk android-device
3+
# 658 -- `mcpp run --target aarch64-linux-android` on a real device (#622).
4+
#
5+
# The Android rows were `verified` through qemu-user on a system image's
6+
# bionic. This script is the device reading: the program is pushed, executed
7+
# and its exit status returned by `adb-run`, the session program
8+
# `xim:android-platform-tools` ships (37.0.1-3 and later), named as this
9+
# target's runner and provisioned by the same manifest. The engine hands the
10+
# runner the link output and nothing else; the session is the package's.
11+
#
12+
# The capability probe in run_all.sh selects an arm64 device that is not an
13+
# emulator and exports its serial as E2E_ANDROID_SERIAL; adb-run addresses it
14+
# through ANDROID_SERIAL, which is adb's own selector.
15+
#
16+
# 1. a program prints its marker on the device and mcpp run exits 0;
17+
# 2. a program that exits 7 makes mcpp run exit non-zero -- the status
18+
# crosses the session, which is what distinguishes a runner from a
19+
# launcher that reports its own success.
20+
set -e
21+
TARGET=aarch64-linux-android
22+
: "${E2E_ANDROID_SERIAL:?run_all.sh exports the serial of the selected device}"
23+
export ANDROID_SERIAL="$E2E_ANDROID_SERIAL"
24+
25+
TMP=$(mktemp -d)
26+
trap "rm -rf $TMP" EXIT
27+
fail() { echo "FAIL: $1"; shift; for f in "$@"; do echo "--- $f ---"; cat "$f" 2>/dev/null; done; exit 1; }
28+
29+
mkdir -p "$TMP/p/src"
30+
cat > "$TMP/p/mcpp.toml" <<TOML
31+
[package]
32+
name = "ondevice"
33+
version = "0.1.0"
34+
35+
[targets.ondevice]
36+
kind = "bin"
37+
main = "src/main.cpp"
38+
39+
[targets.exits7]
40+
kind = "bin"
41+
main = "src/exits7.cpp"
42+
43+
[target.$TARGET]
44+
min_api_level = 24
45+
runner = ["adb-run"]
46+
47+
[target.$TARGET.xlings.workspace]
48+
"xim:android-platform-tools" = ">=37.0.1-3"
49+
TOML
50+
cat > "$TMP/p/src/main.cpp" <<'CPP'
51+
#include <cstdio>
52+
#include <string>
53+
int main() { std::string s = "1-2-" + std::to_string(3); std::puts(s.c_str()); return 0; }
54+
CPP
55+
cat > "$TMP/p/src/exits7.cpp" <<'CPP'
56+
int main() { return 7; }
57+
CPP
58+
59+
cd "$TMP/p"
60+
"$MCPP" run ondevice --target "$TARGET" > run.log 2>&1 \
61+
|| fail "mcpp run on the device failed" run.log
62+
grep -qx '1-2-3' run.log || fail "the program's marker did not come back from the device" run.log
63+
echo "1. the marker printed on device $ANDROID_SERIAL OK"
64+
65+
if "$MCPP" run exits7 --target "$TARGET" > run7.log 2>&1; then
66+
fail "a program exiting 7 on the device was reported as success" run7.log
67+
fi
68+
echo "2. the exit status crossed the session OK"
69+
70+
echo "PASS: 658_a_program_runs_on_an_attached_android_device"

tests/e2e/run_all.sh

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

0 commit comments

Comments
 (0)