feat: Add android build - #1357
Conversation
Adds an Android NDK toolchain to the dev shell (nixpkgs androidenv, NDK-only, license accepted, API level 24) and wires it into cargo via env vars, following the same pattern as the existing OP-TEE cross-toolchain setup. cargo-zigbuild can't be reused here since zig doesn't bundle Android's bionic sysroot. Adds `cargo x android-sweep` to attempt building every workspace crate (or a given list) for aarch64-linux-android and report pass/fail - not every crate is expected to build. Verified end-to-end: orb-const-concat, orb-arc-pool, orb-header-parsing, and orb-attest (with its full zbus/zenoh/dbus dependency stack) all compile and link for aarch64-linux-android with this toolchain, no source changes required.
Marks 24 crates as unsupported on aarch64-linux-android via the existing `[package.metadata.orb] unsupported_targets` mechanism (already used by ci/rust_ci_helper.py to skip Darwin-incompatible crates in `cargo nextest run --workspace`), grouped by root cause: - Bionic libc gaps: can-rs, orb-mcu-interface, orb-mcu-util, orb-update-agent, orb-update-verifier, orb-uart (no SocketCAN, no termios c_ispeed/c_ospeed) - No EFI on Android: efivar, orb-slot-ctrl, orb-update-agent-core - Vendor/native libs with no Android build: orb-gst-jpeg, orb-sound, orb-ui, orb-cone, orb-hil, seek-camera(-sys), orb-thermal-cam-ctrl, orb-connd, orb-secure-storage-ca - squashfs toolchain not cross-built for Android: orb-bidiff-squashfs(-shim), orb-bidiff-cli, orb-tools - Genuine bionic incompatibility: orb-update-agent-loader (the `nix` crate calls `fexecve`, which bionic doesn't implement) Adds `cargo x android-build`, which reads that same metadata to compute the exclude list and runs a single `cargo build --workspace --target aarch64-linux-android --exclude ...` (verified locally, exit 0). This is a CI gate, unlike the existing `android-sweep` discovery command: `--exclude` only removes packages as build roots, so anything depending on an excluded crate (e.g. orb-tools -> orb-bidiff-squashfs-shim) needed its own entry too. Wires this into a new `Android Build` job in rust-ci.yaml, matching the existing job pattern. Note: agentwire-macros failed when built in isolation (missing syn/full feature) but was deliberately left off the exclude list - verified it builds fine as part of the full `--workspace` build, once feature unification pulls in syn/full from elsewhere in the graph.
|
No concrete correctness, security, or merge-blocking issues found in the PR changes. |
Adds `cargo x android-apex-payload`: for every Android-supported binary crate, stages its own payload directory (bin/<binary>, apex_manifest.json, a placeholder etc/init/<binary>.rc) - one per project, not a single shared APEX. Reuses android-build's exclude logic and rebuilds first so staged binaries are current. Verified end-to-end: staged 14 crates (orbd, orb-attest, orb-jobs-agent, etc.), confirmed correct directory layout and that staged binaries are real aarch64 ELF Android binaries. This stops short of invoking `apexer` to produce a real signed .apex - that needs a cluster of AOSP-built host tools (avbtool, mkfs.erofs, aapt2, apexer's own protobuf-generated manifest parser) that this workspace doesn't have a sourcing story for yet. Manifest naming and init.rc contents are marked TODO pending real answers on APEX namespace and SELinux domain.
run_payload already authors every path in the payload (bin/<binary>, etc/init/<binary>.rc); it can emit the canned_fs_config apexer needs directly as it writes those paths, rather than re-deriving the same information by walking the tree after the fact with a separate tool (gen-canned-fs-config.nix). One source of truth for what's in the payload instead of two. Verified: re-ran `cargo x android-apex-payload`, inspected the generated canned_fs_config for orbd - correct paths/uid/gid/mode.
Adds nix/packages/android-apex.nix, building `apexer` from AOSP source (system/apex at android-16.0.0_r4, one repo - manifest.py and mke2fs.conf are single-file fetches, not full extra repo clones) and substituting nixpkgs/android-tools for the rest of its usual AOSP-build-only toolchain: android-tools' mke2fs.android/e2fsdroid for the ext4 payload plus avbtool, e2fsprogs' resize2fs, aapt's aapt2. Payload filesystem is ext4, not erofs - matches apexer-for-clanker.nix. Confirmed nixpkgs' erofs-utils can't produce a canned_fs_config-capable mkfs.erofs even with selinuxSupport=true: --file-contexts needs only libselinux, but --canned_fs_config needs a second, separate -DWITH_ANDROID build plus AOSP's libcutils (system/core) for load_canned_fs_config, which isn't implemented anywhere else and has no standalone build path. android-tools' e2fsdroid already has this built in as a working prebuilt, so ext4 avoids that whole dependency chain. Restructures run_payload's output: content actually going into the APEX (bin/, etc/) now lives under a content/ subdirectory, sibling to apex_manifest.json/canned_fs_config/file_contexts - apexer's e2fsdroid scans whatever directory it's given as literal payload content, so the previous flat layout made it choke trying to find our own sidecar files (e.g. apex_manifest.json) in canned_fs_config. Verified end-to-end: built a real signed orbd.apex from the staged orbd payload. Confirmed with zipfile (AndroidManifest.xml, resources.arsc, apex_manifest.pb, apex_payload.img all present) and debugfs (ext4 image contains /bin/orbd at mode 755, uid/gid 1000, plus apex_manifest.pb at the image root) and avbtool info_image (valid SHA256_RSA4096 hashtree footer). apex_manifest.pb round-trips to name=com.worldcoin.orb.orbd, version=1 as expected. Signing key is a throwaway generated per build-apex invocation - fine for local testing, needs a real managed key before this ships anywhere near a device.
|
Switches the APEX payload filesystem back to erofs, using a real AOSP-built mkfs.erofs binary fetched directly from kernel/prebuilts/build-tools (android-16.0.0_r0.4) rather than trying to build one ourselves. Patched via autoPatchelfHook to run under nixpkgs' glibc; uses AOSP's own libc++.so (also fetched from the same prebuilts repo) rather than nixpkgs' to avoid any ABI mismatch with a binary Google built and tested against that exact one. Confirmed via readelf that mkfs.erofs only NEEDS generic libc/libc++ (libcutils/libbase/liblog are statically linked in for the host build), so no separate AOSP shared libs are needed beyond libc++.so. This drops all the ext4-specific tooling from the previous commit (mke2fs.android, e2fsdroid, resize2fs, the sefcontext_compile shim, AOSP's mke2fs.conf) - erofs needs none of it, mkfs.erofs takes --file-contexts/--fs-config-file directly. Verified end-to-end: rebuilt orbd.apex using this toolchain. Confirmed with zipfile (same structure as before) and, this time, actually extracted the erofs payload with AOSP's own fsck.erofs and confirmed apex_manifest.pb/bin/orbd/etc/init/orbd.rc are all present and orbd is still executable. Image is ~46MB vs ext4's ~135MB, thanks to lz4hc compression.
Exposes the `build-apex` tool from nix/packages/android-apex.nix as a real flake package (packages.x86_64-linux.build-apex), and adds `cargo x android-apex`: stages payloads (same as android-apex-payload), nix builds build-apex once, then packages each staged crate into target/android-apex/<crate>.apex. android-apex-payload's run_payload now returns the list of staged package names (Result<Vec<String>> instead of Result<()>) so run_apex can reuse it directly instead of re-deriving the same crate list a second time. Also fixes a real bug this surfaced: the placeholder APEX name (com.worldcoin.orb.<crate>) breaks for any crate with a hyphen in its name (e.g. orb-attest) since Android package names are dot-joined Java identifiers and aapt2 rejects hyphens outright. Sanitizes to `_`. Verified end-to-end: `cargo x android-apex` packaged all 14 Android-supported binary crates into individual signed .apex files (orb-attest, orbd, orb-supervisor, zorb, etc.), each confirmed with zipfile to have the expected structure.
Replaces the Android Build job's plain `cargo x android-build` step with `cargo x android-apex`, which is a strict superset (it calls the same workspace build internally, then additionally packages every Android-supported binary crate into a signed .apex) - running both would just rebuild the same already-warm workspace a second time for no benefit. Uploads target/android-apex/*.apex as a build artifact (android-apex), matching the existing `build` job's upload-artifact pattern. Note: this makes CI depend on network access to android.googlesource.com (to fetch the AOSP apexer source and Google's prebuilt mkfs.erofs) on a cache miss - a new external dependency this job didn't have before.
|
Two real bugs from automated review: - `run_apex` called `nix build --out-link target/android-apex/build-apex` before creating target/android-apex/ - nix can't create that symlink without its parent existing, so this failed outright on any clean checkout (no prior `target/` dir). Moved the create_dir_all before the nix build, and made it independent of the user-configurable `--out-dir` rather than conflating the two. - `run_payload` never cleaned a package's payload directory between runs, so a binary/init script removed since the last run (or a crate that just became unsupported) would linger in `content/` and get bundled into the next APEX, inconsistent with the freshly generated canned_fs_config. Now removes each package's output dir before restaging it. (A third comment, about crate names with hyphens producing invalid Android package names, was already fixed in f785345e.) Verified: reproduced the clean-checkout scenario (removed target/android-apex and target/android-apex-payloads first) and confirmed `cargo x android-apex` now succeeds, producing all 14 .apex files.
Build the projects for 🤖