2010-10-16 6 views
33

estoy usando Ubuntu 10.10 ¿Cómo compilar la biblioteca dinámica para una aplicación JNI en Linux?

Así que eso es lo que hice.

Hello.java:

class Hello { 
     public native void sayHello(); 

     static { System.loadLibrary("hellolib"); } 

     public static void main(String[] args){ 
       Hello h = new Hello(); 
       h.sayHello(); 
     } 
} 

Entonces me encontré con los comandos siguientes;

[email protected]:~/Scrivania/provajni$ javac Hello.java 

[email protected]:~/Scrivania/provajni$ javah -jni Hello 

que he obtenido Hello.class y Hello.h.

hello.h:

/* DO NOT EDIT THIS FILE - it is machine generated */ 
#include <jni.h> 
/* Header for class Hello */ 

#ifndef _Included_Hello 
#define _Included_Hello 
#ifdef __cplusplus 
extern "C" { 
#endif 
/* 
* Class:  Hello 
* Method: sayHello 
* Signature:()V 
*/ 
JNIEXPORT void JNICALL Java_Hello_sayHello 
    (JNIEnv *, jobject); 

#ifdef __cplusplus 
} 
#endif 
#endif 

Entonces creó Hello.cpp:

#include <jni.h> 
#include "Hello.h" 
#include <iostream> 

using namespace std; 

JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) { 
     cout << "Hello World!" << endl; 
     return; 
} 

Y ahora la parte donde creo que la he cagado. Estaba inspirado por esta guide (Compile the Dynamic or Shared Object Library section):

[email protected]:~/Scrivania/provajni$ gcc -I"/usr/lib/jvm/java-6-sun/include" -I"/usr/lib/jvm/java-6-sun/include/linux" -o hellolib.so -shared -Wl,-soname,hello.so Hello.cpp -static -lc 

que genera el archivo hellolib.so

Pero cuando intento ejecutarlo con java Hello tengo este error:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no hellolib in java.library.path 
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1734) 
at java.lang.Runtime.loadLibrary0(Runtime.java:823) 
at java.lang.System.loadLibrary(System.java:1028) 
at Hello.<clinit>(Hello.java:4) 
Could not find the main class: Hello. Program will exit. 

incluso probé este :

LD_LIBRARY_PATH=`pwd` 
    export LD_LIBRARY_PATH 

sin resultados.

Sé que estoy haciendo algo extremadamente estúpido, pero no puedo entender de qué se trata. La lib dinámica se genera con la opción -shared, ¿no es así?

Actualización # 1

me trataron static { System.load("/home/dierre/Scrivania/provajni/hellolib.so"); } para ver si eso funcionó pero ahora:

Exception in thread "main" java.lang.UnsatisfiedLinkError: /home/dierre/Scrivania/provajni/hello.so: /home/dierre/Scrivania/provajni/hello.so: undefined symbol: _ZSt4cout 
    at java.lang.ClassLoader$NativeLibrary.load(Native Method) 
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1803) 
    at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1699) 
    at java.lang.Runtime.load0(Runtime.java:770) 
    at java.lang.System.load(System.java:1003) 
    at Hello.<clinit>(Hello.java:4) 

Actualización # 2 Ok, para resolver el Actualización # 1 problema que tuve para usar g++ insted de gcc, obviamente. Aún tiene problemas para usar el método load. Parece que no puedo decir el camino correcto.

Respuesta

35

La biblioteca nativa se puede cargar mediante loadLibrary con un nombre válido. Por ejemplo, lib XXXX .so para la familia de Linux, su hellolib.so debe cambiar el nombre a libhello.so. Por cierto, desarrollo java con jni, separaré la implementación y la interfaz nativa (.c o .cpp).

static { 
    System.loadLibrary("hello"); // will load libhello.so 
} 

La cabecera de aplicación (HelloImpl.h):

#ifndef _HELLO_IMPL_H 
#define _HELLO_IMPL_H 

#ifdef __cplusplus 
     extern "C" { 
#endif 

     void sayHello(); 

#ifdef __cplusplus 
     } 
#endif 

#endif 

HelloImpl.cpp:

#include "HelloImpl.h" 
#include <iostream> 

using namespace std; 

void sayHello() { 
    cout << "Hello World!" << endl; 
    return; 
} 

hola.c (prefiero compilar JNI en c):

#include <jni.h> 
#include "Hello.h" 
#include "HelloImpl.h" 

JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) { 
    sayHello(); 
    return; 
} 

Finalmente, podemos compilarlos en algunos pasos:

  1. compilación obj (generar HelloImpl.o)

g++ -c -I"/opt/java/include" -I"/opt/java/include/linux" HelloImpl.cpp

    jni
  1. de compilación con .o

g++ -I"/opt/java/include" -I"/opt/java/include/linux" -o libhello.so -shared -Wl,-soname,hello.so Hello.c HelloImpl.o -static -lc

en el paso 2, usamos g ++ para compilarlo. Esto es muy importante. yor puede ver How to mix C and C++

Después de la compilación, se puede comprobar la función de nomenclatura en los nm:

$ nm libhello.so |grep say 
00000708 T Java_Hello_sayHello 
00000784 t _GLOBAL__I_sayHello 
00000718 T sayHello 

Hay una marcada Java_Hello_sayHello T. Se debe extactly igual a su nombre de método nativo. Si todo esta bien. puede ejecutarlo:

$ java -Djava.library.path=. Hello 
Hello World! 
+4

Esto funciona para mí, si dejo fuera la opción "-static" en el último paso de compilación. Si no lo consigo, obtengo este error: /usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/4.6.1/crtbeginT.o: la reubicación R_X86_64_32 contra '__DTOR_END__ 'no puede ser utilizado al hacer un objeto compartido; volver a compilar con -fPIC Encontré la solución en esta pregunta SO: http://stackoverflow.com/questions/6634387/c-standard-linked-shared-library –

+0

Funcionó. Gracias. ¿Puedes explicar qué es esto -Djava.library.path =. Hola –

+0

-Djava.library.path =. especifica dónde java debe buscar archivos libXXX.so (. significa el directorio actual) – stanm

4

Esto se queja de que los símbolos de C++ no estén disponibles.Me parece recordar, cuando utilizo para hacer cosas JNI todo el tiempo que hubo problemas de vinculación en las bibliotecas de C++ y siempre nos quedamos a la antigua C

Si cambia el código para que sea estándar C (y cambie el nombre archivo):

#include <jni.h> 
#include "Hello.h" 
#include <stdio.h> 

JNIEXPORT void JNICALL Java_Hello_sayHello (JNIEnv *env, jobject obj) { 
     printf("Hello World"); 
     return; 
} 

y compilarlo

gcc -I/usr/lib/jvm/java-6-openjdk/include -o libhellolib.so -shared Hello.c 

funciona

java -Djava.library.path=`pwd` Hello 
Hello World 
+0

dierre @ cox: ~/Scrivania/provajni $ ldd hello.so \t linux-gate.so.1 => (0x00b46000) \t libc.so.6 = > /lib/libc.so.6 (0x0018d000) \t /lib/ld-linux.so.2 (0x00b16000) – dierre

+0

intente cambiarle el nombre ... –

+2

era gcc en lugar de g ++. Pero todavía no puedo usar 'loadLibrary', solo' load' – dierre

25

Finalmente mi código funciona. Esta es hello.java

public class hello { 
    public native void sayHello(int length) ; 
    public static void main (String args[]) { 
    String str = "I am a good boy" ; 
    hello h = new hello() ; 
    h.sayHello (str.length()) ; 
    } 
    static { 
    System.loadLibrary ("hello") ; 
    } 
} 

Debe compilarlo como:

$ javac hello.java 

crear.archivo h se debe ejecutar este comando:

$ javah -jni hello 

Esta es hello.h:

JNIEXPORT void JNICALL Java_hello_sayHello 
(JNIEnv *, jobject, jint); 

Aquí es hello.c:

#include<stdio.h> 
#include<jni.h> 
#include "hello.h" 

JNIEXPORT void JNICALL Java_hello_sayHello 
    (JNIEnv *env, jobject object, jint len) { 
    printf ("\nLength is %d", len); } 

Para compilar este y crear una biblioteca compartida que tenemos que ejecutar este comando:

$ gcc -I/usr/lib/jvm/java-6-openjdk/include -o libhello.so -shared hello.c 

Entonces, finalmente, ejecutar éste:

$ java -Djava.library.path=. hello 
+1

cuando compilo, funciona cuando lo hago: 'gcc -I/usr/local/jdk1.8.0_91/include/usr/local/jdk1. 8.0_91/include/linux -o libhello.so -shared -fPIC hello.c' – tidy

+0

@tidy Su línea de comando funcionó para mí, de lo contrario el ejemplo de Sandipan es bueno. – drlolly

Cuestiones relacionadas