Bug 1873507 - Replace ScopedXFree<GLXFBConfig> with MakeScopeExit. r=gfx-reviewers...
[gecko.git] / build.gradle
blob3d5491745d786d029788158ef349d97e5fb77d2a
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.kotlin_version = '1.8.21'
19     dependencies {
20         classpath 'org.mozilla.apilint:apilint:0.5.2'
21         classpath 'com.android.tools.build:gradle:7.4.2'
22         classpath 'org.apache.commons:commons-exec:1.3'
23         classpath 'com.diffplug.spotless:spotless-plugin-gradle:6.18.0'
24         classpath 'org.tomlj:tomlj:1.1.0'
25         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
26     }
29 def tryInt = { string ->
30     if (string == null) {
31         return string
32     }
33     if (string.isInteger()) {
34         return string as Integer
35     }
36     return string
39 // Parses the Cargo.lock and returns the version for the given package name.
40 def getRustVersionFor(packageName) {
41     String version = null;
42     TomlParseResult result = Toml.parse(file("Cargo.lock").getText());
43     for (object in result.getArray("package").toList()) {
44         def table = (TomlTable) object
45         if (table.getString("name") == packageName) {
46             if (version != null) {
47                 throw new StopExecutionException("Multiple versions for '${packageName}' found." +
48                         " Ensure '${packageName}' is only included once.")
49             }
50             version = table.getString("version")
51         }
52     }
53     return version
56 allprojects {
57     // Expose the per-object-directory configuration to all projects.
58     ext {
59         mozconfig = gradle.mozconfig
60         topsrcdir = gradle.mozconfig.topsrcdir
61         topobjdir = gradle.mozconfig.topobjdir
63         gleanVersion = "56.1.0"
64         if (gleanVersion != getRustVersionFor("glean")) {
65           throw new StopExecutionException("Mismatched Glean version, expected: ${gleanVersion}," +
66               " found ${getRustVersionFor("glean")}")
67         }
69         artifactSuffix = getArtifactSuffix()
70         versionName = getVersionName()
71         versionCode = computeVersionCode()
72         versionNumber = getVersionNumber()
73         buildId = getBuildId()
75         buildToolsVersion = mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
76         compileSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
77         targetSdkVersion = tryInt(mozconfig.substs.ANDROID_TARGET_SDK)
78         minSdkVersion = tryInt(mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION)
79         manifestPlaceholders = [
80             ANDROID_PACKAGE_NAME: mozconfig.substs.ANDROID_PACKAGE_NAME,
81             ANDROID_TARGET_SDK: mozconfig.substs.ANDROID_TARGET_SDK,
82             MOZ_ANDROID_MIN_SDK_VERSION: mozconfig.substs.MOZ_ANDROID_MIN_SDK_VERSION,
83         ]
84     }
86     repositories {
87         gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
88             maven {
89                 url repository
90                 if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
91                     allowInsecureProtocol = true
92                 }
93             }
94         }
95     }
97     // Use the semanticdb-javac and semanticdb-kotlinc plugins to generate semanticdb files for Searchfox
98     if (mozconfig.substs.ENABLE_MOZSEARCH_PLUGIN || mozconfig.substs.DOWNLOAD_ALL_GRADLE_DEPENDENCIES) {
99         def targetRoot = new File(topobjdir, "mozsearch_java_index")
100         def semanticdbJavacVersion = "com.sourcegraph:semanticdb-javac:0.9.6"
101         def semanticdbKotlincVersion = "com.sourcegraph:semanticdb-kotlinc:0.3.2"
103         afterEvaluate {
104             def addDependencyToConfigurationIfExists = { configurationName, dependency ->
105                 def configuration = configurations.findByName(configurationName)
106                 if (configuration != null) {
107                     dependencies.add(configurationName, dependency)
108                 }
109             }
111             addDependencyToConfigurationIfExists("compileOnly", semanticdbJavacVersion)
112             addDependencyToConfigurationIfExists("testCompileOnly", semanticdbJavacVersion)
113             addDependencyToConfigurationIfExists("androidTestCompileOnly", semanticdbJavacVersion)
114             addDependencyToConfigurationIfExists("kotlinCompilerPluginClasspath", semanticdbKotlincVersion)
115         }
117         tasks.withType(JavaCompile) {
118             options.compilerArgs += [
119                 "-Xplugin:semanticdb -sourceroot:${topsrcdir} -targetroot:${targetRoot}",
120             ]
121         }
123         tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
124             compilerOptions.freeCompilerArgs.addAll([
125                 "-P", "plugin:semanticdb-kotlinc:sourceroot=${topsrcdir}",
126                 "-P", "plugin:semanticdb-kotlinc:targetroot=${targetRoot}",
127             ])
128         }
129     }
131     task downloadDependencies() {
132         description 'Download all dependencies to the Gradle cache'
133         doLast {
134             configurations.each { configuration ->
135                 if (configuration.canBeResolved) {
136                     configuration.allDependencies.each { dependency ->
137                         try {
138                             configuration.files(dependency)
139                         } catch(e) {
140                             println("Could not resolve ${configuration.name} -> ${dependency.name}")
141                             println(" > ${e.message}")
142                             if (e.cause) {
143                                 println(" >> ${e.cause}")
144                                 if (e.cause.cause) {
145                                     println(" >> ${e.cause.cause}")
146                                 }
147                             }
148                             println("")
149                         }
150                     }
151                 }
152             }
153         }
154     }
157 buildDir "${topobjdir}/gradle/build"
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     }