Dushistov / flapigen-rs

Tool for connecting programs or libraries written in Rust with other languages
BSD 3-Clause "New" or "Revised" License
775 stars 59 forks source link

How to customize the generated JNI_OnLoad content #440

Closed Flocksserver closed 1 year ago

Flocksserver commented 1 year ago

Hey, i am developing an android lib and would like to use flapigen for it. But for this I have to initialize the ndk-context manually, because I want to use android api calls. I can do this by adding the appropriate calls to JNI_OnLoad. Since flapigen already generates a JNI_OnLoad function I have the question how to get my custom call in there? For an idea I would be very thankful.

Dushistov commented 1 year ago

flapigen is just text processor after all. So you can just code into build.rs after usage of flapigen functions, and extend programmatically JNI_OnLoad.

But for this I have to initialize the ndk-context manually, because I want to use android api calls

In case of Java/JNI you already have implicit function parameter (env : * mut JNIEnv), so if that's what you need, you can just use it.

Flocksserver commented 1 year ago

Thanks for the quick reply. 🙏

In case of Java/JNI you already have implicit function parameter (env : * mut JNIEnv), so if that's what you need, you can just use it.

initializing the android context seems to work only via onload 🤷‍♂️

So you can just code into build.rs

Ok, that felt very much like a workaround to do that afterwards via replace text. I thought maybe there is a more elegant way. I have now solved it as follows. It works as well. But as I said it feels a bit hacky.

    // a more robust way to "search and replace" would be better - but as a poc it works
    let orig_content = "    SWIG_JNI_VERSION\n}";
    let new_content = "    let jvm = java_vm as *mut _ as *mut ::std::os::raw::c_void;\n    unsafe {\n        ndk_context::initialize_android_context(jvm, _reserved);\n    }\n    SWIG_JNI_VERSION\n}";

    let java_glue_content = fs::read_to_string(&out_src).unwrap();
    let java_glue_content_modified = java_glue_content.replace(orig_content, new_content);
    let mut file = OpenOptions::new()
        .write(true)
        .truncate(true)
        .open(&out_src)
        .unwrap();
    file.write(java_glue_content_modified.as_bytes()).unwrap();
Dushistov commented 1 year ago

that felt very much like a workaround

@Flocksserver

I thought about providing access to Rust AST generated by flapigen, but this force user to work with VisitMut, and resulted code would be more complex then std::fs::read_to_string + std::string::String::replace.

The only problem I see with this approach, that build.rs would renegerate out_src file every time, even if nothing changed (that cause rebuild even if generated file not changed), but cargo has it's own caching mechanizm for build.rs, so this is not so bad.

By the way JNI_OnLoad is not the only way to get pointer to JavaVM, you can call GetJavaVM any time, for example, this is used in callbacks: https://github.com/Dushistov/flapigen-rs/blob/d8167dbd6896b96bf1ae8c0db339d2f0adcfb1c4/macroslib/src/java_jni/jni-include.rs#L264

Flocksserver commented 1 year ago

Thank you for your support und for the explanations 🙏