Skip to content

Commit 1d25c1c

Browse files
committed
dist-apk: a NativeActivity that runs to completion on both real rows
`tests/apk-consumer/src/main.cpp` now defines `ANativeActivity_onCreate` under `__ANDROID__`, exported and linked against `libandroid.so`/ `liblog.so` (`[target.'cfg(env = "android")'.runtime] libraries = ["android", "log"]`): it writes the fixture's marker through `std::puts` (redirected to logcat by `adb-run`'s own `log.redirect-stdio`) and, unconditionally, through `__android_log_print`, then calls `ANativeActivity_finish`. `int main` keeps serving the host row from the same translation unit. Run end to end (2026-09-12, mcpp PR #624 head, `mcpp run --format apk --target <triple>`) against two real rows: x86_64-linux-android, xim:android-emulator@37.1.11 + xim:android-system-image@24-default-x86_64, KVM-accelerated, booted through a two-symlink ANDROID_SDK_ROOT (emulator/, platform-tools/ as siblings -- the emulator SIGSEGVs otherwise, see the android-emulator fix in the index repository): exit 0, logcat carries "I apk-consumer: 1-2-3". aarch64-linux-android, a physical arm64-v8a phone over adb (a second device this session found attached beside the emulator): exit 0, same marker, `log.redirect-stdio` itself refused by the device ("Failed to set property") -- which is exactly the case `__android_log_print` covers. Verified installed with `pm list packages`, then uninstalled to leave the phone clean. Both runs surfaced a real adb-run defect, fixed in the index repository (xim-pkgindex, pkgs/a/android-platform-tools.lua, 37.0.1-3): `am start -W` never returns for an activity that finishes from `onCreate` (two independent invocations still alive after 691s and 354s, killed by hand), and `finish()` ends the activity, not the process, so a `pidof`-only wait loop never ends either. adb-run now starts without `-W` and waits for `dumpsys activity activities` to drop the record in addition to `pidof`, then force-stops the app. `.github/workflows/ci.yml`'s dist-apk step still asserts packaging only (build + pack + aapt2/apksigner/jar checks) -- this runner has no emulator and no device, so a `run` step here could only ever report "no device/emulator found", never the marker. Said explicitly in the step's own comment now, rather than left implicit.
1 parent 5ebc06b commit 1d25c1c

3 files changed

Lines changed: 74 additions & 13 deletions

File tree

‎.github/workflows/ci.yml‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,19 @@ jobs:
10941094
# this step needs the release that carries them (see this repository's
10951095
# README, `dist-apk`'s own row) -- raising the pin is P5's own change,
10961096
# together with the rest of this collection's release.
1097+
#
1098+
# THIS STEP ASSERTS PACKAGING ONLY, DELIBERATELY, NOT `mcpp run
1099+
# --format apk`. This fixture's own `main.cpp` and `mcpp.toml` DO wire
1100+
# up a real device row (`ANativeActivity_onCreate`, `runner =
1101+
# ["adb-run"]`) and it was run end to end (2026-09-12) against both a
1102+
# KVM-accelerated x86_64 emulator (`xim:android-emulator` +
1103+
# `xim:android-system-image`) and a physical arm64-v8a phone over adb,
1104+
# both exiting 0 with `1-2-3` in the logcat output -- but this GitHub-
1105+
# hosted runner has neither an emulator image nor a device attached,
1106+
# so a `run` step here would only ever report "no device/emulator
1107+
# found" and never `1-2-3`. That measurement stays a local/self-hosted
1108+
# concern until a runner class with a real device or a KVM-capable
1109+
# emulator is wired into this workflow.
10971110
- name: dist-apk produces a signed APK, level 0 and level 1
10981111
working-directory: tests/apk-consumer
10991112
run: |

‎tests/apk-consumer/mcpp.toml‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,27 @@ plugins = { path = "../..", features = ["dist-apk"], host-module = true }
2525
kind = "app"
2626
main = "src/main.cpp"
2727

28+
# `ANativeActivity_onCreate` (src/main.cpp) calls `ANativeActivity_finish`
29+
# and `__android_log_print`, both declared in `libandroid.so`/`liblog.so` --
30+
# the NDK's own stable stub libraries for the platform's native-activity and
31+
# logging ABIs, present in every system image `dist-apk` targets. Neither is
32+
# pulled in by anything already linked, so the closure needs them stated,
33+
# not inferred.
34+
[target.'cfg(env = "android")'.runtime]
35+
libraries = ["android", "log"]
36+
2837
# #622 A11: `minSdkVersion` reads `mcpp::min_platform_version()`, so the
2938
# criterion below (`aapt2 dump badging` reporting `sdkVersion:'24'`) is a
3039
# property of THIS declaration and not of a default this fixture never
3140
# stated.
3241
[target.x86_64-linux-android]
3342
min_api_level = 24
3443
runner = ["adb-run"]
44+
45+
# Same runner, the emulator's own guest abi. `adb-run` passes no `-s`
46+
# (device selection is `adb`'s own -- ANDROID_SERIAL is the caller's
47+
# configuration), so the identical declaration serves both the emulated
48+
# x86_64 row and a physical arm64-v8a device.
49+
[target.aarch64-linux-android]
50+
min_api_level = 24
51+
runner = ["adb-run"]

‎tests/apk-consumer/src/main.cpp‎

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,54 @@
11
#include <cstdio>
22
#include <string>
33

4-
// This fixture asserts PACKAGING (and, when the emulator lane runs, a real
5-
// launch); nothing else here runs on a device by default -- see dist/
6-
// apk.cppm's header and this fixture's own mcpp.toml for what is measured
7-
// about the row instead. The engine compiles this file as a translation
8-
// unit of `lib<name>.so`, exactly as it does for any other member of the
9-
// shared object -- an `app` target's `main` keeps its ordinary meaning as a
10-
// compiled entry point; the platform's own entry into the library is
11-
// `android.app.NativeActivity`, named in the manifest this build's
12-
// `dist-apk` step generates, and is not this function.
4+
#if defined(__ANDROID__)
5+
#include <android/log.h>
6+
#include <android/native_activity.h>
7+
#endif
8+
9+
// This fixture asserts PACKAGING, and, on the row `adb-run` serves, a real
10+
// launch through `mcpp run --format apk` -- see dist/apk.cppm's header and
11+
// this fixture's own mcpp.toml for what is measured about the row. The
12+
// engine compiles this file as a translation unit of `lib<name>.so`,
13+
// exactly as it does for any other member of the shared object -- an
14+
// `app` target's `main` keeps its ordinary meaning as a compiled entry
15+
// point for the HOST row, where this program is linked and run directly.
16+
// On Android, the platform never calls `main`: it loads the library and
17+
// calls `android.app.NativeActivity`'s own native entry point,
18+
// `ANativeActivity_onCreate`, named in the manifest `dist-apk` generates.
19+
// Both are defined here so the one source file works on both rows.
1320
//
1421
// `std::to_string` FORCES THE libc++ DEPENDENCY THIS FIXTURE MEASURES.
1522
// `dist-apk` bundles `libc++_shared.so` only when the closure NEEDs it
16-
// (`needs_libcxx_shared`, read from the dynamic section); a `main` that
23+
// (`needs_libcxx_shared`, read from the dynamic section); a program that
1724
// touched nothing but `<cstdio>` would not exercise that path honestly, so
18-
// this one builds its marker through `std::string`.
25+
// this one builds its marker through `std::string`, on both rows.
26+
namespace {
27+
std::string marker() {
28+
return "1-2-3, count=" + std::to_string(3);
29+
}
30+
} // namespace
31+
1932
int main() {
20-
std::string marker = "1-2-3, count=" + std::to_string(3);
21-
std::puts(marker.c_str());
33+
std::puts(marker().c_str());
2234
return 0;
2335
}
36+
37+
#if defined(__ANDROID__)
38+
// `adb-run` sets `log.redirect-stdio`, so this process's own stdout reaches
39+
// logcat the same way it would on the host row -- `std::puts` here is not
40+
// decorative, though a locked-down device can still refuse the property
41+
// (measured on a real arm64-v8a phone: "Failed to set property
42+
// 'log.redirect-stdio'"), which is exactly why `__android_log_print` is
43+
// also called, unconditionally, as the channel that does not depend on it.
44+
// `ANativeActivity_finish` ends the ACTIVITY; MEASURED 2026-09-12: it does
45+
// not end the PROCESS (ActivityManager keeps it as a cached, reusable
46+
// process instead), so `adb-run` itself does not wait on `pidof` alone --
47+
// it also watches for this activity's own record to disappear from
48+
// `dumpsys activity activities`, and force-stops the process once it does.
49+
extern "C" void ANativeActivity_onCreate(ANativeActivity* activity, void*, size_t) {
50+
std::puts(marker().c_str());
51+
__android_log_print(ANDROID_LOG_INFO, "apk-consumer", "1-2-3");
52+
ANativeActivity_finish(activity);
53+
}
54+
#endif

0 commit comments

Comments
 (0)