• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.jnigen;
18 
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileNotFoundException;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.util.HashSet;
26 import java.util.Set;
27 import java.util.zip.CRC32;
28 import java.util.zip.ZipEntry;
29 import java.util.zip.ZipFile;
30 
31 /** Loads shared libraries from a natives jar file (desktop) or arm folders (Android). For desktop projects, have the natives jar
32  * in the classpath, for Android projects put the shared libraries in the libs/armeabi and libs/armeabi-v7a folders.
33  *
34  * See {@link AntScriptGenerator}.
35  *
36  * @author mzechner */
37 public class JniGenSharedLibraryLoader {
38 	private static Set<String> loadedLibraries = new HashSet<String>();
39 	private String nativesJar;
40 	private SharedLibraryFinder libraryFinder;
41 
42 	private ZipFile nativesZip = null;
43 
JniGenSharedLibraryLoader()44 	public JniGenSharedLibraryLoader () {
45 	}
46 
47 	/** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly, see MyJniClass.
48 	 * @param nativesJar */
JniGenSharedLibraryLoader(String nativesJar)49 	public JniGenSharedLibraryLoader (String nativesJar) {
50 		this.nativesJar = nativesJar;
51 	}
52 
53 	/** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly, see MyJniClass.
54 	 * @param nativesJar
55 	 * @param libraryFinder A custom libraryfinder, which enables the use of different dynamic libs naming. */
JniGenSharedLibraryLoader(String nativesJar, SharedLibraryFinder libraryFinder)56 	public JniGenSharedLibraryLoader (String nativesJar, SharedLibraryFinder libraryFinder) {
57 		this.nativesJar = nativesJar;
58 		this.libraryFinder = libraryFinder;
59 		if (nativesJar != null) {
60 			try {
61 				nativesZip = new ZipFile(nativesJar);
62 			} catch (IOException e) {
63 				nativesZip = null;
64 			}
65 		}
66 	}
67 
68 	/** Setting a SharedLibraryFinder enables you to load libraries according to a nondefault natives jar layout or library names.
69 	 * @param libraryFinder */
setSharedLibraryFinder(SharedLibraryFinder libraryFinder)70 	public void setSharedLibraryFinder (SharedLibraryFinder libraryFinder) {
71 		this.libraryFinder = libraryFinder;
72 		if (nativesJar != null) {
73 			try {
74 				nativesZip = new ZipFile(nativesJar);
75 			} catch (IOException e) {
76 				nativesZip = null;
77 			}
78 		}
79 	}
80 
81 	/** Returns a CRC of the remaining bytes in the stream. */
crc(InputStream input)82 	public String crc (InputStream input) {
83 		if (input == null) return "" + System.nanoTime(); // fallback
84 		CRC32 crc = new CRC32();
85 		byte[] buffer = new byte[4096];
86 		try {
87 			while (true) {
88 				int length = input.read(buffer);
89 				if (length == -1) break;
90 				crc.update(buffer, 0, length);
91 			}
92 		} catch (Exception ex) {
93 			try {
94 				input.close();
95 			} catch (Exception ignored) {
96 			}
97 		}
98 		return Long.toString(crc.getValue());
99 	}
100 
loadLibrary(String sharedLibName)101 	private boolean loadLibrary (String sharedLibName) {
102 		if (sharedLibName == null) return false;
103 
104 		String path = extractLibrary(sharedLibName);
105 		if (path != null) System.load(path);
106 		return path != null;
107 	}
108 
extractLibrary(String sharedLibName)109 	private String extractLibrary (String sharedLibName) {
110 		String srcCrc = crc(JniGenSharedLibraryLoader.class.getResourceAsStream("/" + sharedLibName));
111 		File nativesDir = new File(System.getProperty("java.io.tmpdir") + "/jnigen/" + srcCrc);
112 		File nativeFile = new File(nativesDir, sharedLibName);
113 
114 		String extractedCrc = null;
115 		if (nativeFile.exists()) {
116 			try {
117 				extractedCrc = crc(new FileInputStream(nativeFile));
118 			} catch (FileNotFoundException ignored) {
119 			}
120 		}
121 
122 		if (extractedCrc == null || !extractedCrc.equals(srcCrc)) {
123 			try {
124 				// Extract native from classpath to temp dir.
125 				InputStream input = null;
126 				if (nativesJar == null)
127 					input = JniGenSharedLibraryLoader.class.getResourceAsStream("/" + sharedLibName);
128 				else
129 					input = getFromJar(nativesJar, sharedLibName);
130 				if (input == null) return null;
131 				nativeFile.getParentFile().mkdirs();
132 				FileOutputStream output = new FileOutputStream(nativeFile);
133 				byte[] buffer = new byte[4096];
134 				while (true) {
135 					int length = input.read(buffer);
136 					if (length == -1) break;
137 					output.write(buffer, 0, length);
138 				}
139 				input.close();
140 				output.close();
141 			} catch (IOException ex) {
142 				ex.printStackTrace();
143 				throw new RuntimeException(ex);
144 			}
145 		}
146 		return nativeFile.exists() ? nativeFile.getAbsolutePath() : null;
147 	}
148 
getFromJar(String jarFile, String sharedLibrary)149 	private InputStream getFromJar (String jarFile, String sharedLibrary) throws IOException {
150 		ZipFile file = new ZipFile(nativesJar);
151 		ZipEntry entry = file.getEntry(sharedLibrary);
152 		return file.getInputStream(entry);
153 	}
154 
155 	/** Loads a shared library with the given name for the platform the application is running on. The name should not contain a
156 	 * prefix (e.g. 'lib') or suffix (e.g. '.dll).
157 	 * @param sharedLibName */
load(String sharedLibName)158 	public synchronized void load (String sharedLibName) {
159 		if (loadedLibraries.contains(sharedLibName)) return;
160 
161 		boolean isWindows = System.getProperty("os.name").contains("Windows");
162 		boolean isLinux = System.getProperty("os.name").contains("Linux");
163 		boolean isMac = System.getProperty("os.name").contains("Mac");
164 		boolean isAndroid = false;
165 		boolean is64Bit = System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64");
166 		boolean isArm = System.getProperty("os.arch").equals("arm");
167 
168 		String vm = System.getProperty("java.vm.name");
169 		if (vm != null && vm.contains("Dalvik")) {
170 			isAndroid = true;
171 			isWindows = false;
172 			isLinux = false;
173 			isMac = false;
174 			is64Bit = false;
175 		}
176 
177 		boolean loaded = false;
178 		if (isWindows) {
179 			if (libraryFinder != null)
180 				loaded = loadLibrary(libraryFinder.getSharedLibraryNameWindows(sharedLibName, is64Bit, nativesZip));
181 			else if (!is64Bit)
182 				loaded = loadLibrary(sharedLibName + ".dll");
183 			else
184 				loaded = loadLibrary(sharedLibName + "64.dll");
185 		}
186 		if (isLinux) {
187 			if (libraryFinder != null)
188 				loaded = loadLibrary(libraryFinder.getSharedLibraryNameLinux(sharedLibName, is64Bit, isArm, nativesZip));
189 			else if (!is64Bit) {
190 				if (isArm)
191 					loaded = loadLibrary("lib" + sharedLibName + "Arm.so");
192 				else
193 					loaded = loadLibrary("lib" + sharedLibName + ".so");
194 			} else {
195 				if (isArm)
196 					loaded = loadLibrary("lib" + sharedLibName + "Arm64.so");
197 				else
198 					loaded = loadLibrary("lib" + sharedLibName + "64.so");
199 			}
200 		}
201 		if (isMac) {
202 			if (libraryFinder != null)
203 				loaded = loadLibrary(libraryFinder.getSharedLibraryNameMac(sharedLibName, is64Bit, nativesZip));
204 			else if (!is64Bit)
205 				loaded = loadLibrary("lib" + sharedLibName + ".dylib");
206 			else
207 				loaded = loadLibrary("lib" + sharedLibName + "64.dylib");
208 		}
209 		if (isAndroid) {
210 			if (libraryFinder != null)
211 				System.loadLibrary(libraryFinder.getSharedLibraryNameAndroid(sharedLibName, nativesZip));
212 			else
213 				System.loadLibrary(sharedLibName);
214 			loaded = true;
215 		}
216 		if (loaded) loadedLibraries.add(sharedLibName);
217 	}
218 }
219