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 }