Bug 1291372 - Produce Maven repository with GeckoView Javadoc and Sources JARs. r=snorp draft
authorNick Alexander <nalexander@mozilla.com>
Mon, 07 Nov 2016 20:07:30 -0800
changeset 435689 514cd6d8cb7f7d95f878667b9bd2e356302e7275
parent 435199 b2721e0ef0ef0fc3398e02d80293321bcd3ebe8f
child 536360 29d1d949227fcd479bd0303da38b88558ecdce23
push id35097
push usernalexander@mozilla.com
push dateWed, 09 Nov 2016 00:12:53 +0000
reviewerssnorp
bugs1291372
milestone52.0a1
Bug 1291372 - Produce Maven repository with GeckoView Javadoc and Sources JARs. r=snorp We use a Maven repository and the (misleadingly named!) uploadArchives task because this is the best way to make Android Studio download and recognize the Javadoc and sources. With this, it's automatic; with a single AAR file, it's a nightmare of point-and-click configuration. This patch does a bunch of Gradle hacking to make -javadoc and -sources JARs; there's nothing special or particularly likely to break here. This patch also adds Proguard declarations to the :geckoview library project. That involves moving a good part of the Proguard configuration into mobile/android/geckoview. (I also expand upon the existing configuration.) This should be only a re-arrangement, and the resulting file is included in the original, so nothing should be changed. MozReview-Commit-ID: BGNW1v92J0k
mobile/android/config/proguard/proguard.cfg
mobile/android/geckoview/build.gradle
mobile/android/geckoview/proguard-rules.txt
mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
taskcluster/ci/build/android.yml
testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle.py
--- a/mobile/android/config/proguard/proguard.cfg
+++ b/mobile/android/config/proguard/proguard.cfg
@@ -128,43 +128,16 @@
 
 # This optimisation causes corrupt bytecode if we run more than two passes.
 # Testing shows that running the extra passes of everything else saves us
 # more than this optimisation does, so bye bye!
 -optimizations !code/allocation/variable
 
 # Keep miscellaneous targets.
 
-# Keep the annotation.
--keep @interface org.mozilla.gecko.annotation.JNITarget
-
-# Keep classes tagged with the annotation.
--keep @org.mozilla.gecko.annotation.JNITarget class *
-
-# Keep all members of an annotated class.
--keepclassmembers @org.mozilla.gecko.annotation.JNITarget class * {
-    *;
-}
-
-# Keep annotated members of any class.
--keepclassmembers class * {
-    @org.mozilla.gecko.annotation.JNITarget *;
-}
-
-# Keep classes which contain at least one annotated element. Split over two directives
-# because, according to the developer of ProGuard, "the option -keepclasseswithmembers
-# doesn't combine well with the '*' wildcard" (And, indeed, using it causes things to
-# be deleted that we want to keep.)
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.JNITarget <methods>;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.JNITarget <fields>;
-}
-
 # Keep Robocop targets. TODO: Can omit these from release builds. Also, Bug 916507.
 
 # Same formula as above...
 -keep @interface org.mozilla.gecko.annotation.RobocopTarget
 -keep @org.mozilla.gecko.annotation.RobocopTarget class *
 -keepclassmembers class * {
     @org.mozilla.gecko.annotation.RobocopTarget *;
 }
@@ -173,79 +146,18 @@
 }
 -keepclasseswithmembers class * {
     @org.mozilla.gecko.annotation.RobocopTarget <methods>;
 }
 -keepclasseswithmembers class * {
     @org.mozilla.gecko.annotation.RobocopTarget <fields>;
 }
 
-# keep Reflection targets
--keep @interface org.mozilla.gecko.annotation.ReflectionTarget
--keep @org.mozilla.gecko.annotation.ReflectionTarget class *
--keepclassmembers class * {
-    @org.mozilla.gecko.annotation.ReflectionTarget *;
-}
--keepclassmembers @org.mozilla.gecko.annotation.ReflectionTarget class * {
-    *;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.ReflectionTarget <methods>;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.ReflectionTarget <fields>;
-}
-
-# Keep WebRTC targets.
--keep @interface org.mozilla.gecko.annotation.WebRTCJNITarget
--keep @org.mozilla.gecko.annotation.WebRTCJNITarget class *
--keepclassmembers class * {
-    @org.mozilla.gecko.annotation.WebRTCJNITarget *;
-}
--keepclassmembers @org.mozilla.gecko.annotation.WebRTCJNITarget class * {
-    *;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.WebRTCJNITarget <methods>;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.WebRTCJNITarget <fields>;
-}
-
-# Keep generator-targeted entry points.
--keep @interface org.mozilla.gecko.annotation.WrapForJNI
--keep @org.mozilla.gecko.annotation.WrapForJNI class *
--keepclassmembers class * {
-    @org.mozilla.gecko.annotation.WrapForJNI *;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.WrapForJNI <methods>;
-}
--keepclasseswithmembers class * {
-    @org.mozilla.gecko.annotation.WrapForJNI <fields>;
-}
-
-# Keep all members of an annotated class.
--keepclassmembers @org.mozilla.gecko.annotation.WrapForJNI class * {
-    *;
-}
-
 -keep class **.R$*
 
-# Keep classes, and all their contents, compiled before annotation.RobocopTarget.
--keep class org.mozilla.gecko.AppConstants {
-    *;
-}
--keep class org.mozilla.gecko.AppConstants$Versions {
-    *;
-}
--keep class org.mozilla.gecko.SysInfo {
-    *;
-}
-
 # Keep all interfaces that might be dynamically required by Java Addons.
 -keep class org.mozilla.javaaddons.* {
     *;
 }
 
 -keep class org.mozilla.javaaddons.*$* {
     *;
 }
@@ -264,8 +176,10 @@
 
 -include "adjust-keeps.cfg"
 
 -include "leakcanary-keeps.cfg"
 
 -include "appcompat-v7-keeps.cfg"
 
 -include "proguard-android.cfg"
+
+-include "../../geckoview/proguard-rules.txt"
--- a/mobile/android/geckoview/build.gradle
+++ b/mobile/android/geckoview/build.gradle
@@ -1,20 +1,28 @@
 buildDir "${topobjdir}/gradle/build/mobile/android/geckoview"
 
 apply plugin: 'android-sdk-manager' // Must come before 'com.android.*'.
 apply plugin: 'com.android.library'
 
+def VERSION_NAME = '0.0.1'
+
 android {
     compileSdkVersion 23
     buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
 
     defaultConfig {
+        // TODO: version GeckoView explicitly.  We'd like to avoid
+        // mozconfig.substs.ANDROID_VERSION_CODE, which won't be intuitive to
+        // consumer (and advances very quickly on pre-release channels).
+        versionCode 1
+        versionName VERSION_NAME
         targetSdkVersion 23
         minSdkVersion 15
+        consumerProguardFiles 'proguard-rules.txt' 
     }
 
     buildTypes {
         withGeckoBinaries {
             initWith release
         }
         withoutGeckoBinaries { // For clarity and consistency throughout the tree.
             initWith release
@@ -78,9 +86,91 @@ android.libraryVariants.all { variant ->
 
     // It would be most natural for :geckoview to always include the Gecko
     // binaries, but that's difficult; see the notes in
     // mobile/android/gradle/with_gecko_binaries.gradle.  Instead :app uses
     // :geckoview:release and handles it's own Gecko binary inclusion.
     if (buildType.equals('withGeckoBinaries')) {
         configureVariantWithGeckoBinaries(variant)
     }
+
+    // Javadoc and Sources JAR configuration cribbed from
+    // https://github.com/mapbox/mapbox-gl-native/blob/d169ea55c1cfa85cd8bf19f94c5f023569f71810/platform/android/MapboxGLAndroidSDK/build.gradle#L85
+    // informed by
+    // https://code.tutsplus.com/tutorials/creating-and-publishing-an-android-library--cms-24582,
+    // and amended from numerous Stackoverflow posts.
+    def name = variant.name
+    def javadoc = task "javadoc${name.capitalize()}"(type: Javadoc) {
+        description = "Generate Javadoc for build variant $name"
+        failOnError = false
+        destinationDir = new File(destinationDir, variant.baseName)
+        source = files(variant.javaCompile.source)
+        classpath = files(variant.javaCompile.classpath.files) + files(android.bootClasspath)
+        options.windowTitle("Mozilla GeckoView Android API $VERSION_NAME Reference")
+        options.docTitle("Mozilla GeckoView Android API $VERSION_NAME")
+        options.header("Mozilla GeckoView Android API $VERSION_NAME Reference")
+        options.bottom("&copy; 2016 Mozilla. All rights reserved.")
+        options.links("http://docs.oracle.com/javase/7/docs/api/")
+        options.linksOffline("http://d.android.com/reference/", "$System.env.ANDROID_HOME/docs/reference")
+        // TODO: options.overview("src/main/java/overview.html")
+        options.group("Mozilla GeckoView", "org.mozilla.gecko*") // TODO: narrow this down.
+        exclude '**/R.java', '**/BuildConfig.java', 'com/googlecode/**'
+    }
+
+    task "javadocJar${name.capitalize()}"(type: Jar, dependsOn: javadoc) {
+        classifier = 'javadoc'
+        from javadoc.destinationDir
+    }
+
+    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)
+    }
 }
+
+apply plugin: 'maven'
+ 
+uploadArchives {
+    repositories.mavenDeployer {
+        pom.groupId = 'org.mozilla'
+        pom.artifactId = 'geckoview'
+        pom.version = VERSION_NAME
+        pom.project {
+            licenses {
+                license {
+                    name 'The Mozilla Public License, v. 2.0'
+                    url 'http://mozilla.org/MPL/2.0/'
+                    distribution 'repo'
+                }
+            }
+        }
+        repository(url: "file://${project.buildDir}/maven")
+    }
+}
+
+// This is all related to the withGeckoBinaries approach; see
+// mobile/android/gradle/with_gecko_binaries.gradle.
+afterEvaluate {
+    // The bundle tasks are only present when the particular configuration is
+    // being built, so this task might not exist.  (This is due to the way the
+    // Android Gradle plugin defines things during configuration.)
+    def bundleWithGeckoBinaries = tasks.findByName('bundleWithGeckoBinaries')
+    if (!bundleWithGeckoBinaries) {
+        return
+    }
+
+    // Remove default configuration, which is the release configuration, when
+    // we're actually building withGeckoBinaries.  This makes `gradle install`
+    // install the withGeckoBinaries artifacts, not the release artifacts (which
+    // are withoutGeckoBinaries and not suitable for distribution.)
+    def Configuration archivesConfig = project.getConfigurations().getByName('archives')
+    archivesConfig.artifacts.removeAll { it.extension.equals('aar') }
+
+    artifacts {
+        // Instead of default (release) configuration, publish one with Gecko binaries.
+        archives bundleWithGeckoBinaries
+        // Javadoc and sources for developer ergononomics.
+        archives javadocJarWithGeckoBinaries
+        archives sourcesJarWithGeckoBinaries
+    }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/geckoview/proguard-rules.txt
@@ -0,0 +1,175 @@
+# Modified from https://robotsandpencils.com/blog/use-proguard-android-library/.
+
+# Preserve all annotations.
+
+-keepattributes *Annotation*
+
+# Preserve all public classes, and their public and protected fields and
+# methods.
+
+-keep public class * {
+    public protected *;
+}
+
+# Preserve all .class method names.
+
+-keepclassmembernames class * {
+    java.lang.Class class$(java.lang.String);
+    java.lang.Class class$(java.lang.String, boolean);
+}
+
+# Preserve all native method names and the names of their classes.
+
+-keepclasseswithmembernames class * {
+    native <methods>;
+}
+
+# Preserve the special static methods that are required in all enumeration
+# classes.
+
+-keepclassmembers class * extends java.lang.Enum {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+# Explicitly preserve all serialization members. The Serializable interface
+# is only a marker interface, so it wouldn't save them.
+# You can comment this out if your library doesn't use serialization.
+# If your code contains serializable classes that have to be backward
+# compatible, please refer to the manual.
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    static final java.io.ObjectStreamField[] serialPersistentFields;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+# Preserve all View implementations and their special context constructors.
+
+-keep public class * extends android.view.View {
+    public <init>(android.content.Context);
+    public <init>(android.content.Context, android.util.AttributeSet);
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+    public void set*(...);
+}
+
+# Keep setters in Views so that animations can still work.
+# See http://proguard.sourceforge.net/manual/examples.html#beans
+# From tools/proguard/proguard-android.txt.
+-keepclassmembers public class * extends android.view.View {
+   void set*(***);
+   *** get*();
+}
+
+# Preserve all classes that have special context constructors, and the
+# constructors themselves.
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+# Preserve the special fields of all Parcelable implementations.
+
+-keepclassmembers class * implements android.os.Parcelable {
+    static android.os.Parcelable$Creator CREATOR;
+}
+
+# Preserve static fields of inner classes of R classes that might be accessed
+# through introspection.
+
+-keepclassmembers class **.R$* {
+  public static <fields>;
+}
+
+# GeckoView specific rules.
+
+# Keep classes, and all their contents, compiled before annotation.*.
+-keep class org.mozilla.gecko.AppConstants {
+    *;
+}
+-keep class org.mozilla.gecko.AppConstants$Versions {
+    *;
+}
+-keep class org.mozilla.gecko.SysInfo {
+    *;
+}
+
+# Keep the annotation.
+-keep @interface org.mozilla.gecko.annotation.JNITarget
+
+# Keep classes tagged with the annotation.
+-keep @org.mozilla.gecko.annotation.JNITarget class *
+
+# Keep all members of an annotated class.
+-keepclassmembers @org.mozilla.gecko.annotation.JNITarget class * {
+    *;
+}
+
+# Keep annotated members of any class.
+-keepclassmembers class * {
+    @org.mozilla.gecko.annotation.JNITarget *;
+}
+
+# Keep classes which contain at least one annotated element. Split over two directives
+# because, according to the developer of ProGuard, "the option -keepclasseswithmembers
+# doesn't combine well with the '*' wildcard" (And, indeed, using it causes things to
+# be deleted that we want to keep.)
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.JNITarget <methods>;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.JNITarget <fields>;
+}
+
+# Keep WebRTC targets.
+-keep @interface org.mozilla.gecko.annotation.WebRTCJNITarget
+-keep @org.mozilla.gecko.annotation.WebRTCJNITarget class *
+-keepclassmembers class * {
+    @org.mozilla.gecko.annotation.WebRTCJNITarget *;
+}
+-keepclassmembers @org.mozilla.gecko.annotation.WebRTCJNITarget class * {
+    *;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.WebRTCJNITarget <methods>;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.WebRTCJNITarget <fields>;
+}
+
+# Keep generator-targeted entry points.
+-keep @interface org.mozilla.gecko.annotation.WrapForJNI
+-keep @org.mozilla.gecko.annotation.WrapForJNI class *
+-keepclassmembers class * {
+    @org.mozilla.gecko.annotation.WrapForJNI *;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.WrapForJNI <methods>;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.WrapForJNI <fields>;
+}
+
+# Keep all members of an annotated class.
+-keepclassmembers @org.mozilla.gecko.annotation.WrapForJNI class * {
+    *;
+}
+
+# Keep Reflection targets.
+-keep @interface org.mozilla.gecko.annotation.ReflectionTarget
+-keep @org.mozilla.gecko.annotation.ReflectionTarget class *
+-keepclassmembers class * {
+    @org.mozilla.gecko.annotation.ReflectionTarget *;
+}
+-keepclassmembers @org.mozilla.gecko.annotation.ReflectionTarget class * {
+    *;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.ReflectionTarget <methods>;
+}
+-keepclasseswithmembers class * {
+    @org.mozilla.gecko.annotation.ReflectionTarget <fields>;
+}
--- a/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
+++ b/mobile/android/geckoview_example/src/main/java/org/mozilla/geckoview_example/GeckoViewActivity.java
@@ -7,29 +7,35 @@ package org.mozilla.geckoview_example;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
 import android.util.Log;
 import android.widget.Toast;
 
+import org.mozilla.gecko.BaseGeckoInterface;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.GeckoThread;
 import org.mozilla.gecko.GeckoView;
 import org.mozilla.gecko.PrefsHelper;
 
+import static org.mozilla.gecko.GeckoView.setGeckoInterface;
+
 public class GeckoViewActivity extends Activity {
     private static final String LOGTAG = "GeckoViewActivity";
 
     GeckoView mGeckoView;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        setGeckoInterface(new BaseGeckoInterface(getApplicationContext()));
+
         setContentView(R.layout.geckoview_activity);
 
         mGeckoView = (GeckoView) findViewById(R.id.gecko_view);
         mGeckoView.setChromeDelegate(new MyGeckoViewChrome());
         mGeckoView.setContentDelegate(new MyGeckoViewContent());
     }
 
     @Override
--- a/taskcluster/ci/build/android.yml
+++ b/taskcluster/ci/build/android.yml
@@ -111,19 +111,19 @@ android-api-15-gradle/opt:
     worker-type: aws-provisioner-v1/gecko-{level}-b-android
     worker:
         implementation: docker-worker
         max-run-time: 7200
         env:
             # Bug 1292762 - Set GRADLE_USER_HOME to avoid sdk-manager-plugin intermittent
             GRADLE_USER_HOME: /home/worker/workspace/build/src/dotgradle
         artifacts:
-          - name: public/android/geckoview.aar
-            path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/outputs/aar/geckoview-withGeckoBinaries.aar
-            type: file
+          - name: public/android/maven
+            path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview/maven/
+            type: directory
           - name: public/android/geckoview_example.apk
             path: /home/worker/workspace/build/src/obj-firefox/gradle/build/mobile/android/geckoview_example/outputs/apk/geckoview_example-withGeckoBinaries.apk
             type: file
           - name: public/build
             path: /home/worker/artifacts/
             type: directory
     run:
         using: mozharness
--- a/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle.py
+++ b/testing/mozharness/configs/builds/releng_sub_android_configs/64_api_15_gradle.py
@@ -7,11 +7,12 @@ config = {
     'multi_locale_config_platform': 'android',
     # It's not obvious, but postflight_build is after packaging, so the Gecko
     # binaries are in the object directory, ready to be packaged into the
     # GeckoView AAR.
     'postflight_build_mach_commands': [
         ['gradle',
          'geckoview:assembleWithGeckoBinaries',
          'geckoview_example:assembleWithGeckoBinaries',
+         'uploadArchives',
         ],
     ],
 }