tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

build.gradle (15467B)


      1 // Top-level build file where you can add configuration options common to all sub-projects/modules.
      2 
      3 import io.gitlab.arturbosch.detekt.Detekt
      4 import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
      5 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
      6 
      7 import static org.gradle.api.tasks.testing.TestResult.ResultType
      8 
      9 buildscript {
     10    repositories {
     11        gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
     12            maven {
     13                url = repository
     14                if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
     15                    allowInsecureProtocol = true
     16                }
     17            }
     18        }
     19    }
     20 
     21    dependencies {
     22        classpath libs.android.gradle.plugin
     23    }
     24 }
     25 
     26 plugins {
     27    alias(libs.plugins.detekt)
     28    alias(libs.plugins.kotlin.android) apply false
     29    alias(libs.plugins.kotlin.compose) apply false
     30    alias(libs.plugins.ksp)
     31 }
     32 
     33 allprojects {
     34    repositories {
     35        gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository ->
     36            maven {
     37                url = repository
     38                if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) {
     39                    allowInsecureProtocol = true
     40                }
     41            }
     42        }
     43 
     44        maven {
     45            url = "${gradle.mozconfig.topobjdir}/gradle/maven"
     46        }
     47    }
     48 
     49    ext {
     50        gleanVersion = libs.versions.glean.get()
     51    }
     52 
     53    // Enable Kotlin warnings as errors for all modules
     54    tasks.withType(KotlinCompile).configureEach {
     55        compilerOptions.allWarningsAsErrors = true
     56    }
     57 
     58    if (gradle.mozconfig.substs.DOWNLOAD_ALL_GRADLE_DEPENDENCIES) {
     59        // Work around https://github.com/google/ksp/issues/1964 by explicitly
     60        // declaring dependencies that are dynamically added during Gradle task
     61        // execution.
     62        pluginManager.withPlugin('com.google.devtools.ksp') {
     63            project.configurations {
     64                kspDependencies
     65            }
     66 
     67            project.dependencies {
     68                kspDependencies libs.ksp.symbol.processing.aa
     69                kspDependencies libs.ksp.symbol.processing.aa.embeddable
     70                kspDependencies libs.ksp.symbol.processing.api
     71                kspDependencies libs.ksp.symbol.processing.common.deps
     72            }
     73        }
     74    }
     75 }
     76 
     77 subprojects {
     78    apply plugin: 'jacoco'
     79 
     80    // Prevent some dependencies used by Fenix/Focus from being used in AC.
     81    project.configurations.all {
     82        exclude group: 'com.adjust.sdk', module: 'adjust-android'
     83        exclude group: 'io.mockk', module: 'mockk'
     84    }
     85 
     86    project.configurations.configureEach {
     87        // Dependencies can't depend on a different major version of Glean than A-C itself.
     88        resolutionStrategy.eachDependency { details ->
     89            if (details.requested.group == 'org.mozilla.telemetry'
     90                    && details.requested.name.contains('glean') ) {
     91                def requested = details.requested.version.tokenize(".")
     92                def defined = project.ext.gleanVersion.tokenize(".")
     93                // Check the major version
     94                if (requested[0] != defined[0]) {
     95                    throw new AssertionError("Cannot resolve to a single Glean version. Requested: ${details.requested.version}, A-C uses: ${project.ext.gleanVersion}")
     96                } else {
     97                    // Enforce that all (transitive) dependencies are using the defined Glean version
     98                    details.useVersion project.ext.gleanVersion
     99                }
    100            }
    101        }
    102 
    103        resolutionStrategy.capabilitiesResolution.withCapability("org.mozilla.telemetry:glean-native") {
    104            def toBeSelected = candidates.find { it.id instanceof ModuleComponentIdentifier && it.id.module.contains('geckoview') }
    105            if (toBeSelected != null) {
    106                select(toBeSelected)
    107            }
    108            because 'use GeckoView Glean instead of standalone Glean'
    109        }
    110    }
    111 
    112    if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) {
    113        if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) {
    114            ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir"
    115        }
    116        ext.topsrcdir = gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir"
    117        apply from: "${topsrcdir}/substitute-local-geckoview.gradle"
    118    }
    119 
    120    afterEvaluate {
    121        if (it.hasProperty('android')) {
    122            // Format test output
    123            tasks.matching {it instanceof Test}.configureEach() {
    124                systemProperty "robolectric.logging", "stdout"
    125                systemProperty "logging.test-mode", "true"
    126                systemProperty "javax.net.ssl.trustStoreType", "JKS"
    127 
    128                testLogging.events = []
    129 
    130                beforeSuite { descriptor ->
    131                    if (descriptor.getClassName() != null) {
    132                        println("\nSUITE: " + descriptor.getClassName())
    133                    }
    134                }
    135 
    136                beforeTest { descriptor ->
    137                    println("  TEST: " + descriptor.getName())
    138                }
    139 
    140                onOutput { descriptor, event ->
    141                    it.logger.lifecycle("    " + event.message.trim())
    142                }
    143 
    144                afterTest { descriptor, result ->
    145                    switch (result.getResultType()) {
    146                        case ResultType.SUCCESS:
    147                            println("  SUCCESS")
    148                            break
    149 
    150                        case ResultType.FAILURE:
    151                            def testId = descriptor.getClassName() + "." + descriptor.getName()
    152                            println("  TEST-UNEXPECTED-FAIL | " + testId + " | " + result.getException())
    153                            break
    154 
    155                        case ResultType.SKIPPED:
    156                            println("  SKIPPED")
    157                            break
    158                    }
    159                    it.logger.lifecycle("")
    160                }
    161            }
    162 
    163            dependencies {
    164                lintChecks project(':components:tooling-lint')
    165            }
    166 
    167            kotlin {
    168                jvmToolchain(config.jvmTargetCompatibility)
    169            }
    170 
    171            android {
    172                // We can't have one baseline file at the root of android-components because
    173                // this is not a project module and we would have to coordinate every module to
    174                // merge baselines.
    175                lint {
    176                    baseline = file("${projectDir}/lint-baseline.xml")
    177                }
    178 
    179                buildToolsVersion gradle.mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
    180 
    181                testOptions {
    182                    testCoverage {
    183                        jacocoVersion = libs.versions.jacoco.get()
    184                    }
    185                    unitTests {
    186                        includeAndroidResources = true
    187                    }
    188                }
    189 
    190                packaging {
    191                    resources {
    192                        excludes += ['META-INF/LICENSE.md', 'META-INF/LICENSE-notice.md']
    193                    }
    194                }
    195 
    196                androidResources {
    197                    ignoreAssetsPattern = "manifest.template.json"
    198                }
    199            }
    200 
    201            if (project.name != "support-test") {
    202                android.buildTypes.all { buildType ->
    203                    tasks.withType(Test).configureEach() {
    204                        jacoco {
    205                            includeNoLocationClasses = true
    206                            excludes = ['jdk.internal.*']
    207                        }
    208 
    209                        finalizedBy { "jacoco${buildType.name.capitalize()}TestReport" }
    210                    }
    211 
    212                    tasks.register("jacoco${buildType.name.capitalize()}TestReport", JacocoReport) {
    213                        reports {
    214                            xml.required = true
    215                            html.required = true
    216                        }
    217 
    218                        def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*',
    219                                          '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*']
    220                        def kotlinDebugTree = fileTree(dir: "$project.layout.buildDirectory/tmp/kotlin-classes/${buildType.name}", excludes: fileFilter)
    221                        def javaDebugTree = fileTree(dir: "$project.layout.buildDirectory/intermediates/classes/${buildType.name}", excludes: fileFilter)
    222                        def mainSrc = "$project.projectDir/src/main/java"
    223 
    224                        sourceDirectories.setFrom(files([mainSrc]))
    225                        classDirectories.setFrom(files([kotlinDebugTree, javaDebugTree]))
    226                        getExecutionData().setFrom(fileTree(project.layout.buildDirectory).include([
    227                                "jacoco/test${buildType.name.capitalize()}UnitTest.exec"
    228                        ]))
    229                    }
    230                }
    231 
    232                android {
    233                    buildTypes {
    234                        debug {
    235                            // Enable test coverage when fetching dependencies: it's an easy way to
    236                            // ensure we fetch the Jacoco agent dependency `org.jacoco.agent`, which
    237                            // resolves to `org.jacoco.agent-$VERSION-runtime.jar`.
    238                            testCoverageEnabled = project.hasProperty("coverage") || gradle.mozconfig.substs.DOWNLOAD_ALL_GRADLE_DEPENDENCIES
    239                        }
    240                    }
    241                }
    242            }
    243        }
    244    }
    245 
    246    tasks.withType(KotlinCompile).configureEach {
    247        // Translate Kotlin messages like "w: ..." and "e: ..." into
    248        // "...: warning: ..." and "...: error: ...", to make Treeherder understand.
    249        def listener = {
    250            if (it.startsWith("e: warnings found")) {
    251                return
    252            }
    253 
    254            if (it.startsWith('w: ') || it.startsWith('e: ')) {
    255                def matches = (it =~ /([ew]): (.+):(\d+):(\d+) (.*)/)
    256                if (!matches) {
    257                    logger.quiet "kotlinc message format has changed!"
    258                    if (it.startsWith('w: ')) {
    259                        // For warnings, don't continue because we don't want to throw an
    260                        // exception. For errors, we want the exception so that the new error
    261                        // message format gets translated properly.
    262                        return
    263                    }
    264                }
    265                def (_, type, file, line, column, message) = matches[0]
    266                type = (type == 'w') ? 'warning' : 'error'
    267                // Use logger.lifecycle, which does not go through stderr again.
    268                logger.lifecycle "$file:$line:$column: $type: $message"
    269            }
    270        } as StandardOutputListener
    271 
    272        doFirst {
    273            logging.addStandardErrorListener(listener)
    274        }
    275        doLast {
    276            logging.removeStandardErrorListener(listener)
    277        }
    278    }
    279 
    280    tasks.withType(AbstractTestTask).configureEach {
    281        failOnNoDiscoveredTests = false
    282    }
    283 }
    284 
    285 if (findProject(":geckoview") == null) {
    286    // Avoid adding this task if it already exists in a different root project.
    287    tasks.register("clean", Delete) {
    288        delete rootProject.layout.buildDirectory
    289    }
    290 }
    291 
    292 detekt {
    293    input = files("$projectDir/components", "$projectDir/buildSrc", "$projectDir/samples")
    294    config = files("$projectDir/config/detekt.yml")
    295    baseline = file("$projectDir/config/detekt-baseline.xml")
    296 
    297    reports {
    298        html {
    299            enabled = true
    300            destination = file("$projectDir/build/reports/detekt.html")
    301        }
    302        xml {
    303            enabled = false
    304        }
    305        txt {
    306            enabled = false
    307        }
    308    }
    309 }
    310 
    311 tasks.named("detekt").configure {
    312    reports {
    313        custom {
    314            reportId = "suppression-count"
    315            outputLocation.set(file("$projectDir/build/reports/suppressions.txt"))
    316        }
    317    }
    318 }
    319 
    320 tasks.withType(Detekt).configureEach() {
    321    // Custom detekt rules should be built before.
    322    // See https://detekt.dev/docs/introduction/extensions#pitfalls
    323    dependsOn(":components:tooling-detekt:assemble")
    324 
    325    autoCorrect = true
    326 
    327    exclude "**/build.gradle.kts"
    328    exclude "**/build/**"
    329    exclude "**/docs/**"
    330    exclude "**/resources/**"
    331    exclude "**/src/androidTest/**"
    332    exclude "**/src/iosTest/**"
    333    exclude "**/src/main/assets/extensions/**"
    334    exclude "**/src/test/**"
    335    exclude "**/test/src/**"
    336    exclude "**/tmp/**"
    337    exclude "**/tooling/fetch-tests/**"
    338 }
    339 
    340 // Apply same path exclusions as for the main task
    341 tasks.withType(DetektCreateBaselineTask).configureEach() {
    342    dependsOn(":components:browser-icons:updateBuiltInExtensionVersion")
    343    dependsOn(":components:feature-accounts:updateBuiltInExtensionVersion")
    344    dependsOn(":components:feature-readerview:updateBuiltInExtensionVersion")
    345    dependsOn(":components:feature-search:updateAdsExtensionVersion")
    346    dependsOn(":components:feature-search:updateCookiesExtensionVersion")
    347    dependsOn(":components:samples-browser:updateBorderifyExtensionVersion")
    348    dependsOn(":components:samples-browser:updateTestExtensionVersion")
    349    dependsOn(":components:samples-compose-browser:updateBorderifyExtensionVersion")
    350    dependsOn(":components:samples-compose-browser:updateTestExtensionVersion")
    351    dependsOn(":components:tooling-detekt:assemble")
    352 
    353    exclude "**/build.gradle.kts"
    354    exclude "**/build/**"
    355    exclude "**/docs/**"
    356    exclude "**/resources/**"
    357    exclude "**/src/androidTest/**"
    358    exclude "**/src/iosTest/**"
    359    exclude "**/src/main/assets/extensions/**"
    360    exclude "**/src/test/**"
    361    exclude "**/test/src/**"
    362    exclude "**/tmp/**"
    363    exclude "**/tooling/fetch-tests/**"
    364 }
    365 
    366 configurations {
    367    ktlint
    368 
    369    detektDependencies
    370 }
    371 
    372 dependencies {
    373    ktlint(libs.ktlint) {
    374        attributes {
    375            attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL))
    376        }
    377    }
    378    detektPlugins project(":components:tooling-detekt")
    379    detekt libs.detekt.cli
    380 
    381    detektDependencies libs.detekt.cli
    382 }
    383 
    384 tasks.register("ktlint", JavaExec) {
    385    group = "verification"
    386    description = "Check Kotlin code style."
    387    classpath = configurations.ktlint
    388    mainClass.set("com.pinterest.ktlint.Main")
    389    args "components/**/*.kt"
    390    args "samples/**/*.kt"
    391    args "!**/build/**/*.kt"
    392    args "buildSrc/**/*.kt"
    393    args "--reporter=json,output=build/reports/ktlint/ktlint.json"
    394    args "--reporter=plain"
    395 }
    396 
    397 tasks.register("ktlintFormat", JavaExec) {
    398    group = "formatting"
    399    description = "Fix Kotlin code style deviations."
    400    classpath = configurations.ktlint
    401    mainClass.set("com.pinterest.ktlint.Main")
    402    args "-F"
    403    args "components/**/*.kt"
    404    args "samples/**/*.kt"
    405    args "!**/build/**/*.kt"
    406    args "buildSrc/**/*.kt"
    407    args "--reporter=json,output=build/reports/ktlint/ktlintFormat.json"
    408    args "--reporter=plain"
    409    jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
    410 }
    411 
    412 tasks.register("listRepositories") {
    413    def reposData = project.provider {
    414        project.repositories.collect { repo ->
    415            [name: repo.name, url: repo.url.toString()]
    416        }
    417    }
    418    doLast {
    419        println "Repositories:"
    420        reposData.get().each { println "Name: " + it.name + "; url: " + it.url }
    421    }
    422 }