¿Cómo agrupar una biblioteca nativa y una biblioteca JNI dentro de un JAR?
La biblioteca en cuestión es el Gabinete de Tokio .
Lo que quiero es tener la biblioteca nativa, la biblioteca JNI y todas las clases de API de Java en un archivo JAR para evitar dolores de cabeza por la redistribución.
Parece haber un intento de esto en GitHub , pero
- No incluye la biblioteca nativa real, solo la biblioteca JNI.
- Parece ser específico del complemento de dependencias nativas de Leiningen (no funcionará como redistribuible).
La pregunta es, ¿puedo agrupar todo en un JAR y redistribuirlo? Si es así, ¿cómo?
PD: Sí, me doy cuenta de que puede tener implicaciones de portabilidad.
Es posible crear un único archivo JAR con todas las dependencias, incluidas las bibliotecas JNI nativas para una o más plataformas. El mecanismo básico es utilizar System.load(File) para cargar la biblioteca en lugar del típico System.loadLibrary(String) que busca la propiedad del sistema java.library.path. Este método simplifica mucho la instalación, ya que el usuario no tiene que instalar la biblioteca JNI en su sistema, pero a costa de que no todas las plataformas sean compatibles, ya que la biblioteca específica de una plataforma podría no estar incluida en un único archivo JAR. .
El proceso es el siguiente:
- incluya las bibliotecas JNI nativas en el archivo JAR en una ubicación específica de la plataforma, por ejemplo en NATIVE/${os.arch}/${os.name}/libname.lib
- crear código en un inicializador estático de la clase principal para
- Calc el actual os.arch y os.name
- busque la biblioteca en el archivo JAR en la ubicación predefinida usando Class.getResource(String)
- si existe, extráigalo a un archivo temporal y cárguelo con System.load(File).
Agregué funcionalidad para hacer esto para jzmq, los enlaces Java de ZeroMQ (complemento descarado). El código se puede encontrar aquí . El código jzmq utiliza una solución híbrida de modo que si no se puede cargar una biblioteca integrada, el código volverá a buscar la biblioteca JNI a lo largo de java.library.path.
https://www.adamheinrich.com/blog/2012/12/how-to-load-native-jni-library-from-jar/
Es un gran artículo, que resuelve mi problema.
En mi caso tengo el siguiente código para inicializar la biblioteca:
static {
try {
System.loadLibrary("crypt"); // used for tests. This library in classpath only
} catch (UnsatisfiedLinkError e) {
try {
NativeUtils.loadLibraryFromJar("/natives/crypt.dll"); // during runtime. .DLL within .JAR
} catch (IOException e1) {
throw new RuntimeException(e1);
}
}
}
JarClassLoader es un cargador de clases para cargar clases, bibliotecas nativas y recursos desde un único JAR monstruoso y desde archivos JAR dentro del JAR monstruoso.
Solución para Kotlin:
build.gradle.dsl
: copie el tiempo de ejecución de Kotlin (kotlin-stdlib-1.4.0.jar) y la biblioteca nativa (librust_kotlin.dylib) en JARtasks.withType<Jar> { manifest { attributes("Main-Class" to "MainKt") } val libs = setOf("kotlin-stdlib-1.4.0.jar") from(configurations.runtimeClasspath.get() .filter { it.name in libs } .map { zipTree(it) }) from("librust_kotlin.dylib") }
main
método: copie la biblioteca a un archivo temporal para cargarla usando la ruta absolutawith(createTempFile()) { deleteOnExit() val bytes = My::class.java.getResource("librust_kotlin.dylib") .readBytes() outputStream().write(bytes) System.load(path) }