@@ -13,19 +13,138 @@ file -L "$LLVM_ROOT/bin/clang++" | grep -q 'ARM aarch64' || {
1313}
1414" $LLVM_ROOT /bin/clang++" --version
1515work=" $( mktemp -d) "
16+ report=" ${MCPP_NATIVE_REPORT_DIR:- ${RUNNER_TEMP:- $work } / native-llvm-arm64} "
17+ mkdir -p " $report "
1618trap ' rm -rf "$work"' EXIT
1719export MCPP_HOME=" $work /mcpp-home"
1820" $MCPP " self config --mirror " ${MCPP_E2E_MIRROR:- GLOBAL} "
1921" $MCPP " self env --format json | python3 -c ' import json,sys; d=json.load(sys.stdin); assert d["data"]["defaultToolchain"] == "llvm@23.1.3", d'
2022" $MCPP " new " $work /native-probe"
23+ mkdir -p " $work /nativeabi/src"
24+ cat > " $work /nativeabi/mcpp.toml" << 'TOML '
25+ [package]
26+ name = "nativeabi"
27+ version = "0.1.0"
28+ [build]
29+ sources = ["src/*.c"]
30+ [targets.nativeabi]
31+ kind = "shared"
32+ soname = "libnativeabi.so.1"
33+ TOML
34+ printf ' int native_answer(int x) { return x + 1; }\n' > " $work /nativeabi/src/answer.c"
2135cd " $work /native-probe"
36+ cat >> mcpp.toml << 'TOML '
37+
38+ [dependencies]
39+ nativeabi = { path = "../nativeabi" }
40+ TOML
41+ cat > src/main.cpp << 'CPP '
42+ import std;
43+ import std.compat;
44+ extern "C" int native_answer(int);
45+ int native_headers();
46+ struct alignas(16) Pair { unsigned long a, b; };
47+ static_assert(sizeof(Pair) == 16);
48+ int main() {
49+ auto memory = std::make_unique<int>(41);
50+ int value = 0;
51+ std::thread worker([&] { value = native_answer(*memory); });
52+ worker.join();
53+ bool caught = false;
54+ try { throw std::runtime_error("native"); }
55+ catch (const std::runtime_error&) { caught = true; }
56+ std::atomic<Pair> atom;
57+ atom.store(Pair{0, 0});
58+ Pair expected{0, 0};
59+ bool exchanged = atom.compare_exchange_strong(expected, Pair{42, 7});
60+ auto result = atom.load();
61+ void* raw = ::malloc(32);
62+ bool allocated = raw != nullptr;
63+ ::free(raw);
64+ bool ok = value == 42 && caught && exchanged && result.a == 42
65+ && result.b == 7 && allocated && native_headers() == 1;
66+ ::printf("native-stdlib-cabi=%s\n", ok ? "ok" : "failed");
67+ return ok ? 0 : 1;
68+ }
69+ CPP
70+ cat > src/headers.cpp << 'CPP '
71+ #include <features.h>
72+ #include <stddef.h>
73+ #include <unistd.h>
74+ #include <pthread.h>
75+ int native_headers() { return 1; }
76+ CPP
2277" $MCPP " build
23- " $MCPP " run
78+ " $MCPP " run | tee " $report /runtime.log"
79+ grep -qF ' native-stdlib-cabi=ok' " $report /runtime.log"
2480binary=" $( find target/aarch64-linux-gnu -type f -path ' */bin/native-probe' | head -1) "
2581[[ -n " $binary " ]] || { echo ' FAIL: default did not produce a native GNU artifact' ; exit 1; }
26- readelf -l " $binary " | grep -q ' ld-linux-aarch64.so.1' || {
82+ readelf -l " $binary " > " $report /program-headers.txt"
83+ readelf -d " $binary " > " $report /dynamic.txt"
84+ grep -q ' ld-linux-aarch64.so.1' " $report /program-headers.txt" || {
2785 echo ' FAIL: native default is not glibc-linked' ; exit 1;
2886}
87+ interpreter=" $( sed -n ' s/.*Requesting program interpreter: \(.*\)]/\1/p' " $report /program-headers.txt" ) "
88+ case " $interpreter " in
89+ " $MCPP_HOME " /registry/data/xpkgs/xim-x-glibc/* /lib* /ld-linux-aarch64.so.1) ;;
90+ * ) echo " FAIL: GNU default uses an ambient loader: $interpreter " ; exit 1 ;;
91+ esac
92+ " $interpreter " --list " $binary " > " $report /loader-resolution.txt"
93+ python3 - " $report /loader-resolution.txt" << 'PYLOADER '
94+ import pathlib, re, sys
95+ text = pathlib.Path(sys.argv[1]).read_text()
96+ assert not re.search(r'=> /(?:usr/lib|lib64?|usr/local/lib)/', text), text
97+ print('PASS: the native loader resolves no ambient system library')
98+ PYLOADER
99+ grep -q ' libnativeabi.so.1' " $report /dynamic.txt" || {
100+ echo ' FAIL: the C ABI consumer does not depend on the shared library' ; exit 1;
101+ }
102+ # Replay the engine's effective header compile as a preprocess trace. The trace
103+ # must resolve libc headers through the payload, without an ambient /usr tree.
104+ python3 - " $report " << 'PYTRACE '
105+ import json, pathlib, re, shlex, subprocess, sys
106+ report = pathlib.Path(sys.argv[1])
107+ cdb = next(pathlib.Path('target/aarch64-linux-gnu').rglob('compile_commands.json'))
108+ entries = json.loads(cdb.read_text())
109+ entry = next(e for e in entries if e['file'].endswith('/headers.cpp'))
110+ args = entry.get('arguments') or shlex.split(entry['command'])
111+ clean = []
112+ i = 0
113+ while i < len(args):
114+ arg = args[i]
115+ if arg in ('-o', '-MF', '-MT', '-MQ'):
116+ i += 2
117+ continue
118+ if arg in ('-c', '-MMD', '-MD', '-MP'):
119+ i += 1
120+ continue
121+ clean.append(arg)
122+ i += 1
123+ result = subprocess.run(clean + ['-E', '-H'], cwd=entry['directory'],
124+ text=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
125+ (report / 'include-trace.txt').write_text(result.stderr)
126+ assert result.returncode == 0, result.stderr
127+ headers = [m.group(1) for line in result.stderr.splitlines()
128+ if (m := re.match(r'^\.+ (.+)$', line))]
129+ assert any('xim-x-glibc' in path and path.endswith('/features.h') for path in headers), headers
130+ assert not any(re.match(r'/usr/(include|lib/gcc|local/include)(/|$)', path) for path in headers), headers
131+ print('PASS: libc include trace uses the managed payload')
132+ PYTRACE
133+ cat " $report /program-headers.txt" " $report /loader-resolution.txt" " $report /include-trace.txt"
134+ " $MCPP " pack --mode self-contained --format dir --message-format json > " $report /pack.json"
135+ bundle=" $( python3 - " $report /pack.json" << 'PYPACK '
136+ import json, sys
137+ artifact = json.load(open(sys.argv[1]))['data']['artifacts'][0]
138+ assert artifact['type'] == 'directory', artifact
139+ print(artifact['path'])
140+ PYPACK
141+ ) "
142+ # Directory deployment exercises the documented portable bundle entry point.
143+ cp -a " $bundle " " $work /deployed"
144+ (cd / && env -u LD_LIBRARY_PATH " $work /deployed/native-probe" ) | tee " $report /deployed.log"
145+ grep -qF ' native-stdlib-cabi=ok' " $report /deployed.log"
146+ " $MCPP " new " $work /musl-probe"
147+ cd " $work /musl-probe"
29148" $MCPP " build --target aarch64-linux-musl --toolchain gcc@16.1.0-musl
30149" $MCPP " run --target aarch64-linux-musl --toolchain gcc@16.1.0-musl
31150printf ' %s\n' ' PASS: native ARM64 LLVM installs, builds and runs'
0 commit comments