aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGlavo <zjx001202@126.com>2019-12-06 04:44:07 +0800
committerGlavo <zjx001202@126.com>2019-12-06 04:44:07 +0800
commite7b0c093f4a8f0354021b0bd1a70351ab53fa0b2 (patch)
tree6abc99c906693aaeee0df7c792b1eb119fe73eb1
parent221b3843a5a947d803507b8e61778ebfea072390 (diff)
downloadsbt-jni-e7b0c093f4a8f0354021b0bd1a70351ab53fa0b2.tar.gz
sbt-jni-e7b0c093f4a8f0354021b0bd1a70351ab53fa0b2.tar.bz2
sbt-jni-e7b0c093f4a8f0354021b0bd1a70351ab53fa0b2.zip
update gjavah version
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassMetaInfo.java46
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassName.java86
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassPath.java43
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/Constant.java81
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/HeaderGenerator.java397
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/JNIGenerator.java236
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/JavahTask.java81
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/ModulePath.java50
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/NativeMethod.java90
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/RuntimeSearchPath.java44
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/SearchPath.java90
-rw-r--r--plugin/src/main/java/ch/jodersky/sbt/jni/javah/Utils.java133
-rw-r--r--plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala11
13 files changed, 986 insertions, 402 deletions
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassMetaInfo.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassMetaInfo.java
new file mode 100644
index 0000000..c1e5b6b
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassMetaInfo.java
@@ -0,0 +1,46 @@
+package ch.jodersky.sbt.jni.javah;
+
+import org.objectweb.asm.*;
+
+import java.util.*;
+
+class ClassMetaInfo extends ClassVisitor {
+ final List<Constant> constants = new LinkedList<>();
+ final List<NativeMethod> methods = new LinkedList<>();
+ final Map<String, Integer> counts = new HashMap<>();
+
+ ClassName superClassName;
+ ClassName name;
+
+ public ClassMetaInfo() {
+ super(Opcodes.ASM6);
+ }
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ this.superClassName = superName == null ? null : ClassName.of(superName.replace('/', '.'));
+ this.name = ClassName.of(name.replace('/', '.'));
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
+ counts.put(name, counts.getOrDefault(name, 0) + 1);
+ if ((access & Opcodes.ACC_NATIVE) != 0) {
+ this.methods.add(NativeMethod.of(access, name, descriptor));
+ }
+ return null;
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
+ if (value != null && !(value instanceof String)) {
+ constants.add(Constant.of(name, value));
+ }
+ return null;
+ }
+
+ boolean isOverloadMethod(NativeMethod method) {
+ Objects.requireNonNull(method);
+ return counts.getOrDefault(method.name(), 1) > 1;
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassName.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassName.java
new file mode 100644
index 0000000..8cb8f28
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassName.java
@@ -0,0 +1,86 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.util.Objects;
+
+public final class ClassName {
+ private final String moduleName;
+ private final String className;
+ private final String simpleName;
+ private final String mangledName;
+
+ public static ClassName of(String moduleName, String className) {
+ Objects.requireNonNull(className, "Class name is null");
+
+ if (moduleName != null && !Utils.FULL_NAME_PATTERN.matcher(moduleName).matches()) {
+ throw new IllegalArgumentException("Illegal module name: " + moduleName);
+ }
+ if (!Utils.FULL_NAME_PATTERN.matcher(className).matches()) {
+ throw new IllegalArgumentException("Illegal class name: " + moduleName);
+ }
+
+ return new ClassName(moduleName, className);
+ }
+
+ public static ClassName of(String fullName) {
+ Objects.requireNonNull(fullName, "class name is null");
+ int idx = fullName.indexOf('/');
+ if (idx == -1) {
+ return ClassName.of(null, fullName);
+ }
+
+ return ClassName.of(fullName.substring(0, idx), fullName.substring(idx + 1));
+ }
+
+ private ClassName(String moduleName, String className) {
+ this.moduleName = moduleName;
+ this.className = className;
+ this.simpleName = className.substring(className.lastIndexOf('.') + 1);
+ this.mangledName = Utils.mangleName(className);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ClassName)) return false;
+ ClassName className1 = (ClassName) o;
+ return Objects.equals(moduleName, className1.moduleName) && className.equals(className1.className);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(moduleName, className);
+ }
+
+ @Override
+ public String toString() {
+ if (moduleName == null) {
+ return className;
+ }
+ return moduleName + '/' + className;
+ }
+
+
+ //
+ // Getters and Setters
+ //
+
+ public final String moduleName() {
+ return moduleName;
+ }
+
+ public final String className() {
+ return className;
+ }
+
+ public final String simpleName() {
+ return simpleName;
+ }
+
+ public final String mangledName() {
+ return mangledName;
+ }
+
+ public final String relativePath() {
+ return className.replace('.', '/') + ".class";
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassPath.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassPath.java
new file mode 100644
index 0000000..47de2ee
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ClassPath.java
@@ -0,0 +1,43 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class ClassPath implements SearchPath {
+ private final Path path;
+ private final List<Path> roots;
+
+ public ClassPath(Path path) {
+ Objects.requireNonNull(path);
+ this.path = path.toAbsolutePath();
+
+ Path root = Utils.classPathRoot(path);
+ roots = root == null ? Collections.emptyList() : multiReleaseRoots(root);
+ }
+
+ @Override
+ public Path search(ClassName name) {
+ Objects.requireNonNull(name);
+ return searchFromRoots(roots, name);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ClassPath classPath = (ClassPath) o;
+ return Objects.equals(path, classPath.path);
+ }
+
+ @Override
+ public int hashCode() {
+ return path.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "ClassPath[" + path + "]";
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Constant.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Constant.java
new file mode 100644
index 0000000..3aca9ac
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Constant.java
@@ -0,0 +1,81 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+public final class Constant {
+ private static final List<Class<?>> TYPES = Arrays.asList(
+ Byte.class, Short.class, Integer.class, Long.class, Character.class, Float.class, Double.class
+ );
+
+ private final String name;
+ private final Object value;
+ private final String mangledName;
+
+ public static Constant of(String name, Object value) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(value);
+
+ if (!TYPES.contains(value.getClass())) {
+ throw new IllegalArgumentException();
+ }
+ if (!Utils.SIMPLE_NAME_PATTERN.matcher(name).matches()) {
+ throw new IllegalArgumentException(String.format("\"%s\" is not a qualified constant name", name));
+ }
+
+ return new Constant(name, value);
+ }
+
+ private Constant(String name, Object value) {
+ this.name = name;
+ this.value = value;
+ this.mangledName = Utils.mangleName(name);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof Constant)) return false;
+ Constant constant = (Constant) o;
+ return name.equals(constant.name) && value.equals(constant.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, value);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Constant[name=%s, value=%s]", name, value);
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public Object value() {
+ return value;
+ }
+
+ public String mangledName() {
+ return mangledName;
+ }
+
+ public String valueToString() {
+ if (value instanceof Double) {
+ return value.toString();
+ }
+ if (value instanceof Float) {
+ return value + "f";
+ }
+ if (value instanceof Long) {
+ return value + "i64";
+ }
+ if (value instanceof Character) {
+ return ((int) (char) value) + "L";
+ }
+ return value + "L";
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/HeaderGenerator.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/HeaderGenerator.java
deleted file mode 100644
index a26aa20..0000000
--- a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/HeaderGenerator.java
+++ /dev/null
@@ -1,397 +0,0 @@
-package ch.jodersky.sbt.jni.javah;
-
-/*
- * Code is copied from (https://github.com/Glavo/gjavah)
- * MIT License
- */
-
-import org.objectweb.asm.*;
-
-import java.io.*;
-import java.nio.file.*;
-import java.util.*;
-
-public final class HeaderGenerator {
- private static final Path[] EMPTY_PATH_ARRAY = new Path[0];
- private static final List<String> THROWABLE_NAME_LIST = Arrays.asList("Ljava/lang/Throwable;", "Ljava/lang/Error;", "Ljava/lang/Exception");
- private static final HeaderGenerator generator = new HeaderGenerator();
-
- private static String escape(String source, Boolean escapeDots) {
- StringBuilder builder = new StringBuilder();
- char ch;
- for (int i = 0; i < source.length(); i++) {
- switch (ch = source.charAt(i)) {
- case '_':
- builder.append("_1");
- break;
- case ';':
- builder.append("_2");
- break;
- case '[':
- builder.append("_3");
- break;
- case '/':
- if(escapeDots)
- builder.append('_');
- else
- builder.append('.');
- break;
- default:
- if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
- builder.append(ch);
- } else {
- builder.append("_0").append(String.format("%04x", (int) ch));
- }
- }
- }
- return builder.toString();
- }
-
- public static void generateFunctionDeclarations(ClassReader reader, PrintWriter output) {
- getGenerator().classGenerateFunctionDeclarations(reader, output);
- }
-
- public static void generateHeader(ClassReader reader, PrintWriter output) {
- getGenerator().classGenerateHeader(reader, output);
- }
-
- public static void generateHeader(byte[] classFile, PrintWriter output) {
- getGenerator().classGenerateHeader(classFile, output);
- }
-
- public static void generateHeader(byte[] classFileBuffer, int classFileOffset, int classFileLength, PrintWriter output) {
- getGenerator().classGenerateHeader(classFileBuffer, classFileOffset, classFileLength, output);
- }
-
- public static void generateHeader(String className, PrintWriter output) throws IOException {
- getGenerator().classGenerateHeader(className, output);
- }
-
- public static void generateHeader(InputStream input, PrintWriter output) throws IOException {
- getGenerator().classGenerateHeader(input, output);
- }
-
- public static HeaderGenerator getGenerator() {
- return generator;
- }
-
- private Path[] classPaths;
- private boolean useRuntimeClassPath;
-
- public HeaderGenerator() {
- this(EMPTY_PATH_ARRAY, true);
- }
-
- public HeaderGenerator(boolean useRuntimeClassPath) {
- this(EMPTY_PATH_ARRAY, useRuntimeClassPath);
- }
-
- public HeaderGenerator(Path[] classPaths) {
- this(classPaths, true);
- }
-
- public HeaderGenerator(Path[] classPaths, boolean useRuntimeClassPath) {
- Objects.requireNonNull(classPaths);
- this.classPaths = classPaths;
- this.useRuntimeClassPath = useRuntimeClassPath;
- }
-
- private boolean isThrowable(Type type) {
- String desc = type.getDescriptor();
- if (!desc.startsWith("L")) {
- return false;
- }
- if (classPaths.length == 0 && !useRuntimeClassPath) {
- return THROWABLE_NAME_LIST.contains(type.getDescriptor());
- }
- String className = type.getInternalName();
- while (true) {
- if (className == null) {
- return false;
- }
- if (className.equals("java/lang/Throwable")) {
- return true;
- }
- try {
- ClassReader reader = findClass(className);
- if (reader == null) {
- return false;
- }
- className = reader.getSuperName();
- } catch (Exception ignored) {
- return false;
- }
- }
- }
-
- private String typeToNative(Type tpe) {
- if (tpe == Type.BOOLEAN_TYPE) {
- return "jboolean";
- } else if (tpe == Type.BYTE_TYPE) {
- return "jbyte";
- } else if (tpe == Type.CHAR_TYPE) {
- return "jchar";
- } else if (tpe == Type.SHORT_TYPE) {
- return "jshort";
- } else if (tpe == Type.INT_TYPE) {
- return "jint";
- } else if (tpe == Type.LONG_TYPE) {
- return "jlong";
- } else if (tpe == Type.FLOAT_TYPE) {
- return "jfloat";
- } else if (tpe == Type.DOUBLE_TYPE) {
- return "jdouble";
- } else if (tpe == Type.VOID_TYPE) {
- return "void";
- } else {
- String desc = tpe.getDescriptor();
- if (desc.startsWith("[")) {
- Type elemTpe = tpe.getElementType();
- String descriptor = elemTpe.getDescriptor();
- if (descriptor.startsWith("[") || descriptor.startsWith("L")) {
- return "jobjectArray";
- }
- return typeToNative(elemTpe) + "Array";
- }
- if (desc.equals("Ljava/lang/String;")) {
- return "jstring";
- }
- if (desc.equals("Ljava/lang/Class;")) {
- return "jclass";
- }
- if (isThrowable(tpe)) {
- return "jthrowable";
- }
- return "jobject";
- }
- }
-
- private ClassReader findClass(String name) {
- loop:
- for (Path path : classPaths) {
- path = path.toAbsolutePath();
- if (!Files.exists(path)) {
- continue;
- }
- String[] ps = name.split("/|\\.");
- if (ps.length == 0) {
- continue;
- }
- ps[ps.length - 1] += ".class";
- for (String p : ps) {
- path = path.resolve(p);
- if (!Files.exists(path)) {
- continue loop;
- }
- }
- try {
- return new ClassReader(Files.newInputStream(path));
- } catch (IOException ignored) {
- }
- }
- try {
- return new ClassReader(name.replace('/', '.'));
- } catch (IOException e) {
- return null;
- }
- }
-
- private void classGenerateFunctionDeclarations(Generator generator, PrintWriter output) {
- String className = escape(generator.getClassName(), false);
- String classNameNoDots = escape(generator.getClassName(), true);
-
- for (Map.Entry<String, Set<MethodDesc>> entry : generator.getMethods().entrySet()) {
- boolean overload = entry.getValue().size() > 1;
- for (MethodDesc desc : entry.getValue()) {
- String methodName = escape(entry.getKey(), false);
- output.println("/*" + "\n" +
- " * Class: " + className + "\n" +
- " * Method: " + entry.getKey() + "\n" +
- " * Signature: " + desc.descriptor + "\n" +
- " */"
- );
-
- Type[] argTypes = Type.getArgumentTypes(desc.descriptor);
- Type retType = Type.getReturnType(desc.descriptor);
-
- output.print(
- "JNIEXPORT " + typeToNative(retType) + " JNICALL Java_" + classNameNoDots + "_" + methodName
- );
-
- if (overload) {
- output.print("__");
- for (Type tpe : argTypes) {
- output.print(escape(tpe.toString(), true));
- }
- }
- output.println();
-
- output.print(" (JNIEnv *, ");
- if (desc.isStatic) {
- output.print("jclass");
- } else {
- output.print("jobject");
- }
- for (Type tpe : argTypes) {
- output.print(", " + typeToNative(tpe));
- }
- output.println(");\n");
- }
-
- }
- }
-
- public void classGenerateFunctionDeclarations(ClassReader reader, PrintWriter output) {
- Generator generator = new Generator();
- reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- classGenerateFunctionDeclarations(generator, output);
- }
-
- private void classGenerateHeaderWithoutInclude(Generator generator, PrintWriter output) {
- String className = escape(generator.getClassName(), true);
-
- output.println("/* Header for class " + className + " */");
-
- String includeHeader = "_Include_" + className;
- output.println("#ifndef " + includeHeader);
- output.println("#define " + includeHeader);
-
- output.println("#ifdef __cplusplus\n" +
- "extern \"C\" {\n" +
- "#endif");
-
- classGenerateFunctionDeclarations(generator, output);
-
- output.println("#ifdef __cplusplus\n" +
- "}\n" +
- "#endif\n" +
- "#endif\n");
- }
-
- private void classGenerateHeaderWithoutInclude(ClassReader reader, PrintWriter output) {
- Generator generator = new Generator();
- reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- classGenerateHeaderWithoutInclude(generator, output);
- }
-
- private void classGenerateHeader(Generator generator, PrintWriter output) {
- output.println("/* DO NOT EDIT THIS FILE - it is machine generated */");
- output.println("#include <jni.h>");
-
- classGenerateHeaderWithoutInclude(generator, output);
- }
-
- public void classGenerateHeader(ClassReader reader, PrintWriter output) {
- Generator generator = new Generator();
- reader.accept(generator, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
- classGenerateHeader(generator, output);
- }
-
- public void classGenerateHeader(byte[] classFile, PrintWriter output) {
- classGenerateHeader(new ClassReader(classFile), output);
- }
-
- public void classGenerateHeader(byte[] classFileBuffer, int classFileOffset, int classFileLength, PrintWriter output) {
- classGenerateHeader(new ClassReader(classFileBuffer, classFileOffset, classFileLength), output);
- }
-
- public void classGenerateHeader(String className, PrintWriter output) throws IOException {
- ClassReader reader = findClass(className);
- if (reader == null) {
- throw new IOException();
- }
- classGenerateHeader(reader, output);
- }
-
- public void classGenerateHeader(InputStream input, PrintWriter output) throws IOException {
- classGenerateHeader(new ClassReader(input), output);
- }
-
- public Path[] getClassPaths() {
- return classPaths;
- }
-
- public void setClassPaths(Path[] classPaths) {
- Objects.requireNonNull(classPaths);
- this.classPaths = classPaths;
- }
-
- public boolean isUseRuntimeClassPath() {
- return useRuntimeClassPath;
- }
-
- public void setUseRuntimeClassPath(boolean useRuntimeClassPath) {
- this.useRuntimeClassPath = useRuntimeClassPath;
- }
-
- public static void run(ArrayList<String> classNames, Path outputDir , ArrayList<Path> cps) throws IOException {
- HeaderGenerator generator = new HeaderGenerator(cps.toArray(new Path[0]));
- if (Files.notExists(outputDir)) {
- Files.createDirectories(outputDir);
- }
- for (String name : classNames) {
- ClassReader reader = generator.findClass(name);
- if (reader == null) {
- System.err.println("Error: Could not find class file for '" + name + "'.");
- return;
- }
- Generator g = new Generator();
- reader.accept(g, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
-
- try (PrintWriter writer = new PrintWriter(Files.newBufferedWriter(
- outputDir.resolve(name.replace('.', '_').replace("$", "__") + ".h"),
- StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))) {
- generator.classGenerateHeader(g, writer);
- }
- }
- }
-
-}
-
-final class MethodDesc {
- public final boolean isStatic;
- public final String descriptor;
-
- public MethodDesc(boolean isStatic, String descriptor) {
- this.isStatic = isStatic;
- this.descriptor = descriptor;
- }
-}
-
-final class Generator extends ClassVisitor {
- private String className;
-
- private Map<String, String> constants = new LinkedHashMap<>();
- private Map<String, Set<MethodDesc>> methods = new LinkedHashMap<String, Set<MethodDesc>>();
-
- public Generator() {
- super(Opcodes.ASM5);
- }
-
- @Override
- public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
- className = name;
- }
-
- @Override
- public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
- if ((access & Opcodes.ACC_NATIVE) != 0) {
- if (methods.containsKey(name)) {
- methods.get(name).add(new MethodDesc((access & Opcodes.ACC_STATIC) != 0, descriptor));
- } else {
- LinkedHashSet<MethodDesc> set = new LinkedHashSet<>();
- set.add(new MethodDesc((access & Opcodes.ACC_STATIC) != 0, descriptor));
- methods.put(name, set);
- }
- }
- return null;
- }
-
- public String getClassName() {
- return className;
- }
-
- public Map<String, Set<MethodDesc>> getMethods() {
- return methods;
- }
-}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/JNIGenerator.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/JNIGenerator.java
new file mode 100644
index 0000000..15166b0
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/JNIGenerator.java
@@ -0,0 +1,236 @@
+package ch.jodersky.sbt.jni.javah;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Type;
+
+import java.io.*;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+
+public class JNIGenerator {
+
+ private final PrintWriter errorHandle;
+ private final Iterable<SearchPath> searchPaths;
+ private final Path outputDir;
+
+ public JNIGenerator(Path outputDir) {
+ this(outputDir, null, null);
+ }
+
+ public JNIGenerator(Path outputDir, Iterable<SearchPath> searchPaths) {
+ this(outputDir, searchPaths, null);
+ }
+
+ public JNIGenerator(Path outputDir, Iterable<SearchPath> searchPaths, PrintWriter errorHandle) {
+ Objects.requireNonNull(outputDir);
+
+ if (searchPaths == null) {
+ searchPaths = Collections.singleton(RuntimeSearchPath.INSTANCE);
+ }
+ if (errorHandle == null) {
+ errorHandle = Utils.NOOP_WRITER;
+ }
+
+ this.errorHandle = errorHandle;
+ this.searchPaths = searchPaths;
+ this.outputDir = outputDir;
+ }
+
+ public void generate(ClassName name) {
+ Objects.requireNonNull(name);
+ if (Files.exists(outputDir) && !Files.isDirectory(outputDir)) {
+ throw new IllegalArgumentException(outputDir + "is not a directory");
+ }
+ if (Files.notExists(outputDir)) {
+ try {
+ Files.createDirectories(outputDir);
+ } catch (IOException e) {
+ errorHandle.println("error: cannot create directory " + outputDir);
+ e.printStackTrace(errorHandle);
+ return;
+ }
+ }
+ Path op = outputDir.resolve(name.mangledName() + ".h");
+ try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(op))) {
+ generateTo(name, out);
+ } catch (Exception ex) {
+ errorHandle.println("error: cannot write to " + op);
+ ex.printStackTrace(errorHandle);
+ try {
+ Files.deleteIfExists(op);
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ public void generateTo(ClassName name, Writer writer) throws IOException {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(writer);
+
+ ClassMetaInfo meta = new ClassMetaInfo();
+ {
+ Path f = search(name);
+ if (f == null) {
+ errorHandle.println("Not found class " + name);
+ return;
+ }
+
+ try (InputStream in = Files.newInputStream(f)) {
+ ClassReader reader = new ClassReader(in);
+ reader.accept(meta, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
+ } catch (IOException e) {
+ errorHandle.println("error: cannot open class file of " + name);
+ e.printStackTrace(errorHandle);
+ errorHandle.flush();
+ throw e;
+ }
+ }
+
+ PrintWriter out = writer instanceof PrintWriter ? (PrintWriter) writer : new PrintWriter(writer);
+ out.println("/* DO NOT EDIT THIS FILE - it is machine generated */");
+ out.println("#include <jni.h>");
+ out.println("/* Header for class " + name.mangledName() + " */");
+ out.println();
+ out.println("#ifndef _Included_" + name.mangledName());
+ out.println("#define _Included_" + name.mangledName());
+ out.println("#ifdef __cplusplus");
+ out.println("extern \"C\" {");
+ out.println("#endif");
+
+ for (Constant constant : meta.constants) {
+ String cn = name.mangledName() + "_" + constant.mangledName();
+ out.println("#undef " + cn);
+ out.println("#define " + cn + " " + constant.valueToString());
+ }
+
+ for (NativeMethod method : meta.methods) {
+ String ret = mapTypeToNative(method.type().getReturnType());
+ List<String> args = new ArrayList<>();
+ args.add("JNIEnv *");
+ args.add(method.isStatic() ? "jclass" : "jobject");
+ args.addAll(Arrays.asList(mapArgsTypeToNative(method.type())));
+
+ String methodName =
+ "Java_" + name.mangledName() + "_" + (meta.isOverloadMethod(method) ? method.longMangledName() : method.mangledName());
+
+ out.println("/*");
+ out.println(" * Class: " + name.mangledName());
+ out.println(" * Method: " + method.mangledName());
+ out.println(" * Signature: " + Utils.escape(method.type().toString()));
+ out.println(" */");
+ out.println("JNIEXPORT " + ret + " JNICALL " + methodName);
+ out.println(" (" + String.join(", ", args) + ");");
+ out.println();
+ }
+
+ out.println("#ifdef __cplusplus");
+ out.println("}");
+ out.println("#endif");
+ out.println("#endif");
+
+ }
+
+ private Path search(ClassName name) {
+ return SearchPath.searchFrom(searchPaths, name);
+ }
+
+ private String mapTypeToNative(Type type) {
+ Objects.requireNonNull(type);
+ String tpe = type.toString();
+ if (tpe.startsWith("(")) {
+ throw new IllegalArgumentException();
+ }
+
+ switch (tpe) {
+ case "Z":
+ return "jboolean";
+ case "B":
+ return "jbyte";
+ case "C":
+ return "jchar";
+ case "S":
+ return "jshort";
+ case "I":
+ return "jint";
+ case "J":
+ return "jlong";
+ case "F":
+ return "jfloat";
+ case "D":
+ return "jdouble";
+ case "V":
+ return "void";
+ case "Ljava/lang/Class;":
+ return "jclass";
+ case "Ljava/lang/String;":
+ return "jstring";
+ case "Ljava/lang/Throwable;":
+ return "jthrowable";
+ case "[Z":
+ return "jbooleanArray";
+ case "[B":
+ return "jbyteArray";
+ case "[C":
+ return "jcharArray";
+ case "[S":
+ return "jshortArray";
+ case "[I":
+ return "jintArray";
+ case "[J":
+ return "jlongArray";
+ case "[F":
+ return "jfloatArray";
+ case "[D":
+ return "jdoubleArray";
+ }
+
+ if (tpe.startsWith("[")) {
+ return "jobjectArray";
+ }
+
+ if (tpe.startsWith("L") && tpe.endsWith(";")) {
+ ClassName n = ClassName.of(tpe.substring(1, tpe.length() - 1).replace('/', '.'));
+ if (isThrowable(n)) {
+ return "jthrowable";
+ } else {
+ return "jobject";
+ }
+ }
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+
+ private String[] mapArgsTypeToNative(Type methodType) {
+ Objects.requireNonNull(methodType);
+ if (!Utils.METHOD_TYPE_PATTERN.matcher(methodType.toString()).matches()) {
+ throw new IllegalArgumentException(methodType + " is not a method type");
+ }
+ Type[] args = methodType.getArgumentTypes();
+ String[] ans = new String[args.length];
+ for (int i = 0; i < args.length; i++) {
+ ans[i] = mapTypeToNative(args[i]);
+ }
+ return ans;
+ }
+
+ private boolean isThrowable(ClassName name) {
+ if (name == null) {
+ return false;
+ }
+ switch (name.className()) {
+ case "java.lang.Throwable":
+ case "java.lang.Error":
+ case "java.lang.Exception":
+ return true;
+ case "java.lang.Object":
+ return false;
+ }
+
+ try (InputStream in = Files.newInputStream(search(name))) {
+ return isThrowable(Utils.superClassOf(new ClassReader(in)));
+ } catch (Exception ignored) {
+ errorHandle.println("warning: class " + name + " not found");
+ return false;
+ }
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/JavahTask.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/JavahTask.java
new file mode 100644
index 0000000..23627a4
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/JavahTask.java
@@ -0,0 +1,81 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.file.Path;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+public final class JavahTask {
+ private final List<SearchPath> searchPaths = new LinkedList<>();
+ private Path outputDir;
+ private PrintWriter errorHandle = new PrintWriter(System.err, true);
+ private final List<ClassName> classes = new LinkedList<>();
+
+ public void run() {
+ Objects.requireNonNull(outputDir, "outputDir");
+ JNIGenerator generator = new JNIGenerator(outputDir, searchPaths, errorHandle);
+ for (ClassName cls : classes) {
+ try {
+ generator.generate(cls);
+ } catch (Exception ex) {
+ ex.printStackTrace(errorHandle);
+ }
+ }
+ }
+
+ public void addClass(ClassName name) {
+ Objects.requireNonNull(name);
+ classes.add(name);
+ }
+
+ public void addClass(String name) {
+ Objects.requireNonNull(name);
+ classes.add(ClassName.of(name));
+ }
+
+ public void addClasses(Iterable<String> i) {
+ Objects.requireNonNull(i);
+ i.forEach(c -> classes.add(ClassName.of(c)));
+ }
+
+ public void addRuntimeSearchPath() {
+ searchPaths.add(RuntimeSearchPath.INSTANCE);
+ }
+
+ public void addSearchPath(SearchPath searchPath) {
+ Objects.requireNonNull(searchPath);
+ searchPaths.add(searchPath);
+ }
+
+ public void addClassPath(Path classPath) {
+ Objects.requireNonNull(classPath);
+ searchPaths.add(new ClassPath(classPath));
+ }
+
+ public void addModulePath(Path modulePath) {
+ Objects.requireNonNull(modulePath);
+ searchPaths.add(new ModulePath(modulePath));
+ }
+
+ public Path getOutputDir() {
+ return outputDir;
+ }
+
+ public void setOutputDir(Path outputDir) {
+ this.outputDir = outputDir;
+ }
+
+ public PrintWriter getErrorHandle() {
+ return errorHandle;
+ }
+
+ public void setErrorHandle(Writer errorHandle) {
+ if (errorHandle instanceof PrintWriter || errorHandle == null) {
+ this.errorHandle = (PrintWriter) errorHandle;
+ } else {
+ this.errorHandle = new PrintWriter(errorHandle);
+ }
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ModulePath.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ModulePath.java
new file mode 100644
index 0000000..59b0d99
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/ModulePath.java
@@ -0,0 +1,50 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class ModulePath implements SearchPath {
+ private final Path path;
+ private List<Path> roots;
+
+ public ModulePath(Path path) {
+ Objects.requireNonNull(path);
+ path = path.toAbsolutePath();
+ this.path = path;
+ if (Files.notExists(path) || !Files.isDirectory(path)) {
+ roots = Collections.emptyList();
+ } else {
+ try {
+ roots = Files.list(path)
+ .map(Path::toAbsolutePath)
+ .filter(Files::isRegularFile)
+ .filter(p -> {
+ String n = p.getFileName().toString().toLowerCase();
+ return n.endsWith(".jar") || n.endsWith(".zip") || n.endsWith(".jmod");
+ })
+ .map(Utils::classPathRoot)
+ .filter(Objects::nonNull)
+ .flatMap(p -> SearchPath.multiReleaseRoots(p).stream())
+ .collect(Collectors.toList());
+ } catch (IOException e) {
+ roots = Collections.emptyList();
+ }
+ }
+ }
+
+ @Override
+ public Path search(ClassName name) {
+ Objects.requireNonNull(name);
+ return SearchPath.searchFromRoots(roots, name);
+ }
+
+ @Override
+ public String toString() {
+ return "ModulePath[" + path + "]";
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/NativeMethod.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/NativeMethod.java
new file mode 100644
index 0000000..aee2c8d
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/NativeMethod.java
@@ -0,0 +1,90 @@
+package ch.jodersky.sbt.jni.javah;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import java.util.Objects;
+import java.util.regex.Matcher;
+
+public final class NativeMethod {
+ private final int access;
+ private final String name;
+ private final Type type;
+ private final String mangledName;
+ private final String longMangledName;
+
+ public static NativeMethod of(String name, String descriptor) {
+ return NativeMethod.of(0, name, descriptor);
+ }
+
+ public static NativeMethod of(String name, Type type) {
+ return NativeMethod.of(0, name, type);
+ }
+
+ public static NativeMethod of(int access, String name, String descriptor) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(descriptor);
+ return NativeMethod.of(access, name, Type.getType(descriptor));
+ }
+
+ public static NativeMethod of(int access, String name, Type type) {
+ Objects.requireNonNull(name);
+ Objects.requireNonNull(type);
+ if (!Utils.METHOD_NAME_PATTERN.matcher(name).matches()) {
+ throw new IllegalArgumentException(String.format("\"%s\" is not a qualified method name", name));
+ }
+ Matcher m = Utils.METHOD_TYPE_PATTERN.matcher(type.toString());
+ if (!m.matches()) {
+ throw new IllegalArgumentException(String.format("\"%s\" is not a method type", type));
+ }
+ return new NativeMethod(access, name, type, m.group("args"));
+ }
+
+ private NativeMethod(int access, String name, Type type, String arguments) {
+ this.access = access;
+ this.name = name;
+ this.type = type;
+ this.mangledName = Utils.mangleName(name);
+ this.longMangledName = mangledName + "__" + Utils.mangleName(arguments);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof NativeMethod)) {
+ return false;
+ }
+ NativeMethod that = (NativeMethod) o;
+ return name.equals(that.name) && type.equals(that.type);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NativeMethod[name=%s, type=%s}", name, type);
+ }
+
+ public String name() {
+ return name;
+ }
+
+ public Type type() {
+ return type;
+ }
+
+ public String mangledName() {
+ return mangledName;
+ }
+
+ public String longMangledName() {
+ return longMangledName;
+ }
+
+ public boolean isStatic() {
+ return (access & Opcodes.ACC_STATIC) != 0;
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/RuntimeSearchPath.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/RuntimeSearchPath.java
new file mode 100644
index 0000000..a43759c
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/RuntimeSearchPath.java
@@ -0,0 +1,44 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.*;
+import java.util.Collections;
+import java.util.Objects;
+
+public class RuntimeSearchPath implements SearchPath {
+ public static final RuntimeSearchPath INSTANCE = new RuntimeSearchPath();
+
+ private RuntimeSearchPath() {
+
+ }
+
+ @Override
+ public Path search(ClassName name) {
+ Objects.requireNonNull(name);
+ URI uri = null;
+ try {
+ Class<?> cls = Class.forName(name.className());
+ uri = cls.getResource(name.simpleName() + ".class").toURI();
+ return Paths.get(uri);
+ } catch (FileSystemNotFoundException ex) {
+ if (uri == null) {
+ return null;
+ }
+ try {
+ return FileSystems.newFileSystem(uri, Collections.emptyMap()).getPath("/", name.relativePath());
+ } catch (IOException | NullPointerException ignored) {
+ }
+ } catch (Exception ignored) {
+ }
+ return null;
+ }
+
+ public static Path searchClass(String name) {
+ return INSTANCE.search(name);
+ }
+
+ public static Path searchClass(ClassName name) {
+ return INSTANCE.search(name);
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/SearchPath.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/SearchPath.java
new file mode 100644
index 0000000..7c6b43b
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/SearchPath.java
@@ -0,0 +1,90 @@
+package ch.jodersky.sbt.jni.javah;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.jar.Manifest;
+import java.util.stream.Collectors;
+
+public interface SearchPath {
+ Path search(ClassName name);
+
+ default Path search(String fullName) {
+ Objects.requireNonNull(fullName);
+ return search(ClassName.of(fullName));
+ }
+
+ static Path searchFrom(Iterable<SearchPath> searchPaths, ClassName name) {
+ Objects.requireNonNull(searchPaths);
+ Objects.requireNonNull(name);
+
+ for (SearchPath searchPath : searchPaths) {
+ if (searchPath == null) {
+ continue;
+ }
+ Path p = searchPath.search(name);
+ if (p != null) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ static Path searchFromRoots(Iterable<Path> roots, ClassName name) {
+ Objects.requireNonNull(roots);
+ Objects.requireNonNull(name);
+ for (Path root : roots) {
+ if (root == null || !Files.isDirectory(root)) {
+ continue;
+ }
+
+ Path p = root.resolve(name.relativePath());
+ if (Files.isRegularFile(p)) {
+ return p;
+ }
+ if (Files.isSymbolicLink(p)) {
+ try {
+ p = Files.readSymbolicLink(p);
+ if (Files.isRegularFile(p)) {
+ return p;
+ }
+ } catch (IOException ignored) {
+ }
+ }
+ }
+
+ return null;
+ }
+
+ static List<Path> multiReleaseRoots(Path root) {
+ Objects.requireNonNull(root);
+ if (!Files.isDirectory(root)) {
+ return Collections.emptyList();
+ }
+ boolean isMultiRelease = false;
+ try (InputStream in = Files.newInputStream(root.resolve("META-INF").resolve("MANIFEST.MF"))) {
+ isMultiRelease = "true".equals(new Manifest(in).getMainAttributes().getValue("Multi-Release"));
+ } catch (IOException | NullPointerException ignored) {
+ }
+
+ if (isMultiRelease) {
+ Path base = root.resolve("META-INF").resolve("versions");
+ if (Files.isDirectory(base)) {
+ try {
+ List<Path> list = Files.list(base)
+ .map(Path::toAbsolutePath)
+ .filter(Files::isDirectory)
+ .filter(p -> Utils.MULTI_RELEASE_VERSIONS.contains(p.getFileName().toString()))
+ .sorted(Comparator.comparing((Path p) -> Integer.parseInt(p.getFileName().toString())).reversed())
+ .collect(Collectors.toCollection(LinkedList::new));
+ list.add(root);
+ return Collections.unmodifiableList(list);
+ } catch (IOException ignored) {
+ }
+ }
+ }
+ return Collections.singletonList(root);
+ }
+}
diff --git a/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Utils.java b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Utils.java
new file mode 100644
index 0000000..61e7a96
--- /dev/null
+++ b/plugin/src/main/java/ch/jodersky/sbt/jni/javah/Utils.java
@@ -0,0 +1,133 @@
+package ch.jodersky.sbt.jni.javah;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Objects;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+class Utils {
+ public static final int MAX_SUPPORTED_VERSION = 13;
+
+ public static final List<String> MULTI_RELEASE_VERSIONS =
+ IntStream.rangeClosed(9, MAX_SUPPORTED_VERSION).mapToObj(Integer::toString).collect(Collectors.toList());
+
+ public static final Pattern SIMPLE_NAME_PATTERN = Pattern.compile("[^.;\\[/]+");
+ public static final Pattern FULL_NAME_PATTERN =
+ Pattern.compile("[^.;\\[/]+(\\.[^.;\\[/]+)*");
+
+ public static final Pattern METHOD_NAME_PATTERN = Pattern.compile("(<init>)|(<cinit>)|([^.;\\[/<>]+)");
+ public static final Pattern METHOD_TYPE_PATTERN =
+ Pattern.compile("\\((?<args>(\\[*([BCDFIJSZ]|L[^.;\\[/]+(/[^.;\\\\\\[/]+)*;))*)\\)(?<ret>\\[*([BCDFIJSZV]|L[^.;\\[/]+(/[^.;\\[/]+)*;))");
+
+ public static final PrintWriter NOOP_WRITER = new PrintWriter(new Writer() {
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ }
+
+ @Override
+ public void flush() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ });
+
+ public static String mangleName(String name) {
+ StringBuilder builder = new StringBuilder(name.length() * 2);
+ int len = name.length();
+ for (int i = 0; i < len; i++) {
+ char ch = name.charAt(i);
+ if (ch == '.') {
+ builder.append('_');
+ } else if (ch == '_') {
+ builder.append("_1");
+ } else if (ch == ';') {
+ builder.append("_2");
+ } else if (ch == '[') {
+ builder.append("_3");
+ } else if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && (ch <= 'Z'))) {
+ builder.append(ch);
+ } else {
+ builder.append(String.format("_0%04x", (int) ch));
+ }
+ }
+ return builder.toString();
+ }
+
+ public static String escape(String unicode) {
+ Objects.requireNonNull(unicode);
+ int len = unicode.length();
+ StringBuilder builder = new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ char ch = unicode.charAt(i);
+ if (ch >= ' ' && ch <= '~') {
+ builder.append(ch);
+ } else {
+ builder.append(String.format("\\u%04x", (int) ch));
+ }
+ }
+ return builder.toString();
+ }
+
+ public static Path classPathRoot(Path p) {
+ Objects.requireNonNull(p);
+ p = p.toAbsolutePath();
+
+ if (Files.notExists(p)) {
+ return null;
+ }
+ if (Files.isDirectory(p)) {
+ return p;
+ }
+
+ try {
+ FileSystem fs = FileSystems.newFileSystem(p, (ClassLoader) null);
+ String name = p.getFileName().toString().toLowerCase();
+ if (name.endsWith(".jar") || name.endsWith(".zip")) {
+ return fs.getPath("/");
+ }
+ if (name.endsWith(".jmod")) {
+ return fs.getPath("/", "classes");
+ }
+
+ fs.close();
+ } catch (IOException ignored) {
+ return null;
+ }
+ return null;
+ }
+
+ public static ClassName superClassOf(ClassReader reader) {
+ Objects.requireNonNull(reader);
+ class V extends ClassVisitor {
+ V() {
+ super(Opcodes.ASM6);
+ }
+
+ ClassName superName = null;
+
+ @Override
+ public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ if (superName != null) {
+ this.superName = ClassName.of(superName.replace('/', '.'));
+ }
+ }
+ }
+ V v = new V();
+ reader.accept(v, ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG);
+ return v.superName;
+ }
+}
diff --git a/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
index a9832a5..0c068ed 100644
--- a/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
+++ b/plugin/src/main/scala/ch/jodersky/sbt/jni/plugins/JniJavah.scala
@@ -61,11 +61,12 @@ object JniJavah extends AutoPlugin {
log.info("Headers will be generated to " + out.getAbsolutePath)
}
- import scala.collection.JavaConverters._
-
- ch.jodersky.sbt.jni.javah.HeaderGenerator.run(new util.ArrayList[String](classes.asJava),
- Paths.get(out.getAbsolutePath), new util.ArrayList[Path](jcp.map(_.toPath).asJava)
- )
+ val task = new ch.jodersky.sbt.jni.javah.JavahTask
+ classes.foreach(task.addClass(_))
+ jcp.map(_.toPath).foreach(task.addClassPath(_))
+ task.addRuntimeSearchPath()
+ task.setOutputDir(Paths.get(out.getAbsolutePath))
+ task.run()
out
}