diff --git a/pom.xml b/pom.xml
index 73c2a17f8..953aba2ca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,6 +82,12 @@
3.0
true
+
+ org.eclipse.aether
+ aether-api
+ 1.1.0
+ true
+
junit
junit
diff --git a/src/main/java/org/bytedeco/javacpp/Loader.java b/src/main/java/org/bytedeco/javacpp/Loader.java
index 094270b4e..398f530fc 100644
--- a/src/main/java/org/bytedeco/javacpp/Loader.java
+++ b/src/main/java/org/bytedeco/javacpp/Loader.java
@@ -23,15 +23,22 @@
package org.bytedeco.javacpp;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
+import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -42,7 +49,9 @@
import java.util.WeakHashMap;
import org.bytedeco.javacpp.annotation.Platform;
import org.bytedeco.javacpp.tools.Builder;
+import org.bytedeco.javacpp.tools.Coordinates;
import org.bytedeco.javacpp.tools.Logger;
+import org.bytedeco.javacpp.tools.Repository;
/**
* The Loader contains functionality to load native libraries, but also has a bit
@@ -361,8 +370,8 @@ public static File extractResource(URL resourceURL, File directoryOrFile,
/** User-specified cache directory set and returned by {@link #getCacheDir()}. */
static File cacheDir = null;
- /** Temporary directory set and returned by {@link #getTempDir()}. */
- static File tempDir = null;
+ /** JavaCPP repository */
+ static Repository repository = null;
/** Contains all the native libraries that we have loaded to avoid reloading them. */
static Map loadedLibraries = Collections.synchronizedMap(new HashMap());
@@ -385,25 +394,19 @@ public static File getCacheDir() {
}
/**
- * Creates a unique name for {@link #tempDir} out of
- * {@code System.getProperty("java.io.tmpdir")} and {@code System.nanoTime()}.
- *
- * @return {@link #tempDir}
+ * Creates and returns the repository in which native libraries are stored.
*/
- public static File getTempDir() {
- if (tempDir == null) {
- File tmpdir = new File(System.getProperty("java.io.tmpdir"));
- File f;
- for (int i = 0; i < 1000; i++) {
- f = new File(tmpdir, "javacpp" + System.nanoTime());
- if (f.mkdir()) {
- tempDir = f;
- tempDir.deleteOnExit();
- break;
- }
- }
- }
- return tempDir;
+ private static Repository getRepository() {
+ if (repository == null) {
+ Path root;
+ File cache = getCacheDir();
+ if (cache != null)
+ root = cache.toPath();
+ else
+ root = Paths.get(System.getProperty("user.home"));
+ repository = new Repository(root);
+ }
+ return repository;
}
/** Returns {@code System.getProperty("org.bytedeco.javacpp.loadlibraries")}.
@@ -433,6 +436,7 @@ public static String load(boolean pathsFirst) {
public static String load(Class cls) {
return load(cls, loadProperties(), false);
}
+
/**
* Loads native libraries associated with the given {@link Class}.
*
@@ -443,8 +447,8 @@ public static String load(Class cls) {
* (but {@code if (!isLoadLibraries() || cls == null) { return null; }})
* @throws NoClassDefFoundError on Class initialization failure
* @throws UnsatisfiedLinkError on native library loading failure
- * @see #findLibrary(Class, ClassProperties, String, boolean)
- * @see #loadLibrary(URL[], String)
+ * @see #findLibrary(Class, ClassProperties, String, boolean, Coordinates)
+ * @see #loadLibrary(URL[], String, Coordinates)
*/
public static String load(Class cls, Properties properties, boolean pathsFirst) {
if (!isLoadLibraries() || cls == null) {
@@ -455,6 +459,15 @@ public static String load(Class cls, Properties properties, boolean pathsFirst)
cls = getEnclosingClass(cls);
ClassProperties p = loadProperties(cls, properties, true);
+ // Get library coordinates if available
+ Coordinates coordinates = null;
+ if (cls.isAnnotationPresent(org.bytedeco.javacpp.annotation.Coordinates.class)) {
+ org.bytedeco.javacpp.annotation.Coordinates c =
+ (org.bytedeco.javacpp.annotation.Coordinates)
+ cls.getAnnotation(org.bytedeco.javacpp.annotation.Coordinates.class);
+ coordinates = new Coordinates(c.group(), c.id(), c.version());
+ }
+
// Force initialization of all the target classes in case they need it
List targets = p.get("target");
if (targets.isEmpty()) {
@@ -488,8 +501,8 @@ public static String load(Class cls, Properties properties, boolean pathsFirst)
UnsatisfiedLinkError preloadError = null;
for (String preload : preloads) {
try {
- URL[] urls = findLibrary(cls, p, preload, pathsFirst);
- loadLibrary(urls, preload);
+ URL[] urls = findLibrary(cls, p, preload, pathsFirst, coordinates);
+ loadLibrary(urls, preload, coordinates);
} catch (UnsatisfiedLinkError e) {
preloadError = e;
}
@@ -497,8 +510,8 @@ public static String load(Class cls, Properties properties, boolean pathsFirst)
try {
String library = p.getProperty("platform.library");
- URL[] urls = findLibrary(cls, p, library, pathsFirst);
- return loadLibrary(urls, library);
+ URL[] urls = findLibrary(cls, p, library, pathsFirst, coordinates);
+ return loadLibrary(urls, library, coordinates);
} catch (UnsatisfiedLinkError e) {
if (preloadError != null && e.getCause() == null) {
e.initCause(preloadError);
@@ -519,9 +532,11 @@ public static String load(Class cls, Properties properties, boolean pathsFirst)
* @param libnameversion the name of the library + "@" + optional version tag
* + "#" + a second optional name used at extraction (or empty to prevent it)
* @param pathsFirst search the paths first before bundled resources
+ * @param coordinates maven coordinates
* @return URLs that point to potential locations of the library
*/
- public static URL[] findLibrary(Class cls, ClassProperties properties, String libnameversion, boolean pathsFirst) {
+ public static URL[] findLibrary(Class cls, ClassProperties properties, String libnameversion,
+ boolean pathsFirst, Coordinates coordinates) {
if (libnameversion.trim().endsWith("#")) {
return new URL[0];
}
@@ -629,7 +644,7 @@ public static URL[] findLibrary(Class cls, ClassProperties properties, String li
* (but {@code if (!isLoadLibraries) { return null; }})
* @throws UnsatisfiedLinkError on failure
*/
- public static String loadLibrary(URL[] urls, String libnameversion) {
+ public static String loadLibrary(URL[] urls, String libnameversion, Coordinates coordinates) {
if (!isLoadLibraries()) {
return null;
}
@@ -640,7 +655,6 @@ public static String loadLibrary(URL[] urls, String libnameversion) {
return filename;
}
- File tempFile = null;
UnsatisfiedLinkError loadError = null;
try {
for (URL url : urls) {
@@ -674,46 +688,39 @@ public static String loadLibrary(URL[] urls, String libnameversion) {
// ... get the URL fragment to let users rename library files ...
name = url.getRef();
}
- // ... then check if it has not already been extracted, and if not ...
- file = new File(getCacheDir() != null ? getCacheDir() : getTempDir(), name);
- if (!file.exists()) {
- if (tempFile != null && tempFile.exists()) {
- tempFile.deleteOnExit();
- }
- // ... then extract it from our resources ...
- if (logger.isDebugEnabled()) {
- logger.debug("Extracting " + url);
- }
- extractResource(url, file, null, null);
- if (getCacheDir() == null) {
- tempFile = file;
- }
- } else while (System.currentTimeMillis() - file.lastModified() < 1000) {
- // ... else wait until the file is at least 1 second old ...
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ex) {
- // ... reset interrupt to be nice ...
- Thread.currentThread().interrupt();
- }
- }
+
+ // ... then get it from repo ...
+
+ JarURLConnection c = (JarURLConnection) url.openConnection();
+ Path jar = Paths.get(c.getJarFileURL().getFile());
+
+ // For legacy jars without coordinates
+ if (coordinates == null)
+ coordinates = new Coordinates(
+ "legacy",
+ jar.getFileName().toString(),
+ Long.toString(Files.size(jar)) + "-" +
+ Long.toString(jar.toFile().lastModified()));
+
+ Path repo = getRepository().getPath(
+ new Coordinates(coordinates, getPlatform(), jar));
+ file = new File(repo.toFile(), name);
}
- if (file != null && file.exists()) {
- filename = file.getAbsolutePath();
- try {
- // ... and load it!
- if (logger.isDebugEnabled()) {
- logger.debug("Loading " + filename);
- }
- loadedLibraries.put(libnameversion, filename);
- System.load(filename);
- return filename;
- } catch (UnsatisfiedLinkError e) {
- loadError = e;
- loadedLibraries.remove(libnameversion);
- if (logger.isDebugEnabled()) {
- logger.debug("Failed to load " + filename + ": " + e);
- }
+
+ filename = file.getAbsolutePath();
+ try {
+ // ... and load it!
+ if (logger.isDebugEnabled()) {
+ logger.debug("Loading " + filename);
+ }
+ loadedLibraries.put(libnameversion, filename);
+ System.load(filename);
+ return filename;
+ } catch (UnsatisfiedLinkError e) {
+ loadError = e;
+ loadedLibraries.remove(libnameversion);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to load " + filename + ": " + e);
}
}
}
@@ -745,59 +752,9 @@ public static String loadLibrary(URL[] urls, String libnameversion) {
logger.debug("Failed to extract for " + libnameversion + ": " + e);
}
throw e;
- } finally {
- if (tempFile != null && tempFile.exists()) {
- tempFile.deleteOnExit();
- }
- // But under Windows, it won't get deleted!
- }
- }
-
- // So, let's use a shutdown hook...
- static {
- if (getPlatform().startsWith("windows")) {
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override public void run() {
- if (tempDir == null) {
- return;
- }
- try {
- // ... to launch a separate process ...
- List command = new ArrayList();
- command.add(System.getProperty("java.home") + "/bin/java");
- command.add("-classpath");
- command.add((new File(Loader.class.getProtectionDomain().getCodeSource().getLocation().toURI())).toString());
- command.add(Loader.class.getName());
- command.add(tempDir.getAbsolutePath());
- new ProcessBuilder(command).start();
- } catch (IOException e) {
- throw new RuntimeException(e);
- } catch (URISyntaxException e) {
- throw new RuntimeException(e);
- }
- }
- });
- }
- }
-
- // ... that makes sure to delete all our files.
- public static void main(String[] args) throws InterruptedException {
- File tmpdir = new File(System.getProperty("java.io.tmpdir"));
- File tempDir = new File(args[0]);
- if (!tmpdir.equals(tempDir.getParentFile()) ||
- !tempDir.getName().startsWith("javacpp")) {
- // Someone is trying to break us ... ?
- return;
}
- for (File file : tempDir.listFiles()) {
- while (file.exists() && !file.delete()) {
- Thread.sleep(100);
- }
- }
- tempDir.delete();
}
-
/**
* Contains {@code offsetof()} and {@code sizeof()} values of native types
* of {@code struct}, {@code class}, and {@code union}. A {@link WeakHashMap}
diff --git a/src/main/java/org/bytedeco/javacpp/annotation/Coordinates.java b/src/main/java/org/bytedeco/javacpp/annotation/Coordinates.java
new file mode 100644
index 000000000..74b33abe0
--- /dev/null
+++ b/src/main/java/org/bytedeco/javacpp/annotation/Coordinates.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014-2016 Samuel Audet
+ *
+ * Licensed either under the Apache License, Version 2.0, or (at your option)
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (subject to the "Classpath" exception),
+ * either version 2, or any later version (collectively, 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
+ * http://www.gnu.org/licenses/
+ * http://www.gnu.org/software/classpath/license.html
+ *
+ * or as provided in the LICENSE.txt file that accompanied this code.
+ * 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 org.bytedeco.javacpp.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * Uniquely identifies a library in Maven and the local JavaCPP repository.
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Coordinates {
+ String group();
+ String id();
+ String version();
+}
\ No newline at end of file
diff --git a/src/main/java/org/bytedeco/javacpp/tools/BuildMojo.java b/src/main/java/org/bytedeco/javacpp/tools/BuildMojo.java
index b562d0bfa..e247c8b35 100644
--- a/src/main/java/org/bytedeco/javacpp/tools/BuildMojo.java
+++ b/src/main/java/org/bytedeco/javacpp/tools/BuildMojo.java
@@ -194,6 +194,12 @@ String[] merge(String[] ss, String s) {
@Override public void warn (String s) { log.warn(s); }
@Override public void error(String s) { log.error(s); }
};
+
+ Coordinates coordinates = new Coordinates(
+ project.getGroupId(),
+ project.getArtifactId(),
+ project.getVersion()
+ );
Builder builder = new Builder(logger)
.classPaths(classPaths)
.outputDirectory(outputDirectory)
@@ -208,10 +214,12 @@ String[] merge(String[] ss, String s) {
.properties(propertyKeysAndValues)
.classesOrPackages(classOrPackageNames)
.environmentVariables(environmentVariables)
- .compilerOptions(compilerOptions);
+ .compilerOptions(compilerOptions)
+ .coordinates(coordinates);
Properties properties = builder.properties;
log.info("Detected platform \"" + Loader.getPlatform() + "\"");
log.info("Building for platform \"" + properties.get("platform") + "\"");
+ log.info("Coordinates for builder: " + coordinates.canonical());
String separator = properties.getProperty("platform.path.separator");
for (String s : merge(includePaths, includePath)) {
String v = properties.getProperty("platform.includepath", "");
diff --git a/src/main/java/org/bytedeco/javacpp/tools/Builder.java b/src/main/java/org/bytedeco/javacpp/tools/Builder.java
index 2f5a2c54a..6b3c20cac 100644
--- a/src/main/java/org/bytedeco/javacpp/tools/Builder.java
+++ b/src/main/java/org/bytedeco/javacpp/tools/Builder.java
@@ -56,7 +56,8 @@
public class Builder {
/**
- * Calls {@link Parser#parse(File, String[], Class)} after creating an instance of the Class.
+ * Calls {@link Parser#parse(File, String[], Class, Coordinates)} after creating an
+ * instance of the Class.
*
* @param classPath an array of paths to try to load header files from
* @param cls The class annotated with {@link org.bytedeco.javacpp.annotation.Properties}
@@ -66,7 +67,7 @@ public class Builder {
* @throws ParserException on C/C++ header file parsing error
*/
File parse(String[] classPath, Class cls) throws IOException, ParserException {
- return new Parser(logger, properties).parse(outputDirectory, classPath, cls);
+ return new Parser(logger, properties).parse(outputDirectory, classPath, cls, coordinates);
}
/**
@@ -503,6 +504,8 @@ public Builder(Logger logger) {
Map environmentVariables = null;
/** Contains additional command line options from the user for the native compiler. */
Collection compilerOptions = null;
+ /** Coordinates of the library. */
+ Coordinates coordinates = null;
/** Splits argument with {@link File#pathSeparator} and appends result to paths of the {@link #classScanner}. */
public Builder classPaths(String classPaths) {
@@ -629,6 +632,11 @@ public Builder compilerOptions(String ... options) {
}
return this;
}
+ /** Sets the library coordinates. */
+ public Builder coordinates(Coordinates coordinates) {
+ this.coordinates = coordinates;
+ return this;
+ }
/**
* Starts the build process and returns an array of {@link File} produced.
@@ -689,7 +697,7 @@ public File[] build() throws IOException, InterruptedException, ParserException
File directory = f.getParentFile();
for (String s : preloads) {
- URL[] urls = Loader.findLibrary(null, p, s, true);
+ URL[] urls = Loader.findLibrary(null, p, s, true, coordinates);
File fi;
try {
fi = new File(urls[0].toURI());
@@ -757,6 +765,7 @@ public static void printHelp() {
System.out.println(" -jarprefix Also create a JAR file named \"-.jar\"");
System.out.println(" -properties Load all properties from resource");
System.out.println(" -propertyfile Load all properties from file");
+ System.out.println(" -coordinates Library identifier (group:id:version).");
System.out.println(" -D= Set property to value");
System.out.println(" -Xcompiler