Bug 1822393 - Support Android Components consuming GeckoView directly. r=nalexander...
[gecko.git] / mobile / android / android-components / build.gradle
blob4d34aea3bede88ef963410ec41e1fb3fe658cd01
1 // Top-level build file where you can add configuration options common to all sub-projects/modules.
3 import io.gitlab.arturbosch.detekt.Detekt
4 import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
5 import org.gradle.internal.logging.text.StyledTextOutput.Style
6 import org.gradle.internal.logging.text.StyledTextOutputFactory
7 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
8 import static org.gradle.api.tasks.testing.TestResult.ResultType
10 buildscript {
11     repositories {
12         if (project.hasProperty("googleRepo")) {
13             maven {
14                 name "Google"
15                 url project.property("googleRepo")
16                 allowInsecureProtocol true // Local Nexus in CI uses HTTP
17             }
18         } else {
19             google()
20         }
22         if (project.hasProperty("centralRepo")) {
23             maven {
24                 name "MavenCentral"
25                 url project.property("centralRepo")
26                 allowInsecureProtocol true // Local Nexus in CI uses HTTP
27             }
28         } else {
29             mavenCentral()
30         }
31     }
33     dependencies {
34         classpath ComponentsDependencies.tools_androidgradle
35         classpath ComponentsDependencies.tools_kotlingradle
36     }
38     // Variables in plugins {} aren't directly supported. Hack around it by setting an
39     // intermediate variable which can pull from FenixDependencies.kt and be used later.
40     ext {
41         detekt_plugin = Versions.detekt
42         python_envs_plugin = Versions.python_envs_plugin
43         ksp_plugin = Versions.ksp_plugin
44     }
47 plugins {
48     id("io.gitlab.arturbosch.detekt").version("$detekt_plugin")
49     id("com.google.devtools.ksp").version("$ksp_plugin")
52 allprojects {
53     repositories {
54         if (project.hasProperty("googleRepo")) {
55             maven {
56                 name "Google"
57                 url project.property("googleRepo")
58                 allowInsecureProtocol true // Local Nexus in CI uses HTTP
59             }
60         } else {
61             google()
62         }
64         if (project.hasProperty("centralRepo")) {
65             maven {
66                 name "MavenCentral"
67                 url project.property("centralRepo")
68                 allowInsecureProtocol true // Local Nexus in CI uses HTTP
69             }
70         } else {
71             mavenCentral()
72         }
74         maven {
75             name "Mozilla"
76             url "https://maven.mozilla.org/maven2"
77         }
79         if (ExtraRepositories.mozillaStaging) {
80             maven {
81                 name "Mozilla Staging"
82                 url "https://maven-default.stage.mozaws.net/maven2"
83             }
84         }
85     }
88 subprojects {
89     apply plugin: 'jacoco'
91     // Enable Kotlin warnings as errors for all modules
92     tasks.withType(KotlinCompile).configureEach {
93         kotlinOptions.allWarningsAsErrors = true
94     }
96     project.configurations.configureEach {
97         // Dependencies can't depend on a different major version of Glean than A-C itself.
98         resolutionStrategy.eachDependency { details ->
99             if (details.requested.group == 'org.mozilla.telemetry'
100                     && details.requested.name.contains('glean') ) {
101                 def requested = details.requested.version.tokenize(".")
102                 def defined = Versions.mozilla_glean.tokenize(".")
103                 // Check the major version
104                 if (requested[0] != defined[0]) {
105                     throw new AssertionError("Cannot resolve to a single Glean version. Requested: ${details.requested.version}, A-C uses: ${Versions.mozilla_glean}")
106                 } else {
107                     // Enforce that all (transitive) dependencies are using the defined Glean version
108                     details.useVersion Versions.mozilla_glean
109                 }
110             }
111         }
113         resolutionStrategy.capabilitiesResolution.withCapability("org.mozilla.telemetry:glean-native") {
114             def toBeSelected = candidates.find { it.id instanceof ModuleComponentIdentifier && it.id.module.contains('geckoview') }
115             if (toBeSelected != null) {
116                 select(toBeSelected)
117             }
118             because 'use GeckoView Glean instead of standalone Glean'
119         }
120     }
123     // Allow local appservices substitution in each subproject.
124     def appServicesSrcDir = null
125     if (gradle.hasProperty('localProperties.autoPublish.application-services.dir')) {
126         appServicesSrcDir = gradle.getProperty('localProperties.autoPublish.application-services.dir')
127     } else if (gradle.hasProperty('localProperties.branchBuild.application-services.dir')) {
128         appServicesSrcDir = gradle.getProperty('localProperties.branchBuild.application-services.dir')
129     }
130     if (appServicesSrcDir) {
131         if (appServicesSrcDir.startsWith("/")) {
132             apply from: "${appServicesSrcDir}/build-scripts/substitute-local-appservices.gradle"
133         } else {
134             apply from: "${rootProject.projectDir}/${appServicesSrcDir}/build-scripts/substitute-local-appservices.gradle"
135         }
136     }
138     // Allow local Glean substitution in each subproject.
139     if (gradle.hasProperty('localProperties.autoPublish.glean.dir')) {
140         ext.gleanSrcDir = gradle."localProperties.autoPublish.glean.dir"
141         apply from: "${rootProject.projectDir}/${gleanSrcDir}/build-scripts/substitute-local-glean.gradle"
142     }
144     if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) {
145         if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) {
146             ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir"
147         }
148         ext.topsrcdir = gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir"
149         apply from: "${topsrcdir}/substitute-local-geckoview.gradle"
150     }
152     afterEvaluate {
153         if (it.hasProperty('android')) {
154             jacoco {
155                 toolVersion = Versions.jacoco
156             }
158             // Format test output
159             tasks.matching {it instanceof Test}.configureEach() {
160                 systemProperty "robolectric.logging", "stdout"
161                 systemProperty "logging.test-mode", "true"
162                 systemProperty "javax.net.ssl.trustStoreType", "JKS"
164                 testLogging.events = []
166                 def out = services.get(StyledTextOutputFactory).create("an-ouput")
168                 beforeSuite { descriptor ->
169                     if (descriptor.getClassName() != null) {
170                         out.style(Style.Header).println("\nSUITE: " + descriptor.getClassName())
171                     }
172                 }
174                 beforeTest { descriptor ->
175                     out.style(Style.Description).println("  TEST: " + descriptor.getName())
176                 }
178                 onOutput { descriptor, event ->
179                     logger.lifecycle("    " + event.message.trim())
180                 }
182                 afterTest { descriptor, result ->
183                     switch (result.getResultType()) {
184                         case ResultType.SUCCESS:
185                             out.style(Style.Success).println("  SUCCESS")
186                             break
188                         case ResultType.FAILURE:
189                             out.style(Style.Failure).println("  FAILURE")
190                             logger.lifecycle("", result.getException())
191                             break
193                         case ResultType.SKIPPED:
194                             out.style(Style.Info).println("  SKIPPED")
195                             break
196                     }
197                     logger.lifecycle("")
198                 }
199             }
201             dependencies {
202                 lintChecks project(':tooling-lint')
203             }
205             kotlin {
206                 jvmToolchain(config.jvmTargetCompatibility)
207             }
209             android {
210                 testOptions {
211                     unitTests {
212                         includeAndroidResources = true
213                     }
214                 }
216                 packagingOptions {
217                     resources {
218                         excludes += ['META-INF/atomicfu.kotlin_module', 'META-INF/AL2.0', 'META-INF/LGPL2.1']
219                         // Required dependencies using byte-buddy; remove after this is
220                         // fixed by: https://issuetracker.google.com/issues/170131605
221                         excludes.add("META-INF/licenses/ASM")
223                         pickFirsts += ['win32-x86-64/attach_hotspot_windows.dll', 'win32-x86/attach_hotspot_windows.dll']
224                     }
225                 }
227                 androidResources {
228                     ignoreAssetsPattern "manifest.template.json"
229                 }
231                 tasks.withType(KotlinCompile).configureEach {
232                     kotlinOptions.freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn"]
233                 }
234             }
236             if (project.hasProperty("coverage") && project.name != "support-test") {
237                 android.buildTypes.all { buildType ->
238                     tasks.withType(Test).configureEach() {
239                         jacoco {
240                             includeNoLocationClasses = true
241                             excludes = ['jdk.internal.*']
242                         }
244                         finalizedBy { "jacoco${buildType.name.capitalize()}TestReport" }
245                     }
247                     tasks.register("jacoco${buildType.name.capitalize()}TestReport", JacocoReport) {
248                         reports {
249                             xml.required = true
250                             html.required = true
251                         }
253                         def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
254                                           '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
255                         def kotlinDebugTree = fileTree(dir: "$project.layout.buildDirectory/tmp/kotlin-classes/${buildType.name}", excludes: fileFilter)
256                         def javaDebugTree = fileTree(dir: "$project.layout.buildDirectory/intermediates/classes/${buildType.name}", excludes: fileFilter)
257                         def mainSrc = "$project.projectDir/src/main/java"
259                         sourceDirectories.setFrom(files([mainSrc]))
260                         classDirectories.setFrom(files([kotlinDebugTree, javaDebugTree]))
261                         getExecutionData().setFrom(fileTree(project.layout.buildDirectory).include([
262                                 "jacoco/test${buildType.name.capitalize()}UnitTest.exec"
263                         ]))
264                     }
265                 }
267                 android {
268                     buildTypes {
269                         debug {
270                             testCoverageEnabled true
271                         }
272                     }
273                 }
274             }
275         }
276     }
279 if (findProject(":geckoview") == null) {
280     // Avoid adding this task if it already exists in a different root project.
281     tasks.register("clean", Delete) {
282         delete rootProject.layout.buildDirectory
283     }
286 detekt {
287     input = files("$projectDir/components", "$projectDir/buildSrc", "$projectDir/samples")
288     config = files("$projectDir/config/detekt.yml")
289     baseline = file("$projectDir/config/detekt-baseline.xml")
291     reports {
292         html {
293             enabled = true
294             destination = file("$projectDir/build/reports/detekt.html")
295         }
296         xml {
297             enabled = false
298         }
299         txt {
300             enabled = false
301         }
302     }
305 tasks.withType(Detekt).configureEach() {
306     // Custom detekt rules should be build before
307     // See https://arturbosch.github.io/detekt/extensions.html#pitfalls
308     dependsOn(":tooling-detekt:assemble")
310     autoCorrect = true
312     exclude "**/build.gradle.kts"
313     exclude "**/src/androidTest/**"
314     exclude "**/src/iosTest/**"
315     exclude "**/src/test/**"
316     exclude "**/test/src/**"
317     exclude "**/build/**"
318     exclude "**/resources/**"
319     exclude "**/tmp/**"
320     exclude "**/tooling/fetch/tests/**"
321     exclude "**/tooling/fetch-tests/**"
322     exclude "**/src/main/assets/extensions/**"
323     exclude "**/docs/**"
326 // Apply same path exclusions as for the main task
327 tasks.withType(DetektCreateBaselineTask).configureEach() {
328     exclude "**/src/androidTest/**"
329     exclude "**/src/test/**"
330     exclude "**/test/src/**"
331     exclude "**/build/**"
332     exclude "**/resources/**"
333     exclude "**/tmp/**"
334     exclude "**/tooling/fetch/tests/**"
335     exclude "**/tooling/fetch-tests/**"
338 configurations {
339     ktlint
342 dependencies {
343     ktlint("com.pinterest:ktlint:${Versions.ktlint}") {
344         attributes {
345             attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL))
346         }
347     }
348     detektPlugins project(":tooling-detekt")
351 tasks.register("ktlint", JavaExec) {
352     group = "verification"
353     description = "Check Kotlin code style."
354     classpath = configurations.ktlint
355     mainClass.set("com.pinterest.ktlint.Main")
356     args "components/**/*.kt" , "samples/**/*.kt", "!**/build/**/*.kt", "buildSrc/**/*.kt", "--baseline=ktlint-baseline.xml"
359 tasks.register("ktlintFormat", JavaExec) {
360     group = "formatting"
361     description = "Fix Kotlin code style deviations."
362     classpath = configurations.ktlint
363     mainClass.set("com.pinterest.ktlint.Main")
364     args "-F", "components/**/*.kt" , "samples/**/*.kt", "!**/build/**/*.kt", "buildSrc/**/*.kt", "--baseline=ktlint-baseline.xml"
365     jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
368 tasks.register("listRepositories") {
369     doLast {
370         println "Repositories:"
371         project.repositories.each { println "Name: " + it.name + "; url: " + it.url }
372    }
375 tasks.register("githubBuildDetails", GithubDetailsTask) {
376     text = """### Test result files
378 - [Test Coverage]({reportsUrl}/jacoco/jacocoReleaseTestReport/html/index.html)
379 - [Unit Tests]({reportsUrl}/tests/testReleaseUnitTest/index.html)
380 - [Android Lint]({reportsUrl}/lint-results-release.html)"""
383 tasks.register("githubLintDetektDetails", GithubDetailsTask) {
384     text = "### [Detekt Results Android-Components]({reportsUrl}/detekt.html)"
387 tasks.register("githubLintAndroidDetails", GithubDetailsTask) {
388     text = "### [Android Lint Results Android-Components]({reportsUrl}/lint-results-debug.html)"
391 tasks.register("testToolsDir", Exec) {
392     group = "verification"
393     description = "Run tests in the tools/ directory."
394     workingDir = "tools"
395     commandLine = ["python3", "test_list_compatible_dependency_versions.py"]