How to load and use native c code in lein project?

Problem
I cannot load and call methods in a compiled class c in a leiningen project. My basic approach is to load the Java class JavaWrapper.java, which uses the JNI to call some of its own methods in its own code, wrapper.o, and then calls methods through this java shell class.
I assume that there are problems with the Loader class loading a java class that loads its own code from the clojure project, but on the condition that I cannot directly get the clojure code to find wrapper.o in the library path, I'm not sure how to deal with by this.

lein project file

(defproject lein-native-test "0.1.0-SNAPSHOT" ... :java-source-paths ["java-path"] :jvm-opts ["-Djava.library.path=.:./native:/absolute/path/to/native"] ;;not sure what format it wants ) 

clojure file with the main method
I tried to modify it slightly using four approaches, all of which are included in the code below along with the corresponding error in the comments.

 (ns lein-native-test.core (:import (com.test JavaWrapper))) (def -main [] ;;four things I've tried and their errors (clojure.lang.RT.load "/abs/path/to/wrapper.o") ;;could not find file /abs/path/wrapper.o_init.class or wrapper.o.clj (clojure.lang.RT.loadLibrary "wrapper.o") ;;UnsatisfiedLinkError no wrapper.o in java library path (JavaWrapper/load "/abs/path/to/wrapper.o") ;;UnsatisfiedLinkError com.test.JavaWrapper.setup() (assembly-load "/abs/path/to/wrapper.o") ;;unable to resolvesymbol: assembly-load ) 

Java code with native methods that uses JNI, JavaWrapper.java

 public class JavaWrapper{ public native void setup(); public static void load(String lib){ System.load(lib);} } 

Before trying to get this to work with clojure and lein, I successfully downloaded and used my own methods in wrapper.o through JavaWrapper and JNI.

Perhaps related:
I also cannot load wrapper.o in JavaWrapper.java via

 System.loadLibrary("wrapper.o"); 

I need to use

 System.load("/absolute/path/to/wrapper.o"); 

Tool versions
clojure version: 1.5.1
lane version: 2.3.4
jdk: 1.7
os: debian7

A better understanding of ClassLoaders or a particularly working simple example would be very helpful, thanks.

+6
source share
1 answer

The problem was due to a naming error in my method in the C header and source files on jni standard . The correct way to use jni with clojure is to create a Java shell class as I did, and load the dynamic library using the clojure.lang.RT.loadLibrary method
Since I had trouble finding good examples for this, I made a demo on github
<w> Errors
1) (clojure.lang.RT.load "/abs/path/to/wrapper.o") ;; could not find file /abs/path/wrapper.o_init.class or wrapper.o.clj
This loading method is not intended to be used for native code, its expectation for a java class or clj file

2) (clojure.lang.RT.loadLibrary "wrapper.o") ;; UnsatisfiedLinkError no wrapper.o in the path to the java library
Clojure cannot find the library during communication, therefore UnsatisfiedLinkError --- this is due to a naming error

  • first the library should be compiled as a dynamic shared library, i.e. for the gcc compiler use the -shared flag (I really did, but did not name the output file with the normal extension .so)
  • java and therefore clojure expect the native library to be named in a special way: libwrapper.so (or .jnilib for mac or .dll for windows, but always with the lib prefix)

3) (JavaWrapper / load "/abs/path/to/wrapper.o") ;; UnsatisfiedLinkError com.test.JavaWrapper.setup ()
This time, the error lies in the method in JavaWrapper in the file or library, so you know that at least it found the file. An UnsatisfiedLinkError that defines a specific method in a Java class, like this one, should always be caused by a naming error between what is declared by the native method in the Java file and what is really present in the source or header file c.
Pay attention to the namespace "com.test"
When declaring a jni method in c, the method name must follow a specific format,
from http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html
"Dynamic linkers allow entries based on their names. The native method name is combined from the following components:"
  • Java_ prefix
  • changed class fully qualified name
  • underscore delimiter (_)
  • distorted method name
  • for overloaded native methods, two underscores (__) followed by a signature with a malformed argument

In this case, the full signature of source c will be

 void Java_com_test_setup(JNIEnv *env, jobject obj) 


4) (build-download "/abs/path/to/wrapper.o") ;; unable to resolve: build-load
This method is also not designed to load native code.

+3
source

Source: https://habr.com/ru/post/971234/


All Articles