2 import org.tomlj.TomlParseResult
3 import org.tomlj.TomlTable
5 def tryInt = { string ->
9 if (string.isInteger()) {
10 return string as Integer
15 // Parses the Cargo.lock and returns the version for the given package name.
16 def getRustVersionFor(packageName) {
17 String version = null;
18 TomlParseResult result = Toml.parse(file("Cargo.lock").getText());
19 for (object in result.getArray("package").toList()) {
20 def table = (TomlTable) object
21 if (table.getString("name") == packageName) {
22 if (version != null) {
23 throw new StopExecutionException("Multiple versions for '${packageName}' found." +
24 " Ensure '${packageName}' is only included once.")
26 version = table.getString("version")
33 // Expose the per-object-directory configuration to all projects.
35 mozconfig = gradle.mozconfig
36 topsrcdir = gradle.mozconfig.topsrcdir
37 topobjdir = gradle.mozconfig.topobjdir
39 gleanVersion = "43.0.2"
40 if (gleanVersion != getRustVersionFor("glean")) {
41 throw new StopExecutionException("Mismatched Glean version, expected: ${gleanVersion}," +
42 " found ${getRustVersionFor("glean")}")
45 artifactSuffix = getArtifactSuffix()
46 versionName = getVersionName()
47 versionCode = computeVersionCode()
48 versionNumber = getVersionNumber()
49 buildId = getBuildId()
51 buildToolsVersion = mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
52 compileSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
53 targetSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
54 minSdkVersion = tryInt(mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION)
55 manifestPlaceholders = [
56 ANDROID_PACKAGE_NAME: mozconfig.substs.ANDROID_PACKAGE_NAME,
57 ANDROID_TARGET_SDK: mozconfig.substs.ANDROID_TARGET_SDK,
58 MOZ_ANDROID_MIN_SDK_VERSION: mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION,
63 gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
66 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
67 allowInsecureProtocol = true
73 task downloadDependencies() {
74 description 'Download all dependencies to the Gradle cache'
76 configurations.each { configuration ->
77 if (configuration.canBeResolved) {
78 configuration.allDependencies.each { dependency ->
80 configuration.files(dependency)
82 println("Could not resolve ${configuration.name} -> ${dependency.name}")
83 println(" > ${e.message}")
85 println(" >> ${e.cause}")
87 println(" >> ${e.cause.cause}")
99 buildDir "${topobjdir}/gradle/build"
103 gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
106 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
107 allowInsecureProtocol = true
113 ext.kotlin_version = '1.5.31'
116 classpath 'org.mozilla.apilint:apilint:0.5.1'
117 classpath 'com.android.tools.build:gradle:7.0.3'
118 classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
119 classpath 'org.apache.commons:commons-exec:1.3'
120 classpath 'com.diffplug.spotless:spotless-plugin-gradle:5.16.0'
121 classpath 'org.tomlj:tomlj:1.0.0'
122 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
126 // A stream that processes bytes line by line, prepending a tag before sending
127 // each line to Gradle's logging.
128 class TaggedLogOutputStream extends org.apache.commons.exec.LogOutputStream {
132 TaggedLogOutputStream(tag, logger) {
137 void processLine(String line, int level) {
138 logger.lifecycle("${this.tag} ${line}")
142 ext.geckoBinariesOnlyIf = { task ->
143 // Never when Gradle was invoked within `mach build`.
144 if ('1' == System.env.GRADLE_INVOKED_WITHIN_MACH_BUILD) {
145 rootProject.logger.lifecycle("Skipping task ${task.path} because: within `mach build`")
149 // Never for official builds.
150 if (mozconfig.substs.MOZILLA_OFFICIAL) {
151 rootProject.logger.lifecycle("Skipping task ${task.path} because: MOZILLA_OFFICIAL")
155 // Multi-l10n builds set `AB_CD=multi`, which isn't a valid locale. To
156 // avoid failures, if Gradle is invoked with AB_CD=multi, we don't invoke
158 if ('multi' == System.env.AB_CD) {
159 rootProject.logger.lifecycle("Skipping task ${task.path} because: AB_CD=multi")
163 // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and handle resource
164 // and code generation themselves.
165 if ('1' == System.env.IS_LANGUAGE_REPACK) {
166 rootProject.logger.lifecycle("Skipping task ${task.path} because: IS_LANGUAGE_REPACK")
170 rootProject.logger.lifecycle("Executing task ${task.path}")
174 // Non-official versions are like "61.0a1", where "a1" is the milestone.
175 // This simply strips that off, leaving "61.0" in this example.
176 def getAppVersionWithoutMilestone() {
177 return project.ext.mozconfig.substs.MOZ_APP_VERSION.replaceFirst(/a[0-9]/, "")
180 // This converts MOZ_APP_VERSION into an integer
183 // We take something like 58.1.2a1 and come out with 5800102
184 // This gives us 3 digits for the major number, and 2 digits
185 // each for the minor and build number. Beta and Release
187 // This must be synchronized with _compute_gecko_version(...) in /taskcluster/gecko_taskgraph/transforms/task.py
188 def computeVersionCode() {
189 String appVersion = getAppVersionWithoutMilestone()
191 // Split on the dot delimiter, e.g. 58.1.1a1 -> ["58, "1", "1a1"]
192 String[] parts = appVersion.split('\\.')
194 assert parts.size() == 2 || parts.size() == 3
197 int code = Integer.parseInt(parts[0]) * 100000
200 code += Integer.parseInt(parts[1]) * 100
203 if (parts.size() == 3) {
204 code += Integer.parseInt(parts[2])
210 def getVersionName() {
211 return "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
214 // Mimic Python: open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]
216 return file("${topobjdir}/buildid.h").getText('utf-8').split()[2]
219 def getVersionNumber() {
220 def appVersion = getAppVersionWithoutMilestone()
221 def parts = appVersion.split('\\.')
222 def version = parts[0] + "." + parts[1] + "." + getBuildId()
223 def substs = project.ext.mozconfig.substs
224 if (!substs.MOZILLA_OFFICIAL && !substs.MOZ_ANDROID_FAT_AAR_ARCHITECTURES) {
225 // Use -SNAPSHOT versions locally to enable the local GeckoView substitution flow.
226 version += "-SNAPSHOT"
231 def getArtifactSuffix() {
232 def substs = project.ext.mozconfig.substs
235 // Release artifacts don't specify the channel, for the sake of simplicity.
236 if (substs.MOZ_UPDATE_CHANNEL != 'release') {
237 suffix += "-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
243 class MachExec extends Exec {
245 // Bug 1543982: When invoking `mach build` recursively, the outer `mach
246 // build` itself modifies the environment, causing configure to run
247 // again. This tries to restore the environment that the outer `mach
248 // build` was invoked in. See the comment in
249 // $topsrcdir/settings.gradle.
250 project.ext.mozconfig.mozconfig.env.unmodified.each { k, v -> environment.remove(k) }
251 environment project.ext.mozconfig.orig_mozconfig.env.unmodified
252 environment 'MOZCONFIG', project.ext.mozconfig.substs.MOZCONFIG
256 task machBuildFaster(type: MachExec) {
257 onlyIf rootProject.ext.geckoBinariesOnlyIf
259 workingDir "${topsrcdir}"
261 commandLine mozconfig.substs.PYTHON3
262 args "${topsrcdir}/mach"
266 // Add `-v` if we're running under `--info` (or `--debug`).
267 if (project.logger.isEnabled(LogLevel.INFO)) {
271 // `path` is like `:machBuildFaster`.
272 standardOutput = new TaggedLogOutputStream("${path}>", logger)
273 errorOutput = standardOutput
276 task machStagePackage(type: MachExec) {
277 onlyIf rootProject.ext.geckoBinariesOnlyIf
279 dependsOn rootProject.machBuildFaster
281 workingDir "${topobjdir}"
283 // We'd prefer this to be a `mach` invocation, but `mach build
284 // mobile/android/installer/stage-package` doesn't work as expected.
285 commandLine mozconfig.substs.GMAKE
287 args "${topobjdir}/mobile/android/installer"
290 outputs.file "${topobjdir}/dist/geckoview/assets/omni.ja"
292 outputs.file "${topobjdir}/dist/geckoview/assets/${mozconfig.substs.ANDROID_CPU_ARCH}/libxul.so"
293 outputs.file "${topobjdir}/dist/geckoview/lib/${mozconfig.substs.ANDROID_CPU_ARCH}/libmozglue.so"
295 // Force running `stage-package`.
296 outputs.upToDateWhen { false }
298 // `path` is like `:machStagePackage`.
299 standardOutput = new TaggedLogOutputStream("${path}>", logger)
300 errorOutput = standardOutput
304 subprojects { project ->
305 tasks.withType(JavaCompile) {
306 // Add compiler args for all code except third-party code.
307 options.compilerArgs += [
308 // Turn on all warnings, except...
310 // Deprecation, because we do use deprecated API for compatibility.
311 "-Xlint:-deprecation",
312 // Serial, because we don't use Java serialization.
314 // Classfile, because javac has a bug with MethodParameters attributes
315 // with Java 7. https://bugs.openjdk.java.net/browse/JDK-8190452
317 // Turn all remaining warnings into errors,
318 // unless marked by @SuppressWarnings.
328 languageLevel = '1.8'
332 // Object directories take a huge amount of time for IntelliJ to index.
333 // Exclude them. Convention is that object directories start with obj.
334 // IntelliJ is clever and will not exclude the parts of the object
335 // directory that are referenced, if there are any. In practice,
336 // indexing the entirety of the tree is taking too long, so exclude all
338 def topsrcdirURI = file(topsrcdir).toURI()
339 excludeDirs += files(file(topsrcdir)
340 .listFiles({it.isDirectory()} as FileFilter)
341 .collect({topsrcdirURI.relativize(it.toURI()).toString()}) // Relative paths.
342 .findAll({!it.equals('mobile/')}))
344 // If topobjdir is below topsrcdir, hide only some portions of that tree.
345 def topobjdirURI = file(topobjdir).toURI()
346 if (!topsrcdirURI.relativize(topobjdirURI).isAbsolute()) {
347 excludeDirs -= file(topobjdir)
348 excludeDirs += files(file(topobjdir).listFiles())
349 excludeDirs -= file("${topobjdir}/gradle")
355 apply plugin: "com.diffplug.spotless"
359 target project.fileTree(project.projectDir) {
361 exclude '**/thirdparty/**'
363 googleJavaFormat('1.7')