Bug 1378410 - 1. Support BuildFlag annotation for generated bindings; r=snorp
authorJim Chen <nchen@mozilla.com>
Fri, 01 Sep 2017 14:02:29 -0400
changeset 378362 1bf78591e4542c69372da02898950e3304e935b5
parent 378361 3aada22a678f9826ccc1952b927340266f26c178
child 378363 704e761343e0403262220a5dc112b4a3bacce890
push id32428
push userarchaeopteryx@coole-files.de
push dateSat, 02 Sep 2017 08:52:28 +0000
treeherdermozilla-central@b01a7e57425b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1378410
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
build/annotationProcessors/AnnotationProcessor.java
build/annotationProcessors/CodeGenerator.java
build/annotationProcessors/SDKProcessor.java
build/annotationProcessors/classloader/ClassWithOptions.java
build/annotationProcessors/classloader/JarClassIterator.java
build/annotationProcessors/utils/GeneratableElementIterator.java
build/annotationProcessors/utils/Utils.java
mobile/android/base/moz.build
mobile/android/geckoview/src/main/java/org/mozilla/gecko/annotation/BuildFlag.java
--- 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 "";
+}