summaryrefslogtreecommitdiffstats
path: root/substitute-local-geckoview.gradle
diff options
context:
space:
mode:
Diffstat (limited to 'substitute-local-geckoview.gradle')
-rw-r--r--substitute-local-geckoview.gradle192
1 files changed, 192 insertions, 0 deletions
diff --git a/substitute-local-geckoview.gradle b/substitute-local-geckoview.gradle
new file mode 100644
index 0000000000..a32dc5ae8a
--- /dev/null
+++ b/substitute-local-geckoview.gradle
@@ -0,0 +1,192 @@
+/* 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/. */
+
+// 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}")
+}
+
+def warn(message) {
+ logger.warn("[substitute-local-geckoview] Warning: ${message}")
+}
+
+if (!project.ext.has('topsrcdir')) {
+ throw new GradleException("ext.topsrcdir must be specified to substitute for a local GeckoView")
+}
+
+/**
+ * Loads the mozconfig and returns any variables derived from it, avoiding side effects.
+ *
+ * This method is relatively slow because it calls mach, which starts a python interpreter, will
+ * becomes very slow if called for numerous subprojects. Therefore, it should only be called once
+ * per build.
+ */
+def loadMozconfig() {
+ apply from: "${topsrcdir}/mobile/android/gradle/mach_env.gradle"
+
+ // Cribbed from https://hg.mozilla.org/mozilla-central/file/tip/settings.gradle. 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(
+ machEnv(topsrcdir),
+ new File(ext.has('topobjdir') ? ext.get('topobjdir') : topsrcdir))
+ def standardOutput = new ByteArrayOutputStream()
+ def standardError = new ByteArrayOutputStream()
+ proc.consumeProcessOutput(standardOutput, standardError)
+ 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"
+ + "stdout:\n${standardOutput.toString()}\n\n"
+ + "stderr:\n${standardError.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}')")
+ }
+
+ def topobjdir
+ if (ext.has('topobjdir')) {
+ topobjdir = ext.topobjdir
+ } else {
+ 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 GeckoView, 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}")
+ }
+
+ return [mozconfig, topobjdir]
+}
+
+// This script is expected to be called for every subproject in the build (in ac, this is over 100)
+// but loadMozconfig should only be called once per build (see the javadoc) so we store the output
+// of that call as a global variable and re-use it when this script is called again.
+def LOAD_MOZCONFIG_CACHE = "substitute-local-geckoview-mozconfig-cache"
+if (!rootProject.ext.has(LOAD_MOZCONFIG_CACHE)) {
+ rootProject.ext.set(LOAD_MOZCONFIG_CACHE, loadMozconfig())
+}
+def (mozconfig, topobjdir) = rootProject.ext.get(LOAD_MOZCONFIG_CACHE)
+
+repositories {
+ maven {
+ name "Local GeckoView Maven repository"
+ url "${topobjdir}/gradle/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 = [
+ 'geckoview-nightly',
+ 'geckoview-nightly-armeabi-v7a',
+ 'geckoview-nightly-arm64-v8a',
+ 'geckoview-nightly-x86',
+ 'geckoview-nightly-x86_64',
+ 'geckoview-beta',
+ 'geckoview-beta-armeabi-v7a',
+ 'geckoview-beta-arm64-v8a',
+ 'geckoview-beta-x86',
+ 'geckoview-beta-x86_64',
+ ]
+
+ def geckoviewOmniModules = [
+ 'geckoview-nightly-omni',
+ 'geckoview-nightly-omni-armeabi-v7a',
+ 'geckoview-nightly-omni-arm64-v8a',
+ 'geckoview-nightly-omni-x86',
+ 'geckoview-nightly-omni-x86_64',
+ 'geckoview-beta-omni',
+ 'geckoview-beta-omni-armeabi-v7a',
+ 'geckoview-beta-omni-arm64-v8a',
+ 'geckoview-beta-omni-x86',
+ 'geckoview-beta-omni-x86_64',
+ ]
+
+ 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.
+ return
+ }
+
+ def group = dependency.requested.group
+ def module = dependency.requested.module
+ if (group == 'org.mozilla.geckoview'
+ && (geckoviewModules.contains(module) || geckoviewOmniModules.contains(module))) {
+ def name = ''
+ def isLite = mozconfig.substs.MOZ_ANDROID_GECKOVIEW_LITE
+
+ if (isLite) {
+ name = 'geckoview-default'
+ } else {
+ name = 'geckoview-default-omni'
+ }
+
+ if (geckoviewModules.contains(module) && !isLite) {
+ warn("Substituting a geckoview omni build into a lite dependency. Add ac_add_options --enable-geckoview-lite to ${mozconfig.mozconfig.path} to fix this.")
+ } else if (geckoviewOmniModules.contains(module) && isLite) {
+ // Substituting lite into omni is unlikely to work at
+ // all so we just error out here.
+ 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.")
+ }
+
+ 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'
+ strategy.cacheChangingModulesFor 0, 'seconds'
+ }
+ }
+ }
+ }
+ }
+}