author Agi Sferro <>
Wed, 13 Nov 2019 20:29:15 +0000
changeset 501843 bf09025d6f98793965458b5805a8f53564ce540a
parent 488354 9417714d5e347ebef4150d28c18a6cd4e1d2a96c
permissions -rw-r--r--
Bug 1530402 - Implement {Browser,Page}Action for GeckoView. r=snorp,mixedpuppy,esawin Design doc: Differential Revision:

/* 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 */

// Substitute a local GeckoView AAR into a consuming Gradle project.
// To use this, in a consuming Gradle project's `/build.gradle` add a stanza like:
// ext.topsrcdir = '/absolute/path/to/mozilla-central'; apply from: "${ext.topsrcdir}/substitute-local-geckoview.gradle"
// The object directory will be determined using `mach environment` and will agree with `./mach
// gradle` and Android Studio.  Or, specify the exact object directory with a stanza like:
// ext.topsrcdir = '/absolute/path/to/mozilla-central'
// ext.topobjdir = '/absolute/path/to/objdir'
// apply from: "${ext.topsrcdir}/substitute-local-geckoview.gradle"
// Substitution works with artifact and non-artifact builds.
// If you get errors about .jar files not being found, ensure that the consuming
// application is using a recent Android-Gradle plugin (say, 3.4+).  There were
// issues with Jetifier, and issues with .jar vs. .aar extensions, in older
// versions.

import groovy.json.JsonSlurper

def log(message) {
    logger.lifecycle("[substitute-local-geckoview] ${message}")

if (!project.ext.has('topsrcdir')) {
    throw new GradleException("ext.topsrcdir must be specified to substitute for a local GeckoView")

// Cribbed from  When run in
// topobjdir, `mach environment` correctly finds the mozconfig corresponding to that object
// directory.
def commandLine = ["${topsrcdir}/mach", "environment", "--format", "json", "--verbose"]
def proc = commandLine.execute(null, new File(ext.has('topobjdir') ? ext.get('topobjdir') : topsrcdir))
def standardOutput = new ByteArrayOutputStream()
proc.consumeProcessOutput(standardOutput, standardOutput)

// 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()}")

def slurper = new JsonSlurper()
def mozconfig = slurper.parseText(standardOutput.toString())

if (topsrcdir != mozconfig.topsrcdir) {
    throw new GradleException("Specified topsrcdir ('${topsrcdir}') is not mozconfig topsrcdir ('${mozconfig.topsrcdir}')")

if (!ext.has('topobjdir')) {
    ext.topobjdir = mozconfig.topobjdir
    log("Found topobjdir ${topobjdir} from topsrcdir ${topsrcdir}")

if (mozconfig.substs.MOZ_BUILD_APP != 'mobile/android') {
    throw new GradleException("Building with Gradle is only supported for Fennec, i.e., MOZ_BUILD_APP == 'mobile/android'.")

log("Will substitute GeckoView (geckoview-{nightly,beta}) with local GeckoView (geckoview-default) from ${topobjdir}/gradle/build/mobile/android/geckoview/maven")

if (!mozconfig.substs.COMPILE_ENVIRONMENT) {
    log("To update the local GeckoView, run `./mach gradle geckoview:publishWithGeckoBinariesDebugPublicationToMavenRepository` in ${topsrcdir}")
} else {
    log("To update the local GeckoView, run `./mach build binaries && ./mach gradle geckoview:publishWithGeckoBinariesDebugPublicationToMavenRepository` in ${topsrcdir}")

repositories {
    maven {
        name "Local GeckoView Maven repository"
        url "${topobjdir}/gradle/build/mobile/android/geckoview/maven"

configurations.all { config ->
    // Like `geckoview-nightly` for a multi-architecture fat AAR or
    // `geckoview-nightly-armeabi-v7a` for an architecture-specific AAR.
    def geckoviewModules = [

    if (config.isCanBeResolved()) {
        config.resolutionStrategy { strategy ->
            dependencySubstitution {
                all { dependency ->
                    // We could restrict based on target architecture, but there doesn't seem to
                    // be much advantage to doing so right now.

                    if (!(dependency.requested instanceof ModuleComponentSelector)) {
                        // We can only substitute for a module: we're never going to substitute
                        // for a project.

                    def group =
                    if (group == 'org.mozilla.geckoview' && geckoviewModules.contains(dependency.requested.module)) {
                        def name = 'geckoview-default'
                        log("Substituting ${group}:${dependency.requested.module} with local GeckoView ${group}:${name} in ${config}")

                        dependency.useTarget([group: group, name: name, version: '+'])

                        // We substitute with a dynamic version ('+').  It seems that Gradle
                        // discovers the underlying AAR is out of date correctly based on file
                        // timestamp already, but let's try to avoid some class of cache
                        // invalidation error while we're here.
                        strategy.cacheDynamicVersionsFor 0, 'seconds'