Android NDK generator
Most of core:sys/android and the vendor:android/* packages are generated from the Android NDK's C headers by generate.py:
python core/sys/android/_gen/generate.py --ndk <path/to/ndk> --verify
--ndk defaults to the ODIN_ANDROID_NDK environment variable. Only Python 3 and the NDK are needed.
The NDK's own clang reads the headers (-Xclang -ast-dump=json), so there is no libclang or pip dependency.
Last generated from NDK r30 (30.0.16248370).
What it does
- Every header in a target's list becomes one Odin file. Small related headers share one.
- A header with nothing to bind (only function-like macros) produces no file.
- Names stay exactly as in C (
ALooper_pollOnce,ANativeWindow,JNIEnv), apart from the members of enums below.- A record that shares its name with a procedure gets
_t, as incore:sys/posix(sync_file_info_t).
- A record that shares its name with a procedure gets
- clang evaluates every enum constant and object-like macro. The source spelling is kept when it is a plain literal.
- The NDK's stub libraries decide each function's
foreign importand the API level it appeared in.- Functions newer than Odin's minimum API level (29) get an
// API level Ncomment.
- Functions newer than Odin's minimum API level (29) get an
- Types from
coreor a target inusesrefer to that package (android.ANativeWindow,android_media.media_status_t). - Records only forward-declared in a bound header (
struct timespec) map tocore:sys/posixthroughEXTERNAL_RECORDS. - Parameter names of function-pointer types come from the header source, when every parameter has one.
- A pointer to a table of function pointers becomes a struct with a
usingfield, so->calls through it.JNIEnv :: struct { using functions: ^JNINativeInterface }, soenv->FindClass(name)works on a^JNIEnv.JavaVMis the same.- OpenSL ES and OpenMAX AL interfaces, e.g.
SLObjectItf :: ^struct { using itf: ^SLObjectItf_ }, soobject->Realize(SL_BOOLEAN_FALSE)works.
- Every generated file starts with a marker line. Only marked files are overwritten or deleted.
Enums, bit_sets, and bit_fields
C enums become Odin types, and so do the anonymous constants table.py, the reviewed table, groups:
- A named C enum (
media_status_t), or an anonymous enum right before an integer typedef (aaudio_format_t), is anenumof that name. table.pyadds new types for anonymous enums and#definefamilies (AKeyCode,AInputSources), bit_sets and bit_fields (ADataSpace,AHardwareBuffer_UsageFlags), and the slots each type takes:AKeyEvent_getKeyCodereturns anAKeyCode.- Members drop their shared prefix (
.OK,.PCM_I16). A member that would start with a digit gets_(._12KEY), orNUMon a key code (.NUM0). - A bit_set's single bits are its element enum's members (
ALooperEventsoverALooperEvent). A member of no bits or several bits keeps its C name as a typed constant (AMETA_NONE :: AMetaState{}), and so does every named value of a bit_field. - The C constants that become members go. The rest stay.
- A slot takes a type of exactly its width. A rule that does not fit its slot, or matches nothing, is an error.
Not generated:
| What | Why |
|---|---|
JNI_OnLoad, JNI_OnUnload |
Callbacks the app exports, not functions it calls. |
inline functions |
No library exports them. They are hand-written in the package's inlines.odin. |
android.odin |
The package documentation and va_list_arg. |
native_app_glue |
Hand-written Odin, not a binding. |
jni_env.odin |
The implicit JNIEnv (jni_set_vm, jni_env) is hand-written. |
assets.odin |
The asset_* helpers over AAssetManager, listing directories through JNI. |
stdio_logcat.odin |
redirect_stdio_to_logcat, which the glue calls before main. |
| EGL, GLES, Vulkan, zlib | vendor:egl, vendor:OpenGL (load_gles_up_to), vendor:vulkan and vendor:zlib. |
| GLES 1.x (libGLESv1_CM) | Fixed-function OpenGL ES. Not bound. |
| The Java APIs | vendor:android/java, generated from the SDK's android.jar by its own _gen. |
Targets
| Target | Package | Libraries | Notes |
|---|---|---|---|
core |
core:sys/android |
libandroid, libnativewindow, liblog, libnativehelper, libc, libdl | |
aaudio |
vendor:android/aaudio |
libaaudio | |
camera |
vendor:android/camera |
libcamera2ndk | |
media |
vendor:android/media |
libmediandk | |
amidi |
vendor:android/amidi |
libamidi | Uses media. |
jnigraphics |
vendor:android/jnigraphics |
libjnigraphics | bitmap.h, imagedecoder.h. |
binder |
vendor:android/binder |
libbinder_ndk, libandroid, libnativewindow | With the binder-based parts of libandroid and libnativewindow. |
sync |
vendor:android/sync |
libsync | |
neuralnetworks |
vendor:android/neuralnetworks |
libneuralnetworks | Deprecated in API level 35. |
icu |
vendor:android/icu |
libicu | API level 31. |
opensles |
vendor:android/opensles |
libOpenSLES | Deprecated. Use AAudio. |
openmaxal |
vendor:android/openmaxal |
libOpenMAXAL | Deprecated. |
A vendor package is called android_<target>, but an import is named after its path: import "vendor:android/camera" gives camera.
--target <name> generates one of them. The models of core and the target's uses are always built, since their types are referred to.
A header whose functions live outside its target's libraries is an error. Such a header belongs to another target.
Verification
--verify checks every generated package against clang on all four Android ABIs (arm64, x86_64, armv7, x86):
- the size and alignment of every type, the offset of every field, and the value of every constant, enum member, bit_set element, and typed constant;
- that every procedure and variable links against the newest stub libraries.
Run it after every regeneration.