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 = "51.5.0"
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.7.20'
116 classpath 'org.mozilla.apilint:apilint:0.5.2'
117 classpath 'com.android.tools.build:gradle:7.3.0'
118 classpath 'org.apache.commons:commons-exec:1.3'
119 classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.10.0'
120 classpath 'org.tomlj:tomlj:1.0.0'
121 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
125 // A stream that processes bytes line by line, prepending a tag before sending
126 // each line to Gradle's logging.
127 class TaggedLogOutputStream extends org.apache.commons.exec.LogOutputStream {
131 TaggedLogOutputStream(tag, logger) {
136 void processLine(String line, int level) {
137 logger.lifecycle("${this.tag} ${line}")
141 ext.geckoBinariesOnlyIf = { task ->
142 // Never when Gradle was invoked within `mach build`.
143 if ('1' == System.env.GRADLE_INVOKED_WITHIN_MACH_BUILD) {
144 rootProject.logger.lifecycle("Skipping task ${task.path} because: within `mach build`")
148 // Never for official builds.
149 if (mozconfig.substs.MOZILLA_OFFICIAL) {
150 rootProject.logger.lifecycle("Skipping task ${task.path} because: MOZILLA_OFFICIAL")
154 // Multi-l10n builds set `AB_CD=multi`, which isn't a valid locale. To
155 // avoid failures, if Gradle is invoked with AB_CD=multi, we don't invoke
157 if ('multi' == System.env.AB_CD) {
158 rootProject.logger.lifecycle("Skipping task ${task.path} because: AB_CD=multi")
162 // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and handle resource
163 // and code generation themselves.
164 if ('1' == System.env.IS_LANGUAGE_REPACK) {
165 rootProject.logger.lifecycle("Skipping task ${task.path} because: IS_LANGUAGE_REPACK")
169 rootProject.logger.lifecycle("Executing task ${task.path}")
173 // Non-official versions are like "61.0a1", where "a1" is the milestone.
174 // This simply strips that off, leaving "61.0" in this example.
175 def getAppVersionWithoutMilestone() {
176 return project.ext.mozconfig.substs.MOZ_APP_VERSION.replaceFirst(/a[0-9]/, "")
179 // This converts MOZ_APP_VERSION into an integer
182 // We take something like 58.1.2a1 and come out with 5800102
183 // This gives us 3 digits for the major number, and 2 digits
184 // each for the minor and build number. Beta and Release
186 // This must be synchronized with _compute_gecko_version(...) in /taskcluster/gecko_taskgraph/transforms/task.py
187 def computeVersionCode() {
188 String appVersion = getAppVersionWithoutMilestone()
190 // Split on the dot delimiter, e.g. 58.1.1a1 -> ["58, "1", "1a1"]
191 String[] parts = appVersion.split('\\.')
193 assert parts.size() == 2 || parts.size() == 3
196 int code = Integer.parseInt(parts[0]) * 100000
199 code += Integer.parseInt(parts[1]) * 100
202 if (parts.size() == 3) {
203 code += Integer.parseInt(parts[2])
209 def getVersionName() {
210 return "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
213 // Mimic Python: open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]
215 return file("${topobjdir}/buildid.h").getText('utf-8').split()[2]
218 def getVersionNumber() {
219 def appVersion = getAppVersionWithoutMilestone()
220 def parts = appVersion.split('\\.')
221 def version = parts[0] + "." + parts[1] + "." + getBuildId()
222 def substs = project.ext.mozconfig.substs
223 if (!substs.MOZILLA_OFFICIAL && !substs.MOZ_ANDROID_FAT_AAR_ARCHITECTURES) {
224 // Use -SNAPSHOT versions locally to enable the local GeckoView substitution flow.
225 version += "-SNAPSHOT"
230 def getArtifactSuffix() {
231 def substs = project.ext.mozconfig.substs
234 // Release artifacts don't specify the channel, for the sake of simplicity.
235 if (substs.MOZ_UPDATE_CHANNEL != 'release') {
236 suffix += "-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
242 class MachExec extends Exec {
244 // Bug 1543982: When invoking `mach build` recursively, the outer `mach
245 // build` itself modifies the environment, causing configure to run
246 // again. This tries to restore the environment that the outer `mach
247 // build` was invoked in. See the comment in
248 // $topsrcdir/settings.gradle.
249 project.ext.mozconfig.mozconfig.env.unmodified.each { k, v -> environment.remove(k) }
250 environment project.ext.mozconfig.orig_mozconfig.env.unmodified
251 environment 'MOZCONFIG', project.ext.mozconfig.substs.MOZCONFIG
255 task machBuildFaster(type: MachExec) {
256 onlyIf rootProject.ext.geckoBinariesOnlyIf
258 workingDir "${topsrcdir}"
260 commandLine mozconfig.substs.PYTHON3
261 args "${topsrcdir}/mach"
265 // Add `-v` if we're running under `--info` (or `--debug`).
266 if (project.logger.isEnabled(LogLevel.INFO)) {
270 // `path` is like `:machBuildFaster`.
271 standardOutput = new TaggedLogOutputStream("${path}>", logger)
272 errorOutput = standardOutput
275 task machStagePackage(type: MachExec) {
276 onlyIf rootProject.ext.geckoBinariesOnlyIf
278 dependsOn rootProject.machBuildFaster
280 workingDir "${topobjdir}"
282 // We'd prefer this to be a `mach` invocation, but `mach build
283 // mobile/android/installer/stage-package` doesn't work as expected.
284 commandLine mozconfig.substs.GMAKE
286 args "${topobjdir}/mobile/android/installer"
289 outputs.file "${topobjdir}/dist/geckoview/assets/omni.ja"
291 outputs.file "${topobjdir}/dist/geckoview/assets/${mozconfig.substs.ANDROID_CPU_ARCH}/libxul.so"
292 outputs.file "${topobjdir}/dist/geckoview/lib/${mozconfig.substs.ANDROID_CPU_ARCH}/libmozglue.so"
294 // Force running `stage-package`.
295 outputs.upToDateWhen { false }
297 // `path` is like `:machStagePackage`.
298 standardOutput = new TaggedLogOutputStream("${path}>", logger)
299 errorOutput = standardOutput
303 subprojects { project ->
304 tasks.withType(JavaCompile) {
305 // Add compiler args for all code except third-party code.
306 options.compilerArgs += [
307 // Turn on all warnings, except...
309 // Deprecation, because we do use deprecated API for compatibility.
310 "-Xlint:-deprecation",
311 // Serial, because we don't use Java serialization.
313 // Classfile, because javac has a bug with MethodParameters attributes
314 // with Java 7. https://bugs.openjdk.java.net/browse/JDK-8190452
316 // Turn all remaining warnings into errors,
317 // unless marked by @SuppressWarnings.
327 languageLevel = '1.8'
331 // Object directories take a huge amount of time for IntelliJ to index.
332 // Exclude them. Convention is that object directories start with obj.
333 // IntelliJ is clever and will not exclude the parts of the object
334 // directory that are referenced, if there are any. In practice,
335 // indexing the entirety of the tree is taking too long, so exclude all
337 def topsrcdirURI = file(topsrcdir).toURI()
338 excludeDirs += files(file(topsrcdir)
339 .listFiles({it.isDirectory()} as FileFilter)
340 .collect({topsrcdirURI.relativize(it.toURI()).toString()}) // Relative paths.
341 .findAll({!it.equals('mobile/')}))
343 // If topobjdir is below topsrcdir, hide only some portions of that tree.
344 def topobjdirURI = file(topobjdir).toURI()
345 if (!topsrcdirURI.relativize(topobjdirURI).isAbsolute()) {
346 excludeDirs -= file(topobjdir)
347 excludeDirs += files(file(topobjdir).listFiles())
348 excludeDirs -= file("${topobjdir}/gradle")
354 apply plugin: "com.diffplug.spotless"
358 target project.fileTree(project.projectDir) {
360 exclude '**/thirdparty/**'
362 googleJavaFormat('1.15.0')