Add Iris Android APK tooling
This commit is contained in:
1 parent
3246f397b5
commit
37f956707e
20 files changed
+1766
-54
No files matched your search
+85
-2
@@ -2,13 +2,96 @@ extern crate proc_macro;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Attribute, Block, Error, GenericParam, Generics, Ident, ItemStruct, ItemTrait, Signature,
|
||||
Token, Type, Visibility,
|
||||
Attribute, Block, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct, ItemTrait,
|
||||
ReturnType, Signature, Token, Type, Visibility,
|
||||
parse::{Parse, ParseStream, Result},
|
||||
parse_macro_input, parse_quote,
|
||||
spanned::Spanned,
|
||||
};
|
||||
|
||||
/// Marks the factory called when Android creates an Iris view.
|
||||
///
|
||||
/// An attribute is necessary here because the Android loader requires one
|
||||
/// exported `JNI_OnLoad` symbol and `android-view` requires a plain function
|
||||
/// pointer monomorphized for the returned application state. The generated
|
||||
/// linker and JNI glue is Android-gated; the annotated function therefore
|
||||
/// does not need its own `cfg` attribute.
|
||||
#[proc_macro_attribute]
|
||||
pub fn app_init(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
if !args.is_empty() {
|
||||
return Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"app_init takes no arguments",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let function = parse_macro_input!(item as ItemFn);
|
||||
let name = &function.sig.ident;
|
||||
let ReturnType::Type(_, state) = &function.sig.output else {
|
||||
return Error::new(
|
||||
function.sig.output.span(),
|
||||
"an app_init function must return its application state",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
};
|
||||
if function.sig.inputs.len() != 2
|
||||
|| function
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.any(|argument| !matches!(argument, FnArg::Typed(_)))
|
||||
{
|
||||
return Error::new(
|
||||
function.sig.inputs.span(),
|
||||
"an app_init function takes AndroidUiState and &mut State::Resources",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
if function.sig.asyncness.is_some()
|
||||
|| function.sig.constness.is_some()
|
||||
|| matches!(function.sig.safety, syn::Safety::Unsafe(_))
|
||||
|| !function.sig.generics.params.is_empty()
|
||||
{
|
||||
return Error::new(
|
||||
function.sig.span(),
|
||||
"an app_init function must be a plain, non-generic synchronous function",
|
||||
)
|
||||
.into_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
quote! {
|
||||
#[cfg(target_os = "android")]
|
||||
#function
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
mod __iris_android_app {
|
||||
use super::*;
|
||||
|
||||
extern "system" fn new_view_peer<'local>(
|
||||
env: ::iris::android::__private::JNIEnv<'local>,
|
||||
view: ::iris::android::__private::View<'local>,
|
||||
context: ::iris::android::__private::Context<'local>,
|
||||
) -> ::iris::android::__private::JLong {
|
||||
::iris::android::new_peer::<#state>(env, view, context, super::#name)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "system" fn JNI_OnLoad(
|
||||
vm: *mut ::iris::android::__private::RawJavaVM,
|
||||
_: *mut ::core::ffi::c_void,
|
||||
) -> ::iris::android::__private::JInt {
|
||||
unsafe { ::iris::android::__private::on_load(vm, new_view_peer) }
|
||||
}
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
struct Input {
|
||||
attrs: Vec<Attribute>,
|
||||
vis: Visibility,
|
||||
|
||||
Reference in new issue
Block a user