kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit bf9137427b2a9497d5c5436fdc159b9cc7a5e56a
parent b4523172e9848697219b5bf500d4e025a4235c51
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 09:39:48 -0700

Add Android arm64 NDK shared library support

Diffstat:
Mdoc/PORT.md | 19+++++++++++--------
Mdoc/TESTING.md | 13++++++++-----
Mdriver/cmd/build.c | 11+++++++----
Mdriver/cmd/cc.c | 12++++++++----
Mdriver/cmd/ld.c | 6++++--
Mdriver/driver.h | 7++++++-
Mdriver/lib/hosted.c | 1+
Mdriver/lib/hosted.h | 1+
Mdriver/lib/link_inputs.c | 4+++-
Mdriver/lib/link_inputs.h | 3++-
Mdriver/lib/runtime.c | 4++++
Mdriver/lib/target.c | 5+++++
Minclude/kit/core.h | 1+
Minclude/kit/os.h | 1+
Minclude/kit/target.h | 8+++++---
Mlang/cpp/pp/pp.c | 1+
Mlang/cpp/pp/pp_directive.c | 6++++++
Mmk/lib_srcs.mk | 3++-
Mmk/rt.mk | 8++++++++
Ascripts/android_ndk_sysroot.sh | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/cross_test.sh | 36+++++++++++++++++++++++++++++++++++-
Mscripts/hosted.sh | 19+++++++++++++++++--
Mscripts/provision.sh | 2+-
Msrc/api/target.c | 42+++++++++++++++++++++++++++++++++++++++++-
Asrc/os/android/hosted.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/os/android/os.c | 10++++++++++
Msrc/os/hosted.c | 5++++-
Msrc/os/registry.c | 3+++
Mtest/api/target_test.c | 15+++++++++++++++
Atest/cross/cases/native_activity.c | 9+++++++++
30 files changed, 527 insertions(+), 36 deletions(-)

diff --git a/doc/PORT.md b/doc/PORT.md @@ -33,19 +33,22 @@ Related: [BUILD.md](BUILD.md) (the 3-stage self-build mechanism), The support set, one token grammar everywhere: **`<os>[-<libc>]-<arch>`**. ``` -cross matrix (16): +cross matrix: linux-{glibc,musl}-{aa64,x64,rv64} (6) freebsd-{aarch64,amd64,riscv64} (3) windows-{aarch64,x64} (2) - macos-aarch64 (1) - freestanding-{aa64,x64,rv64,rv32} (4) ← rv32 lives here only + android-aa64 (1) ← NativeActivity .so build/link + macos-{aa64,x64} (2) + freestanding-{aa64,x64,rv64,rv32,arm32} (5) ← rv32/arm32 live here only -selfhost matrix (12): the same, minus freestanding +selfhost matrix: the same, minus freestanding and android, and filtered to +macOS host-compatible arch ``` -Freestanding has **no OS**, so there is nothing to run a compiler *on*: it -participates in `cross` only. `selfhost` of a `freestanding-*` target is a hard -error. +Freestanding has **no OS**, so there is nothing to run a compiler *on*. +Android currently has no device/emulator runner in the exec seam. Both +participate in `cross` only; `android-*` is build/link-only unless `RUN=0` is +used or an Android runner is added. Arch tokens are the short forms **`aa64` / `x64` / `rv64`** (plus `rv32`, freestanding-only). The long forms (`aarch64`/`amd64`/`x86_64`/`riscv64`) are @@ -75,7 +78,7 @@ Resolved centrally by `scripts/hosted.sh expand <selector> [--mode=cross|selfhos | Selector | Expands to | |---|---| | `all` | every support-set token (mode- and `KIT_VM`-filtered) | -| `linux` / `freebsd` / `windows` / `macos` / `freestanding` | all configs for that OS | +| `linux` / `freebsd` / `windows` / `android` / `macos` / `freestanding` | all configs for that OS | | `linux-musl` / `linux-glibc` | that libc, all 3 arches | | `linux-glibc-x64`, `freebsd-aarch64`, `freestanding-rv32`, … | one exact config | | `a,b,c` | comma list of any of the above | diff --git a/doc/TESTING.md b/doc/TESTING.md @@ -411,8 +411,9 @@ support set** and run on it, checked against an exit-code + stdout oracle. It's the principled counterpart to the per-libc lanes above — one corpus, one runner seam, many OSes. The seed case is `hello.c`. Two verdicts per (case, config): `:build` (compile + link succeeded) and `:run` (correct exit code + stdout). The -full matrix is 15 configs: Linux {aa64,x64,rv64} × {musl-static, musl-dynamic, -glibc} + macOS-arm64 + Windows {x64,aarch64} + FreeBSD {amd64,aarch64,riscv64}. +full matrix is 16 configs: Linux {aa64,x64,rv64} × {musl-static, musl-dynamic, +glibc} + macOS-arm64 + Android-aarch64 shared-library build/link + +Windows {x64,aarch64} + FreeBSD {amd64,aarch64,riscv64}. The libc rides in the exec tag (`<arch>-linux` = musl/alpine, `<arch>-linux-glibc` = glibc/debian), so a single flush routes every config to its runner (alpine/debian container, native, or VM). @@ -421,10 +422,12 @@ The two pieces it composes are reusable on their own: - **`scripts/hosted.sh`** — one front-end over every sysroot provisioner. Targets are `<os>[-<libc>]-<arch>` (`linux-{glibc,musl}-{aa64,x64,rv64}`, - `freebsd-{amd64,aarch64,riscv64}`, `windows-{x64,aarch64}`, `macos-aarch64`). + `freebsd-{amd64,aarch64,riscv64}`, `windows-{x64,aarch64}`, + `android-aarch64`, `macos-aarch64`). `prepare` provisions the sysroot (wrapping `freebsd_sysroot.sh` / - `llvm_mingw_sysroot.sh` / the libc container `extract.sh`); `cc` compiles+links - for a target (adding the right `-target`/`--sysroot`/`-mconsole`/`-isysroot`); + `llvm_mingw_sysroot.sh` / `android_ndk_sysroot.sh` / the libc container + `extract.sh`); `cc` compiles+links for a target (adding the right + `-target`/`--sysroot`/`-mconsole`/`-isysroot`); `path`/`triple`/`tag`/`doctor` round it out. - **`test/lib/exec_target.sh`** runs the result (native / qemu-user / podman / VM), picking the runner from the tag. diff --git a/driver/cmd/build.c b/driver/cmd/build.c @@ -1093,7 +1093,8 @@ static int build_is_link_output(const BuildOptions* o) { * -ffreestanding / -nostdinc / -nostdlib opt back out; -dynamic (shared) keeps * its standalone meaning. */ static void build_enable_hosted_for_sysroot(BuildOptions* o) { - if (o->wants_hosted_libc || o->shared) return; + if (o->wants_hosted_libc) return; + if (o->shared && !driver_target_shared_uses_hosted(o->target)) return; if (!o->sysroot || !o->sysroot[0]) return; if (o->freestanding || o->nostdinc) return; if (o->no_stdlib || o->no_defaultlibs) return; @@ -1108,7 +1109,8 @@ static void build_apply_default_hosted_profile(BuildOptions* o) { } static int build_apply_hosted_profile(BuildOptions* o) { - if (!o->wants_hosted_libc || o->shared) return 0; + int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target); + if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0; if (o->no_stdlib || o->no_defaultlibs) { driver_errf(o->tool, "-lc hosted expansion is disabled by -nostdlib/-nodefaultlibs"); @@ -1118,7 +1120,7 @@ static int build_apply_hosted_profile(BuildOptions* o) { * apply: pass link_action = 1. */ return driver_link_inputs_apply_hosted( &o->inputs, &o->hosted, &o->groups[0].cf, &o->link, o->target, o->sysroot, - o->static_link, o->no_startfiles, 1); + o->static_link, o->shared, o->no_startfiles, 1); } /* ===================================================================== */ @@ -2462,7 +2464,8 @@ static int build_main(int argc, char** argv, int kind, const char* tool, rc = 1; goto done; } - if (!o.shared && !o.no_stdlib && !o.no_defaultlibs) { + if ((!o.shared || driver_target_shared_uses_hosted(o.target)) && + !o.no_stdlib && !o.no_defaultlibs) { DriverRuntimeArchive rt = {0}; if (driver_runtime_prepare_archive(&env, tool, &runtime, o.target, o.epoch, &rt) != 0) { diff --git a/driver/cmd/cc.c b/driver/cmd/cc.c @@ -551,7 +551,8 @@ static int cc_apply_hosted_profile(CcOptions* o) { * + define + lib-search-dir parts apply regardless. */ int link_action = !o->compile_only && !o->preprocess_only && o->dep_mode != CC_DEP_M && o->dep_mode != CC_DEP_MM; - if (!o->wants_hosted_libc || o->shared) return 0; + int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target); + if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0; if (o->no_stdlib || o->no_defaultlibs) { driver_errf(CC_TOOL, "-lc hosted expansion is disabled by -nostdlib/-nodefaultlibs"); @@ -559,7 +560,7 @@ static int cc_apply_hosted_profile(CcOptions* o) { } return driver_link_inputs_apply_hosted( &o->inputs, &o->hosted, &o->cf, &o->link, o->target, o->sysroot, - o->static_link, o->no_startfiles, link_action); + o->static_link, o->shared, o->no_startfiles, link_action); } static int cc_apply_env(CcOptions* o) { @@ -586,7 +587,8 @@ static int cc_has_link_action(const CcOptions* o) { * keep their existing meaning. The Windows-COFF default profile and an explicit * -lc already set the flag, so this is a no-op in those cases. */ static void cc_enable_hosted_for_sysroot(CcOptions* o) { - if (o->wants_hosted_libc || o->shared) return; + if (o->wants_hosted_libc) return; + if (o->shared && !driver_target_shared_uses_hosted(o->target)) return; if (!o->sysroot || !o->sysroot[0]) return; if (o->freestanding || o->nostdinc) return; if (o->no_stdlib || o->no_defaultlibs) return; @@ -2605,7 +2607,9 @@ static int driver_cc_main(int argc, char** argv, int force_check) { return 1; } - if (link_action && !co.shared && !co.no_stdlib && !co.no_defaultlibs) { + if (link_action && + (!co.shared || driver_target_shared_uses_hosted(co.target)) && + !co.no_stdlib && !co.no_defaultlibs) { DriverRuntimeArchive rt_archive = {0}; if (!runtime_resolved) { driver_errf(CC_TOOL, "support dir not found"); diff --git a/driver/cmd/ld.c b/driver/cmd/ld.c @@ -2835,10 +2835,12 @@ static KitStatus ld_print_memory_usage(LdOptions* o, KitLinkSession* link) { * driver_link_inputs_insert_runtime_archives. The wants-hosted / -shared guard * stays here. */ static int ld_apply_hosted(LdOptions* o) { - if (!o->wants_hosted_libc || o->shared) return 0; + int shared_hosted = o->shared && driver_target_shared_uses_hosted(o->target); + if (!o->wants_hosted_libc || (o->shared && !shared_hosted)) return 0; return driver_link_inputs_apply_hosted(&o->inputs, &o->hosted, &o->cf, &o->lf, o->target, o->sysroot, o->static_link, - o->nostartfiles, /*link_action=*/1); + o->shared, o->nostartfiles, + /*link_action=*/1); } /* ---------- link execution ---------- */ diff --git a/driver/driver.h b/driver/driver.h @@ -145,7 +145,7 @@ int driver_argv_wants_help(int argc, char** argv, int accept_short_h); * KitTargetSpec. Recognized arches: x86_64/amd64, i386/i486/i586/i686, aarch64/ * arm64, arm/armv7, riscv64, riscv32, wasm32, wasm64. Recognized OSes (scanned * across the remaining components, so vendor tokens like `pc`/`apple`/ - * `unknown` are skipped): linux, darwin/macos, ios/iphoneos, + * `unknown` are skipped): linux, android, darwin/macos, ios/iphoneos, * iphonesimulator/ios-simulator, windows/win32, wasi, none/freestanding. Sets * arch/os/obj/ptr_size/ptr_align/big_endian; pic and code_model are left at * their defaults. Returns 0 on success, nonzero on unrecognized arch or NULL @@ -268,6 +268,11 @@ int driver_target_needs_sysroot_libdir(KitTargetSpec target); * (Windows-COFF, given a sysroot and no -nostdlib). */ int driver_target_default_hosted_profile(KitTargetSpec target); +/* Whether a shared-library link should still use hosted CRT/libc scaffolding + * and the kit runtime. Most targets leave shared output standalone; Android + * NDK DSOs require crtbegin_so.o/crtend_so.o and Bionic DSOs. */ +int driver_target_shared_uses_hosted(KitTargetSpec target); + /* Set `target->code_model` from a -mcmodel= value. Accepts the x86 spellings * (small/medium/large), the RISC-V aliases (medlow->small, medany->medium), * and kernel as a conservative alias for large. Returns 0 on success, 1 on an diff --git a/driver/lib/hosted.c b/driver/lib/hosted.c @@ -109,6 +109,7 @@ int driver_hosted_resolve(const DriverHostedRequest* req, os_req.tool = req->tool; os_req.target = req->target; os_req.static_link = req->static_link; + os_req.shared_link = req->shared_link; os_req.link_inputs = req->link_inputs; rc = ops->resolve(&os_req, &dirs, out); diff --git a/driver/lib/hosted.h b/driver/lib/hosted.h @@ -27,6 +27,7 @@ typedef struct DriverHostedRequest { KitTargetSpec target; const char* sysroot; int static_link; + int shared_link; int link_inputs; } DriverHostedRequest; diff --git a/driver/lib/link_inputs.c b/driver/lib/link_inputs.c @@ -450,7 +450,8 @@ int driver_link_inputs_apply_hosted(DriverLinkInputSet* set, DriverHostedPlan* plan, DriverCflags* cf, DriverLinkFlags* link, KitTargetSpec target, const char* sysroot, int static_link, - int no_startfiles, int link_action) { + int shared_link, int no_startfiles, + int link_action) { DriverHostedRequest req = {0}; uint32_t i; uint32_t insert_pos = 0; @@ -459,6 +460,7 @@ int driver_link_inputs_apply_hosted(DriverLinkInputSet* set, req.target = target; req.sysroot = sysroot; req.static_link = static_link; + req.shared_link = shared_link; req.link_inputs = link_action; if (driver_hosted_resolve(&req, plan) != 0) return 1; for (i = 0; i < plan->nsystem_includes; ++i) diff --git a/driver/lib/link_inputs.h b/driver/lib/link_inputs.h @@ -198,6 +198,7 @@ int driver_link_inputs_apply_hosted(DriverLinkInputSet* set, DriverHostedPlan* plan, DriverCflags* cf, DriverLinkFlags* link, KitTargetSpec target, const char* sysroot, int static_link, - int no_startfiles, int link_action); + int shared_link, int no_startfiles, + int link_action); #endif diff --git a/driver/lib/runtime.c b/driver/lib/runtime.c @@ -234,6 +234,10 @@ static const RuntimeVariant kRtVariants[] = { "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf, (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])), KIT_FLOAT_ABI_DEFAULT, NULL, NULL}, + {"aarch64-linux-android", KIT_ARCH_ARM_64, KIT_OS_ANDROID, KIT_OBJ_ELF, 8, + 8, "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf, + (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])), + KIT_FLOAT_ABI_DEFAULT, NULL, NULL}, {"aarch64-freebsd", KIT_ARCH_ARM_64, KIT_OS_FREEBSD, KIT_OBJ_ELF, 8, 8, "lib/include/lp64_le", 1, 1, kRtSrcAarch64Elf, (uint32_t)(sizeof(kRtSrcAarch64Elf) / sizeof(kRtSrcAarch64Elf[0])), diff --git a/driver/lib/target.c b/driver/lib/target.c @@ -46,6 +46,11 @@ int driver_target_default_hosted_profile(KitTargetSpec target) { return kit_target_default_hosted_profile(target); } +int driver_target_shared_uses_hosted(KitTargetSpec target) { + return target.os == KIT_OS_ANDROID && target.obj == KIT_OBJ_ELF && + target.arch == KIT_ARCH_ARM_64; +} + static int target_features_grow(DriverTargetFeatures* tf) { DriverEnv* env = tf->env; uint32_t old_cap = tf->cap_features; diff --git a/include/kit/core.h b/include/kit/core.h @@ -146,6 +146,7 @@ typedef enum KitOSKind { KIT_OS_IOS_SIMULATOR, KIT_OS_WINDOWS, KIT_OS_FREEBSD, + KIT_OS_ANDROID, KIT_OS_WASI, } KitOSKind; diff --git a/include/kit/os.h b/include/kit/os.h @@ -85,6 +85,7 @@ typedef struct KitOsHostedRequest { const char* tool; KitTargetSpec target; int static_link; + int shared_link; int link_inputs; } KitOsHostedRequest; diff --git a/include/kit/target.h b/include/kit/target.h @@ -42,7 +42,8 @@ KIT_API bool kit_arch_from_name(const char* name, KitArchKind* arch_out, * KitTargetSpec. Recognized arches: see kit_arch_from_name. Recognized OSes are * scanned across the remaining components (so vendor tokens like * pc/apple/unknown are skipped): linux, darwin/macos, ios/iphoneos, - * iphonesimulator/ios-simulator, windows/win32, freebsd[<ver>], wasi, + * iphonesimulator/ios-simulator, windows/win32, freebsd[<ver>], + * android[<api>] (also a `linux-android[<api>]` env component), wasi, * none/freestanding. An unrecognized or absent OS token resolves to a * freestanding ELF/WASM target by design (covers bare-metal / vendor-only * triples like "riscv64-unknown-elf"). @@ -55,8 +56,9 @@ KIT_API bool kit_arch_from_name(const char* name, KitArchKind* arch_out, KIT_API bool kit_target_from_triple(const char* triple, KitTargetSpec* out); /* Render a canonical target triple for `spec` into `buf` (e.g. - * "x86_64-linux", "aarch64-apple-darwin"). Returns true on success, false when - * `buf` is NULL/zero-length or too small to hold the triple plus its NUL. */ + * "x86_64-linux", "aarch64-apple-darwin", "aarch64-linux-android21"). + * Returns true on success, false when `buf` is NULL/zero-length or too small + * to hold the triple plus its NUL. */ KIT_API bool kit_target_to_triple(KitTargetSpec spec, char* buf, size_t cap); /* Map a -mcmodel= value to a KitCodeModel. Accepts the x86 spellings diff --git a/lang/cpp/pp/pp.c b/lang/cpp/pp/pp.c @@ -828,6 +828,7 @@ static void pp_register_os_predefined(Pp* pp, KitTargetSpec target) { * to avoid a redefinition-with-different-replacement error. */ break; case KIT_OS_LINUX: + case KIT_OS_ANDROID: case KIT_OS_MACOS: case KIT_OS_IOS: case KIT_OS_IOS_SIMULATOR: diff --git a/lang/cpp/pp/pp_directive.c b/lang/cpp/pp/pp_directive.c @@ -1171,6 +1171,12 @@ static void do_include(Pp* pp, const Tok* line, u32 n, LocRef loc, * already serves the bytes syscall-free, but re-lexing them through * skip_until_active still costs the full lex+intern of every token. */ pkey = inc_cache_key(pp, resolved); + if (pkey) { + for (i = pp->nsources; i > 0; --i) { + TokSrc* tp = &pp->sources[i - 1]; + if (tp->kind == SRC_LEX && tp->once && tp->path_key == pkey) return; + } + } if (pkey && pp->inc_cache_ready) { IncEntry* e = IncCache_get(&pp->inc_cache, pkey); if (e && (e->once || (e->guard && mt_get(pp, e->guard) != NULL))) return; diff --git a/mk/lib_srcs.mk b/mk/lib_srcs.mk @@ -254,7 +254,8 @@ ifneq ($(filter 1,$(KIT_OBJ_ELF_ENABLED) $(KIT_EMU_ENABLED)),) LIB_SRCS += src/os/linux/os.c endif ifeq ($(KIT_OBJ_ELF_ENABLED),1) -LIB_SRCS += src/os/linux/hosted.c src/os/freebsd/os.c src/os/freebsd/hosted.c +LIB_SRCS += src/os/linux/hosted.c src/os/freebsd/os.c src/os/freebsd/hosted.c \ + src/os/android/os.c src/os/android/hosted.c endif ifeq ($(KIT_OBJ_MACHO_ENABLED),1) LIB_SRCS += src/os/macos/os.c src/os/macos/hosted.c diff --git a/mk/rt.mk b/mk/rt.mk @@ -19,6 +19,7 @@ RT_VARIANTS = \ x86_64-apple-ios-simulator \ x86_64-elf \ aarch64-linux \ + aarch64-linux-android \ aarch64-freebsd \ aarch64-apple-darwin \ aarch64-apple-ios \ @@ -117,6 +118,13 @@ RT_aarch64-linux_CORO = aarch64 RT_aarch64-linux_LDBL128 = 1 RT_EXTRA_SRCS_aarch64-linux = rt/lib/coro/aarch64_elf.s +RT_aarch64-linux-android_TARGET = aarch64-linux-android21 +RT_aarch64-linux-android_ABI = lp64 +RT_aarch64-linux-android_INT128 = 1 +RT_aarch64-linux-android_CORO = aarch64 +RT_aarch64-linux-android_LDBL128 = 1 +RT_EXTRA_SRCS_aarch64-linux-android = rt/lib/coro/aarch64_elf.s + RT_aarch64-freebsd_TARGET = aarch64-unknown-freebsd RT_aarch64-freebsd_ABI = lp64 RT_aarch64-freebsd_INT128 = 1 diff --git a/scripts/android_ndk_sysroot.sh b/scripts/android_ndk_sysroot.sh @@ -0,0 +1,207 @@ +#!/usr/bin/env bash +# Download and cache the Android NDK, exposing its sysroot for kit's aarch64 +# Android hosted profile. +# +# usage: +# scripts/android_ndk_sysroot.sh prepare [aa64] +# scripts/android_ndk_sysroot.sh path [aa64] +# scripts/android_ndk_sysroot.sh root +# scripts/android_ndk_sysroot.sh env [aa64] + +set -eu + +RELEASE="${KIT_ANDROID_NDK_RELEASE:-r27d}" +REPO_XML="${KIT_ANDROID_NDK_REPOSITORY_XML:-https://dl.google.com/android/repository/repository2-1.xml}" +BASE_URL="${KIT_ANDROID_NDK_BASE_URL:-https://dl.google.com/android/repository}" +XDG_CACHE_HOME="${XDG_CACHE_HOME:-${HOME:-}/.cache}" +CACHE="${KIT_ANDROID_NDK_CACHE:-$XDG_CACHE_HOME/kit/android-ndk/$RELEASE}" + +die() { printf 'android-ndk-sysroot: %s\n' "$*" >&2; exit 1; } + +canon_arch() { + case "${1:-aa64}" in + aa64|aarch64|arm64|"") echo aarch64 ;; + all) echo all ;; + *) die "unknown arch '${1:-}' (Android support is aarch64 only)" ;; + esac +} + +host_asset() { + case "$(uname -s 2>/dev/null || echo unknown)" in + Darwin) echo "android-ndk-$RELEASE-darwin.zip" ;; + Linux) echo "android-ndk-$RELEASE-linux.zip" ;; + MINGW*|MSYS*|CYGWIN*) echo "android-ndk-$RELEASE-windows.zip" ;; + *) die "unsupported host OS: $(uname -s 2>/dev/null || echo unknown)" ;; + esac +} + +sha1_file() { + if command -v shasum >/dev/null 2>&1; then + shasum -a 1 "$1" | awk '{ print $1 }' + return 0 + fi + if command -v sha1sum >/dev/null 2>&1; then + sha1sum "$1" | awk '{ print $1 }' + return 0 + fi + die "missing shasum or sha1sum" +} + +metadata_field() { + local asset="$1" field="$2" xml="$CACHE/repository2-1.xml" + mkdir -p "$CACHE" + if [ "${FORCE:-0}" = 1 ] || [ ! -f "$xml" ]; then + command -v curl >/dev/null 2>&1 || die "curl not found" + curl -fsSL "$REPO_XML" -o "$xml.tmp" + mv "$xml.tmp" "$xml" + fi + case "$field" in + checksum) + awk -v want="$asset" ' + /<checksum>/ { c=$0; sub(/^.*<checksum>/, "", c); sub(/<\/checksum>.*$/, "", c) } + /<url>/ { u=$0; sub(/^.*<url>/, "", u); sub(/<\/url>.*$/, "", u); if (u == want) { print c; exit } } + ' "$xml" + ;; + url) + awk -v want="$asset" ' + /<url>/ { u=$0; sub(/^.*<url>/, "", u); sub(/<\/url>.*$/, "", u); if (u == want) { print u; exit } } + ' "$xml" + ;; + *) die "internal bad metadata field: $field" ;; + esac +} + +archive_path() { printf '%s/%s\n' "$CACHE" "$(host_asset)"; } +install_root() { printf '%s/android-ndk-%s\n' "$CACHE" "$RELEASE"; } + +verify_archive() { + local path="$1" want="$2" got + [ -n "$want" ] || die "no repository checksum found for $(basename "$path")" + got="$(sha1_file "$path")" + [ "$got" = "$want" ] || + die "checksum mismatch for $(basename "$path"): got $got want $want" +} + +fetch_archive() { + local asset full_url path want url + asset="$(host_asset)" + path="$(archive_path)" + want="$(metadata_field "$asset" checksum)" + [ -n "$want" ] || die "no repository checksum found for $asset" + url="$(metadata_field "$asset" url)" + [ -n "$url" ] || die "no repository URL found for $asset" + mkdir -p "$CACHE" + if [ -f "$path" ]; then + verify_archive "$path" "$want" + printf 'archive already verified: %s\n' "$path" + return 0 + fi + command -v curl >/dev/null 2>&1 || die "curl not found" + full_url="$BASE_URL/$url" + printf 'download: %s\n' "$full_url" + curl -fL --retry 3 --retry-delay 2 --continue-at - -o "$path.part" "$full_url" + mv "$path.part" "$path" + verify_archive "$path" "$want" + printf 'verified: %s\n' "$path" +} + +prebuilt_dir() { + local root="$1" d + for d in "$root"/toolchains/llvm/prebuilt/*; do + [ -d "$d" ] || continue + printf '%s\n' "$d" + return 0 + done + return 1 +} + +sysroot_path() { + local root d + canon_arch "${1:-aa64}" >/dev/null + root="$(install_root)" + if d="$(prebuilt_dir "$root" 2>/dev/null)"; then + printf '%s/sysroot\n' "$d" + return 0 + fi + case "$(uname -s 2>/dev/null || echo unknown)" in + Darwin) printf '%s/toolchains/llvm/prebuilt/darwin-x86_64/sysroot\n' "$root" ;; + Linux) printf '%s/toolchains/llvm/prebuilt/linux-x86_64/sysroot\n' "$root" ;; + MINGW*|MSYS*|CYGWIN*) printf '%s/toolchains/llvm/prebuilt/windows-x86_64/sysroot\n' "$root" ;; + *) die "unsupported host OS: $(uname -s 2>/dev/null || echo unknown)" ;; + esac +} + +prepare() { + local arch root path sr + arch="$(canon_arch "${1:-aa64}")" + [ "$arch" = aarch64 ] || die "prepare requires aa64/aarch64" + root="$(install_root)" + if [ "${FORCE:-0}" != 1 ] && [ -r "$(sysroot_path "$arch")/usr/include/android/native_activity.h" ]; then + printf 'sysroot cached: %s\n' "$(sysroot_path "$arch")" + return 0 + fi + fetch_archive + path="$(archive_path)" + rm -rf "$root.tmp" + mkdir -p "$CACHE" + command -v unzip >/dev/null 2>&1 || die "unzip not found" + unzip -q "$path" -d "$root.tmp" + if [ -d "$root.tmp/android-ndk-$RELEASE" ]; then + rm -rf "$root" + mv "$root.tmp/android-ndk-$RELEASE" "$root" + rm -rf "$root.tmp" + else + rm -rf "$root.tmp" + die "archive did not contain android-ndk-$RELEASE" + fi + sr="$(sysroot_path "$arch")" + [ -r "$sr/usr/include/android/native_activity.h" ] || + die "NDK sysroot missing android/native_activity.h" + [ -d "$sr/usr/lib/aarch64-linux-android" ] || + die "NDK sysroot missing aarch64 Android libraries" + { + printf 'release=%s\n' "$RELEASE" + printf 'asset=%s\n' "$(basename "$path")" + printf 'url=%s/%s\n' "$BASE_URL" "$(metadata_field "$(basename "$path")" url)" + printf 'sha1=%s\n' "$(metadata_field "$(basename "$path")" checksum)" + printf 'target=aarch64-linux-android\n' + } > "$root/PROVENANCE" + printf 'sysroot ready: %s\n' "$sr" +} + +doctor() { + local asset root sr + asset="$(host_asset)" + root="$(install_root)" + sr="$(sysroot_path aa64)" + printf 'host: %s/%s\n' "$(uname -s 2>/dev/null)" "$(uname -m 2>/dev/null)" + printf 'release: %s\n' "$RELEASE" + printf 'asset: %s\n' "$asset" + printf 'download cache: %s\n' "$CACHE" + printf 'ndk root: %s%s\n' "$root" "$([ -d "$root" ] && echo '' || echo ' (missing)')" + printf 'sysroot: %s%s\n' "$sr" "$([ -d "$sr" ] && echo '' || echo ' (missing)')" + for tool in curl unzip shasum sha1sum; do + if command -v "$tool" >/dev/null 2>&1; then + printf ' OK %s (%s)\n' "$tool" "$(command -v "$tool")" + else + printf ' MISSING %s\n' "$tool" + fi + done +} + +cmd="${1:-}" +case "$cmd" in + doctor) doctor ;; + fetch) fetch_archive ;; + prepare) shift; prepare "${1:-aa64}" ;; + path) shift; sysroot_path "${1:-aa64}" ;; + root) install_root ;; + env) + shift + printf 'export KIT_SYSROOT=%s\n' "$(sysroot_path "${1:-aa64}")" + ;; + -h|--help|help|"") + sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//' + ;; + *) die "unknown command '$cmd' (try: doctor fetch prepare path root env)" ;; +esac diff --git a/scripts/cross_test.sh b/scripts/cross_test.sh @@ -90,7 +90,7 @@ preflight_token() { local t="$1" os tag sr os="$(tok_os "$t")"; tag="$("$HOSTED" tag "$t")"; sr="$("$HOSTED" path "$t" 2>/dev/null)" case "$os" in - linux|freebsd|windows) + linux|freebsd|windows|android) if [ -z "$sr" ] || [ ! -d "$sr" ]; then echo "sysroot missing — run 'make provision TARGET=$t'"; return fi ;; @@ -101,6 +101,7 @@ preflight_token() { freebsd|windows) echo "VM not provisioned/reachable — run 'make provision TARGET=$t KIT_VM=1'" ;; macos) echo "macOS exec needs a Darwin host with matching arch" ;; linux) echo "no linux runner — run 'make provision TARGET=$t' (sysroot) + 'make test-images'/'make hosted-glibc-images' (run image)" ;; + android) echo "no Android runner — use RUN=0 for build/link-only validation" ;; *) echo "no runner for $tag" ;; esac fi @@ -113,6 +114,10 @@ preflight_token() { smoke_build_token() { local t="$1" os name m os="$(tok_os "$t")" + if [ "$os" = android ]; then + smoke_build_android "$t" + return + fi if [ "$os" = freestanding ]; then smoke_build_case "$t" exit "" # bare-metal: no link mode return @@ -124,9 +129,38 @@ smoke_build_token() { done } +smoke_build_android() { + local t="$1" tag cdir src obj so label + tag="$("$HOSTED" tag "$t")" + cdir="$BUILD_DIR/$t"; mkdir -p "$cdir" + src="$CASES/native_activity.c" + obj="$cdir/native_activity.o" + so="$cdir/libkit_native_activity.so" + label="native_activity/$t" + if ! "$HOSTED" cc "$t" -O1 -fPIC -c "$src" -o "$obj" \ + >"$cdir/native_activity.cc.out" 2>"$cdir/native_activity.cc.err"; then + kit_fail "$label:cc" "cc failed"; sed 's/^/ | /' "$cdir/native_activity.cc.err" | head; return + fi + kit_pass "$label:cc" + if ! "$HOSTED" cc "$t" -shared "$obj" -landroid -o "$so" \ + >"$cdir/native_activity.ld.out" 2>"$cdir/native_activity.ld.err"; then + kit_fail "$label:ld" "ld failed"; sed 's/^/ | /' "$cdir/native_activity.ld.err" | head; return + fi + if [ ! -s "$so" ]; then + kit_fail "$label:ld" "missing shared object" + return + fi + : "$tag" + kit_pass "$label:ld" +} + coarse_build_token() { local t="$1" os mode opt sfx cdir tag os="$(tok_os "$t")" + if [ "$os" = android ]; then + printf 'cross: NOTE %s — Android coarse lane uses the native_activity shared-library smoke only\n' "$t" + return + fi tag="$($HOSTED tag "$t")" cdir="$BUILD_DIR/$t"; mkdir -p "$cdir" for opt in O0 O1; do diff --git a/scripts/hosted.sh b/scripts/hosted.sh @@ -4,12 +4,13 @@ # the per-OS provisioners (it does not replace them): # FreeBSD -> scripts/freebsd_sysroot.sh (base.txz extract) # Windows -> scripts/llvm_mingw_sysroot.sh (llvm-mingw UCRT) +# Android -> scripts/android_ndk_sysroot.sh (Android NDK sysroot) # Linux -> test/libc/{glibc,musl}/extract.sh (podman container extract) # macOS -> host SDK (native; no cross sysroot) # # Targets: <os>[-<libc>]-<arch> (canonical arch tokens: aa64/x64/rv64[/rv32]) # linux-glibc-{aa64,x64,rv64} linux-musl-{aa64,x64,rv64} -# freebsd-{aa64,x64,rv64} windows-{aa64,x64} macos-aa64 +# freebsd-{aa64,x64,rv64} windows-{aa64,x64} android-aa64 macos-aa64 # freestanding-{aa64,x64,rv64,rv32,arm32} (no OS — cross only, bare-metal exec) # Long arch spellings (aarch64/amd64/x86_64/riscv64) are accepted as input # aliases; canon_token normalizes them to the short forms above. @@ -43,6 +44,7 @@ linux-glibc-aa64 linux-glibc-x64 linux-glibc-rv64 linux-musl-aa64 linux-musl-x64 linux-musl-rv64 freebsd-aa64 freebsd-x64 freebsd-rv64 windows-aa64 windows-x64 +android-aa64 macos-aa64 macos-x64 freestanding-aa64 freestanding-x64 freestanding-rv64 freestanding-rv32 freestanding-arm32 " @@ -78,6 +80,11 @@ parse_target() { aarch64|arm64|aa64) T_ARCH=aarch64 ;; *) die "bad windows arch '$rest' in '$t'" ;; esac ;; + android) + case "$rest" in + aarch64|arm64|aa64) T_ARCH=aarch64 ;; + *) die "bad android arch '$rest' in '$t' (want aa64)" ;; + esac ;; macos) case "$rest" in aarch64|arm64|aa64) T_ARCH=aarch64 ;; @@ -118,6 +125,10 @@ triple_of() { x64) echo x86_64-windows ;; aarch64) echo aarch64-windows ;; esac ;; + android) + case "$T_ARCH" in + aarch64) echo aarch64-linux-android21 ;; + esac ;; macos) case "$T_ARCH" in aarch64) echo aarch64-apple-darwin ;; @@ -145,6 +156,7 @@ tag_of() { macos) echo "$ea-macos" ;; freebsd) echo "$T_ARCH-freebsd" ;; windows) echo "$T_ARCH-windows" ;; + android) echo "$ea-android" ;; freestanding) echo "$ea-freestanding" ;; esac } @@ -169,6 +181,7 @@ modes_of() { parse_target "$1" case "$T_OS" in freestanding) echo cross ;; + android) echo cross ;; macos) # Self-host runs the freshly built kit on the host. A non-host macOS arch # (e.g. an x86_64 kit on Apple silicon) would run under Rosetta, but that @@ -194,6 +207,7 @@ path_of() { linux) _linux_sysroot_dir "$T_LIBC" "$T_ARCH" ;; freebsd) "$ROOT/scripts/freebsd_sysroot.sh" path "$T_ARCH" 2>/dev/null ;; windows) "$ROOT/scripts/llvm_mingw_sysroot.sh" path "$T_ARCH" 2>/dev/null ;; + android) "$ROOT/scripts/android_ndk_sysroot.sh" path "$T_ARCH" 2>/dev/null ;; macos) echo "" ;; freestanding) echo "" ;; esac @@ -208,6 +222,7 @@ prepare_one() { bash "$ROOT/test/libc/$T_LIBC/extract.sh" -a "$T_ARCH" ;; freebsd) "$ROOT/scripts/freebsd_sysroot.sh" "$T_ARCH" ;; windows) "$ROOT/scripts/llvm_mingw_sysroot.sh" prepare "$T_ARCH" ;; + android) "$ROOT/scripts/android_ndk_sysroot.sh" prepare "$T_ARCH" ;; macos) printf 'macos: native host SDK, nothing to prepare\n' ;; freestanding) printf 'freestanding: no sysroot to provision; run-time prereq is qemu-system-%s\n' \ @@ -266,7 +281,7 @@ doctor() { _match_one() { local it="$1" t case "$it" in - linux|freebsd|windows|macos|freestanding) + linux|freebsd|windows|android|macos|freestanding) for t in $SUPPORT_SET; do case "$t" in "$it"-*) printf '%s ' "$t" ;; esac; done return ;; linux-musl|linux-glibc) diff --git a/scripts/provision.sh b/scripts/provision.sh @@ -22,7 +22,7 @@ TOKENS="$("$HOSTED" expand "$SELECTOR")" || exit 1 printf 'provision: targets=%s\n' "$(echo $TOKENS | tr '\n' ' ')" # 1. Sysroots (hosted.sh routes per OS: linux extract / freebsd base.txz / -# windows UCRT; macos + freestanding are no-ops). +# windows UCRT / Android NDK; macos + freestanding are no-ops). "$HOSTED" prepare "$SELECTOR" || exit 1 # 2. Run images + VMs, driven by which OSes are in scope. diff --git a/src/api/target.c b/src/api/target.c @@ -42,6 +42,19 @@ static bool triple_tok_is_ios_sim(const char* s, size_t n) { triple_tok_eq(s, n, "iphonesimulator"); } +static bool triple_tok_is_android(const char* s, size_t n, + uint8_t* api_out) { + size_t l = strlen("android"); + unsigned v = 0; + size_t i; + if (n < l || memcmp(s, "android", l) != 0) return false; + for (i = l; i < n && s[i] >= '0' && s[i] <= '9'; ++i) + v = v * 10u + (unsigned)(s[i] - '0'); + if (i != n) return false; + if (api_out) *api_out = (uint8_t)(v > 255u ? 0u : v); + return true; +} + static bool riscv_arch_tok(const char* s, size_t n, const char* base) { size_t l = strlen(base); size_t i; @@ -181,9 +194,29 @@ bool kit_target_from_triple(const char* triple, KitTargetSpec* out) { os_set = 0; for (i = 1; i < np; ++i) { + { + uint8_t api = 0; + if (triple_tok_is_android(parts[i], plen[i], &api)) { + t.os = KIT_OS_ANDROID; + t.obj = KIT_OBJ_ELF; + t.os_version_major = api; + os_set = 1; + break; + } + } if (triple_tok_eq(parts[i], plen[i], "linux")) { - t.os = KIT_OS_LINUX; + int android = 0; + uint8_t api = 0; + int j; + for (j = i + 1; j < np; ++j) { + if (triple_tok_is_android(parts[j], plen[j], &api)) { + android = 1; + break; + } + } + t.os = android ? KIT_OS_ANDROID : KIT_OS_LINUX; t.obj = KIT_OBJ_ELF; + t.os_version_major = android ? api : 0; os_set = 1; break; } @@ -321,6 +354,13 @@ bool kit_target_to_triple(KitTargetSpec target, char* buf, size_t cap) { case KIT_OS_FREEBSD: os = "freebsd"; break; + case KIT_OS_ANDROID: + if (target.os_version_major) + n = snprintf(buf, cap, "%s-linux-android%u", arch, + (unsigned)target.os_version_major); + else + n = snprintf(buf, cap, "%s-linux-android", arch); + return !(n < 0 || (size_t)n >= cap); case KIT_OS_WASI: os = "wasi"; break; diff --git a/src/os/android/hosted.c b/src/os/android/hosted.c @@ -0,0 +1,101 @@ +#include "os/hosted_internal.h" + +#include <stdio.h> + +static unsigned android_api_level(KitTargetSpec target) { + unsigned api = target.os_version_major ? target.os_version_major : 21u; + return api < 21u ? 21u : api; +} + +static int hosted_add_android_defines(KitOsHostedPlan* plan, + KitTargetSpec target) { + char api_buf[4]; + int n = snprintf(api_buf, sizeof api_buf, "%u", android_api_level(target)); + if (n < 0 || (unsigned)n >= sizeof api_buf) return 1; + if (kit_os_hosted_add_clang_compat_defines(plan) != 0 || + kit_os_hosted_add_define(plan, "__ANDROID__", "1") != 0 || + kit_os_hosted_add_define(plan, "__BIONIC__", "1") != 0 || + kit_os_hosted_add_define(plan, "__linux__", "1") != 0 || + kit_os_hosted_add_define(plan, "__linux", "1") != 0 || + kit_os_hosted_add_define(plan, "linux", "1") != 0 || + kit_os_hosted_add_define(plan, "__ELF__", "1") != 0 || + kit_os_hosted_add_define(plan, "__ANDROID_API__", api_buf) != 0) + return 1; + return 0; +} + +static int hosted_resolve_android(const KitOsHostedRequest* req, + const KitOsHostedDirs* dirs, + KitOsHostedPlan* plan) { + if (req->target.arch != KIT_ARCH_ARM_64) { + kit_os_hosted_errf(req->host, req->host_user, req->tool, + "Android hosted profile supports aarch64 only"); + return 1; + } + if (req->static_link) { + kit_os_hosted_errf(req->host, req->host_user, req->tool, + "Android hosted profile supports dynamic links only"); + return 1; + } + if (dirs->nlibdirs == 0) { + kit_os_hosted_errf(req->host, req->host_user, req->tool, + "Android hosted profile requires --sysroot or " + "KIT_SYSROOT"); + return 1; + } + plan->profile_name = + req->shared_link ? "android-bionic-shared" : "android-bionic-dynamic"; + if (!req->shared_link) plan->interp_path = "/system/bin/linker64"; + if (hosted_add_android_defines(plan, req->target) != 0 || + kit_os_hosted_add_incdirs(plan, req->host, req->host_user, dirs) != 0) { + kit_os_hosted_errf(req->host, req->host_user, req->tool, "out of memory"); + return 1; + } + if (!req->link_inputs) return 0; + if (kit_os_hosted_add_required_search( + plan->before, &plan->nbefore, KIT_OS_HOSTED_MAX_BEFORE, req, dirs, + req->shared_link ? "crtbegin_so.o" : "crtbegin_dynamic.o", + KIT_OS_HOSTED_INPUT_OBJECT) != 0) + return 1; + if (kit_os_hosted_add_required_search( + plan->after, &plan->nafter, KIT_OS_HOSTED_MAX_AFTER, req, dirs, + "libc.so", KIT_OS_HOSTED_INPUT_DSO) != 0 || + kit_os_hosted_add_required_search( + plan->after, &plan->nafter, KIT_OS_HOSTED_MAX_AFTER, req, dirs, + "libdl.so", KIT_OS_HOSTED_INPUT_DSO) != 0) + return 1; + if (kit_os_hosted_add_required_search( + plan->final, &plan->nfinal, KIT_OS_HOSTED_MAX_FINAL, req, dirs, + req->shared_link ? "crtend_so.o" : "crtend_android.o", + KIT_OS_HOSTED_INPUT_OBJECT) != 0) + return 1; + return 0; +} + +static int hosted_sysroot_layout_android(KitOsHostedDirs* dirs, + KitTargetSpec target, + const char* sysroot) { + char api_dir[64]; + int n; + if (target.arch != KIT_ARCH_ARM_64) return 0; + n = snprintf(api_dir, sizeof api_dir, "usr/lib/aarch64-linux-android/%u", + android_api_level(target)); + if (n < 0 || (unsigned)n >= sizeof api_dir) return 1; + if (kit_os_hosted_dirs_add_inc_join(dirs, sysroot, "usr/include") != 0 || + kit_os_hosted_dirs_add_inc_join(dirs, sysroot, + "usr/include/aarch64-linux-android") != + 0 || + kit_os_hosted_dirs_add_lib_join(dirs, sysroot, api_dir) != 0 || + kit_os_hosted_dirs_add_lib_join(dirs, sysroot, + "usr/lib/aarch64-linux-android") != 0) + return 1; + return 0; +} + +const KitOsHostedOps kit_os_android_hosted_ops = { + .obj = KIT_OBJ_ELF, + .default_profile = 1, + .needs_sysroot_libdir = 0, + .resolve = hosted_resolve_android, + .sysroot_layout = hosted_sysroot_layout_android, +}; diff --git a/src/os/android/os.c b/src/os/android/os.c @@ -0,0 +1,10 @@ +#include <kit/os.h> + +extern const KitOsHostedOps kit_os_android_hosted_ops; + +const KitOsImpl kit_os_android_impl = { + .kind = KIT_OS_ANDROID, + .name = "android", + .hosted = &kit_os_android_hosted_ops, + .emu = NULL, +}; diff --git a/src/os/hosted.c b/src/os/hosted.c @@ -189,7 +189,10 @@ int kit_os_hosted_add_clang_compat_defines(KitOsHostedPlan* plan) { kit_os_hosted_add_define(plan, "__has_include_next(x)", "0") != 0 || kit_os_hosted_add_define(plan, "__has_feature(x)", "0") != 0 || kit_os_hosted_add_define(plan, "__has_extension(x)", "0") != 0 || - kit_os_hosted_add_define(plan, "__has_attribute(x)", "0") != 0) + kit_os_hosted_add_define(plan, "__has_attribute(x)", "0") != 0 || + kit_os_hosted_add_define(plan, "_Nonnull", "/*empty*/") != 0 || + kit_os_hosted_add_define(plan, "_Nullable", "/*empty*/") != 0 || + kit_os_hosted_add_define(plan, "_Null_unspecified", "/*empty*/") != 0) return 1; return 0; } diff --git a/src/os/registry.c b/src/os/registry.c @@ -11,6 +11,7 @@ extern const KitOsImpl kit_os_linux_impl; #endif #if KIT_OBJ_ELF_ENABLED extern const KitOsImpl kit_os_freebsd_impl; +extern const KitOsImpl kit_os_android_impl; #endif #if KIT_OBJ_COFF_ENABLED extern const KitOsImpl kit_os_windows_impl; @@ -33,6 +34,8 @@ const KitOsImpl* kit_os_lookup(KitOSKind kind) { #if KIT_OBJ_ELF_ENABLED case KIT_OS_FREEBSD: return &kit_os_freebsd_impl; + case KIT_OS_ANDROID: + return &kit_os_android_impl; #endif #if KIT_OBJ_COFF_ENABLED case KIT_OS_WINDOWS: diff --git a/test/api/target_test.c b/test/api/target_test.c @@ -85,6 +85,21 @@ static void check_target_triple_parse(void) { EXPECT(strcmp(buf, "aarch64-apple-ios-simulator") == 0, "iOS simulator target renders canonical triple"); } + + memset(&t, 0, sizeof t); + EXPECT(kit_target_from_triple("aarch64-linux-android24", &t), + "aarch64 Android API triple parses"); + EXPECT(t.arch == KIT_ARCH_ARM_64 && t.os == KIT_OS_ANDROID && + t.obj == KIT_OBJ_ELF && t.os_version_major == 24, + "aarch64 Android API triple maps to ELF Android"); + + { + char buf[64]; + EXPECT(kit_target_to_triple(t, buf, sizeof buf), + "Android target renders"); + EXPECT(strcmp(buf, "aarch64-linux-android24") == 0, + "Android target renders canonical API triple"); + } } static void check_x64_defaults_and_isa(void) { diff --git a/test/cross/cases/native_activity.c b/test/cross/cases/native_activity.c @@ -0,0 +1,9 @@ +#include <android/native_activity.h> +#include <stddef.h> + +void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, + size_t saved_state_size) { + (void)saved_state; + (void)saved_state_size; + ANativeActivity_setWindowFlags(activity, 0, 0); +}