tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

substitute-local-geckoview.gradle (8528B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 // Substitute a local GeckoView AAR into a consuming Gradle project.
      6 //
      7 // To use this, in a consuming Gradle project's `/build.gradle` add a stanza like:
      8 //
      9 // ext.topsrcdir = '/absolute/path/to/mozilla-central'; apply from: "${ext.topsrcdir}/substitute-local-geckoview.gradle"
     10 //
     11 // The object directory will be determined using `mach environment` and will agree with `./mach
     12 // gradle` and Android Studio.  Or, specify the exact object directory with a stanza like:
     13 //
     14 // ext.topsrcdir = '/absolute/path/to/mozilla-central'
     15 // ext.topobjdir = '/absolute/path/to/objdir'
     16 // apply from: "${ext.topsrcdir}/substitute-local-geckoview.gradle"
     17 //
     18 // Substitution works with artifact and non-artifact builds.
     19 //
     20 // If you get errors about .jar files not being found, ensure that the consuming
     21 // application is using a recent Android-Gradle plugin (say, 3.4+).  There were
     22 // issues with Jetifier, and issues with .jar vs. .aar extensions, in older
     23 // versions.
     24 
     25 import groovy.json.JsonSlurper
     26 
     27 def log(message) {
     28    logger.lifecycle("[substitute-local-geckoview] ${message}")
     29 }
     30 
     31 def warn(message) {
     32    logger.warn("[substitute-local-geckoview] Warning: ${message}")
     33 }
     34 
     35 if (!project.ext.has('topsrcdir')) {
     36    throw new GradleException("ext.topsrcdir must be specified to substitute for a local GeckoView")
     37 }
     38 
     39 /**
     40 * Loads the mozconfig and returns any variables derived from it, avoiding side effects.
     41 *
     42 * This method is relatively slow because it calls mach, which starts a python interpreter, will
     43 * becomes very slow if called for numerous subprojects. Therefore, it should only be called once
     44 * per build.
     45 */
     46 def loadMozconfig() {
     47    apply from: "${topsrcdir}/mobile/android/gradle/mach_env.gradle"
     48 
     49    // Cribbed from https://hg.mozilla.org/mozilla-central/file/tip/settings.gradle.  When run in
     50    // topobjdir, `mach environment` correctly finds the mozconfig corresponding to that object
     51    // directory.
     52    def command = ["${topsrcdir}/mach", "environment", "--format", "json", "--verbose"]
     53    def proc = providers.exec {
     54        workingDir = new File(ext.has('topobjdir') ? ext.get('topobjdir') : topsrcdir)
     55        environment = machEnv(topsrcdir)
     56        commandLine = command
     57        ignoreExitValue = true
     58    }
     59    def result = proc.result.get().exitValue
     60    def standardOutput = proc.standardOutput.asText.get()
     61    def standardError = proc.standardError.asText.get()
     62 
     63    // Only show the output if something went wrong.
     64    if (result != 0) {
     65        throw new GradleException("Process '${command}' finished with non-zero exit value ${result}:\n\n"
     66                + "stdout:\n${standardOutput}\n\n"
     67                + "stderr:\n${standardError}")
     68    }
     69 
     70    def slurper = new JsonSlurper()
     71    def mozconfig = slurper.parseText(standardOutput)
     72 
     73    if (topsrcdir != mozconfig.topsrcdir) {
     74        throw new GradleException("Specified topsrcdir ('${topsrcdir}') is not mozconfig topsrcdir ('${mozconfig.topsrcdir}')")
     75    }
     76 
     77    def topobjdir
     78    if (ext.has('topobjdir')) {
     79        topobjdir = ext.topobjdir
     80    } else {
     81        topobjdir = mozconfig.topobjdir
     82        log("Found topobjdir ${topobjdir} from topsrcdir ${topsrcdir}")
     83    }
     84 
     85    if (mozconfig.substs.MOZ_BUILD_APP != 'mobile/android') {
     86        throw new GradleException("Building with Gradle is only supported for GeckoView, i.e., MOZ_BUILD_APP == 'mobile/android'.")
     87    }
     88 
     89    log("Will substitute GeckoView (geckoview-{nightly,beta}) with local GeckoView (geckoview-default) from ${topobjdir}/gradle/build/mobile/android/geckoview/maven")
     90 
     91    if (!mozconfig.substs.COMPILE_ENVIRONMENT) {
     92        log("To update the local GeckoView, run `./mach gradle geckoview:publishWithGeckoBinariesDebugPublicationToMavenRepository` in ${topsrcdir}")
     93    } else {
     94        log("To update the local GeckoView, run `./mach build binaries && ./mach gradle geckoview:publishWithGeckoBinariesDebugPublicationToMavenRepository` in ${topsrcdir}")
     95    }
     96 
     97    return [mozconfig, topobjdir]
     98 }
     99 
    100 // This script is expected to be called for every subproject in the build (in ac, this is over 100)
    101 // but loadMozconfig should only be called once per build (see the javadoc) so we store the output
    102 // of that call as a global variable and re-use it when this script is called again.
    103 def LOAD_MOZCONFIG_CACHE = "substitute-local-geckoview-mozconfig-cache"
    104 if (!rootProject.ext.has(LOAD_MOZCONFIG_CACHE)) {
    105    rootProject.ext.set(LOAD_MOZCONFIG_CACHE, loadMozconfig())
    106 }
    107 def (mozconfig, topobjdir) = rootProject.ext.get(LOAD_MOZCONFIG_CACHE)
    108 
    109 repositories {
    110    maven {
    111        name "Local GeckoView Maven repository"
    112        url "${topobjdir}/gradle/maven"
    113    }
    114 }
    115 
    116 configurations.all { config ->
    117    // Like `geckoview-nightly` for a multi-architecture fat AAR or
    118    // `geckoview-nightly-armeabi-v7a` for an architecture-specific AAR.
    119    def geckoviewModules = [
    120        'geckoview-nightly',
    121        'geckoview-nightly-armeabi-v7a',
    122        'geckoview-nightly-arm64-v8a',
    123        'geckoview-nightly-x86',
    124        'geckoview-nightly-x86_64',
    125        'geckoview-beta',
    126        'geckoview-beta-armeabi-v7a',
    127        'geckoview-beta-arm64-v8a',
    128        'geckoview-beta-x86',
    129        'geckoview-beta-x86_64',
    130    ]
    131 
    132    def geckoviewOmniModules = [
    133        'geckoview-nightly-omni',
    134        'geckoview-nightly-omni-armeabi-v7a',
    135        'geckoview-nightly-omni-arm64-v8a',
    136        'geckoview-nightly-omni-x86',
    137        'geckoview-nightly-omni-x86_64',
    138        'geckoview-beta-omni',
    139        'geckoview-beta-omni-armeabi-v7a',
    140        'geckoview-beta-omni-arm64-v8a',
    141        'geckoview-beta-omni-x86',
    142        'geckoview-beta-omni-x86_64',
    143    ]
    144 
    145    if (config.isCanBeResolved()) {
    146        config.resolutionStrategy { strategy ->
    147            dependencySubstitution {
    148                all { dependency ->
    149                    // We could restrict based on target architecture, but there doesn't seem to
    150                    // be much advantage to doing so right now.
    151 
    152                    if (!(dependency.requested instanceof ModuleComponentSelector)) {
    153                        // We can only substitute for a module: we're never going to substitute
    154                        // for a project.
    155                        return
    156                    }
    157 
    158                    def group = dependency.requested.group
    159                    def module = dependency.requested.module
    160                    if (group == 'org.mozilla.geckoview'
    161                          && (geckoviewModules.contains(module) || geckoviewOmniModules.contains(module))) {
    162                        def name = ''
    163                        def isLite = mozconfig.substs.MOZ_ANDROID_GECKOVIEW_LITE
    164 
    165                        if (isLite) {
    166                          name = 'geckoview-default'
    167                        } else {
    168                          name = 'geckoview-default-omni'
    169                        }
    170 
    171                        if (geckoviewModules.contains(module) && !isLite) {
    172                          warn("Substituting a geckoview omni build into a lite dependency. Add ac_add_options --enable-geckoview-lite to ${mozconfig.mozconfig.path} to fix this.")
    173                        } else if (geckoviewOmniModules.contains(module) && isLite) {
    174                          // Substituting lite into omni is unlikely to work at
    175                          // all so we just error out here.
    176                          throw new GradleException("Substituting a geckoview lite build into an omni dependency. Remove ac_add_options --enable-geckoview-lite in ${mozconfig.mozconfig.path} to fix this.")
    177                        }
    178 
    179                        log("Substituting ${group}:${dependency.requested.module} with local GeckoView ${group}:${name} in ${config}")
    180 
    181                        dependency.useTarget([group: group, name: name, version: '+'])
    182 
    183                        // We substitute with a dynamic version ('+').  It seems that Gradle
    184                        // discovers the underlying AAR is out of date correctly based on file
    185                        // timestamp already, but let's try to avoid some class of cache
    186                        // invalidation error while we're here.
    187                        strategy.cacheDynamicVersionsFor 0, 'seconds'
    188                        strategy.cacheChangingModulesFor 0, 'seconds'
    189                    }
    190                }
    191            }
    192        }
    193    }
    194 }