// Top-level build file where you can add configuration options common to all sub-projects/modules. import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask import org.gradle.internal.logging.text.StyledTextOutput.Style import org.gradle.internal.logging.text.StyledTextOutputFactory import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import static org.gradle.api.tasks.testing.TestResult.ResultType buildscript { repositories { if (project.hasProperty("googleRepo")) { maven { name "Google" url project.property("googleRepo") allowInsecureProtocol true // Local Nexus in CI uses HTTP } } else { google() } if (project.hasProperty("centralRepo")) { maven { name "MavenCentral" url project.property("centralRepo") allowInsecureProtocol true // Local Nexus in CI uses HTTP } } else { mavenCentral() } } dependencies { classpath ComponentsDependencies.tools_androidgradle classpath ComponentsDependencies.tools_kotlingradle } // Variables in plugins {} aren't directly supported. Hack around it by setting an // intermediate variable which can pull from FenixDependencies.kt and be used later. ext { detekt_plugin = Versions.detekt python_envs_plugin = Versions.python_envs_plugin ksp_plugin = Versions.ksp_plugin } } plugins { id("io.gitlab.arturbosch.detekt").version("$detekt_plugin") id("com.google.devtools.ksp").version("$ksp_plugin") } allprojects { repositories { if (project.hasProperty("googleRepo")) { maven { name "Google" url project.property("googleRepo") allowInsecureProtocol true // Local Nexus in CI uses HTTP } } else { google() } if (project.hasProperty("centralRepo")) { maven { name "MavenCentral" url project.property("centralRepo") allowInsecureProtocol true // Local Nexus in CI uses HTTP } } else { mavenCentral() } maven { name "Mozilla" url "https://maven.mozilla.org/maven2" } if (ExtraRepositories.mozillaStaging) { maven { name "Mozilla Staging" url "https://maven-default.stage.mozaws.net/maven2" } } } } subprojects { apply plugin: 'jacoco' // Enable Kotlin warnings as errors for all modules tasks.withType(KotlinCompile).configureEach { kotlinOptions.allWarningsAsErrors = true } project.configurations.configureEach { // Dependencies can't depend on a different major version of Glean than A-C itself. resolutionStrategy.eachDependency { details -> if (details.requested.group == 'org.mozilla.telemetry' && details.requested.name.contains('glean') ) { def requested = details.requested.version.tokenize(".") def defined = Versions.mozilla_glean.tokenize(".") // Check the major version if (requested[0] != defined[0]) { throw new AssertionError("Cannot resolve to a single Glean version. Requested: ${details.requested.version}, A-C uses: ${Versions.mozilla_glean}") } else { // Enforce that all (transitive) dependencies are using the defined Glean version details.useVersion Versions.mozilla_glean } } } resolutionStrategy.capabilitiesResolution.withCapability("org.mozilla.telemetry:glean-native") { def toBeSelected = candidates.find { it.id instanceof ModuleComponentIdentifier && it.id.module.contains('geckoview') } if (toBeSelected != null) { select(toBeSelected) } because 'use GeckoView Glean instead of standalone Glean' } } // Allow local appservices substitution in each subproject. def appServicesSrcDir = null if (gradle.hasProperty('localProperties.autoPublish.application-services.dir')) { appServicesSrcDir = gradle.getProperty('localProperties.autoPublish.application-services.dir') } else if (gradle.hasProperty('localProperties.branchBuild.application-services.dir')) { appServicesSrcDir = gradle.getProperty('localProperties.branchBuild.application-services.dir') } if (appServicesSrcDir) { if (appServicesSrcDir.startsWith("/")) { apply from: "${appServicesSrcDir}/build-scripts/substitute-local-appservices.gradle" } else { apply from: "${rootProject.projectDir}/${appServicesSrcDir}/build-scripts/substitute-local-appservices.gradle" } } // Allow local Glean substitution in each subproject. if (gradle.hasProperty('localProperties.autoPublish.glean.dir')) { ext.gleanSrcDir = gradle."localProperties.autoPublish.glean.dir" apply from: "${rootProject.projectDir}/${gleanSrcDir}/build-scripts/substitute-local-glean.gradle" } if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) { if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) { ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir" } ext.topsrcdir = gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir" apply from: "${topsrcdir}/substitute-local-geckoview.gradle" } afterEvaluate { if (it.hasProperty('android')) { jacoco { toolVersion = Versions.jacoco } // Format test output tasks.matching {it instanceof Test}.configureEach() { systemProperty "robolectric.logging", "stdout" systemProperty "logging.test-mode", "true" systemProperty "javax.net.ssl.trustStoreType", "JKS" testLogging.events = [] def out = services.get(StyledTextOutputFactory).create("an-ouput") beforeSuite { descriptor -> if (descriptor.getClassName() != null) { out.style(Style.Header).println("\nSUITE: " + descriptor.getClassName()) } } beforeTest { descriptor -> out.style(Style.Description).println(" TEST: " + descriptor.getName()) } onOutput { descriptor, event -> logger.lifecycle(" " + event.message.trim()) } afterTest { descriptor, result -> switch (result.getResultType()) { case ResultType.SUCCESS: out.style(Style.Success).println(" SUCCESS") break case ResultType.FAILURE: out.style(Style.Failure).println(" FAILURE") logger.lifecycle("", result.getException()) break case ResultType.SKIPPED: out.style(Style.Info).println(" SKIPPED") break } logger.lifecycle("") } } dependencies { lintChecks project(':tooling-lint') } android { testOptions { unitTests { includeAndroidResources = true } } packagingOptions { resources { excludes += ['META-INF/atomicfu.kotlin_module', 'META-INF/AL2.0', 'META-INF/LGPL2.1'] // Required dependencies using byte-buddy; remove after this is // fixed by: https://issuetracker.google.com/issues/170131605 excludes.add("META-INF/licenses/ASM") pickFirsts += ['win32-x86-64/attach_hotspot_windows.dll', 'win32-x86/attach_hotspot_windows.dll'] } } androidResources { ignoreAssetsPattern "manifest.template.json" } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } tasks.withType(KotlinCompile).configureEach { kotlinOptions.jvmTarget = "17" kotlinOptions.freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn"] } } if (project.hasProperty("coverage") && project.name != "support-test") { android.buildTypes.all { buildType -> tasks.withType(Test).configureEach() { jacoco { includeNoLocationClasses = true excludes = ['jdk.internal.*'] } finalizedBy { "jacoco${buildType.name.capitalize()}TestReport" } } tasks.register("jacoco${buildType.name.capitalize()}TestReport", JacocoReport) { reports { xml.required = true html.required = true } def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*'] def kotlinDebugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/${buildType.name}", excludes: fileFilter) def javaDebugTree = fileTree(dir: "$project.buildDir/intermediates/classes/${buildType.name}", excludes: fileFilter) def mainSrc = "$project.projectDir/src/main/java" sourceDirectories.setFrom(files([mainSrc])) classDirectories.setFrom(files([kotlinDebugTree, javaDebugTree])) getExecutionData().setFrom(fileTree(project.buildDir).include([ "jacoco/test${buildType.name.capitalize()}UnitTest.exec" ])) } } android { buildTypes { debug { testCoverageEnabled true } } } } } } } tasks.register("clean", Delete) { delete rootProject.buildDir } detekt { input = files("$projectDir/components", "$projectDir/buildSrc", "$projectDir/samples") config = files("$projectDir/config/detekt.yml") baseline = file("$projectDir/config/detekt-baseline.xml") reports { html { enabled = true destination = file("$projectDir/build/reports/detekt.html") } xml { enabled = false } txt { enabled = false } } } tasks.withType(Detekt).configureEach() { // Custom detekt rules should be build before // See https://arturbosch.github.io/detekt/extensions.html#pitfalls dependsOn(":tooling-detekt:assemble") autoCorrect = true exclude "**/build.gradle.kts" exclude "**/src/androidTest/**" exclude "**/src/iosTest/**" exclude "**/src/test/**" exclude "**/test/src/**" exclude "**/build/**" exclude "**/resources/**" exclude "**/tmp/**" exclude "**/tooling/fetch/tests/**" exclude "**/tooling/fetch-tests/**" } // Apply same path exclusions as for the main task tasks.withType(DetektCreateBaselineTask).configureEach() { exclude "**/src/androidTest/**" exclude "**/src/test/**" exclude "**/test/src/**" exclude "**/build/**" exclude "**/resources/**" exclude "**/tmp/**" exclude "**/tooling/fetch/tests/**" exclude "**/tooling/fetch-tests/**" } configurations { ktlint } dependencies { ktlint("com.pinterest:ktlint:${Versions.ktlint}") { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL)) } } detektPlugins project(":tooling-detekt") } tasks.register("ktlint", JavaExec) { group = "verification" description = "Check Kotlin code style." classpath = configurations.ktlint mainClass.set("com.pinterest.ktlint.Main") args "components/**/*.kt" , "samples/**/*.kt", "!**/build/**/*.kt", "buildSrc/**/*.kt", "--baseline=ktlint-baseline.xml" } tasks.register("ktlintFormat", JavaExec) { group = "formatting" description = "Fix Kotlin code style deviations." classpath = configurations.ktlint mainClass.set("com.pinterest.ktlint.Main") args "-F", "components/**/*.kt" , "samples/**/*.kt", "!**/build/**/*.kt", "buildSrc/**/*.kt", "--baseline=ktlint-baseline.xml" jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } tasks.register("listRepositories") { doLast { println "Repositories:" project.repositories.each { println "Name: " + it.name + "; url: " + it.url } } } tasks.register("githubBuildDetails", GithubDetailsTask) { text = """### Test result files - [Test Coverage]({reportsUrl}/jacoco/jacocoReleaseTestReport/html/index.html) - [Unit Tests]({reportsUrl}/tests/testReleaseUnitTest/index.html) - [Android Lint]({reportsUrl}/lint-results-release.html)""" } tasks.register("githubLintDetektDetails", GithubDetailsTask) { text = "### [Detekt Results Android-Components]({reportsUrl}/detekt.html)" } tasks.register("githubLintAndroidDetails", GithubDetailsTask) { text = "### [Android Lint Results Android-Components]({reportsUrl}/lint-results-debug.html)" } tasks.register("testToolsDir", Exec) { group = "verification" description = "Run tests in the tools/ directory." workingDir = "tools" commandLine = ["python3", "test_list_compatible_dependency_versions.py"] }