JDK system dependency: link against libjvm
See original GitHub issueDescribe the bug
jni.h
does not only contain structures to run c(++) code from java, but also java from c(++). For this to work the resulting binary has to be linked against libjvm.so
located in $JAVA_HOME/lib/server/
. Otherwise if your code tries to call these, the linker will fail:
FAILED: mjvmtest
cc -o mjvmtest mjvmtest.p/jvm.c.o -Wl,--as-needed -Wl,--no-undefined
/usr/lib64/gcc/x86_64-suse-linux/11/../../../../x86_64-suse-linux/bin/ld: mjvmtest.p/jvm.c.o: in function `main':
/home/admin/workspace/cli/meson/mjvmtest/_build/../jvm.c:16: undefined reference to `JNI_CreateJavaVM'
collect2: error: ld returned 1 exit status
ninja: build stopped: subcommand failed.
To Reproduce I’ve taken the JNI Invokation Example: https://docs.oracle.com/en/java/javase/11/docs/specs/jni/invocation.html, ported it to C and made it actually do something.
#include <jni.h> /* where everything is defined */
#include <stdbool.h>
int main(int, char**) {
JavaVM *jvm; /* denotes a Java VM */
JNIEnv *env; /* pointer to native method interface */
JavaVMInitArgs vm_args; /* JDK/JRE 10 VM initialization arguments */
JavaVMOption options[2];
options[0].optionString = "-Djava.class.path=/usr/lib64/java";
options[1].optionString = "-Xcheck:jni";
vm_args.version = JNI_VERSION_10;
vm_args.nOptions = 1;
vm_args.options = options;
vm_args.ignoreUnrecognized = false;
/* load and initialize a Java VM, return a JNI interface
* pointer in env */
JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
/* get stdout PrintStream */
jclass system = (*env)->FindClass(env, "java/lang/System");
jfieldID jstdout_id = (*env)->GetStaticFieldID(env, system, "out", "Ljava/io/PrintStream;");
jobject jstdout = (*env)->GetStaticObjectField(env, system, jstdout_id);
/* invoke println method of PrintStream */
jclass print_stream = (*env)->FindClass(env, "java/io/PrintStream");
jmethodID mid = (*env)->GetMethodID(env, print_stream, "println", "(Ljava/lang/String;)V");
jstring to_print = (*env)->NewStringUTF(env, "Hello World from C");
(*env)->CallVoidMethod(env, jstdout, mid, to_print);
/* We are done. */
(*jvm)->DestroyJavaVM(jvm);
}
project('mjvmtest', 'c',
version : '0.1',
default_options : ['warning_level=3', 'c_std=gnu2x'])
app = executable('mjvmtest', [
'jvm.c'
],
dependencies: [
#dependency('jdk', version: ['>=11', '<12'])
dependency('jdk', version: '>=11')
],
install : true
)
Expected behavior I expect it to link against libjvm.so.
Adding self.link_args = [f'{self.java_home}/lib/server/libjvm.so']
to the JDKSystemDependency init method in mesonbuild/depenencies/dev.py
makes it work.
What needs to be done is to extend it that it uses the correct file extension for each OS and maybe link it in a way that libjvm.so does not have to be in the library search path.
system parameters
- OS: openSUSE Tumbleweed 20211117
- Python 3.8.12
- Meson 0.59.4 & cc80187
- Ninja 1.10.2
Issue Analytics
- State:
- Created 2 years ago
- Comments:11 (11 by maintainers)
I would assume one use case for this is embedding a JVM in another application. Thanks. I will switch to
modules
. The default behavior would stay as “just add the include directories” since most people probably aren’t linking to the JVM libraries.Right. Information around this is pretty hard to come by to be honest. Will have a PR up in a bit