Bug 1384312 - Support generating JNI wrappers under --with-gradle, r=nalexander
authormaliu <max@mxli.us>
Wed, 11 Oct 2017 15:37:40 -0700
changeset 386473 fab13cda2355312461539ff089bedc32fb2cbdaf
parent 386472 c006ddf45ea82e700275ebdd848eae34d3f67d85
child 386474 4c6dc1727f183d854c6deb9aca9922b94d5d3567
push id32694
push userarchaeopteryx@coole-files.de
push dateTue, 17 Oct 2017 09:43:13 +0000
treeherdermozilla-central@3bd3448d9684 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnalexander
bugs1384312
milestone58.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 1384312 - Support generating JNI wrappers under --with-gradle, r=nalexander MozReview-Commit-ID: HECL60Ggeqn
mobile/android/app/build.gradle
mobile/android/base/Makefile.in
mobile/android/geckoview/build.gradle
mobile/android/gradle/with_gecko_binaries.gradle
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -350,16 +350,20 @@ android.applicationVariants.all { varian
 
     // :app uses :geckoview:release and handles it's own Gecko binary inclusion,
     // even though this would be most naturally done in the :geckoview project.
     if (!audienceDimension.equals('official')) {
         configureVariantWithGeckoBinaries(variant)
     }
 }
 
+android.applicationVariants.all { variant ->
+    configureVariantWithJNIWrappers(variant, "Fennec")
+}
+
 apply plugin: 'spoon'
 
 spoon {
     // For now, let's be verbose.
     debug = true
     // It's not helpful to pass when we don't have a device connected.
     failIfNoDeviceConnected = true
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -20,16 +20,18 @@ GARBAGE += \
   GeneratedJNIWrappers.h \
   FennecJNINatives.h \
   FennecJNIWrappers.cpp \
   FennecJNIWrappers.h \
   $(NULL)
 
 GARBAGE_DIRS += classes db jars res sync services generated
 
+gradle_dir := $(topobjdir)/gradle/build/mobile/android
+
 # The bootclasspath is functionally identical to the classpath, but allows the
 # classes given to redefine classes in core packages, such as java.lang.
 # android.jar is here as it provides Android's definition of the Java Standard
 # Library. The compatability lib here tweaks a few of the core classes to paint
 # over changes in behaviour between versions.
 JAVA_BOOTCLASSPATH := \
     $(ANDROID_SDK)/android.jar \
     $(NULL)
@@ -136,16 +138,17 @@ endif
 # java_bundled_libs.  See the note above.
 
 # uniq purloined from http://stackoverflow.com/a/16151140.
 uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1)))
 
 java_bundled_libs := $(call uniq,$(java_bundled_libs))
 java_bundled_libs := $(subst $(NULL) ,:,$(strip $(java_bundled_libs)))
 
+ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
 GECKOVIEW_JARS = \
   constants.jar \
   gecko-R.jar \
   gecko-mozglue.jar \
   gecko-util.jar \
   gecko-view.jar \
   sync-thirdparty.jar \
   $(NULL)
@@ -161,32 +164,41 @@ endif
 ifdef MOZ_INSTALL_TRACKING
 GECKOVIEW_JARS += gecko-thirdparty-adjust_sdk.jar
 endif
 
 ifdef MOZ_ANDROID_MMA
 GECKOVIEW_JARS += gecko-thirdparty-leanplum_sdk.jar
 endif
 
-geckoview_jars_classpath := $(subst $(NULL) ,:,$(strip $(GECKOVIEW_JARS)))
-
 FENNEC_JARS = \
   gecko-browser.jar \
   gecko-thirdparty.jar \
   services.jar \
   $(NULL)
 
 ifdef MOZ_ANDROID_SEARCH_ACTIVITY
 FENNEC_JARS += search-activity.jar
 endif
 
 ifdef MOZ_ANDROID_MLS_STUMBLER
 FENNEC_JARS += ../stumbler/stumbler.jar
 endif
 
+else # MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
+
+GECKOVIEW_JARS := $(gradle_dir)/geckoview/intermediates/bundles/debug/classes.jar
+FENNEC_JARS := $(gradle_dir)/app/intermediates/packaged/officialPhoton/debug/classes.jar
+
+$(GECKOVIEW_JARS): .gradle.deps
+$(FENNEC_JARS): .gradle.deps
+
+endif # MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
+
+geckoview_jars_classpath := $(subst $(NULL) ,:,$(strip $(GECKOVIEW_JARS)))
 
 # All the jars we're compiling from source. (not to be confused with
 # java_bundled_libs, which holds the jars which we're including as binaries).
 ALL_JARS = \
   $(GECKOVIEW_JARS) \
   $(FENNEC_JARS) \
   $(NULL)
 
@@ -223,32 +235,40 @@ library_jars := \
 # MOZ_ANDROID_MMA requires MOZ_INSTALL_TRACKING, so we don't need a
 # separate clause for MMA (Leanplum) support.
 ifdef MOZ_INSTALL_TRACKING
 library_jars += $(ANDROID_SDK)/optional/org.apache.http.legacy.jar
 endif # MOZ_INSTALL_TRACKING
 
 library_jars := $(subst $(NULL) ,:,$(strip $(library_jars)))
 
-gradle_dir := $(topobjdir)/gradle/build/mobile/android
-
 ifdef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
 .gradle.deps: .aapt.deps FORCE
 	@$(TOUCH) $@
 	$(topsrcdir)/mach gradle \
-		app:assembleOfficialPhotonDebug app:assembleOfficialPhotonDebugAndroidTest -x lint
+		geckoview:generateJNIWrappersForGeneratedRelease \
+		app:generateJNIWrappersForFennecOfficialPhotonDebug \
+		app:assembleOfficialPhotonDebug \
+		app:assembleOfficialPhotonDebugAndroidTest \
+		-x lint
 
 classes.dex: .gradle.deps
 	$(REPORT_BUILD)
 	cp $(gradle_dir)/app/intermediates/transforms/dex/officialPhoton/debug/folders/1000/1f/main/classes.dex $@
+
+GeneratedJNIWrappers.cpp GeneratedJNIWrappers.h GeneratedJNINatives.h : .gradle.deps
+	$(REPORT_BUILD)
+
+FennecJNIWrappers.cpp FennecJNIWrappers.h FennecJNINatives.h: .gradle.deps
+	$(REPORT_BUILD)
+
 else
 classes.dex: .proguard.deps
 	$(REPORT_BUILD)
 	$(DX) --dex --output=classes.dex --force-jumbo jars-proguarded
-endif
 
 ifdef MOZ_DISABLE_PROGUARD
   PROGUARD_PASSES=0
 else
   ifdef MOZ_DEBUG
     PROGUARD_PASSES=1
   else
     ifndef MOZILLA_OFFICIAL
@@ -323,16 +343,18 @@ GeneratedJNIWrappers.cpp: $(ANNOTATION_P
 
 # This annotation processing step also generates
 # FennecJNIWrappers.h and FennecJNINatives.h
 FennecJNIWrappers.cpp: $(ANNOTATION_PROCESSOR_JAR_FILES) $(FENNEC_JARS)
 	$(JAVA) -classpath $(all_jars_classpath):$(JAVA_BOOTCLASSPATH):$(JAVA_CLASSPATH):$(ANNOTATION_PROCESSOR_JAR_FILES) \
 		org.mozilla.gecko.annotationProcessors.AnnotationProcessor \
 		Fennec $(FENNEC_JARS)
 
+endif # MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
+
 include $(topsrcdir)/config/rules.mk
 
 not_android_res_files := \
   *.mkdir.done* \
   *.DS_Store* \
   *\#* \
   *.rej \
   *.orig \
@@ -531,17 +553,16 @@ gradle-omnijar:
 endif
 
 .PHONY: gradle-targets gradle-omnijar
 
 # GeneratedJNIWrappers.cpp target also generates
 #   GeneratedJNIWrappers.h and GeneratedJNINatives.h
 # FennecJNIWrappers.cpp target also generates
 #   FennecJNIWrappers.h and FennecJNINatives.h
-ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
 
 # List of build flags used by auto-generated JNI bindings (through the
 # @BuildFlag annotation in Java). For example, add a "MOZ_FOO \" line to this
 # list to support @BuildFlag(MOZ_FOO).
 BINDING_BUILD_FLAGS = \
   $(NULL)
 
 # Preprocess a JNI binding file using the build flags defined above.
@@ -581,21 +602,22 @@ libs:: FennecJNIWrappers.cpp
 	  echo '***     Error: The Fennec JNI code has changed    ***' && \
 	  echo '* To update generated code in the tree, please run  *' && \
 	  echo && \
 	  echo '  make -C $(CURDIR) update-fennec-wrappers' && \
 	  echo && \
 	  echo '* Repeat the build, and check in any changes.       *' && \
 	  echo '*****************************************************' && \
 	  exit 1)
-endif
 
 libs:: classes.dex
 	$(INSTALL) classes.dex $(FINAL_TARGET)
 
+ifndef MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
+
 # Generate Java binder interfaces from AIDL files.
 GECKOVIEW_AIDLS = \
   org/mozilla/gecko/IGeckoEditableChild.aidl \
   org/mozilla/gecko/IGeckoEditableParent.aidl \
   org/mozilla/gecko/gfx/ISurfaceAllocator.aidl \
   org/mozilla/gecko/media/ICodec.aidl \
   org/mozilla/gecko/media/ICodecCallbacks.aidl \
   org/mozilla/gecko/media/IMediaDrmBridge.aidl \
@@ -618,8 +640,10 @@ FENNEC_AIDLS = \
 
 fennec_aidl_src_path := $(srcdir)/aidl
 fennec_aidl_target_path := generated
 fennec_aidl_targets := $(addprefix $(fennec_aidl_target_path)/,$(patsubst %.aidl,%.java,$(FENNEC_AIDLS:.java=)))
 
 $(fennec_aidl_targets): $(fennec_aidl_target_path)/%.java: $(fennec_aidl_src_path)/%.aidl
 	@echo "Processing AIDL: $< => $@"
 	$(AIDL) -p$(ANDROID_SDK)/framework.aidl -I$(fennec_aidl_src_path) -I$(geckoview_aidl_src_path) -o$(fennec_aidl_target_path) $<
+
+endif # MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -162,16 +162,20 @@ android.libraryVariants.all { variant ->
     task "sourcesJar${name.capitalize()}"(type: Jar) {
         classifier 'sources'
         description = "Generate Javadoc for build variant $name"
         destinationDir = new File(destinationDir, variant.baseName)
         from files(variant.javaCompile.source)
     }
 }
 
+android.libraryVariants.all { variant ->
+    configureVariantWithJNIWrappers(variant, "Generated")
+}
+
 apply plugin: 'maven'
  
 uploadArchives {
     repositories.mavenDeployer {
         pom.groupId = 'org.mozilla'
         pom.artifactId = 'geckoview'
         pom.version = VERSION_NAME
         pom.project {
--- a/mobile/android/gradle/with_gecko_binaries.gradle
+++ b/mobile/android/gradle/with_gecko_binaries.gradle
@@ -116,8 +116,59 @@ ext.configureVariantWithGeckoBinaries = 
     generateAssetsTask.dependsOn syncLibsFromDistDir
     generateAssetsTask.dependsOn syncAssetsFromDistDir
 
     def sourceSet = productFlavor ? "${productFlavor}${buildType.capitalize()}" : buildType
     android.sourceSets."${sourceSet}".assets.srcDir syncOmnijarFromDistDir.destinationDir
     android.sourceSets."${sourceSet}".assets.srcDir syncAssetsFromDistDir.destinationDir
     android.sourceSets."${sourceSet}".jniLibs.srcDir syncLibsFromDistDir.destinationDir
 }
+
+ext.configureVariantWithJNIWrappers = { variant, module ->
+
+    def jarTask = tasks.findByName("jar${variant.name.capitalize()}Classes")
+    if (jarTask == null) {
+        jarTask = tasks.findByName("package${variant.name.capitalize()}JarArtifact")
+    }
+    if (jarTask == null) {
+        throw new GradleException("Jar task not found: \"jar${variant.name.capitalize()}Classes\"\t\"package${variant.name.capitalize()}JarArtifact\"" )
+    }
+    if (jarTask.outputs.files.size() != 1) {
+        throw new GradleException("Jar task output multiple files other than one single jar")
+    }
+
+    // At configuration time, the classpath of dependencies with internal_impl
+    // JAR files may not be correct.  This manifests in
+    //
+    // 'Exception in thread "main" java.lang.NoClassDefFoundError: android/support/v4/app/ActivityCompatApi23$RequestPermissionsRequestCodeValidator'
+    //
+    // when running |mach gradle clean app:generateJNI...|.  We work around this
+    // by configuring the classpath at evaluation-time, not configuration-time.
+    //
+    // The specific dependency on the `prepareDependencies` task may not be
+    // necessary, but commits like
+    // https://github.com/evant/gradle-retrolambda/commit/15108c65ee43be499a1359d9d4f88b0851d46769
+    // suggest that it is.  It certainly doesn't hurt.
+    def prepareDependenciesTask = tasks.getByName("prepare${variant.name.capitalize()}Dependencies")
+
+    def wrapperTask = task("generateJNIWrappersFor${module}${variant.name.capitalize()}", type: JavaExec) {
+        classpath "${topobjdir}/build/annotationProcessors/annotationProcessors.jar"
+
+        // 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 jarTask.outputs.files.iterator().next()
+
+        workingDir "${topobjdir}/mobile/android/base"
+
+        dependsOn jarTask
+        dependsOn prepareDependenciesTask
+    }
+
+    variant.assemble.dependsOn wrapperTask
+}