Bug 1865921 Part 2: Add a test that awaits the device.lost promise. r=ErichDonGubler
[gecko.git] / build.gradle
blob5e222ceaadf7262143b59ea12b16cfe25efb5f01
1 import org.tomlj.Toml
2 import org.tomlj.TomlParseResult
3 import org.tomlj.TomlTable
5 def tryInt = { string ->
6     if (string == null) {
7         return string
8     }
9     if (string.isInteger()) {
10         return string as Integer
11     }
12     return string
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.")
25             }
26             version = table.getString("version")
27         }
28     }
29     return version
32 allprojects {
33     // Expose the per-object-directory configuration to all projects.
34     ext {
35         mozconfig = gradle.mozconfig
36         topsrcdir = gradle.mozconfig.topsrcdir
37         topobjdir = gradle.mozconfig.topobjdir
39         gleanVersion = "55.0.0"
40         if (gleanVersion != getRustVersionFor("glean")) {
41           throw new StopExecutionException("Mismatched Glean version, expected: ${gleanVersion}," +
42               " found ${getRustVersionFor("glean")}")
43         }
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,
59         ]
60     }
62     repositories {
63         gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
64             maven {
65                 url repository
66                 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
67                     allowInsecureProtocol = true
68                 }
69             }
70         }
71     }
73     // Use the semanticdb-javac and semanticdb-kotlinc plugins to generate semanticdb files for Searchfox
74     if (mozconfig.substs.ENABLE_MOZSEARCH_PLUGIN || mozconfig.substs.DOWNLOAD_ALL_GRADLE_DEPENDENCIES) {
75         def targetRoot = new File(topobjdir, "mozsearch_java_index")
76         def semanticdbJavacVersion = "com.sourcegraph:semanticdb-javac:0.9.6"
77         def semanticdbKotlincVersion = "com.sourcegraph:semanticdb-kotlinc:0.3.2"
79         afterEvaluate {
80             def addDependencyToConfigurationIfExists = { configurationName, dependency ->
81                 def configuration = configurations.findByName(configurationName)
82                 if (configuration != null) {
83                     dependencies.add(configurationName, dependency)
84                 }
85             }
87             addDependencyToConfigurationIfExists("compileOnly", semanticdbJavacVersion)
88             addDependencyToConfigurationIfExists("testCompileOnly", semanticdbJavacVersion)
89             addDependencyToConfigurationIfExists("androidTestCompileOnly", semanticdbJavacVersion)
90             addDependencyToConfigurationIfExists("kotlinCompilerPluginClasspath", semanticdbKotlincVersion)
91         }
93         tasks.withType(JavaCompile) {
94             options.compilerArgs += [
95                 "-Xplugin:semanticdb -sourceroot:${topsrcdir} -targetroot:${targetRoot}",
96             ]
97         }
99         tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
100             compilerOptions.freeCompilerArgs.addAll([
101                 "-P", "plugin:semanticdb-kotlinc:sourceroot=${topsrcdir}",
102                 "-P", "plugin:semanticdb-kotlinc:targetroot=${targetRoot}",
103             ])
104         }
105     }
107     task downloadDependencies() {
108         description 'Download all dependencies to the Gradle cache'
109         doLast {
110             configurations.each { configuration ->
111                 if (configuration.canBeResolved) {
112                     configuration.allDependencies.each { dependency ->
113                         try {
114                             configuration.files(dependency)
115                         } catch(e) {
116                             println("Could not resolve ${configuration.name} -> ${dependency.name}")
117                             println(" > ${e.message}")
118                             if (e.cause) {
119                                 println(" >> ${e.cause}")
120                                 if (e.cause.cause) {
121                                     println(" >> ${e.cause.cause}")
122                                 }
123                             }
124                             println("")
125                         }
126                     }
127                 }
128             }
129         }
130     }
133 buildDir "${topobjdir}/gradle/build"
135 buildscript {
136     repositories {
137         gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
138             maven {
139                 url repository
140                 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
141                     allowInsecureProtocol = true
142                 }
143             }
144         }
145     }
147     ext.kotlin_version = '1.8.21'
149     dependencies {
150         classpath 'org.mozilla.apilint:apilint:0.5.2'
151         classpath 'com.android.tools.build:gradle:7.4.2'
152         classpath 'org.apache.commons:commons-exec:1.3'
153         classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.18.0'
154         classpath 'org.tomlj:tomlj:1.1.0'
155         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
156     }
159 // A stream that processes bytes line by line, prepending a tag before sending
160 // each line to Gradle's logging.
161 class TaggedLogOutputStream extends org.apache.commons.exec.LogOutputStream {
162     String tag
163     Logger logger
165     TaggedLogOutputStream(tag, logger) {
166         this.tag = tag
167         this.logger = logger
168     }
170     void processLine(String line, int level) {
171         logger.lifecycle("${this.tag} ${line}")
172     }
175 ext.geckoBinariesOnlyIf = { task ->
176     // Never when Gradle was invoked within `mach build`.
177     if ('1' == System.env.GRADLE_INVOKED_WITHIN_MACH_BUILD) {
178         rootProject.logger.lifecycle("Skipping task ${task.path} because: within `mach build`")
179         return false
180     }
182     // Never for official builds.
183     if (mozconfig.substs.MOZILLA_OFFICIAL) {
184         rootProject.logger.lifecycle("Skipping task ${task.path} because: MOZILLA_OFFICIAL")
185         return false
186     }
188     // Multi-l10n builds set `AB_CD=multi`, which isn't a valid locale, and
189     // `MOZ_CHROME_MULTILOCALE`.  To avoid failures, if Gradle is invoked with
190     // either, we don't invoke Make at all; this allows a multi-locale omnijar
191     // to be consumed without modification.
192     if ('multi' == System.env.AB_CD || System.env.MOZ_CHROME_MULTILOCALE) {
193         rootProject.logger.lifecycle("Skipping task ${task.path} because: AB_CD=multi")
194         return false
195     }
197     // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and handle resource
198     // and code generation themselves.
199     if ('1' == System.env.IS_LANGUAGE_REPACK) {
200         rootProject.logger.lifecycle("Skipping task ${task.path} because: IS_LANGUAGE_REPACK")
201         return false
202     }
204     rootProject.logger.lifecycle("Executing task ${task.path}")
205     return true
208 // Non-official versions are like "61.0a1", where "a1" is the milestone.
209 // This simply strips that off, leaving "61.0" in this example.
210 def getAppVersionWithoutMilestone() {
211     return project.ext.mozconfig.substs.MOZ_APP_VERSION.replaceFirst(/a[0-9]/, "")
214 // This converts MOZ_APP_VERSION into an integer
215 // version code.
217 // We take something like 58.1.2a1 and come out with 5800102
218 // This gives us 3 digits for the major number, and 2 digits
219 // each for the minor and build number. Beta and Release
221 // This must be synchronized with _compute_gecko_version(...) in /taskcluster/gecko_taskgraph/transforms/task.py
222 def computeVersionCode() {
223     String appVersion = getAppVersionWithoutMilestone()
225     // Split on the dot delimiter, e.g. 58.1.1a1 -> ["58, "1", "1a1"]
226     String[] parts = appVersion.split('\\.')
228     assert parts.size() == 2 || parts.size() == 3
230     // Major
231     int code = Integer.parseInt(parts[0]) * 100000
233     // Minor
234     code += Integer.parseInt(parts[1]) * 100
236     // Build
237     if (parts.size() == 3) {
238         code += Integer.parseInt(parts[2])
239     }
241     return code;
244 def getVersionName() {
245     return "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
248 // Mimic Python: open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]
249 def getBuildId() {
250     return file("${topobjdir}/buildid.h").getText('utf-8').split()[2]
253 def getVersionNumber() {
254     def appVersion = getAppVersionWithoutMilestone()
255     def parts = appVersion.split('\\.')
256     def version = parts[0] + "." + parts[1] + "." + getBuildId()
257     def substs = project.ext.mozconfig.substs
258     if (!substs.MOZILLA_OFFICIAL && !substs.MOZ_ANDROID_FAT_AAR_ARCHITECTURES) {
259         // Use -SNAPSHOT versions locally to enable the local GeckoView substitution flow.
260         version += "-SNAPSHOT"
261     }
262     return version
265 def getArtifactSuffix() {
266     def substs = project.ext.mozconfig.substs
268     def suffix = ""
269     // Release artifacts don't specify the channel, for the sake of simplicity.
270     if (substs.MOZ_UPDATE_CHANNEL != 'release') {
271         suffix += "-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
272     }
274     return suffix
277 class MachExec extends Exec {
278     def MachExec() {
279         // Bug 1543982: When invoking `mach build` recursively, the outer `mach
280         // build` itself modifies the environment, causing configure to run
281         // again.  This tries to restore the environment that the outer `mach
282         // build` was invoked in.  See the comment in
283         // $topsrcdir/settings.gradle.
284         project.ext.mozconfig.mozconfig.env.unmodified.each { k, v -> environment.remove(k) }
285         environment project.ext.mozconfig.orig_mozconfig.env.unmodified
286         environment 'MOZCONFIG', project.ext.mozconfig.substs.MOZCONFIG
287     }
290 task machBuildFaster(type: MachExec) {
291     onlyIf rootProject.ext.geckoBinariesOnlyIf
293     workingDir "${topsrcdir}"
295     commandLine mozconfig.substs.PYTHON3
296     args "${topsrcdir}/mach"
297     args 'build'
298     args 'faster'
300     // Add `-v` if we're running under `--info` (or `--debug`).
301     if (project.logger.isEnabled(LogLevel.INFO)) {
302         args '-v'
303     }
305     // `path` is like `:machBuildFaster`.
306     standardOutput = new TaggedLogOutputStream("${path}>", logger)
307     errorOutput = standardOutput
310 task machStagePackage(type: MachExec) {
311     onlyIf rootProject.ext.geckoBinariesOnlyIf
313     dependsOn rootProject.machBuildFaster
315     workingDir "${topobjdir}"
317     // We'd prefer this to be a `mach` invocation, but `mach build
318     // mobile/android/installer/stage-package` doesn't work as expected.
319     commandLine mozconfig.substs.GMAKE
320     args '-C'
321     args "${topobjdir}/mobile/android/installer"
322     args 'stage-package'
324     outputs.file "${topobjdir}/dist/geckoview/assets/omni.ja"
326     outputs.file "${topobjdir}/dist/geckoview/assets/${mozconfig.substs.ANDROID_CPU_ARCH}/libxul.so"
327     outputs.file "${topobjdir}/dist/geckoview/lib/${mozconfig.substs.ANDROID_CPU_ARCH}/libmozglue.so"
329     // Force running `stage-package`.
330     outputs.upToDateWhen { false }
332     // `path` is like `:machStagePackage`.
333     standardOutput = new TaggedLogOutputStream("${path}>", logger)
334     errorOutput = standardOutput
337 afterEvaluate {
338     subprojects { project ->
339         tasks.withType(JavaCompile) {
340             // Add compiler args for all code except third-party code.
341             options.compilerArgs += [
342                 // Turn on all warnings, except...
343                 "-Xlint:all",
344                 // Deprecation, because we do use deprecated API for compatibility.
345                 "-Xlint:-deprecation",
346                 // Serial, because we don't use Java serialization.
347                 "-Xlint:-serial",
348                 // Classfile, because javac has a bug with MethodParameters attributes
349                 // with Java 7. https://bugs.openjdk.java.net/browse/JDK-8190452
350                 "-Xlint:-classfile",
351                 // Turn all remaining warnings into errors,
352                 // unless marked by @SuppressWarnings.
353                 "-Werror"]
354         }
355     }
358 apply plugin: 'idea'
360 idea {
361     project {
362         languageLevel = '1.8'
363     }
365     module {
366         // Object directories take a huge amount of time for IntelliJ to index.
367         // Exclude them.  Convention is that object directories start with obj.
368         // IntelliJ is clever and will not exclude the parts of the object
369         // directory that are referenced, if there are any.  In practice,
370         // indexing the entirety of the tree is taking too long, so exclude all
371         // but mobile/.
372         def topsrcdirURI = file(topsrcdir).toURI()
373         excludeDirs += files(file(topsrcdir)
374             .listFiles({it.isDirectory()} as FileFilter)
375             .collect({topsrcdirURI.relativize(it.toURI()).toString()}) // Relative paths.
376             .findAll({!it.equals('mobile/')}))
378         // If topobjdir is below topsrcdir, hide only some portions of that tree.
379         def topobjdirURI = file(topobjdir).toURI()
380         if (!topsrcdirURI.relativize(topobjdirURI).isAbsolute()) {
381             excludeDirs -= file(topobjdir)
382             excludeDirs += files(file(topobjdir).listFiles())
383             excludeDirs -= file("${topobjdir}/gradle")
384         }
385     }
388 subprojects {
389     apply plugin: "com.diffplug.spotless"
391     spotless {
392         java {
393             target project.fileTree(project.projectDir) {
394                 include '**/*.java'
395                 exclude '**/thirdparty/**'
396             }
397             googleJavaFormat('1.17.0')
398         }
399         kotlin {
400             target project.fileTree(project.projectDir) {
401                 include '**/*.kt'
402                 exclude '**/thirdparty/**'
403             }
404             ktlint('0.48.2')
405         }
406     }