2014-11-13 21:47:22 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
package org.mozilla.gecko.annotationProcessors;
|
|
|
|
|
2014-11-13 21:47:23 +03:00
|
|
|
import com.android.tools.lint.checks.ApiLookup;
|
|
|
|
import com.android.tools.lint.LintCliClient;
|
|
|
|
|
2014-11-13 21:47:22 +03:00
|
|
|
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
|
|
|
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
|
|
|
import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
|
|
|
|
import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
|
2014-11-13 21:47:23 +03:00
|
|
|
import org.mozilla.gecko.annotationProcessors.utils.Utils;
|
2014-11-13 21:47:22 +03:00
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.Arrays;
|
2014-11-13 21:47:23 +03:00
|
|
|
import java.util.ArrayList;
|
2014-11-13 21:47:22 +03:00
|
|
|
import java.util.Comparator;
|
|
|
|
import java.util.Iterator;
|
2014-11-13 21:47:23 +03:00
|
|
|
import java.util.Properties;
|
2014-11-13 21:47:22 +03:00
|
|
|
import java.util.Scanner;
|
|
|
|
import java.util.Vector;
|
|
|
|
import java.net.URL;
|
|
|
|
import java.net.URLClassLoader;
|
|
|
|
|
|
|
|
import java.lang.reflect.Constructor;
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
import java.lang.reflect.Member;
|
|
|
|
import java.lang.reflect.Method;
|
|
|
|
import java.lang.reflect.Modifier;
|
|
|
|
|
|
|
|
public class SDKProcessor {
|
|
|
|
public static final String GENERATED_COMMENT =
|
|
|
|
"// GENERATED CODE\n" +
|
2015-01-10 03:33:57 +03:00
|
|
|
"// Generated by the Java program at /build/annotationProcessors at compile time\n" +
|
|
|
|
"// from annotations on Java methods. To update, change the annotations on the\n" +
|
|
|
|
"// corresponding Javamethods and rerun the build. Manually updating this file\n" +
|
|
|
|
"// will cause your build to fail.\n" +
|
|
|
|
"\n";
|
2014-11-13 21:47:22 +03:00
|
|
|
|
2014-11-13 21:47:23 +03:00
|
|
|
private static ApiLookup sApiLookup;
|
|
|
|
private static int sMaxSdkVersion;
|
|
|
|
|
2014-11-27 00:16:08 +03:00
|
|
|
public static void main(String[] args) throws Exception {
|
2014-11-13 21:47:22 +03:00
|
|
|
// We expect a list of jars on the commandline. If missing, whinge about it.
|
2014-11-13 21:47:23 +03:00
|
|
|
if (args.length < 5) {
|
|
|
|
System.err.println("Usage: java SDKProcessor sdkjar classlistfile outdir fileprefix max-sdk-version");
|
2014-11-13 21:47:22 +03:00
|
|
|
System.exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
System.out.println("Processing platform bindings...");
|
|
|
|
|
|
|
|
String sdkJar = args[0];
|
|
|
|
Vector classes = getClassList(args[1]);
|
|
|
|
String outdir = args[2];
|
|
|
|
String generatedFilePrefix = args[3];
|
2014-11-13 21:47:23 +03:00
|
|
|
sMaxSdkVersion = Integer.parseInt(args[4]);
|
|
|
|
|
|
|
|
LintCliClient lintClient = new LintCliClient();
|
|
|
|
sApiLookup = ApiLookup.get(lintClient);
|
2014-11-13 21:47:22 +03:00
|
|
|
|
|
|
|
// Start the clock!
|
|
|
|
long s = System.currentTimeMillis();
|
|
|
|
|
|
|
|
// Get an iterator over the classes in the jar files given...
|
|
|
|
// Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
|
|
|
|
|
|
|
|
StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
2015-01-10 03:33:57 +03:00
|
|
|
headerFile.append(
|
|
|
|
"#ifndef " + generatedFilePrefix + "_h__\n" +
|
|
|
|
"#define " + generatedFilePrefix + "_h__\n" +
|
|
|
|
"\n" +
|
|
|
|
"#include \"mozilla/jni/Refs.h\"\n" +
|
|
|
|
"\n" +
|
|
|
|
"namespace mozilla {\n" +
|
2016-07-21 09:07:13 +03:00
|
|
|
"namespace widget {\n" +
|
2015-01-10 03:33:57 +03:00
|
|
|
"namespace sdk {\n" +
|
|
|
|
"\n");
|
2014-11-13 21:47:22 +03:00
|
|
|
|
|
|
|
StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
|
2015-01-10 03:33:57 +03:00
|
|
|
implementationFile.append(
|
|
|
|
"#include \"" + generatedFilePrefix + ".h\"\n" +
|
|
|
|
"#include \"mozilla/jni/Accessors.h\"\n" +
|
|
|
|
"\n" +
|
|
|
|
"namespace mozilla {\n" +
|
2016-07-21 09:07:13 +03:00
|
|
|
"namespace widget {\n" +
|
2015-01-10 03:33:57 +03:00
|
|
|
"namespace sdk {\n" +
|
|
|
|
"\n");
|
2014-11-13 21:47:22 +03:00
|
|
|
|
|
|
|
// Used to track the calls to the various class-specific initialisation functions.
|
|
|
|
ClassLoader loader = null;
|
|
|
|
try {
|
|
|
|
loader = URLClassLoader.newInstance(new URL[] { new URL("file://" + sdkJar) },
|
|
|
|
SDKProcessor.class.getClassLoader());
|
|
|
|
} catch (Exception e) {
|
2015-01-10 03:33:57 +03:00
|
|
|
throw new RuntimeException(e.toString());
|
2014-11-13 21:47:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
for (Iterator<String> i = classes.iterator(); i.hasNext(); ) {
|
|
|
|
String className = i.next();
|
2014-11-13 21:47:24 +03:00
|
|
|
System.out.println("Looking up: " + className);
|
2014-11-13 21:47:22 +03:00
|
|
|
|
2014-11-27 00:16:08 +03:00
|
|
|
generateClass(Class.forName(className, true, loader),
|
|
|
|
implementationFile,
|
|
|
|
headerFile);
|
2014-11-13 21:47:22 +03:00
|
|
|
}
|
|
|
|
|
2015-01-10 03:33:57 +03:00
|
|
|
implementationFile.append(
|
|
|
|
"} /* sdk */\n" +
|
|
|
|
"} /* widget */\n" +
|
|
|
|
"} /* mozilla */\n");
|
2014-11-13 21:47:22 +03:00
|
|
|
|
2015-01-10 03:33:57 +03:00
|
|
|
headerFile.append(
|
|
|
|
"} /* sdk */\n" +
|
|
|
|
"} /* widget */\n" +
|
|
|
|
"} /* mozilla */\n" +
|
|
|
|
"#endif\n");
|
2014-11-13 21:47:22 +03:00
|
|
|
|
|
|
|
writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile);
|
|
|
|
long e = System.currentTimeMillis();
|
|
|
|
System.out.println("SDK processing complete in " + (e - s) + "ms");
|
|
|
|
}
|
|
|
|
|
2015-07-29 22:11:15 +03:00
|
|
|
private static int getAPIVersion(Class<?> cls, Member m) {
|
|
|
|
if (m instanceof Method || m instanceof Constructor) {
|
|
|
|
return sApiLookup.getCallVersion(
|
|
|
|
cls.getName().replace('.', '/'),
|
|
|
|
Utils.getMemberName(m),
|
|
|
|
Utils.getSignature(m));
|
|
|
|
} else if (m instanceof Field) {
|
|
|
|
return sApiLookup.getFieldVersion(
|
|
|
|
Utils.getClassDescriptor(m.getDeclaringClass()), m.getName());
|
|
|
|
} else {
|
|
|
|
throw new IllegalArgumentException("expected member to be Method, Constructor, or Field");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Member[] sortAndFilterMembers(Class<?> cls, Member[] members) {
|
2014-11-13 21:47:22 +03:00
|
|
|
Arrays.sort(members, new Comparator<Member>() {
|
|
|
|
@Override
|
|
|
|
public int compare(Member a, Member b) {
|
|
|
|
return a.getName().compareTo(b.getName());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-11-13 21:47:23 +03:00
|
|
|
ArrayList<Member> list = new ArrayList<>();
|
|
|
|
for (Member m : members) {
|
2015-07-29 22:11:15 +03:00
|
|
|
// Sometimes (e.g. Bundle) has methods that moved to/from a superclass in a later SDK
|
|
|
|
// version, so we check for both classes and see if we can find a minimum SDK version.
|
|
|
|
int version = getAPIVersion(cls, m);
|
|
|
|
final int version2 = getAPIVersion(m.getDeclaringClass(), m);
|
|
|
|
if (version2 > 0 && version2 < version) {
|
|
|
|
version = version2;
|
2014-11-13 21:47:23 +03:00
|
|
|
}
|
|
|
|
if (version > sMaxSdkVersion) {
|
|
|
|
System.out.println("Skipping " + m.getDeclaringClass().getName() + "." + m.getName() +
|
|
|
|
", version " + version + " > " + sMaxSdkVersion);
|
|
|
|
continue;
|
2015-10-23 00:45:45 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sometimes (e.g. KeyEvent) a field can appear in both a class and a superclass. In
|
|
|
|
// that case we want to filter out the version that appears in the superclass, or
|
|
|
|
// we'll have bindings with duplicate names.
|
|
|
|
try {
|
|
|
|
if (m instanceof Field && !m.equals(cls.getField(m.getName()))) {
|
|
|
|
// m is a field in a superclass that has been hidden by
|
|
|
|
// a field with the same name in a subclass.
|
|
|
|
System.out.println("Skipping " + m.getName() +
|
|
|
|
" from " + m.getDeclaringClass());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} catch (final NoSuchFieldException e) {
|
2014-11-13 21:47:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
list.add(m);
|
|
|
|
}
|
|
|
|
|
|
|
|
return list.toArray(new Member[list.size()]);
|
2014-11-13 21:47:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
private static void generateClass(Class<?> clazz,
|
|
|
|
StringBuilder implementationFile,
|
|
|
|
StringBuilder headerFile) {
|
|
|
|
String generatedName = clazz.getSimpleName();
|
|
|
|
|
2015-01-10 03:33:57 +03:00
|
|
|
CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
|
2014-11-13 21:47:22 +03:00
|
|
|
|
2015-07-29 22:11:15 +03:00
|
|
|
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getConstructors()));
|
|
|
|
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getMethods()));
|
|
|
|
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getFields()));
|
2014-11-13 21:47:22 +03:00
|
|
|
|
|
|
|
headerFile.append(generator.getHeaderFileContents());
|
|
|
|
implementationFile.append(generator.getWrapperFileContents());
|
|
|
|
}
|
|
|
|
|
|
|
|
private static Vector<String> getClassList(String path) {
|
|
|
|
Scanner scanner = null;
|
|
|
|
try {
|
|
|
|
scanner = new Scanner(new FileInputStream(path));
|
|
|
|
|
|
|
|
Vector lines = new Vector();
|
|
|
|
while (scanner.hasNextLine()) {
|
|
|
|
lines.add(scanner.nextLine());
|
|
|
|
}
|
|
|
|
return lines;
|
|
|
|
} catch (Exception e) {
|
|
|
|
System.out.println(e.toString());
|
|
|
|
return null;
|
|
|
|
} finally {
|
|
|
|
if (scanner != null) {
|
|
|
|
scanner.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void writeOutputFiles(String aOutputDir, String aPrefix, StringBuilder aHeaderFile,
|
|
|
|
StringBuilder aImplementationFile) {
|
|
|
|
FileOutputStream implStream = null;
|
|
|
|
try {
|
|
|
|
implStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".cpp"));
|
|
|
|
implStream.write(aImplementationFile.toString().getBytes());
|
|
|
|
} catch (IOException e) {
|
|
|
|
System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
|
|
|
|
e.printStackTrace(System.err);
|
|
|
|
} finally {
|
|
|
|
if (implStream != null) {
|
|
|
|
try {
|
|
|
|
implStream.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
System.err.println("Unable to close implStream due to "+e);
|
|
|
|
e.printStackTrace(System.err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FileOutputStream headerStream = null;
|
|
|
|
try {
|
|
|
|
headerStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".h"));
|
|
|
|
headerStream.write(aHeaderFile.toString().getBytes());
|
|
|
|
} catch (IOException e) {
|
|
|
|
System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
|
|
|
|
e.printStackTrace(System.err);
|
|
|
|
} finally {
|
|
|
|
if (headerStream != null) {
|
|
|
|
try {
|
|
|
|
headerStream.close();
|
|
|
|
} catch (IOException e) {
|
|
|
|
System.err.println("Unable to close headerStream due to "+e);
|
|
|
|
e.printStackTrace(System.err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-17 22:12:00 +03:00
|
|
|
}
|