Bug 1444546 - Part 3: Use GENERATED_FILES for Android SDK bindings generation. r=froydnj,jchen
authorNick Alexander <nalexander@mozilla.com>
Tue, 06 Mar 2018 19:19:48 -0800
changeset 417175 731eb2e4b5579cc727068d346a32a67d79ec6756
parent 417174 d8c9882a3b3ca27904d6169567829e79c458e567
child 417176 f0a4cabc8c940923aef39a2be39c10056eef891e
push id33962
push useraiakab@mozilla.com
push dateTue, 08 May 2018 12:37:43 +0000
treeherdermozilla-central@632bb768b1dd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, jchen
bugs1444546
milestone61.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 1444546 - Part 3: Use GENERATED_FILES for Android SDK bindings generation. r=froydnj,jchen MozReview-Commit-ID: 2blmzKTvpj3
build/autoconf/android.m4
config/recurse.mk
mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/SDKProcessor.java
mobile/android/base/Makefile.in
mobile/android/geckoview/build.gradle
mobile/android/gradle.configure
mobile/android/gradle.py
mobile/android/gradle/with_gecko_binaries.gradle
mobile/android/mach_commands.py
old-configure.in
widget/android/bindings/Makefile.in
widget/android/bindings/moz.build
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -89,18 +89,16 @@ AC_SUBST_LIST([STLPORT_LIBS])
 
 ])
 
 
 dnl Configure an Android SDK.
 dnl Arg 1: compile SDK version, like 23.
 dnl Arg 2: target SDK version, like 23.
 dnl Arg 3: list of build-tools versions, like "23.0.3 23.0.1".
-dnl Arg 4: list of target lint versions, like "25.3.2 25.3.1" (note: we fall back to
-dnl        unversioned lint if this version is not found).
 AC_DEFUN([MOZ_ANDROID_SDK],
 [
 
 MOZ_ARG_WITH_STRING(android-sdk,
 [  --with-android-sdk=DIR
                           location where the Android SDK can be found (like ~/.mozbuild/android-sdk-linux)],
     android_sdk_root=$withval)
 
@@ -189,43 +187,16 @@ case "$target" in
     AC_SUBST(ANDROID_TARGET_SDK)
     AC_SUBST(ANDROID_SDK_ROOT)
     AC_SUBST(ANDROID_SDK)
     AC_SUBST(ANDROID_TOOLS)
     AC_SUBST(ANDROID_BUILD_TOOLS_VERSION)
     ;;
 esac
 
-AC_MSG_CHECKING([for Android lint classpath])
-ANDROID_LINT_CLASSPATH=""
-for version in $4; do
-    android_lint_versioned_jar="$ANDROID_SDK_ROOT/tools/lib/lint-$version.jar"
-    if test -e "$android_lint_versioned_jar" ; then
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $android_lint_versioned_jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/lint-checks-$version.jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/sdklib-$version.jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/repository-$version.jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/common-$version.jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/lint-api-$version.jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/manifest-merger-$version.jar"
-        break
-    fi
-done
-if test -z "$ANDROID_LINT_CLASSPATH" ; then
-    android_lint_unversioned_jar="$ANDROID_SDK_ROOT/tools/lib/lint.jar"
-    if test -e "$android_lint_unversioned_jar" ; then
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $android_lint_unversioned_jar"
-        ANDROID_LINT_CLASSPATH="$ANDROID_LINT_CLASSPATH $ANDROID_SDK_ROOT/tools/lib/lint-checks.jar"
-    else
-        AC_MSG_ERROR([Unable to find android sdk's lint jar. This probably means that you need to update android.m4 to find the latest version of lint-*.jar and all its dependencies. (looked for $android_lint_versioned_jar and $android_lint_unversioned_jar)])
-    fi
-fi
-AC_MSG_RESULT([$ANDROID_LINT_CLASSPATH])
-AC_SUBST(ANDROID_LINT_CLASSPATH)
-
 MOZ_ARG_WITH_STRING(android-min-sdk,
 [  --with-android-min-sdk=[VER]     Impose a minimum Firefox for Android SDK version],
 [ MOZ_ANDROID_MIN_SDK_VERSION=$withval ])
 
 MOZ_ARG_WITH_STRING(android-max-sdk,
 [  --with-android-max-sdk=[VER]     Impose a maximum Firefox for Android SDK version],
 [ MOZ_ANDROID_MAX_SDK_VERSION=$withval ])
 
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -160,16 +160,20 @@ recurse:
 	@$(RECURSED_COMMAND)
 	$(LOOP_OVER_DIRS)
 
 ifeq (.,$(DEPTH))
 # Interdependencies for parallel export.
 js/xpconnect/src/export: dom/bindings/export xpcom/xpidl/export
 accessible/xpcom/export: xpcom/xpidl/export
 
+# The Android SDK bindings needs to build the Java generator code
+# source code in order to write the SDK bindings.
+widget/android/bindings/export: mobile/android/base/export
+
 # .xpt generation needs the xpidl lex/yacc files
 xpcom/xpidl/export: xpcom/idl-parser/xpidl/export
 
 # CSS2Properties.webidl needs ServoCSSPropList.py from layout/style
 dom/bindings/export: layout/style/export
 
 ifdef ENABLE_CLANG_PLUGIN
 $(filter-out config/host build/unix/stdc++compat/% build/clang-plugin/%,$(compile_targets)): build/clang-plugin/target build/clang-plugin/tests/target
--- a/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/SDKProcessor.java
+++ b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/SDKProcessor.java
@@ -2,26 +2,26 @@
  * 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;
 
 /**
  * Generate C++ bindings for SDK classes using a config file.
  *
- * java SDKProcessor <sdkjar> <configfile> <outdir> <fileprefix> <max-sdk-version>
+ * java SDKProcessor <sdkjar> <max-sdk-version> <outdir> [<configfile> <fileprefix>]+
  *
  * <sdkjar>: jar file containing the SDK classes (e.g. android.jar)
- * <configfile>: config file for generating bindings
- * <outdir>: output directory for generated binding files
- * <fileprefix>: prefix used for generated binding files
  * <max-sdk-version>: SDK version for generated class members (bindings will not be
  *                     generated for members with SDK versions higher than max-sdk-version)
+ * <outdir>: output directory for generated binding files
+ * <configfile>: config file for generating bindings
+ * <fileprefix>: prefix used for generated binding files
  *
- * The config file is a text file following the .ini format:
+ * Each config file is a text file following the .ini format:
  *
  * ; comment
  * [section1]
  * property = value
  *
  * # comment
  * [section2]
  * property = value
@@ -221,103 +221,108 @@ public class SDKProcessor {
                 }
             }
             return new AnnotationInfo(stubName, mode, thread, target, noLiteral);
         }
     }
 
     public static void main(String[] args) throws Exception {
         // We expect a list of jars on the commandline. If missing, whinge about it.
-        if (args.length < 5) {
-            System.err.println("Usage: java SDKProcessor sdkjar configfile outdir fileprefix max-sdk-version");
+        if (args.length < 5 || args.length % 2 != 1) {
+            System.err.println("Usage: java SDKProcessor sdkjar max-sdk-version outdir [configfile fileprefix]+");
             System.exit(1);
         }
 
         System.out.println("Processing platform bindings...");
 
-        String sdkJar = args[0];
-        String outdir = args[2];
-        String generatedFilePrefix = args[3];
-        sMaxSdkVersion = Integer.parseInt(args[4]);
+        final String sdkJar = args[0];
+        sMaxSdkVersion = Integer.parseInt(args[1]);
+        final String outdir = args[2];
 
-        LintCliClient lintClient = new LintCliClient();
+        final LintCliClient lintClient = new LintCliClient();
         sApiLookup = ApiLookup.get(lintClient);
 
-        // Start the clock!
-        long s = System.currentTimeMillis();
+        for (int argIndex = 3; argIndex < args.length; argIndex += 2) {
+            final String configFile = args[argIndex];
+            final String generatedFilePrefix = args[argIndex + 1];
+            System.out.println("Processing bindings from " + configFile);
 
-        // Get an iterator over the classes in the jar files given...
-        // Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
+            // 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);
-        headerFile.append(
-                "#ifndef " + generatedFilePrefix + "_h__\n" +
-                "#define " + generatedFilePrefix + "_h__\n" +
-                "\n" +
-                "#include \"mozilla/jni/Refs.h\"\n" +
-                "\n" +
-                "namespace mozilla {\n" +
-                "namespace java {\n" +
-                "namespace sdk {\n" +
-                "\n");
+            StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
+            headerFile.append(
+                    "#ifndef " + generatedFilePrefix + "_h__\n" +
+                    "#define " + generatedFilePrefix + "_h__\n" +
+                    "\n" +
+                    "#include \"mozilla/jni/Refs.h\"\n" +
+                    "\n" +
+                    "namespace mozilla {\n" +
+                    "namespace java {\n" +
+                    "namespace sdk {\n" +
+                    "\n");
+
+            StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
+            implementationFile.append(
+                    "#include \"" + generatedFilePrefix + ".h\"\n" +
+                    "#include \"mozilla/jni/Accessors.h\"\n" +
+                    "\n" +
+                    "namespace mozilla {\n" +
+                    "namespace java {\n" +
+                    "namespace sdk {\n" +
+                    "\n");
 
-        StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
-        implementationFile.append(
-                "#include \"" + generatedFilePrefix + ".h\"\n" +
-                "#include \"mozilla/jni/Accessors.h\"\n" +
-                "\n" +
-                "namespace mozilla {\n" +
-                "namespace java {\n" +
-                "namespace sdk {\n" +
-                "\n");
+            // 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) {
+                throw new RuntimeException(e.toString());
+            }
 
-        // 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) {
-            throw new RuntimeException(e.toString());
+            try {
+                final ClassInfo[] classes = getClassList(configFile);
+                for (final ClassInfo cls : classes) {
+                    System.out.println("Looking up: " + cls.name);
+                    generateClass(Class.forName(cls.name, true, loader),
+                            cls,
+                            implementationFile,
+                            headerFile);
+                }
+            } catch (final IllegalStateException | IOException | ParseException e) {
+                System.err.println("***");
+                System.err.println("*** Error parsing config file: " + configFile);
+                System.err.println("*** " + e);
+                System.err.println("***");
+                if (e.getCause() != null) {
+                    e.getCause().printStackTrace(System.err);
+                }
+                System.exit(1);
+                return;
+            }
+
+            implementationFile.append(
+                    "} /* sdk */\n" +
+                    "} /* java */\n" +
+                    "} /* mozilla */\n");
+
+            headerFile.append(
+                    "} /* sdk */\n" +
+                    "} /* java */\n" +
+                    "} /* mozilla */\n" +
+                    "#endif\n");
+
+            writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile);
+            long e = System.currentTimeMillis();
+            System.out.println("SDK processing complete in " + (e - s) + "ms");
         }
-
-        try {
-            final ClassInfo[] classes = getClassList(args[1]);
-            for (final ClassInfo cls : classes) {
-                System.out.println("Looking up: " + cls.name);
-                generateClass(Class.forName(cls.name, true, loader),
-                              cls,
-                              implementationFile,
-                              headerFile);
-            }
-        } catch (final IllegalStateException|IOException|ParseException e) {
-            System.err.println("***");
-            System.err.println("*** Error parsing config file: " + args[1]);
-            System.err.println("*** " + e);
-            System.err.println("***");
-            if (e.getCause() != null) {
-                e.getCause().printStackTrace(System.err);
-            }
-            System.exit(1);
-            return;
-        }
-
-        implementationFile.append(
-                "} /* sdk */\n" +
-                "} /* java */\n" +
-                "} /* mozilla */\n");
-
-        headerFile.append(
-                "} /* sdk */\n" +
-                "} /* java */\n" +
-                "} /* mozilla */\n" +
-                "#endif\n");
-
-        writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile);
-        long e = System.currentTimeMillis();
-        System.out.println("SDK processing complete in " + (e - s) + "ms");
     }
 
     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));
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -163,8 +163,10 @@ libs:: FennecJNIWrappers.cpp
 	  echo '  make -C $(CURDIR) update-fennec-wrappers' && \
 	  echo && \
 	  echo '* Repeat the build, and check in any changes.       *' && \
 	  echo '*****************************************************' && \
 	  exit 1)
 
 endif # ifneq (multi,$(AB_CD))
 endif # ifeq (,$(IS_LANGUAGE_REPACK))
+
+export:: android_apks
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -1,15 +1,19 @@
 buildDir "${topobjdir}/gradle/build/mobile/android/geckoview"
 
 apply plugin: 'com.android.library'
 apply plugin: 'kotlin-android'
 
 apply from: "${topsrcdir}/mobile/android/gradle/product_flavors.gradle"
 
+// The SDK binding generation tasks depend on the JAR creation task of the
+// :annotations project.
+evaluationDependsOn(':annotations')
+
 // Non-official versions are like "61.0a1", where "a1" is the milestone.
 // This simply strips that off, leaving "61.0" in this example.
 def getAppVersionWithoutMilestone() {
     return mozconfig.substs.MOZ_APP_VERSION.replaceFirst(/a[0-9]/, "")
 }
 
 // This converts MOZ_APP_VERSION into an integer
 // version code.
@@ -322,8 +326,38 @@ afterEvaluate {
             }
         }
     }
 }
 
 // Bug 1353055 - Strip 'vars' debugging information to agree with moz.build.
 apply from: "${topsrcdir}/mobile/android/gradle/debug_level.gradle"
 android.libraryVariants.all configureVariantDebugLevel
+
+// There's nothing specific to the :geckoview project here -- this just needs to
+// be somewhere where the Android plugin is available so that we can fish the
+// path to "android.jar".
+task("generateSDKBindings", type: JavaExec) {
+    classpath project(':annotations').jar.archivePath
+    classpath project(':annotations').compileJava.classpath
+
+    // To use the lint APIs: "Lint must be invoked with the System property
+    // com.android.tools.lint.bindir pointing to the ANDROID_SDK tools
+    // directory"
+    systemProperties = [
+        'com.android.tools.lint.bindir': "${android.sdkDirectory}/tools",
+    ]
+
+    main = 'org.mozilla.gecko.annotationProcessors.SDKProcessor'
+    args android.bootClasspath
+    args 16
+    args "${topobjdir}/widget/android/bindings"
+
+    // Configure the arguments at evaluation-time, not at configuration-time.
+    doFirst {
+        // From -Pgenerate_sdk_bindings_args=... on command line.
+        args project.generate_sdk_bindings_args.split(':')
+    }
+
+    workingDir "${topsrcdir}/widget/android/bindings"
+
+    dependsOn project(':annotations').jar
+}
--- a/mobile/android/gradle.configure
+++ b/mobile/android/gradle.configure
@@ -96,16 +96,26 @@ def gradle_android_app_tasks(build_confi
         'app:generateJNIWrappersForFennec{app.variant.name}'.format(app=build_config.app),
         'app:assemble{app.variant.name}'.format(app=build_config.app),
         'app:assemble{app.variant.name}AndroidTest'.format(app=build_config.app),
     ]
 
 set_config('GRADLE_ANDROID_APP_TASKS', gradle_android_app_tasks)
 
 
+@dependable
+def gradle_android_generate_sdk_bindings_tasks():
+    '''Gradle tasks run by |mach android generate-sdk-bindings|.'''
+    return [
+        'geckoview:generateSDKBindings',
+    ]
+
+set_config('GRADLE_ANDROID_GENERATE_SDK_BINDINGS_TASKS', gradle_android_generate_sdk_bindings_tasks)
+
+
 @depends(gradle_android_build_config, check_build_environment)
 @imports(_from='itertools', _import='imap')
 def gradle_android_app_apks(build_config, build_env):
     '''Paths to APK files produced by |mach android assemble-app|.'''
     def capitalize(s):
         # str.capitalize lower cases trailing letters.
         if s:
             return s[0].upper() + s[1:]
@@ -199,16 +209,17 @@ set_config('GRADLE_ANDROID_ARCHIVE_GECKO
 
 @depends(
     gradle_android_app_tasks,
     gradle_android_test_tasks,
     gradle_android_lint_tasks,
     gradle_android_checkstyle_tasks,
     gradle_android_findbugs_tasks,
     gradle_android_archive_geckoview_tasks,
+    gradle_android_generate_sdk_bindings_tasks,
 )
 @imports(_from='itertools', _import='imap')
 @imports(_from='itertools', _import='chain')
 @imports(_from='itertools', _import='ifilterfalse')
 def gradle_android_dependencies_tasks(*tasks):
     '''Gradle tasks run by |mach android dependencies|.'''
     # The union, plus a bit more, of all of the Gradle tasks
     # invoked by the android-* automation jobs.
--- a/mobile/android/gradle.py
+++ b/mobile/android/gradle.py
@@ -32,8 +32,12 @@ def android(verb, *args):
 
         return 0
     finally:
         del lock_instance
 
 
 def assemble_app(dummy_output_file, *inputs):
     return android('assemble-app')
+
+
+def generate_sdk_bindings(dummy_output_file, *args):
+    return android('generate-sdk-bindings', *args)
--- a/mobile/android/gradle/with_gecko_binaries.gradle
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -3,16 +3,19 @@
  * 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/. */
 
 // The omnijar inputs are listed as resource directory inputs to a dummy JAR.
 // That arrangement labels them nicely in IntelliJ.  See the comment in the
 // :omnijar project for more context.
 evaluationDependsOn(':omnijar')
 
+// The JNI wrapper generation tasks depend on the JAR creation task of the :annotations project.
+evaluationDependsOn(':annotations')
+
 task buildOmnijars(type:Exec) {
     dependsOn rootProject.generateCodeAndResources
 
     // See comment in :omnijar project regarding interface mismatches here.
     inputs.file(project(':omnijar').sourceSets.main.resources.srcDirs).skipWhenEmpty() 
 
     // Produce both the Fennec and the GeckoView omnijars.
     outputs.file "${topobjdir}/dist/fennec/assets/omni.ja"
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -48,27 +48,45 @@ class MachCommands(MachCommandBase):
 
 
     @Command('android', category='devenv',
         description='Run Android-specific commands.',
         conditions=[conditions.is_android])
     def android(self):
         pass
 
-
     @SubCommand('android', 'assemble-app',
         """Assemble Firefox for Android.
         See http://firefox-source-docs.mozilla.org/build/buildsystem/toolchains.html#firefox-for-android-with-gradle""")
     @CommandArgument('args', nargs=argparse.REMAINDER)
     def android_assemble_app(self, args):
         ret = self.gradle(self.substs['GRADLE_ANDROID_APP_TASKS'] + ['-x', 'lint', '--continue'] + args, verbose=True)
 
         return ret
 
 
+    @SubCommand('android', 'generate-sdk-bindings',
+        """Generate SDK bindings used when building GeckoView.""")
+    @CommandArgument('inputs', nargs='+', help='config files, like [/path/to/ClassName-classes.txt]+')
+    @CommandArgument('args', nargs=argparse.REMAINDER)
+    def android_generate_sdk_bindings(self, inputs, args):
+        import itertools
+
+        def stem(input):
+            # Turn "/path/to/ClassName-classes.txt" into "ClassName".
+            return os.path.basename(input).rsplit('-classes.txt', 1)[0]
+
+        bindings_inputs = list(itertools.chain(*((input, stem(input)) for input in inputs)))
+        bindings_args = '-Pgenerate_sdk_bindings_args={}'.format(':'.join(bindings_inputs))
+
+        ret = self.gradle(self.substs['GRADLE_ANDROID_GENERATE_SDK_BINDINGS_TASKS'] + [bindings_args] + args, verbose=True)
+
+        return ret
+
+
     @SubCommand('android', 'test',
         """Run Android local unit tests.
         See https://developer.mozilla.org/en-US/docs/Mozilla/Android-specific_test_suites#android-test""")
     @CommandArgument('args', nargs=argparse.REMAINDER)
     def android_test(self, args):
         ret = self.gradle(self.substs['GRADLE_ANDROID_TEST_TASKS'] + ["--continue"] + args, verbose=True)
 
         ret |= self._parse_android_test_results('public/app/unittest', 'gradle/build/mobile/android/app',
--- a/old-configure.in
+++ b/old-configure.in
@@ -2075,17 +2075,17 @@ AC_SUBST(MOZ_MULET)
 
 dnl ========================================================
 dnl Ensure Android SDK and build-tools versions depending on
 dnl mobile target.
 dnl ========================================================
 
 case "$MOZ_BUILD_APP" in
 mobile/android)
-    MOZ_ANDROID_SDK(23, 23, 26.0.2, 26.0.0 26.0.0-dev 25.3.2 25.3.1)
+    MOZ_ANDROID_SDK(23, 23, 26.0.2)
     ;;
 esac
 
 dnl ========================================================
 dnl =
 dnl = Toolkit Options
 dnl =
 dnl ========================================================
deleted file mode 100644
--- a/widget/android/bindings/Makefile.in
+++ /dev/null
@@ -1,26 +0,0 @@
-# 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/.
-
-# Bug 1099345 - The SDK's lint code (used by the code generator) does not enjoy
-# concurrent access to a cache that it generates.
-.NOTPARALLEL:
-
-annotation_processor_jar_files := \
-  $(DEPTH)/build/annotationProcessors/annotationProcessors.jar \
-  $(ANDROID_LINT_CLASSPATH) \
-  $(NULL)
-
-sdk_processor := \
-  $(JAVA) \
-  -Dcom.android.tools.lint.bindir='$(ANDROID_TOOLS)' \
-  -classpath $(subst $(NULL) ,:,$(strip $(annotation_processor_jar_files))) \
-  org.mozilla.gecko.annotationProcessors.SDKProcessor
-
-# For the benefit of readers: the following pattern rule says that,
-# for example, MediaCodec.cpp and MediaCodec.h can be produced from
-# MediaCodec-classes.txt.  This formulation invokes the SDK processor
-# at most once.
-
-%.cpp %.h: $(ANDROID_SDK)/android.jar %-classes.txt $(annotation_processor_jar_files)
-	$(sdk_processor) $(ANDROID_SDK)/android.jar $(srcdir)/$*-classes.txt $(CURDIR) $* 16
--- a/widget/android/bindings/moz.build
+++ b/widget/android/bindings/moz.build
@@ -18,27 +18,36 @@ generated = [
     'SurfaceTexture',
     'ViewConfiguration'
 ]
 
 SOURCES += ['!%s.cpp' % stem for stem in generated]
 
 EXPORTS += ['!%s.h' % stem for stem in generated]
 
-# We'd like to add these to a future GENERATED_EXPORTS list, but for now we mark
-# them as generated here and manually install them in Makefile.in.
-GENERATED_FILES += [stem + '.h' for stem in generated]
 
 # There is an unfortunate race condition when using generated SOURCES and
 # pattern rules (see Makefile.in) that manifests itself as a VPATH resolution
 # conflict: MediaCodec.o looks for MediaCodec.cpp and $(CURDIR)/MediaCodec.cpp,
 # and the pattern rule is matched but doesn't resolve both sources, causing a
 # failure.  Adding the SOURCES to GENERATED_FILES causes the sources
 # to be built at export time, which is before MediaCodec.o needs them; and by
 # the time MediaCodec.o is built, the source is in place and the VPATH
 # resolution works as expected.
-GENERATED_FILES += [f[1:] for f in SOURCES]
+GENERATED_FILES += ['%s.h' % stem for stem in generated]
+
+# The recursive make backend treats the first output specially: it's passed as
+# an open FileAvoidWrite to the invoked script.  That doesn't work well with
+# the Gradle task that generates all of the outputs, so we add a dummy first
+# output.
+t = tuple(['sdk_bindings'] +
+          ['%s.cpp' % stem for stem in generated] +
+          ['%s.h' % stem for stem in generated])
+
+GENERATED_FILES += [t]
+GENERATED_FILES[t].script = '/mobile/android/gradle.py:generate_sdk_bindings'
+GENERATED_FILES[t].inputs += ['%s-classes.txt' % stem for stem in generated]
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/widget/android',
 ]