import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask import org.jetbrains.kotlin.gradle.tasks.KotlinCompile // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> maven { url = repository if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) { allowInsecureProtocol = true } } } } dependencies { classpath libs.tools.androidgradle classpath libs.osslicenses.plugin classpath libs.glean.gradle.plugin classpath "${ApplicationServicesConfig.groupId}:tooling-nimbus-gradle:${ApplicationServicesConfig.version}" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } plugins { id "mozac.ConfigPlugin" alias(libs.plugins.detekt) alias(libs.plugins.kotlin.android) apply false alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.ksp) } detekt { input = files("$projectDir/app") config = files("$projectDir/quality/detekt.yml") baseline = file("$projectDir/quality/detekt-baseline.xml") reports { html { enabled = true destination = file("$projectDir/build/reports/detekt.html") } xml { enabled = false } txt { enabled = false } } } tasks.withType(Detekt).configureEach() { autoCorrect = true exclude "**/test/**" exclude "**/androidTest/**" exclude "**/build/**" exclude "**/resources/**" exclude "**/tmp/**" } // Apply same path exclusions as for the main task tasks.withType(DetektCreateBaselineTask).configureEach() { exclude "**/test/**" exclude "**/androidTest/**" exclude "**/build/**" exclude "**/resources/**" exclude "**/tmp/**" } allprojects { repositories { gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> maven { url = repository if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) { allowInsecureProtocol = true } } } maven { url = "${gradle.mozconfig.topobjdir}/gradle/maven" } } tasks.withType(KotlinCompile).configureEach { compilerOptions.allWarningsAsErrors = true } } subprojects { afterEvaluate { kotlin { jvmToolchain(config.jvmTargetCompatibility) } if (it.hasProperty('android')) { android { buildToolsVersion gradle.mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION } } } 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 = libs.versions.mozilla.glean.get().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: ${libs.versions.mozilla.glean.get()}") } else { // Enforce that all (transitive) dependencies are using the defined Glean version details.useVersion libs.versions.mozilla.glean.get() } } } 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' } } tasks.withType(KotlinCompile).configureEach { task -> // Translate Kotlin messages like "w: ..." and "e: ..." into // "...: warning: ..." and "...: error: ...", to make Treeherder understand. def listener = { if (it.startsWith("e: warnings found")) { return } if (it.startsWith('w: ') || it.startsWith('e: ')) { def matches = (it =~ /([ew]): (.+):(\d+):(\d+) (.*)/) if (!matches) { logger.quiet "kotlinc message format has changed!" if (it.startsWith('w: ')) { // For warnings, don't continue because we don't want to throw an // exception. For errors, we want the exception so that the new error // message format gets translated properly. return } } def (_, type, file, line, column, message) = matches[0] type = (type == 'w') ? 'warning' : 'error' // Use logger.lifecycle, which does not go through stderr again. logger.lifecycle "$file:$line:$column: $type: $message" } } as StandardOutputListener doFirst { logging.addStandardErrorListener(listener) } doLast { logging.removeStandardErrorListener(listener) } } } tasks.register('clean', Delete) { delete rootProject.layout.buildDirectory } configurations { ktlint } dependencies { ktlint(libs.ktlint) { attributes { attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL)) } } } tasks.register('ktlint', JavaExec) { description = "Check Kotlin code style." classpath = configurations.ktlint mainClass.set("com.pinterest.ktlint.Main") args "app/**/*.kt" args "!**/build/**/*.kt" args "buildSrc/**/*.kt" args "--reporter=json,output=build/reports/ktlint/ktlint.json" args "--reporter=plain" } tasks.register('ktlintFormat', JavaExec) { description = "Fix Kotlin code style deviations." classpath = configurations.ktlint mainClass.set("com.pinterest.ktlint.Main") args "-F" args "app/**/*.kt" args "!**/build/**/*.kt" args "buildSrc/**/*.kt" args "--reporter=json,output=build/reports/ktlint/ktlintFormat.json" args "--reporter=plain" jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED") } tasks.register("listRepositories") { def reposData = project.provider { project.repositories.collect { repo -> [name: repo.name, url: repo.url.toString()] } } doLast { println "Repositories:" reposData.get().each { println "Name: " + it.name + "; url: " + it.url } } }