Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / build.gradle
blobcdd097f208c11824e2c8737726e96e4b3a7a6b5d
1 import org.tomlj.Toml
2 import org.tomlj.TomlParseResult
3 import org.tomlj.TomlTable
5 buildscript {
6     repositories {
7         gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
8             maven {
9                 url repository
10                 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
11                     allowInsecureProtocol = true
12                 }
13             }
14         }
15     }
17     ext {
18         detekt_plugin = Versions.detekt
19         python_envs_plugin = Versions.python_envs_plugin
20         ksp_plugin = Versions.ksp_plugin
22         // Used in mobile/android/fenix/app/build.gradle
23         protobuf_plugin = FenixVersions.protobuf_plugin
24     }
26     dependencies {
27         classpath 'org.mozilla.apilint:apilint:0.5.2'
28         classpath 'com.android.tools.build:gradle:8.0.2'  // Bug 1881001 for using AC version
29         classpath 'org.apache.commons:commons-exec:1.3'
30         classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.25.0'
31         classpath 'org.tomlj:tomlj:1.1.0'
32         classpath ComponentsDependencies.tools_kotlingradle
34         // Used in mobile/android/fenix/app/build.gradle
35         classpath ComponentsDependencies.androidx_safeargs
36         classpath FenixDependencies.osslicenses_plugin
37         classpath FenixDependencies.tools_benchmarkgradle
38         classpath "org.mozilla.telemetry:glean-gradle-plugin:${Versions.mozilla_glean}"
39         classpath "${ApplicationServicesConfig.groupId}:tooling-nimbus-gradle:${ApplicationServicesConfig.version}"
40     }
43 plugins {
44     id("io.gitlab.arturbosch.detekt").version("$detekt_plugin")
45     id("com.google.devtools.ksp").version("$ksp_plugin")
48 def tryInt = { string ->
49     if (string == null) {
50         return string
51     }
52     if (string.isInteger()) {
53         return string as Integer
54     }
55     return string
58 // Parses the Cargo.lock and returns the version for the given package name.
59 def getRustVersionFor(packageName) {
60     String version = null;
61     TomlParseResult result = Toml.parse(file("Cargo.lock").getText());
62     for (object in result.getArray("package").toList()) {
63         def table = (TomlTable) object
64         if (table.getString("name") == packageName) {
65             if (version != null) {
66                 throw new StopExecutionException("Multiple versions for '${packageName}' found." +
67                         " Ensure '${packageName}' is only included once.")
68             }
69             version = table.getString("version")
70         }
71     }
72     return version
75 allprojects {
76     // Expose the per-object-directory configuration to all projects.
77     ext {
78         mozconfig = gradle.mozconfig
79         topsrcdir = gradle.mozconfig.topsrcdir
80         topobjdir = gradle.mozconfig.topobjdir
82         gleanVersion = Versions.mozilla_glean
83         if (gleanVersion != getRustVersionFor("glean")) {
84           throw new StopExecutionException("Mismatched Glean version, expected: ${gleanVersion}," +
85               " found ${getRustVersionFor("glean")}")
86         }
88         artifactSuffix = getArtifactSuffix()
89         versionName = getVersionName()
90         versionCode = computeVersionCode()
91         versionNumber = getVersionNumber()
92         buildId = getBuildId()
94         buildToolsVersion = mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
95         compileSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
96         targetSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
97         minSdkVersion = tryInt(mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION)
98         manifestPlaceholders = [
99             ANDROID_PACKAGE_NAME: mozconfig.substs.ANDROID_PACKAGE_NAME,
100             ANDROID_TARGET_SDK: mozconfig.substs.ANDROID_TARGET_SDK,
101             MOZ_ANDROID_MIN_SDK_VERSION: mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION,
102         ]
103     }
105     repositories {
106         gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
107             maven {
108                 url repository
109                 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
110                     allowInsecureProtocol = true
111                 }
112             }
113         }
114     }
116     // Use the semanticdb-javac and semanticdb-kotlinc plugins to generate semanticdb files for Searchfox
117     if (mozconfig.substs.ENABLE_MOZSEARCH_PLUGIN || mozconfig.substs.DOWNLOAD_ALL_GRADLE_DEPENDENCIES) {
118         def targetRoot = new File(topobjdir, "mozsearch_java_index")
119         def semanticdbJavacVersion = "com.sourcegraph:semanticdb-javac:0.9.6"
120         def semanticdbKotlincVersion = "com.sourcegraph:semanticdb-kotlinc:0.4.0"
122         afterEvaluate {
123             def addDependencyToConfigurationIfExists = { configurationName, dependency ->
124                 def configuration = configurations.findByName(configurationName)
125                 if (configuration != null) {
126                     dependencies.add(configurationName, dependency)
127                 }
128             }
130             addDependencyToConfigurationIfExists("compileOnly", semanticdbJavacVersion)
131             addDependencyToConfigurationIfExists("testCompileOnly", semanticdbJavacVersion)
132             addDependencyToConfigurationIfExists("androidTestCompileOnly", semanticdbJavacVersion)
133             addDependencyToConfigurationIfExists("kotlinCompilerPluginClasspath", semanticdbKotlincVersion)
134         }
136         tasks.withType(JavaCompile) {
137             options.compilerArgs += [
138                 "-Xplugin:semanticdb -sourceroot:${topsrcdir} -targetroot:${targetRoot}",
139             ]
140         }
142         tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
143             compilerOptions.freeCompilerArgs.addAll([
144                 "-P", "plugin:semanticdb-kotlinc:sourceroot=${topsrcdir}",
145                 "-P", "plugin:semanticdb-kotlinc:targetroot=${targetRoot}",
146             ])
147         }
148     }
150     task downloadDependencies() {
151         description 'Download all dependencies to the Gradle cache'
152         doLast {
153             configurations.each { configuration ->
154                 if (configuration.canBeResolved) {
155                     configuration.allDependencies.each { dependency ->
156                         try {
157                             configuration.files(dependency)
158                         } catch(e) {
159                             println("Could not resolve ${configuration.name} -> ${dependency.name}")
160                             println(" > ${e.message}")
161                             if (e.cause) {
162                                 println(" >> ${e.cause}")
163                                 if (e.cause.cause) {
164                                     println(" >> ${e.cause.cause}")
165                                 }
166                             }
167                             println("")
168                         }
169                     }
170                 }
171             }
172         }
173     }
176 buildDir "${topobjdir}/gradle/build"
178 // A stream that processes bytes line by line, prepending a tag before sending
179 // each line to Gradle's logging.
180 class TaggedLogOutputStream extends org.apache.commons.exec.LogOutputStream {
181     String tag
182     Logger logger
184     TaggedLogOutputStream(tag, logger) {
185         this.tag = tag
186         this.logger = logger
187     }
189     void processLine(String line, int level) {
190         logger.lifecycle("${this.tag} ${line}")
191     }
194 ext.geckoBinariesOnlyIf = { task ->
195     // Never when Gradle was invoked within `mach build`.
196     if ('1' == System.env.GRADLE_INVOKED_WITHIN_MACH_BUILD) {
197         rootProject.logger.lifecycle("Skipping task ${task.path} because: within `mach build`")
198         return false
199     }
201     // Never for official builds.
202     if (mozconfig.substs.MOZILLA_OFFICIAL) {
203         rootProject.logger.lifecycle("Skipping task ${task.path} because: MOZILLA_OFFICIAL")
204         return false
205     }
207     // Multi-l10n builds set `AB_CD=multi`, which isn't a valid locale, and
208     // `MOZ_CHROME_MULTILOCALE`.  To avoid failures, if Gradle is invoked with
209     // either, we don't invoke Make at all; this allows a multi-locale omnijar
210     // to be consumed without modification.
211     if ('multi' == System.env.AB_CD || System.env.MOZ_CHROME_MULTILOCALE) {
212         rootProject.logger.lifecycle("Skipping task ${task.path} because: AB_CD=multi")
213         return false
214     }
216     // Single-locale l10n repacks set `IS_LANGUAGE_REPACK=1` and handle resource
217     // and code generation themselves.
218     if ('1' == System.env.IS_LANGUAGE_REPACK) {
219         rootProject.logger.lifecycle("Skipping task ${task.path} because: IS_LANGUAGE_REPACK")
220         return false
221     }
223     rootProject.logger.lifecycle("Executing task ${task.path}")
224     return true
227 // Non-official versions are like "61.0a1", where "a1" is the milestone.
228 // This simply strips that off, leaving "61.0" in this example.
229 def getAppVersionWithoutMilestone() {
230     return project.ext.mozconfig.substs.MOZ_APP_VERSION.replaceFirst(/a[0-9]/, "")
233 // This converts MOZ_APP_VERSION into an integer
234 // version code.
236 // We take something like 58.1.2a1 and come out with 5800102
237 // This gives us 3 digits for the major number, and 2 digits
238 // each for the minor and build number. Beta and Release
240 // This must be synchronized with _compute_gecko_version(...) in /taskcluster/gecko_taskgraph/transforms/task.py
241 def computeVersionCode() {
242     String appVersion = getAppVersionWithoutMilestone()
244     // Split on the dot delimiter, e.g. 58.1.1a1 -> ["58, "1", "1a1"]
245     String[] parts = appVersion.split('\\.')
247     assert parts.size() == 2 || parts.size() == 3
249     // Major
250     int code = Integer.parseInt(parts[0]) * 100000
252     // Minor
253     code += Integer.parseInt(parts[1]) * 100
255     // Build
256     if (parts.size() == 3) {
257         code += Integer.parseInt(parts[2])
258     }
260     return code;
263 def getVersionName() {
264     return "${mozconfig.substs.MOZ_APP_VERSION}-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
267 // Mimic Python: open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2]
268 def getBuildId() {
269     return file("${topobjdir}/buildid.h").getText('utf-8').split()[2]
272 def getVersionNumber() {
273     def appVersion = getAppVersionWithoutMilestone()
274     def parts = appVersion.split('\\.')
275     def version = parts[0] + "." + parts[1] + "." + getBuildId()
276     def substs = project.ext.mozconfig.substs
277     if (!substs.MOZILLA_OFFICIAL && !substs.MOZ_ANDROID_FAT_AAR_ARCHITECTURES) {
278         // Use -SNAPSHOT versions locally to enable the local GeckoView substitution flow.
279         version += "-SNAPSHOT"
280     }
281     return version
284 def getArtifactSuffix() {
285     def substs = project.ext.mozconfig.substs
287     def suffix = ""
288     // Release artifacts don't specify the channel, for the sake of simplicity.
289     if (substs.MOZ_UPDATE_CHANNEL != 'release') {
290         suffix += "-${mozconfig.substs.MOZ_UPDATE_CHANNEL}"
291     }
293     return suffix
296 class MachExec extends Exec {
297     def MachExec() {
298         // Bug 1543982: When invoking `mach build` recursively, the outer `mach
299         // build` itself modifies the environment, causing configure to run
300         // again.  This tries to restore the environment that the outer `mach
301         // build` was invoked in.  See the comment in
302         // $topsrcdir/settings.gradle.
303         project.ext.mozconfig.mozconfig.env.unmodified.each { k, v -> environment.remove(k) }
304         environment project.ext.mozconfig.orig_mozconfig.env.unmodified
305         environment 'MOZCONFIG', project.ext.mozconfig.substs.MOZCONFIG
306     }
309 task machBuildFaster(type: MachExec) {
310     onlyIf rootProject.ext.geckoBinariesOnlyIf
312     workingDir "${topsrcdir}"
314     commandLine mozconfig.substs.PYTHON3
315     args "${topsrcdir}/mach"
316     args 'build'
317     args 'faster'
319     // Add `-v` if we're running under `--info` (or `--debug`).
320     if (project.logger.isEnabled(LogLevel.INFO)) {
321         args '-v'
322     }
324     // `path` is like `:machBuildFaster`.
325     standardOutput = new TaggedLogOutputStream("${path}>", logger)
326     errorOutput = standardOutput
329 task machStagePackage(type: MachExec) {
330     onlyIf rootProject.ext.geckoBinariesOnlyIf
332     dependsOn rootProject.machBuildFaster
334     workingDir "${topobjdir}"
336     // We'd prefer this to be a `mach` invocation, but `mach build
337     // mobile/android/installer/stage-package` doesn't work as expected.
338     commandLine mozconfig.substs.GMAKE
339     args '-C'
340     args "${topobjdir}/mobile/android/installer"
341     args 'stage-package'
343     outputs.file "${topobjdir}/dist/geckoview/assets/omni.ja"
345     outputs.file "${topobjdir}/dist/geckoview/assets/${mozconfig.substs.ANDROID_CPU_ARCH}/libxul.so"
346     outputs.file "${topobjdir}/dist/geckoview/lib/${mozconfig.substs.ANDROID_CPU_ARCH}/libmozglue.so"
348     // Force running `stage-package`.
349     outputs.upToDateWhen { false }
351     // `path` is like `:machStagePackage`.
352     standardOutput = new TaggedLogOutputStream("${path}>", logger)
353     errorOutput = standardOutput
356 afterEvaluate {
357     subprojects { project ->
358         tasks.withType(JavaCompile) {
359             // Add compiler args for all code except third-party code.
360             options.compilerArgs += [
361                 // Turn on all warnings, except...
362                 "-Xlint:all",
363                 // Deprecation, because we do use deprecated API for compatibility.
364                 "-Xlint:-deprecation",
365                 // Serial, because we don't use Java serialization.
366                 "-Xlint:-serial",
367                 // Classfile, because javac has a bug with MethodParameters attributes
368                 // with Java 7. https://bugs.openjdk.java.net/browse/JDK-8190452
369                 "-Xlint:-classfile"]
371             // In GeckoView java projects only, turn all remaining warnings
372             // into errors unless marked by @SuppressWarnings.
373             def projectName = project.getName()
374             if (projectName.startsWith('geckoview')
375                 || projectName == 'annotations'
376                 || projectName == 'exoplayer2'
377                 || projectName == 'messaging_example'
378                 || projectName == 'port_messaging_example'
379                 || projectName == 'test_runner'
380             ) {
381                 options.compilerArgs += [
382                         "-Werror"
383                 ]
384             }
385         }
387     }
390 apply plugin: 'idea'
392 idea {
393     project {
394         languageLevel = '1.8'
395     }
397     module {
398         // Object directories take a huge amount of time for IntelliJ to index.
399         // Exclude them.  Convention is that object directories start with obj.
400         // IntelliJ is clever and will not exclude the parts of the object
401         // directory that are referenced, if there are any.  In practice,
402         // indexing the entirety of the tree is taking too long, so exclude all
403         // but mobile/.
404         def topsrcdirURI = file(topsrcdir).toURI()
405         excludeDirs += files(file(topsrcdir)
406             .listFiles({it.isDirectory()} as FileFilter)
407             .collect({topsrcdirURI.relativize(it.toURI()).toString()}) // Relative paths.
408             .findAll({!it.equals('mobile/')}))
410         // If topobjdir is below topsrcdir, hide only some portions of that tree.
411         def topobjdirURI = file(topobjdir).toURI()
412         if (!topsrcdirURI.relativize(topobjdirURI).isAbsolute()) {
413             excludeDirs -= file(topobjdir)
414             excludeDirs += files(file(topobjdir).listFiles())
415             excludeDirs -= file("${topobjdir}/gradle")
416         }
417     }
420 subprojects { project ->
421     // Perform spotless lint in GeckoView projects only.
422     def projectName = project.getName()
423     if (projectName.startsWith('geckoview')
424             || projectName == 'annotations'
425             || projectName == 'exoplayer2'
426             || projectName == 'messaging_example'
427             || projectName == 'port_messaging_example'
428             || projectName == 'test_runner'
429     ) {
430         apply plugin: "com.diffplug.spotless"
432         spotless {
433             java {
434                 target project.fileTree(project.projectDir) {
435                     include '**/*.java'
436                     exclude '**/thirdparty/**'
437                 }
438                 googleJavaFormat('1.17.0')
439             }
440             kotlin {
441                 target project.fileTree(project.projectDir) {
442                     include '**/*.kt'
443                     exclude '**/thirdparty/**'
444                 }
445                 ktlint('0.49.1')
446             }
447         }
448     } else {
449         afterEvaluate {
450             // Set the source and target compatibility for non-GeckoView projects only.
451             if (it.hasProperty('android')) {
452                 android {
453                     compileOptions {
454                         sourceCompatibility JavaVersion.VERSION_17
455                         targetCompatibility JavaVersion.VERSION_17
456                     }
457                 }
458             }
459         }
460     }
462     project.configurations.configureEach {
463         resolutionStrategy.capabilitiesResolution.withCapability("org.mozilla.telemetry:glean-native") {
464             def toBeSelected = candidates.find {
465                 it.id instanceof ProjectComponentIdentifier && it.id.projectName.contains('geckoview')
466             }
467             if (toBeSelected != null) {
468                 select(toBeSelected)
469             }
470             because 'use GeckoView Glean instead of standalone Glean'
471         }
472     }