com.badlogic.gdx.jnigen.JniGenSharedLibraryLoader Maven / Gradle / Ivy
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.jnigen;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/** Loads shared libraries from a natives jar file (desktop) or arm folders (Android). For desktop projects, have the natives jar
* in the classpath, for Android projects put the shared libraries in the libs/armeabi and libs/armeabi-v7a folders.
*
* See {@link AntScriptGenerator}.
*
* @author mzechner */
public class JniGenSharedLibraryLoader {
private static Set loadedLibraries = new HashSet();
private String nativesJar;
private SharedLibraryFinder libraryFinder;
private ZipFile nativesZip = null;
public JniGenSharedLibraryLoader () {
}
/** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly, see MyJniClass.
* @param nativesJar */
public JniGenSharedLibraryLoader (String nativesJar) {
this.nativesJar = nativesJar;
}
/** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly, see MyJniClass.
* @param nativesJar
* @param libraryFinder A custom libraryfinder, which enables the use of different dynamic libs naming. */
public JniGenSharedLibraryLoader (String nativesJar, SharedLibraryFinder libraryFinder) {
this.nativesJar = nativesJar;
this.libraryFinder = libraryFinder;
if (nativesJar != null) {
try {
nativesZip = new ZipFile(nativesJar);
} catch (IOException e) {
nativesZip = null;
}
}
}
/** Setting a SharedLibraryFinder enables you to load libraries according to a nondefault natives jar layout or library names.
* @param libraryFinder */
public void setSharedLibraryFinder (SharedLibraryFinder libraryFinder) {
this.libraryFinder = libraryFinder;
if (nativesJar != null) {
try {
nativesZip = new ZipFile(nativesJar);
} catch (IOException e) {
nativesZip = null;
}
}
}
/** Returns a CRC of the remaining bytes in the stream. */
public String crc (InputStream input) {
if (input == null) return "" + System.nanoTime(); // fallback
CRC32 crc = new CRC32();
byte[] buffer = new byte[4096];
try {
while (true) {
int length = input.read(buffer);
if (length == -1) break;
crc.update(buffer, 0, length);
}
} catch (Exception ex) {
try {
input.close();
} catch (Exception ignored) {
}
}
return Long.toString(crc.getValue());
}
private boolean loadLibrary (String sharedLibName) {
if (sharedLibName == null) return false;
String path = extractLibrary(sharedLibName);
if (path != null) System.load(path);
return path != null;
}
private String extractLibrary (String sharedLibName) {
String srcCrc = crc(JniGenSharedLibraryLoader.class.getResourceAsStream("/" + sharedLibName));
File nativesDir = new File(System.getProperty("java.io.tmpdir") + "/jnigen/" + srcCrc);
File nativeFile = new File(nativesDir, sharedLibName);
String extractedCrc = null;
if (nativeFile.exists()) {
try {
extractedCrc = crc(new FileInputStream(nativeFile));
} catch (FileNotFoundException ignored) {
}
}
if (extractedCrc == null || !extractedCrc.equals(srcCrc)) {
try {
// Extract native from classpath to temp dir.
InputStream input = null;
if (nativesJar == null)
input = JniGenSharedLibraryLoader.class.getResourceAsStream("/" + sharedLibName);
else
input = getFromJar(nativesJar, sharedLibName);
if (input == null) return null;
nativeFile.getParentFile().mkdirs();
FileOutputStream output = new FileOutputStream(nativeFile);
byte[] buffer = new byte[4096];
while (true) {
int length = input.read(buffer);
if (length == -1) break;
output.write(buffer, 0, length);
}
input.close();
output.close();
} catch (IOException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
return nativeFile.exists() ? nativeFile.getAbsolutePath() : null;
}
private InputStream getFromJar (String jarFile, String sharedLibrary) throws IOException {
ZipFile file = new ZipFile(nativesJar);
ZipEntry entry = file.getEntry(sharedLibrary);
return file.getInputStream(entry);
}
/** Loads a shared library with the given name for the platform the application is running on. The name should not contain a
* prefix (e.g. 'lib') or suffix (e.g. '.dll).
* @param sharedLibName */
public synchronized void load (String sharedLibName) {
if (loadedLibraries.contains(sharedLibName)) return;
boolean isWindows = System.getProperty("os.name").contains("Windows");
boolean isLinux = System.getProperty("os.name").contains("Linux");
boolean isMac = System.getProperty("os.name").contains("Mac");
boolean isAndroid = false;
boolean is64Bit = System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64");
boolean isArm = System.getProperty("os.arch").equals("arm");
String vm = System.getProperty("java.vm.name");
if (vm != null && vm.contains("Dalvik")) {
isAndroid = true;
isWindows = false;
isLinux = false;
isMac = false;
is64Bit = false;
}
boolean loaded = false;
if (isWindows) {
if (libraryFinder != null)
loaded = loadLibrary(libraryFinder.getSharedLibraryNameWindows(sharedLibName, is64Bit, nativesZip));
else if (!is64Bit)
loaded = loadLibrary(sharedLibName + ".dll");
else
loaded = loadLibrary(sharedLibName + "64.dll");
}
if (isLinux) {
if (libraryFinder != null)
loaded = loadLibrary(libraryFinder.getSharedLibraryNameLinux(sharedLibName, is64Bit, isArm, nativesZip));
else if (!is64Bit) {
if (isArm)
loaded = loadLibrary("lib" + sharedLibName + "Arm.so");
else
loaded = loadLibrary("lib" + sharedLibName + ".so");
} else {
if (isArm)
loaded = loadLibrary("lib" + sharedLibName + "Arm64.so");
else
loaded = loadLibrary("lib" + sharedLibName + "64.so");
}
}
if (isMac) {
if (libraryFinder != null)
loaded = loadLibrary(libraryFinder.getSharedLibraryNameMac(sharedLibName, is64Bit, nativesZip));
else if (!is64Bit)
loaded = loadLibrary("lib" + sharedLibName + ".dylib");
else
loaded = loadLibrary("lib" + sharedLibName + "64.dylib");
}
if (isAndroid) {
if (libraryFinder != null)
System.loadLibrary(libraryFinder.getSharedLibraryNameAndroid(sharedLibName, nativesZip));
else
System.loadLibrary(sharedLibName);
loaded = true;
}
if (loaded) loadedLibraries.add(sharedLibName);
}
}