Bug 1444546 - Part 2: Build annotationProcessors with Gradle. r=jchen draft
authorNick Alexander <nalexander@mozilla.com>
Thu, 22 Feb 2018 16:11:14 -0800
changeset 792112 d99b74a0a1e0bb3e8f4d4540978328388e5c2e42
parent 792111 65b85ec338cc5a7cb12afc2ad7678271339a6e89
child 792113 3bb0dfa4b20de712607e2f858031a5060eaf606e
child 792120 d977b46daedf2270003604629a3935e8133e8f52
push id108996
push usernalexander@mozilla.com
push dateMon, 07 May 2018 17:23:26 +0000
reviewersjchen
bugs1444546
milestone61.0a1
Bug 1444546 - Part 2: Build annotationProcessors with Gradle. r=jchen We want annotationProcessors to be compiled and archived into a JAR at build time, ready to generate JNI wrappers. (That is, until we turn the whole thing into a real annotation processor.) But even if we do use a real annotation processor, we still need to generate SDK bindings, which is less clearly expressed as an annotation processor. (It's more of a build step.) Gradle provides a huge number of ways to organize build logic to achieve this: see https://docs.gradle.org/current/userguide/organizing_build_logic.html. Unfortunately, the best such way -- putting the code into $topsrcdir/buildSrc -- has key disadvantages: 1) it pollutes the top-level $topsrcdir, and there's no way to change the location of buildSrc (https://github.com/gradle/gradle/issues/2472); 2) it's complicated to have a dependent project (mobile/android/annotations) expose its code via a buildSrc project; 3) using buildSrc at all appears to conflict with the Android-Gradle plugin version that we are using. Therefore, this commit does something much simpler: it adds a Java-only project and uses the resulting Gradle "Jar" task and archive output as input to the existing Gradle "generate JNI wrappers" task. MozReview-Commit-ID: 2OyYLPneE1M
build/annotationProcessors/Makefile.in
build/annotationProcessors/moz.build
build/moz.build
config/recurse.mk
mobile/android/annotations/build.gradle
mobile/android/gradle/with_gecko_binaries.gradle
settings.gradle
deleted file mode 100644
--- a/build/annotationProcessors/Makefile.in
+++ /dev/null
@@ -1,10 +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/.
-
-include $(topsrcdir)/config/rules.mk
-
-# Include Android specific java flags, instead of what's in rules.mk.
-include $(topsrcdir)/config/android-common.mk
-
-export:: annotationProcessors.jar
deleted file mode 100644
--- a/build/annotationProcessors/moz.build
+++ /dev/null
@@ -1,21 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
-jar = add_java_jar('annotationProcessors')
-jar.sources += [
-    'AnnotationInfo.java',
-    'AnnotationProcessor.java',
-    'classloader/AnnotatableEntity.java',
-    'classloader/ClassWithOptions.java',
-    'classloader/IterableJarLoadingURLClassLoader.java',
-    'classloader/JarClassIterator.java',
-    'CodeGenerator.java',
-    'SDKProcessor.java',
-    'utils/AlphabeticAnnotatableEntityComparator.java',
-    'utils/GeneratableElementIterator.java',
-    'utils/Utils.java',
-]
-jar.extra_jars += CONFIG['ANDROID_LINT_CLASSPATH'].split()
--- a/build/moz.build
+++ b/build/moz.build
@@ -19,19 +19,16 @@ if CONFIG['OS_ARCH'] == 'WINNT':
         CONFIGURE_SUBST_FILES += ['win64/cargo-linker.bat']
 else:
     DIRS += ['unix']
 
 CRAMTEST_MANIFESTS += [
     'tests/cram/cram.ini',
 ]
 
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
-    DIRS += ['annotationProcessors']
-
 DEFINES['ACCEPTED_MAR_CHANNEL_IDS'] = CONFIG['ACCEPTED_MAR_CHANNEL_IDS']
 
 if CONFIG['MOZ_BUILD_APP'] == 'browser':
     PYTHON_UNITTEST_MANIFESTS += [
         'compare-mozconfig/python.ini',
     ]
 
 if CONFIG['ENABLE_TESTS'] or CONFIG['MOZ_DMD']:
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -160,19 +160,16 @@ 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 widget binding generator code is part of the annotationProcessors.
-widget/android/bindings/export: build/annotationProcessors/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
new file mode 100644
--- /dev/null
+++ b/mobile/android/annotations/build.gradle
@@ -0,0 +1,11 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/annotations"
+
+apply plugin: 'java'
+
+dependencies {
+    compile 'com.android.tools.lint:lint:25.3.1'
+    compile 'com.android.tools.lint:lint-checks:25.3.1'
+}
+
+sourceCompatibility = JavaVersion.VERSION_1_8
+targetCompatibility = JavaVersion.VERSION_1_8
--- a/mobile/android/gradle/with_gecko_binaries.gradle
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -114,62 +114,67 @@ ext.configureLibraryVariantWithJNIWrappe
     // ...WithPrepareIntermediateJars..., so we make the consuming moz.build
     // system invoke this target directly, and force the
     // ...WithPrepareIntermediateJars... dependency.  The real consumer of the
     // JNI wrappers is the moz.build system, which always builds geckoview to
     // consume from Fennec, so that dependency likely adds less to the build time.
     def jarTask = tasks["transformClassesAndResourcesWithPrepareIntermediateJarsFor${variant.name.capitalize()}"]
     def output = jarTask.outputs.files.find({ it.absolutePath.contains('/classes.jar') })
 
+    def annotationProcessorsJarTask = project(':annotations').jar
+
     def wrapperTask
     if (System.env.IS_LANGUAGE_REPACK == '1') {
         // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and don't
         // really have a build environment.
         wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}")
     } else {
         wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
-            classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
+            classpath annotationProcessorsJarTask.archivePath
         
             // Configure the classpath at evaluation-time, not at
             // configuration-time: see above comment.
             doFirst {
                 classpath variant.javaCompile.classpath
                 // Include android.jar.
                 classpath variant.javaCompile.options.bootClasspath
             }
     
             main = 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor'
             args module
             args output
             
             workingDir "${topobjdir}/mobile/android/base"
             
             dependsOn jarTask
+            dependsOn annotationProcessorsJarTask
         }
     }
 
     if (module == 'Generated') {
         tasks["bundle${variant.name.capitalize()}"].dependsOn wrapperTask
     } else {
         tasks["assemble${variant.name.capitalize()}"].dependsOn wrapperTask
     }
 }
 
 ext.configureApplicationVariantWithJNIWrappers = { variant, module ->
     def jarTask = tasks["bundleAppClasses${variant.name.capitalize()}"]
     def output = jarTask.outputs.files.find({ it.absolutePath.contains('/classes.jar') })
 
+    def annotationProcessorsJarTask = project(':annotations').jar
+
     def wrapperTask
     if (System.env.IS_LANGUAGE_REPACK == '1') {
         // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and don't
         // really have a build environment.
         wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}")
     } else {
         wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
-            classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
+            classpath annotationProcessorsJarTask.archivePath
         
             // Configure the classpath at evaluation-time, not at
             // configuration-time: see above comment.
             doFirst {
                 classpath variant.javaCompile.classpath
                 // Include android.jar.
                 classpath variant.javaCompile.options.bootClasspath
             }
@@ -177,11 +182,13 @@ ext.configureApplicationVariantWithJNIWr
             main = 'org.mozilla.gecko.annotationProcessors.AnnotationProcessor'
             args module
             args output
         
             workingDir "${topobjdir}/mobile/android/base"
     
             // This forces bundling, which isn't usually part of the assemble* process.
             dependsOn jarTask
+
+            dependsOn annotationProcessorsJarTask
         }
     }
 }
--- a/settings.gradle
+++ b/settings.gradle
@@ -9,36 +9,39 @@ proc.consumeProcessOutput(standardOutput
 proc.waitFor()
 
 // Only show the output if something went wrong.
 if (proc.exitValue() != 0) {
     throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${proc.exitValue()}:\n\n${standardOutput.toString()}")
 }
 
 import groovy.json.JsonSlurper
+
 def slurper = new JsonSlurper()
 def json = slurper.parseText(standardOutput.toString())
 
 if (json.substs.MOZ_BUILD_APP != 'mobile/android') {
     throw new GradleException("Building with Gradle is only supported for Fennec, i.e., MOZ_BUILD_APP == 'mobile/android'.")
 }
 
 // Set the Android SDK location.  This is the *least specific* mechanism, which
 // is unfortunate: we'd prefer to use the *most specific* mechanism.  That is,
 // local.properties (first 'sdk.dir', then 'android.dir') and then the
 // environment variable ANDROID_HOME will override this.  That's unfortunate,
 // but it's hard to automatically arrange better.
 System.setProperty('android.home', json.substs.ANDROID_SDK_ROOT)
 
+include ':annotations'
 include ':app'
 include ':geckoview'
 include ':geckoview_example'
 include ':omnijar'
 include ':thirdparty'
 
+project(':annotations').projectDir = new File("${json.topsrcdir}/mobile/android/annotations")
 project(':app').projectDir = new File("${json.topsrcdir}/mobile/android/app")
 project(':geckoview').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview")
 project(':geckoview_example').projectDir = new File("${json.topsrcdir}/mobile/android/geckoview_example")
 project(':omnijar').projectDir = new File("${json.topsrcdir}/mobile/android/app/omnijar")
 project(':thirdparty').projectDir = new File("${json.topsrcdir}/mobile/android/thirdparty")
 
 // The Gradle instance is shared between settings.gradle and all the
 // other build.gradle files (see