Bug 1378410 - 1. Support BuildFlag annotation for generated bindings; r=snorp
Add a BuildFlag annotation, which when specified for classes, will wrap
generated code in `#ifdef` or `#ifndef` blocks. This functionality is
used for conditionally excluding generated code when NIghtly becomes
Beta, without the need to regenerate bindings.
MozReview-Commit-ID: L2NFM8CHKqF
--- a/build/annotationProcessors/AnnotationProcessor.java
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -24,17 +24,17 @@ public class AnnotationProcessor {
"\n";
private static final StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
private static final StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
private static final StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT);
public static void main(String[] args) {
// We expect a list of jars on the commandline. If missing, whinge about it.
- if (args.length <= 2) {
+ if (args.length < 2) {
System.err.println("Usage: java AnnotationProcessor outprefix jarfiles ...");
System.exit(1);
}
final String OUTPUT_PREFIX = args[0];
final String SOURCE_FILE = OUTPUT_PREFIX + "JNIWrappers.cpp";
final String HEADER_FILE = OUTPUT_PREFIX + "JNIWrappers.h";
final String NATIVES_FILE = OUTPUT_PREFIX + "JNINatives.h";
@@ -51,36 +51,42 @@ public class AnnotationProcessor {
// Get an iterator over the classes in the jar files given...
Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(jars);
headerFile.append(
"#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
"#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
"\n" +
+ "#ifndef MOZ_PREPROCESSOR\n" +
"#include \"mozilla/jni/Refs.h\"\n" +
+ "#endif\n" +
"\n" +
"namespace mozilla {\n" +
"namespace java {\n" +
"\n");
implementationFile.append(
+ "#ifndef MOZ_PREPROCESSOR\n" +
"#include \"" + HEADER_FILE + "\"\n" +
"#include \"mozilla/jni/Accessors.h\"\n" +
+ "#endif\n" +
"\n" +
"namespace mozilla {\n" +
"namespace java {\n" +
"\n");
nativesFile.append(
"#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
"#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
"\n" +
+ "#ifndef MOZ_PREPROCESSOR\n" +
"#include \"" + HEADER_FILE + "\"\n" +
"#include \"mozilla/jni/Natives.h\"\n" +
+ "#endif\n" +
"\n" +
"namespace mozilla {\n" +
"namespace java {\n" +
"\n");
while (jarClassIterator.hasNext()) {
generateClass(jarClassIterator.next());
}
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -22,43 +22,48 @@ public class CodeGenerator {
// Buffers holding the strings to ultimately be written to the output files.
private final StringBuilder cpp = new StringBuilder();
private final StringBuilder header = new StringBuilder();
private final StringBuilder natives = new StringBuilder();
private final StringBuilder nativesInits = new StringBuilder();
private final Class<?> cls;
private final String clsName;
+ private final ClassWithOptions options;
private AnnotationInfo.CallingThread callingThread = null;
private int numNativesInits;
private final HashSet<String> takenMethodNames = new HashSet<String>();
public CodeGenerator(ClassWithOptions annotatedClass) {
this.cls = annotatedClass.wrappedClass;
this.clsName = annotatedClass.generatedName;
+ this.options = annotatedClass;
final String unqualifiedName = Utils.getUnqualifiedName(clsName);
header.append(
+ Utils.getIfdefHeader(annotatedClass.ifdef) +
"class " + clsName + " : public mozilla::jni::ObjectBase<" +
unqualifiedName + ">\n" +
"{\n" +
"public:\n" +
" static const char name[];\n" +
"\n" +
" explicit " + unqualifiedName + "(const Context& ctx) : ObjectBase<" +
unqualifiedName + ">(ctx) {}\n" +
"\n");
cpp.append(
+ Utils.getIfdefHeader(annotatedClass.ifdef) +
"const char " + clsName + "::name[] =\n" +
" \"" + cls.getName().replace('.', '/') + "\";\n" +
"\n");
natives.append(
+ Utils.getIfdefHeader(annotatedClass.ifdef) +
"template<class Impl>\n" +
"class " + clsName + "::Natives : " +
"public mozilla::jni::NativeImpl<" + unqualifiedName + ", Impl>\n" +
"{\n" +
"public:\n");
}
private String getTraitsName(String uniqueName, boolean includeScope) {
@@ -551,16 +556,18 @@ public class CodeGenerator {
}
/**
* Get the finalised bytes to go into the generated wrappers file.
*
* @return The bytes to be written to the wrappers file.
*/
public String getWrapperFileContents() {
+ cpp.append(
+ Utils.getIfdefFooter(options.ifdef));
return cpp.toString();
}
/**
* Get the finalised bytes to go into the generated header file.
*
* @return The bytes to be written to the header file.
*/
@@ -575,17 +582,18 @@ public class CodeGenerator {
"\n");
if (nativesInits.length() > 0) {
header.append(
" template<class Impl> class Natives;\n");
}
header.append(
"};\n" +
- "\n");
+ "\n" +
+ Utils.getIfdefFooter(options.ifdef));
return header.toString();
}
/**
* Get the finalised bytes to go into the generated natives header file.
*
* @return The bytes to be written to the header file.
*/
@@ -595,12 +603,13 @@ public class CodeGenerator {
}
natives.append(
" static const JNINativeMethod methods[" + numNativesInits + "];\n" +
"};\n" +
"\n" +
"template<class Impl>\n" +
"const JNINativeMethod " + clsName + "::Natives<Impl>::methods[] = {" + nativesInits + '\n' +
"};\n" +
- "\n");
+ "\n" +
+ Utils.getIfdefFooter(options.ifdef));
return natives.toString();
}
}
--- a/build/annotationProcessors/SDKProcessor.java
+++ b/build/annotationProcessors/SDKProcessor.java
@@ -407,17 +407,18 @@ public class SDKProcessor {
}
private static void generateClass(Class<?> clazz,
ClassInfo clsInfo,
StringBuilder implementationFile,
StringBuilder headerFile) throws ParseException {
String generatedName = clazz.getSimpleName();
- CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
+ CodeGenerator generator = new CodeGenerator(
+ new ClassWithOptions(clazz, generatedName, /* ifdef */ ""));
generateMembers(generator, clsInfo,
sortAndFilterMembers(clazz, clazz.getConstructors()));
generateMembers(generator, clsInfo, sortAndFilterMembers(clazz, clazz.getMethods()));
generateMembers(generator, clsInfo, sortAndFilterMembers(clazz, clazz.getFields()));
headerFile.append(generator.getHeaderFileContents());
implementationFile.append(generator.getWrapperFileContents());
--- a/build/annotationProcessors/classloader/ClassWithOptions.java
+++ b/build/annotationProcessors/classloader/ClassWithOptions.java
@@ -2,14 +2,16 @@
* 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.classloader;
public class ClassWithOptions {
public final Class<?> wrappedClass;
public final String generatedName;
+ public final String ifdef;
- public ClassWithOptions(Class<?> someClass, String name) {
+ public ClassWithOptions(Class<?> someClass, String name, String ifdef) {
wrappedClass = someClass;
generatedName = name;
+ this.ifdef = ifdef;
}
}
--- a/build/annotationProcessors/classloader/JarClassIterator.java
+++ b/build/annotationProcessors/classloader/JarClassIterator.java
@@ -1,14 +1,16 @@
/* 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.classloader;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
import java.util.Iterator;
/**
* Class for iterating over an IterableJarLoadingURLClassLoader's classes.
*
* This class is not thread safe: use it only from a single thread.
*/
public class JarClassIterator implements Iterator<ClassWithOptions> {
@@ -62,17 +64,37 @@ public class JarClassIterator implements
}
if (enclosingClass != null) {
// Anonymous inner class - unsupported.
// Or named inner class, which will be processed when we process the outer class.
return fillLookAheadIfPossible();
}
- lookAhead = new ClassWithOptions(ret, ret.getSimpleName());
+ String ifdef = "";
+ for (final Annotation annotation : ret.getDeclaredAnnotations()) {
+ Class<? extends Annotation> annotationType = annotation.annotationType();
+ if (!annotationType.getName().equals(
+ "org.mozilla.gecko.annotation.BuildFlag")) {
+ continue;
+ }
+
+ try {
+ final Method valueMethod = annotationType.getDeclaredMethod("value");
+ valueMethod.setAccessible(true);
+ ifdef = (String) valueMethod.invoke(annotation);
+ break;
+ } catch (final Exception e) {
+ System.err.println("Unable to read BuildFlag annotation.");
+ e.printStackTrace(System.err);
+ System.exit(1);
+ }
+ }
+
+ lookAhead = new ClassWithOptions(ret, ret.getSimpleName(), ifdef);
return true;
} catch (ClassNotFoundException e) {
System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
e.printStackTrace();
System.exit(2);
}
return false;
}
--- a/build/annotationProcessors/utils/GeneratableElementIterator.java
+++ b/build/annotationProcessors/utils/GeneratableElementIterator.java
@@ -72,18 +72,18 @@ public class GeneratableElementIterator
}
private Class<?>[] getFilteredInnerClasses() {
// Go through all inner classes and see which ones we want to generate.
final Class<?>[] candidates = mClass.wrappedClass.getDeclaredClasses();
int count = 0;
for (int i = 0; i < candidates.length; ++i) {
- final GeneratableElementIterator testIterator
- = new GeneratableElementIterator(new ClassWithOptions(candidates[i], null));
+ final GeneratableElementIterator testIterator = new GeneratableElementIterator(
+ new ClassWithOptions(candidates[i], null, /* ifdef */ ""));
if (testIterator.hasNext()
|| testIterator.getFilteredInnerClasses() != null) {
count++;
continue;
}
// Clear out ones that don't match.
candidates[i] = null;
}
@@ -103,17 +103,19 @@ public class GeneratableElementIterator
}
}
final ClassWithOptions[] ret = new ClassWithOptions[count];
count = 0;
for (Class<?> candidate : candidates) {
if (candidate != null) {
ret[count++] = new ClassWithOptions(
- candidate, mClass.generatedName + "::" + candidate.getSimpleName());
+ candidate,
+ mClass.generatedName + "::" + candidate.getSimpleName(),
+ /* ifdef */ "");
}
}
assert ret.length == count;
Arrays.sort(ret, new Comparator<ClassWithOptions>() {
@Override public int compare(ClassWithOptions lhs, ClassWithOptions rhs) {
return lhs.generatedName.compareTo(rhs.generatedName);
}
--- a/build/annotationProcessors/utils/Utils.java
+++ b/build/annotationProcessors/utils/Utils.java
@@ -322,9 +322,25 @@ public class Utils {
System.err.println("*** Invalid value \"" + name + "\" for " + type.getSimpleName());
System.err.println("*** Specify one of " + names.toString());
System.err.println("***");
e.printStackTrace(System.err);
System.exit(1);
return null;
}
}
+
+ public static String getIfdefHeader(String ifdef) {
+ if (ifdef.isEmpty()) {
+ return "";
+ } else if (ifdef.startsWith("!")) {
+ return "#ifndef " + ifdef.substring(1) + "\n";
+ }
+ return "#ifdef " + ifdef + "\n";
+ }
+
+ public static String getIfdefFooter(String ifdef) {
+ if (ifdef.isEmpty()) {
+ return "";
+ }
+ return "#endif // " + ifdef + "\n";
+ }
}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -143,16 +143,17 @@ z.inputs += ['AndroidManifest.xml.in']
include('android-services.mozbuild')
geckoview_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/main/'
geckoview_thirdparty_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/thirdparty/'
thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/'
constants_jar = add_java_jar('constants')
constants_jar.sources += [geckoview_source_dir + 'java/org/mozilla/gecko/' + x for x in [
+ 'annotation/BuildFlag.java',
'annotation/JNITarget.java',
'annotation/ReflectionTarget.java',
'annotation/RobocopTarget.java',
'annotation/WebRTCJNITarget.java',
'annotation/WrapForJNI.java',
'SysInfo.java',
]]
constants_jar.sources += ['java/org/mozilla/gecko/' + x for x in [
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/BuildFlag.java
@@ -0,0 +1,26 @@
+/* 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.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used to tag classes that are conditionally built behind
+ * build flags. Any generated JNI bindings will incorporate the specified build
+ * flags.
+ */
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BuildFlag {
+ /**
+ * Preprocessor macro for conditionally building the generated bindings.
+ * "MOZ_FOO" wraps generated bindings in "#ifdef MOZ_FOO / #endif"
+ * "!MOZ_FOO" wraps generated bindings in "#ifndef MOZ_FOO / #endif"
+ */
+ String value() default "";
+}