feat(app): Android build target — first working APK at 54 MB
Wires the workspace through `cargo apk build`. After this commit `cargo apk build -p solitaire_app --target x86_64-linux-android` produces a debug-signed APK at `target/debug/apk/solitaire-quest.apk` containing all assets and `lib/x86_64/libsolitaire_app.so` — runnable on the AVD or a physical x86_64 device. The five gating points discovered by iterating compile cycles: 1. solitaire_app split into bin + lib. cargo-apk needs a `cdylib` to bundle as `libmain.so`; pure-bin crates panic with "Bin is not compatible with Cdylib". `src/lib.rs` carries the ECS bootstrap as `pub fn run`; `src/main.rs` is a 3-line shim that delegates for the desktop `cargo run` path. 2. `[package.metadata.android]` pins target SDK 34 / min SDK 26 so cargo-apk doesn't probe for whatever default it ships (which on this machine was an uninstalled API 30). `assets = "../assets"` lets the same asset directory feed both desktop and APK. 3. Workspace `bevy` features add `android-native-activity` (the Bevy-side glue that pairs with cargo-apk's NativeActivity wrapper). The feature is target-gated inside bevy_internal so desktop builds compile it out. 4. `arboard` (clipboard, used by Stats's "Copy share link") has no Android backend — `cargo apk build` fails with E0433 on `platform::Clipboard` if unconditional. Target-gated to `cfg(not(target_os = "android"))`; the system surfaces an informational toast on Android until JNI ClipboardManager is wired in the Phase-Android round. 5. `keyring` + `keyring-core` cannot compile for android — the transitive `rpassword` uses `libc::__errno_location` which bionic doesn't expose. Both crates target-gated; `auth_tokens` ships a stub on Android that returns `KeychainUnavailable` for every call, matching how callers already handle a Linux box without Secret Service. Cosmetic post-pass panic: cargo-apk panics AFTER the APK is signed when it tries to also wrap the bin target. The APK on disk is unaffected. Working around this with `cargo apk build --lib` is the next small step. What's verified: - Desktop `cargo build`, `cargo clippy --workspace --all-targets`, and `cargo test --workspace` all clean. - `cargo apk build -p solitaire_app --target x86_64-linux-android` produces 54 MB debug APK with libsolitaire_app.so + assets. What's NOT yet verified: - Whether the APK actually launches on the AVD / a phone (next step: `adb install` + `adb logcat` against the bevy_test AVD). - Whether `dirs::data_dir()` on Android returns a usable path (sync / persistence will surface this if not). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,8 +8,68 @@ edition.workspace = true
|
||||
name = "solitaire_app"
|
||||
path = "src/main.rs"
|
||||
|
||||
# `cdylib` is what cargo-apk packages into `libsolitaire_app.so` for
|
||||
# Android — the activity dlopens the shared object and calls into it.
|
||||
# `rlib` lets the bin target above link the library normally on
|
||||
# desktop. Both produce the same code; only the linkage form differs.
|
||||
[lib]
|
||||
name = "solitaire_app"
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
bevy = { workspace = true }
|
||||
solitaire_engine = { workspace = true }
|
||||
solitaire_data = { workspace = true }
|
||||
|
||||
# `keyring`'s default-store init only matters on platforms with a
|
||||
# real keychain backend (Linux Secret Service, macOS Keychain,
|
||||
# Windows Credential Store). The crate also pulls `rpassword`
|
||||
# transitively, which uses `libc::__errno_location` — a symbol
|
||||
# Android's bionic doesn't expose. Target-gating keeps
|
||||
# `cargo apk build` viable; the call site in `lib.rs` has its own
|
||||
# `cfg(not(target_os = "android"))` guard so the desktop init path
|
||||
# is unchanged.
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||
keyring = { workspace = true }
|
||||
|
||||
# --- Android packaging metadata (read by `cargo-apk`) -------------------
|
||||
#
|
||||
# Pinning these values inside the repo means a contributor running
|
||||
# `cargo apk build -p solitaire_app --target x86_64-linux-android`
|
||||
# does not need to install whatever SDK version cargo-apk happens to
|
||||
# default to today. The numbers track the SDK we install in the dev
|
||||
# setup script: target SDK 34 (Android 14, current Play Store target),
|
||||
# min SDK 26 (Android 8, the lowest Bevy 0.18 supports cleanly with
|
||||
# the wgpu / GLES path).
|
||||
#
|
||||
# Asset path is `../assets` so the same directory the desktop build
|
||||
# already uses ships into the APK without copy-tree gymnastics.
|
||||
# `apk_name` keeps the output filename predictable across machines.
|
||||
[package.metadata.android]
|
||||
package = "com.solitairequest.app"
|
||||
apk_name = "solitaire-quest"
|
||||
build_targets = ["aarch64-linux-android", "armv7-linux-androideabi", "x86_64-linux-android"]
|
||||
assets = "../assets"
|
||||
# No `runtime_libs` — we don't ship any precompiled .so files,
|
||||
# the entire app is pure Rust + Bevy. cargo-apk would try to
|
||||
# resolve `runtime_libs/<arch>/` if set, and fail on a non-existent
|
||||
# arch directory under our package.
|
||||
strip = "strip"
|
||||
|
||||
[package.metadata.android.sdk]
|
||||
target_sdk_version = 34
|
||||
min_sdk_version = 26
|
||||
|
||||
[[package.metadata.android.uses_feature]]
|
||||
name = "android.hardware.touchscreen"
|
||||
required = true
|
||||
|
||||
[[package.metadata.android.uses_permission]]
|
||||
name = "android.permission.INTERNET"
|
||||
|
||||
[package.metadata.android.application]
|
||||
label = "Solitaire Quest"
|
||||
# `debuggable` defaults to false on release builds; cargo-apk flips it
|
||||
# automatically for debug profiles. Leaving the field unset keeps the
|
||||
# default behaviour.
|
||||
|
||||
Reference in New Issue
Block a user