build.gradle (41718B)
1 import com.android.build.api.variant.FilterConfiguration 2 import com.google.android.gms.oss.licenses.plugin.DependencyTask 3 import groovy.json.JsonOutput 4 5 import java.time.format.DateTimeFormatter 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.kotlin.serialization 23 } 24 } 25 26 plugins { 27 alias(libs.plugins.kotlin.android) 28 alias(libs.plugins.kotlin.compose) 29 alias(libs.plugins.python.envs.plugin) 30 alias(libs.plugins.protobuf.plugin) 31 id 'org.mozilla.nimbus-gradle-plugin' 32 } 33 34 apply plugin: 'com.android.application' 35 apply plugin: 'kotlin-parcelize' 36 apply plugin: 'jacoco' 37 apply plugin: 'androidx.navigation.safeargs.kotlin' 38 apply plugin: 'kotlinx-serialization' 39 40 if (gradle.mozconfig.substs.MOZILLA_OFFICIAL) { 41 apply plugin: 'com.google.android.gms.oss-licenses-plugin' 42 } 43 44 apply from: 'benchmark.gradle' 45 46 ext.getAbi = { output -> 47 // Assume that if the ABI isn't set, it's an app bundle or universal APK 48 output.getFilter(FilterConfiguration.FilterType.ABI.name()) ?: "universal" 49 } 50 51 def isAppBundle = gradle.startParameter.taskNames.any { it.toLowerCase().contains("bundle") } 52 53 // Mimic Python: open(os.path.join(buildconfig.topobjdir, 'buildid.h')).readline().split()[2] 54 def getBuildId() { 55 if (System.env.MOZ_BUILD_DATE) { 56 if (System.env.MOZ_BUILD_DATE.length() == 14) { 57 return System.env.MOZ_BUILD_DATE 58 } 59 logger.warn("Ignoring invalid MOZ_BUILD_DATE: ${System.env.MOZ_BUILD_DATE}") 60 } 61 return file("${gradle.mozconfig.topobjdir}/buildid.h").getText('utf-8').split()[2] 62 } 63 64 def obtainTestBuildType() { 65 def result = "debug"; 66 if (project.hasProperty("testBuildType")) { 67 result = project.getProperties().get("testBuildType") 68 } 69 result 70 } 71 72 android { 73 testBuildType obtainTestBuildType() 74 75 project.maybeConfigForJetpackBenchmark(it) 76 if (project.hasProperty("testBuildType")) { 77 // Allowing to configure the test build type via command line flag (./gradlew -PtestBuildType=beta ..) 78 // in order to run UI tests against other build variants than debug in automation. 79 testBuildType project.property("testBuildType") 80 } 81 82 compileSdk { version = release(config.compileSdkMajorVersion) { minorApiLevel = config.compileSdkMinorVersion } } 83 84 defaultConfig { 85 applicationId "org.torproject" 86 minSdk config.minSdkVersion 87 targetSdk config.targetSdkVersion 88 versionCode 1 89 versionName Config.generateDebugVersionName() 90 vectorDrawables.useSupportLibrary = true 91 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 92 testInstrumentationRunnerArguments clearPackageData: 'true' 93 testInstrumentationRunnerArguments "detect-leaks": 'true' 94 resValue "bool", "IS_DEBUG", "false" 95 buildConfigField "boolean", "USE_RELEASE_VERSIONING", "false" 96 // Blank in debug builds so that tests are deterministic. 97 buildConfigField "String", "VCS_HASH", "\"\"" 98 // This should be the "public" base URL of AMO. 99 buildConfigField "String", "AMO_BASE_URL", "\"https://addons.mozilla.org\"" 100 buildConfigField "String", "AMO_COLLECTION_NAME", "\"Extensions-for-Android\"" 101 buildConfigField "String", "AMO_COLLECTION_USER", "\"mozilla\"" 102 // This should be the base URL used to call the AMO API. 103 buildConfigField "String", "AMO_SERVER_URL", "\"https://services.addons.mozilla.org\"" 104 105 def deepLinkSchemeValue = "torbrowser-dev" 106 buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" 107 108 manifestPlaceholders = [ 109 "deepLinkScheme": deepLinkSchemeValue 110 ] 111 112 buildConfigField "String[]", "SUPPORTED_LOCALE_ARRAY", getSupportedLocales() 113 buildConfigField "boolean", "IS_BENCHMARK_BUILD", "false" 114 115 buildConfigField "boolean", "DATA_COLLECTION_DISABLED", "true" 116 } 117 118 def releaseTemplate = { 119 // We allow disabling optimization by passing `-PdisableOptimization` to gradle. This is used 120 // in automation for UI testing non-debug builds. 121 shrinkResources = !project.hasProperty("disableOptimization") 122 minifyEnabled = !project.hasProperty("disableOptimization") 123 proguardFiles = [getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'] 124 matchingFallbacks = ['release'] // Use on the "release" build type in dependencies (AARs) 125 126 // Present in release builds. 127 if (gradle.mozconfig.substs.MOZ_INCLUDE_SOURCE_INFO) { 128 buildConfigField("String", "VCS_HASH", "\"" + gradle.mozconfig.source_repo.MOZ_SOURCE_STAMP + "\"") 129 } 130 131 // If building locally, just use debug signing. In official automation builds, the 132 // files are signed using a separate service and not within gradle. 133 if (!gradle.mozconfig.substs.MOZ_AUTOMATION) { 134 signingConfig = signingConfigs.debug 135 } 136 137 if (gradle.hasProperty("localProperties.debuggable") || 138 gradle.mozconfig.substs.MOZ_ANDROID_DEBUGGABLE) { 139 debuggable true 140 } 141 } 142 143 buildTypes { 144 debug { 145 shrinkResources = false 146 minifyEnabled = false 147 applicationIdSuffix ".torbrowser_debug" 148 resValue "bool", "IS_DEBUG", "true" 149 pseudoLocalesEnabled = true 150 } 151 nightly releaseTemplate >> { 152 applicationIdSuffix ".torbrowser_nightly" 153 buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" 154 def deepLinkSchemeValue = "torbrowser-nightly" 155 buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" 156 manifestPlaceholders.putAll([ 157 "deepLinkScheme": deepLinkSchemeValue 158 ]) 159 } 160 beta releaseTemplate >> { 161 buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" 162 applicationIdSuffix ".torbrowser_alpha" 163 def deepLinkSchemeValue = "torbrowser-alpha" 164 buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" 165 manifestPlaceholders.putAll([ 166 // This release type is meant to replace Firefox (Beta channel) and therefore needs to inherit 167 // its sharedUserId for all eternity. See: 168 // https://searchfox.org/mozilla-esr68/search?q=moz_android_shared_id&case=false®exp=false&path= 169 // Shipping an app update without sharedUserId can have 170 // fatal consequences. For example see: 171 // - https://issuetracker.google.com/issues/36924841 172 // - https://issuetracker.google.com/issues/36905922 173 "sharedUserId": "org.torproject.torbrowser_alpha.sharedID", 174 "deepLinkScheme": deepLinkSchemeValue, 175 ]) 176 } 177 release releaseTemplate >> { 178 buildConfigField "boolean", "USE_RELEASE_VERSIONING", "true" 179 applicationIdSuffix ".torbrowser" 180 def deepLinkSchemeValue = "torbrowser" 181 buildConfigField "String", "DEEP_LINK_SCHEME", "\"$deepLinkSchemeValue\"" 182 manifestPlaceholders.putAll([ 183 // This release type is meant to replace Firefox (Release channel) and therefore needs to inherit 184 // its sharedUserId for all eternity. See: 185 // https://searchfox.org/mozilla-esr68/search?q=moz_android_shared_id&case=false®exp=false&path= 186 // Shipping an app update without sharedUserId can have 187 // fatal consequences. For example see: 188 // - https://issuetracker.google.com/issues/36924841 189 // - https://issuetracker.google.com/issues/36905922 190 "sharedUserId": "org.torproject.torbrowser.sharedID", 191 "deepLinkScheme": deepLinkSchemeValue, 192 ]) 193 } 194 benchmark releaseTemplate >> { 195 initWith buildTypes.nightly 196 shrinkResources = false 197 minifyEnabled = false 198 applicationIdSuffix ".fenix" 199 signingConfig = signingConfigs.debug 200 debuggable false 201 buildConfigField "boolean", "IS_BENCHMARK_BUILD", "true" 202 } 203 } 204 205 buildFeatures { 206 viewBinding = true 207 buildConfig = true 208 } 209 210 androidResources { 211 // All JavaScript code used internally by GeckoView is packaged in a 212 // file called omni.ja. If this file is compressed in the APK, 213 // GeckoView must uncompress it before it can do anything else which 214 // causes a significant delay on startup. 215 noCompress 'ja' 216 217 // manifest.template.json is converted to manifest.json at build time. 218 // No need to package the template in the APK. 219 ignoreAssetsPattern = "manifest.template.json" 220 } 221 222 testOptions { 223 animationsDisabled = true 224 execution = 'ANDROIDX_TEST_ORCHESTRATOR' 225 testCoverage { 226 jacocoVersion = libs.versions.jacoco.get() 227 } 228 unitTests { 229 all { 230 // We keep running into memory issues when running our tests. With this config we 231 // reserve more memory and also create a new process after every 80 test classes. This 232 // is a band-aid solution and eventually we should try to find and fix the leaks 233 // instead. :) 234 forkEvery = 80 235 maxHeapSize = "3072m" 236 minHeapSize = "1024m" 237 } 238 includeAndroidResources = true 239 returnDefaultValues = true 240 } 241 } 242 243 244 sourceSets { 245 androidTest { 246 resources.srcDirs += ['src/androidTest/resources'] 247 } 248 if (project.hasProperty('baselineProfilePath')) { 249 main { 250 baselineProfiles.srcDirs(project.property('baselineProfilePath')) 251 } 252 } 253 } 254 255 splits { 256 abi { 257 enable = !isAppBundle 258 259 reset() 260 261 // As gradle is unable to pick the right apk to install when multiple apks are generated 262 // while running benchmark tests or generating baseline profiles. To circumvent this, 263 // this flag is passed to make sure only one apk is generated so gradle can pick that one. 264 if (project.hasProperty("benchmarkTest")) { 265 include "arm64-v8a" 266 } else { 267 include "armeabi-v7a", "arm64-v8a", "x86_64" 268 if (gradle.mozconfig.substs.MOZILLA_OFFICIAL) { 269 universalApk true 270 } 271 } 272 } 273 } 274 275 bundle { 276 language { 277 // Because we have runtime language selection we will keep all strings and languages 278 // in the base APKs. 279 enableSplit = false 280 } 281 } 282 283 // Exclude native libraries for unsupported architectures from third-party dependencies 284 packagingOptions { 285 jniLibs { 286 excludes += ["**/armeabi/*.so", "**/mips/*.so", "**/mips64/*.so", "**/x86/*.so"] 287 } 288 } 289 290 lint { 291 lintConfig = file("lint.xml") 292 baseline = file("lint-baseline.xml") 293 sarifReport = true 294 sarifOutput = file("../build/reports/lint/lint-report.sarif.json") 295 abortOnError = false 296 } 297 298 packaging { 299 if (!isAppBundle) { 300 jniLibs { 301 useLegacyPackaging = true 302 } 303 } 304 resources { 305 excludes += ["META-INF/LICENSE.md", "META-INF/LICENSE-notice.md"] 306 } 307 } 308 309 buildFeatures { 310 compose = true 311 } 312 313 namespace = 'org.mozilla.fenix' 314 } 315 316 android.applicationVariants.configureEach { variant -> 317 318 // ------------------------------------------------------------------------------------------------- 319 // Generate version codes for builds 320 // ------------------------------------------------------------------------------------------------- 321 322 def isDebug = variant.buildType.resValues['bool/IS_DEBUG']?.value ?: false 323 def isDataCollectionDisabled = variant.buildType.buildConfigFields['DATA_COLLECTION_DISABLED']?.value ?: true 324 def isDebugOrDCD = isDebug || isDataCollectionDisabled 325 def useReleaseVersioning = variant.buildType.buildConfigFields['USE_RELEASE_VERSIONING']?.value ?: false 326 327 // When this is set, a-s doesn't attempt to download NIMBUS_FML. 328 if (gradle.mozconfig.substs.NIMBUS_FML) { 329 System.setProperty("nimbusFml", gradle.mozconfig.substs.NIMBUS_FML) 330 } 331 332 def disableTor = false 333 if (project.hasProperty("disableTor")) { 334 disableTor = project.getProperty("disableTor") 335 } 336 337 project.logger.debug("----------------------------------------------") 338 project.logger.debug("Variant name: " + variant.name) 339 project.logger.debug("Application ID: " + [variant.applicationId, variant.buildType.applicationIdSuffix].findAll().join()) 340 project.logger.debug("Build type: " + variant.buildType.name) 341 project.logger.debug("Flavor: " + variant.flavorName) 342 project.logger.debug("Telemetry enabled: " + !isDebugOrDCD) 343 project.logger.debug("nimbusFml: " + System.getProperty("nimbusFml")) 344 project.logger.debug("Tor is disabled: " + disableTor) 345 346 buildConfigField "boolean", "DISABLE_TOR", "$disableTor" 347 348 if (useReleaseVersioning) { 349 // The Google Play Store does not allow multiple APKs for the same app that all have the 350 // same version code. Therefore we need to have different version codes for our ARM and x86 351 // builds. 352 353 def versionName = variant.buildType.name == 'nightly' ? 354 "${Config.nightlyVersionName(project)}" : 355 "${Config.releaseVersionName(project)}" 356 project.logger.debug("versionName override: $versionName") 357 358 variant.outputs.each { output -> 359 // We use the same version code generator, that we inherited from Fennec, across all channels - even on 360 // channels that never shipped a Fennec build. 361 def abi = getAbi(output) 362 def versionCodeOverride = Config.generateFennecVersionCode(abi) 363 364 project.logger.debug("versionCode for $abi = $versionCodeOverride") 365 366 if (versionName != null) { 367 output.versionNameOverride = versionName 368 } 369 output.versionCodeOverride = versionCodeOverride 370 } 371 } else if (gradle.hasProperty("localProperties.branchBuild.fenix.version")) { 372 def versionName = gradle.getProperty("localProperties.branchBuild.fenix.version") 373 project.logger.debug("versionName override: $versionName") 374 variant.outputs.each { output -> 375 output.versionNameOverride = versionName 376 } 377 } 378 379 // ------------------------------------------------------------------------------------------------- 380 // BuildConfig: Set variables for Sentry, Crash Reporting, and Telemetry 381 // ------------------------------------------------------------------------------------------------- 382 383 buildConfigField 'String', 'SENTRY_TOKEN', 'null' 384 if (!isDebugOrDCD) { 385 buildConfigField 'boolean', 'CRASH_REPORTING', 'true' 386 // Reading sentry token from local file (if it exists). In a release task on taskcluster it will be available. 387 try { 388 def token = new File("${rootDir}/.sentry_token").text.trim() 389 buildConfigField 'String', 'SENTRY_TOKEN', '"' + token + '"' 390 } catch (FileNotFoundException ignored) {} 391 } else { 392 buildConfigField 'boolean', 'CRASH_REPORTING', 'false' 393 } 394 395 if (!isDebugOrDCD) { 396 buildConfigField 'boolean', 'TELEMETRY', 'true' 397 } else { 398 buildConfigField 'boolean', 'TELEMETRY', 'false' 399 } 400 401 // Setting buildDate with every build ID changes the generated BuildConfig, which slows down the 402 // build. Only do this for non-debug builds, to speed-up builds produced during local development. 403 if (isDebug) { 404 buildConfigField 'String', 'BUILD_DATE', '"debug build"' 405 } else { 406 def buildDate = LocalDateTime.parse(getBuildId(), DateTimeFormatter.ofPattern("yyyyMMddHHmmss")).toString() 407 buildConfigField 'String', 'BUILD_DATE', '"' + buildDate + '"' 408 } 409 410 // ------------------------------------------------------------------------------------------------- 411 // Adjust: Read token from local file if it exists (Only release builds) 412 // ------------------------------------------------------------------------------------------------- 413 414 project.logger.debug("Adjust token: ") 415 416 if (!isDebug) { 417 try { 418 def token = new File("${rootDir}/.adjust_token").text.trim() 419 buildConfigField 'String', 'ADJUST_TOKEN', '"' + token + '"' 420 project.logger.debug("(Added from .adjust_token file)") 421 } catch (FileNotFoundException ignored) { 422 buildConfigField 'String', 'ADJUST_TOKEN', 'null' 423 project.logger.debug("X_X") 424 } 425 } else { 426 buildConfigField 'String', 'ADJUST_TOKEN', 'null' 427 project.logger.debug("--") 428 } 429 430 // ------------------------------------------------------------------------------------------------- 431 // MLS: Read token from local file if it exists 432 // ------------------------------------------------------------------------------------------------- 433 434 project.logger.debug("MLS token: ") 435 436 try { 437 def token = new File("${rootDir}/.mls_token").text.trim() 438 buildConfigField 'String', 'MLS_TOKEN', '"' + token + '"' 439 project.logger.debug("(Added from .mls_token file)") 440 } catch (FileNotFoundException ignored) { 441 buildConfigField 'String', 'MLS_TOKEN', '""' 442 project.logger.debug("X_X") 443 } 444 445 // ------------------------------------------------------------------------------------------------- 446 // Nimbus: Read endpoint from local.properties of a local file if it exists 447 // ------------------------------------------------------------------------------------------------- 448 449 project.logger.debug("Nimbus endpoint: ") 450 451 if (!isDebug) { 452 try { 453 def url = new File("${rootDir}/.nimbus").text.trim() 454 buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"' 455 project.logger.debug("(Added from .nimbus file)") 456 } catch (FileNotFoundException ignored) { 457 buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null' 458 project.logger.debug("X_X") 459 } 460 } else if (gradle.hasProperty("localProperties.nimbus.remote-settings.url")) { 461 def url=gradle.getProperty("localProperties.nimbus.remote-settings.url") 462 buildConfigField 'String', 'NIMBUS_ENDPOINT', '"' + url + '"' 463 project.logger.debug("(Added from local.properties file)") 464 } else { 465 buildConfigField 'String', 'NIMBUS_ENDPOINT', 'null' 466 project.logger.debug("--") 467 } 468 469 // ------------------------------------------------------------------------------------------------- 470 // Glean: Read custom server URL from local.properties of a local file if it exists 471 // ------------------------------------------------------------------------------------------------- 472 473 project.logger.debug("Glean custom server URL: ") 474 475 if (gradle.hasProperty("localProperties.glean.custom.server.url")) { 476 def url=gradle.getProperty("localProperties.glean.custom.server.url") 477 buildConfigField 'String', 'GLEAN_CUSTOM_URL', url 478 project.logger.debug("(Added from local.properties file)") 479 } else { 480 buildConfigField 'String', 'GLEAN_CUSTOM_URL', 'null' 481 project.logger.debug("--") 482 } 483 484 // ------------------------------------------------------------------------------------------------- 485 // BuildConfig: Set flag for official builds; similar to MOZILLA_OFFICIAL in mozilla-central. 486 // ------------------------------------------------------------------------------------------------- 487 488 if (project.hasProperty("official") || gradle.hasProperty("localProperties.official")) { 489 buildConfigField 'Boolean', 'MOZILLA_OFFICIAL', 'true' 490 } else { 491 buildConfigField 'Boolean', 'MOZILLA_OFFICIAL', 'false' 492 } 493 494 // ------------------------------------------------------------------------------------------------- 495 // BuildConfig: Set remote wallpaper URL using local file if it exists 496 // ------------------------------------------------------------------------------------------------- 497 498 project.logger.debug("Wallpaper URL: ") 499 500 try { 501 def token = new File("${rootDir}/.wallpaper_url").text.trim() 502 buildConfigField 'String', 'WALLPAPER_URL', '"' + token + '"' 503 project.logger.debug("(Added from .wallpaper_url file)") 504 } catch (FileNotFoundException ignored) { 505 buildConfigField 'String', 'WALLPAPER_URL', '""' 506 project.logger.debug("--") 507 } 508 509 // ------------------------------------------------------------------------------------------------- 510 // BuildConfig: Set the Pocket consumer key from a local file if it exists 511 // ------------------------------------------------------------------------------------------------- 512 513 project.logger.debug("Pocket consumer key: ") 514 515 try { 516 def token = new File("${rootDir}/.pocket_consumer_key").text.trim() 517 buildConfigField 'String', 'POCKET_CONSUMER_KEY', '"' + token + '"' 518 project.logger.debug("(Added from .pocket_consumer_key file)") 519 } catch (FileNotFoundException ignored) { 520 buildConfigField 'String', 'POCKET_CONSUMER_KEY', '""' 521 project.logger.debug("--") 522 } 523 524 // ------------------------------------------------------------------------------------------------- 525 // BuildConfig: Set flag to disable LeakCanary in debug (on CI builds) 526 // ------------------------------------------------------------------------------------------------- 527 528 if (isDebug) { 529 if (project.hasProperty("disableLeakCanary") || gradle.hasProperty("localProperties.disableLeakCanary")) { 530 buildConfigField "boolean", "LEAKCANARY", "false" 531 project.logger.debug("LeakCanary enabled in debug: false") 532 } else { 533 buildConfigField "boolean", "LEAKCANARY", "true" 534 project.logger.debug("LeakCanary enabled in debug: true") 535 } 536 } else { 537 buildConfigField "boolean", "LEAKCANARY", "false" 538 } 539 } 540 541 // Generate Kotlin code for the Fenix Glean metrics. 542 ext { 543 // Enable expiration by major version. 544 gleanExpireByVersion = Config.majorVersion(project) 545 gleanNamespace = "mozilla.telemetry.glean" 546 gleanPythonEnvDir = gradle.mozconfig.substs.GRADLE_GLEAN_PARSER_VENV 547 } 548 apply plugin: "org.mozilla.telemetry.glean-gradle-plugin" 549 550 nimbus { 551 // The path to the Nimbus feature manifest file 552 manifestFile = "nimbus.fml.yaml" 553 // The fully qualified class name for the generated features. 554 // Map from the variant name to the channel as experimenter and nimbus understand it. 555 // If nimbus's channels were accurately set up well for this project, then this 556 // shouldn't be needed. 557 channels = [ 558 debug: "developer", 559 nightly: "nightly", 560 beta: "beta", 561 release: "release", 562 benchmark: "developer", 563 ] 564 // This is generated by the FML and should be checked into git. 565 // It will be fetched by Experimenter (the Nimbus experiment website) 566 // and used to inform experiment configuration. 567 experimenterManifest = ".experimenter.yaml" 568 applicationServicesDir = gradle.hasProperty('localProperties.autoPublish.application-services.dir') 569 ? gradle.getProperty('localProperties.autoPublish.application-services.dir') : null 570 } 571 572 dependencies { 573 implementation project(':components:browser-engine-gecko') 574 implementation project(':components:compose-awesomebar') 575 implementation project(':components:compose-base') 576 implementation project(':components:compose-browser-toolbar') 577 implementation project(':components:compose-cfr') 578 implementation project(':components:concept-awesomebar') 579 implementation project(':components:concept-base') 580 implementation project(':components:concept-engine') 581 implementation project(':components:concept-menu') 582 implementation project(':components:concept-push') 583 implementation project(':components:concept-storage') 584 implementation project(':components:concept-sync') 585 implementation project(':components:concept-tabstray') 586 implementation project(':components:concept-toolbar') 587 implementation project(':components:browser-domains') 588 implementation project(':components:browser-icons') 589 implementation project(':components:browser-menu') 590 implementation project(':components:browser-menu2') 591 implementation project(':components:browser-session-storage') 592 implementation project(':components:browser-state') 593 implementation project(':components:browser-storage-sync') 594 implementation project(':components:browser-tabstray') 595 implementation project(':components:browser-thumbnails') 596 implementation project(':components:browser-toolbar') 597 implementation project(':components:feature-accounts') 598 implementation project(':components:feature-accounts-push') 599 implementation project(':components:feature-addons') 600 implementation project(':components:feature-app-links') 601 implementation project(':components:feature-autofill') 602 implementation project(':components:feature-awesomebar') 603 implementation project(':components:feature-contextmenu') 604 implementation project(':components:feature-customtabs') 605 implementation project(':components:feature-downloads') 606 implementation project(':components:feature-findinpage') 607 implementation project(':components:feature-fxsuggest') 608 implementation project(':components:feature-intent') 609 implementation project(':components:feature-logins') 610 implementation project(':components:feature-media') 611 implementation project(':components:feature-privatemode') 612 implementation project(':components:feature-prompts') 613 implementation project(':components:feature-push') 614 implementation project(':components:feature-pwa') 615 implementation project(':components:feature-qr') 616 implementation project(':components:feature-readerview') 617 implementation project(':components:feature-recentlyclosed') 618 implementation project(':components:feature-search') 619 implementation project(':components:feature-session') 620 implementation project(':components:feature-share') 621 implementation project(':components:feature-sitepermissions') 622 implementation project(':components:feature-syncedtabs') 623 implementation project(':components:feature-toolbar') 624 implementation project(':components:feature-tab-collections') 625 implementation project(':components:feature-tabs') 626 implementation project(':components:feature-top-sites') 627 implementation project(':components:feature-webauthn') 628 // implementation project(':components:feature-webcompat') 629 // implementation project(':components:feature-webcompat-reporter') 630 implementation project(':components:feature-webnotifications') 631 implementation project(':components:service-digitalassetlinks') 632 implementation project(':components:service-firefox-accounts') 633 implementation project(':components:service-glean') 634 implementation project(':components:service-location') 635 implementation project(':components:service-mars') 636 implementation project(':components:service-nimbus') 637 implementation project(':components:service-pocket') 638 implementation project(':components:service-sync-autofill') 639 implementation project(':components:service-sync-logins') 640 implementation project(':components:support-appservices') 641 implementation project(':components:support-base') 642 implementation project(':components:support-images') 643 implementation project(':components:support-ktx') 644 implementation project(':components:support-locale') 645 implementation project(':components:support-remotesettings') 646 implementation project(':components:support-utils') 647 implementation project(':components:support-webextensions') 648 implementation project(':components:lib-publicsuffixlist') 649 implementation project(':components:ui-icons') 650 implementation project(':components:ui-colors') 651 implementation project(':components:ui-tabcounter') 652 implementation project(':components:ui-widgets') 653 implementation project(':components:lib-crash') 654 implementation project(':components:lib-dataprotect') 655 implementation project(':components:lib-push-firebase') 656 implementation project(':components:lib-state') 657 658 implementation ComponentsDependencies.mozilla_appservices_syncmanager 659 implementation libs.accompanist.drawablepainter 660 implementation libs.androidx.activity 661 implementation libs.androidx.activity.ktx 662 implementation libs.androidx.annotation 663 implementation libs.androidx.appcompat 664 implementation libs.androidx.biometric 665 implementation platform(libs.androidx.compose.bom) 666 implementation libs.androidx.compose.animation 667 implementation libs.androidx.compose.foundation 668 implementation libs.androidx.compose.material.icons 669 implementation libs.androidx.compose.material3 670 implementation libs.androidx.compose.material3.adaptive 671 implementation libs.androidx.compose.material3.adaptivelayout 672 implementation libs.androidx.compose.material3.adaptivenavigation 673 implementation libs.androidx.compose.material3.adaptivenavigationsuite 674 implementation libs.androidx.compose.material3.windowsizeclass 675 implementation libs.androidx.compose.ui 676 implementation libs.androidx.compose.ui.tooling.preview 677 implementation libs.androidx.constraintlayout 678 implementation libs.androidx.coordinatorlayout 679 implementation libs.androidx.core 680 implementation libs.androidx.core.ktx 681 implementation libs.androidx.core.splashscreen 682 implementation libs.androidx.datastore 683 implementation libs.androidx.datastore.preferences 684 implementation libs.androidx.fragment 685 implementation libs.androidx.fragment.compose 686 implementation libs.androidx.lifecycle.common 687 implementation libs.androidx.lifecycle.livedata 688 implementation libs.androidx.lifecycle.process 689 implementation libs.androidx.lifecycle.runtime 690 implementation libs.androidx.lifecycle.service 691 implementation libs.androidx.lifecycle.viewmodel 692 implementation libs.androidx.localbroadcastmanager 693 implementation libs.androidx.navigation.compose 694 implementation libs.androidx.navigation.fragment 695 implementation libs.androidx.navigation.ui 696 implementation libs.androidx.navigation3.ui 697 implementation libs.androidx.navigation3.runtime 698 implementation libs.androidx.paging 699 implementation libs.androidx.preferences 700 implementation libs.androidx.profileinstaller 701 implementation libs.androidx.recyclerview 702 implementation libs.androidx.swiperefreshlayout 703 implementation libs.androidx.transition 704 implementation libs.androidx.viewpager2 705 implementation libs.androidx.work.runtime 706 implementation libs.google.material 707 implementation libs.installreferrer 708 implementation libs.kotlinx.coroutines 709 implementation libs.kotlinx.serialization.json 710 implementation libs.mozilla.glean 711 implementation libs.play.review 712 implementation libs.play.review.ktx 713 implementation libs.protobuf.javalite 714 715 debugImplementation libs.androidx.compose.ui.tooling 716 debugImplementation libs.leakcanary 717 718 testImplementation project(':components:support-test') 719 testImplementation project(':components:support-test-fakes') 720 testImplementation project(':components:support-test-libstate') 721 testImplementation testFixtures(project(':components:feature-downloads')) 722 723 testImplementation ComponentsDependencies.mozilla_appservices_full_megazord_libsForTests 724 725 testImplementation libs.androidx.compose.ui.test 726 testImplementation libs.androidx.test.core 727 testImplementation libs.androidx.test.junit 728 testImplementation libs.androidx.work.testing 729 testImplementation libs.kotlinx.coroutines.test 730 testImplementation libs.mockk 731 testImplementation libs.mozilla.glean.forUnitTests 732 testImplementation libs.robolectric 733 734 androidTestImplementation libs.androidx.benchmark.junit4 735 // We need a version constraint due to AGP silently trying to downgrade the version otherwise. 736 // https://issuetracker.google.com/issues/349628366 737 constraints { 738 implementation(libs.androidx.concurrent) 739 } 740 androidTestImplementation libs.androidx.concurrent 741 androidTestImplementation platform(libs.androidx.compose.bom) 742 androidTestImplementation libs.androidx.compose.ui.test 743 androidTestImplementation libs.androidx.test.core 744 androidTestImplementation(libs.androidx.test.espresso.contrib) { 745 exclude module: 'protobuf-lite' 746 } 747 androidTestImplementation libs.androidx.test.espresso.core 748 androidTestImplementation libs.androidx.test.espresso.idling.resource 749 androidTestImplementation libs.androidx.test.espresso.intents 750 androidTestImplementation libs.androidx.test.junit 751 androidTestImplementation libs.androidx.test.monitor 752 androidTestImplementation libs.androidx.test.rules 753 androidTestImplementation libs.androidx.test.runner 754 androidTestImplementation libs.androidx.test.uiautomator 755 androidTestImplementation libs.androidx.work.testing 756 androidTestImplementation libs.leakcanary.instrumentation 757 androidTestImplementation libs.mockk.android 758 androidTestImplementation libs.mockwebserver 759 760 androidTestUtil libs.androidx.test.orchestrator 761 762 lintChecks project(':components:tooling-lint') 763 764 // Tor Expert Bundle 765 implementation files(gradle.mozconfig.substs.TOR_EXPERT_BUNDLE_AAR) 766 } 767 768 protobuf { 769 protoc { 770 artifact = libs.protobuf.compiler.get() 771 } 772 773 // Generates the java Protobuf-lite code for the Protobufs in this project. See 774 // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation 775 // for more information. 776 generateProtoTasks { 777 all().each { task -> 778 task.builtins { 779 java { 780 option 'lite' 781 } 782 } 783 } 784 } 785 } 786 787 if (project.hasProperty("coverage")) { 788 tasks.withType(Test).configureEach { 789 jacoco.includeNoLocationClasses = true 790 jacoco.excludes = ['jdk.internal.*'] 791 } 792 793 android.applicationVariants.configureEach { variant -> 794 tasks.register("jacoco${variant.name.capitalize()}TestReport", JacocoReport) { 795 dependsOn "test${variant.name.capitalize()}UnitTest" 796 797 reports { 798 xml.required = true 799 html.required = true 800 } 801 802 def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', 803 '**/*Test*.*', 'android/**/*.*', '**/*$[0-9].*'] 804 def kotlinDebugTree = fileTree(dir: "$project.layout.buildDirectory/tmp/kotlin-classes/${variant.name}", excludes: fileFilter) 805 def javaDebugTree = fileTree(dir: "$project.layout.buildDirectory/intermediates/classes/${variant.flavorName}/${variant.buildType.name}", 806 excludes: fileFilter) 807 def mainSrc = "$project.projectDir/src/main/java" 808 809 sourceDirectories.setFrom(files([mainSrc])) 810 classDirectories.setFrom(files([kotlinDebugTree, javaDebugTree])) 811 executionData.setFrom(fileTree(dir: project.layout.buildDirectory, includes: [ 812 "jacoco/test${variant.name.capitalize()}UnitTest.exec", 813 'outputs/code-coverage/connected/*coverage.ec' 814 ])) 815 } 816 } 817 818 android { 819 buildTypes { 820 debug { 821 testCoverageEnabled = true 822 } 823 } 824 } 825 } 826 827 // ------------------------------------------------------------------------------------------------- 828 // Task for printing APK information for the requested variant 829 // Usage: "./gradlew printVariants 830 // ------------------------------------------------------------------------------------------------- 831 tasks.register('printVariants') { 832 def variants = project.provider { 833 android.applicationVariants.collect { variant -> [ 834 apks: variant.outputs.collect { output -> [ 835 abi: getAbi(output), 836 fileName: output.outputFile.name 837 ]}, 838 build_type: variant.buildType.name, 839 name: variant.name, 840 ]}} 841 doLast { 842 // AndroidTest is a special case not included above 843 variants.get().add([ 844 apks: [[ 845 abi: 'noarch', 846 fileName: 'app-debug-androidTest.apk', 847 ]], 848 build_type: 'androidTest', 849 name: 'androidTest', 850 ]) 851 println 'variants: ' + JsonOutput.toJson(variants.get()) 852 } 853 } 854 855 // Work around generated SafeArgs code hitting USELESS_CAST warnings with Kotlin 2.3+ 856 // https://issuetracker.google.com/issues/458082829 857 def safeArgsSrc = fileTree(layout.buildDirectory.dir("generated/source/navigation-args")) { 858 include "**/*.kt" 859 } 860 861 tasks.register("suppressUselessCastInSafeArgs") { 862 inputs.files(safeArgsSrc) 863 outputs.upToDateWhen { false } 864 865 doLast { 866 safeArgsSrc.each { File f -> 867 if (!f.text.contains('@file:Suppress("USELESS_CAST")')) { 868 f.text = 869 """@file:Suppress("USELESS_CAST") 870 871 ${f.text}""" 872 } 873 } 874 } 875 } 876 877 tasks.matching { it.name.startsWith("generateSafeArgs") }.configureEach { 878 finalizedBy tasks.named("suppressUselessCastInSafeArgs") 879 } 880 881 afterEvaluate { 882 883 // Format test output. Ported from AC #2401 884 tasks.withType(Test).configureEach { 885 systemProperty "robolectric.logging", "stdout" 886 systemProperty "logging.test-mode", "true" 887 888 testLogging.events = [] 889 890 beforeSuite { descriptor -> 891 if (descriptor.getClassName() != null) { 892 println("\nSUITE: " + descriptor.getClassName()) 893 } 894 } 895 896 beforeTest { descriptor -> 897 println(" TEST: " + descriptor.getName()) 898 } 899 900 onOutput { descriptor, event -> 901 it.logger.lifecycle(" " + event.message.trim()) 902 } 903 904 afterTest { descriptor, result -> 905 switch (result.getResultType()) { 906 case ResultType.SUCCESS: 907 println(" SUCCESS") 908 break 909 910 case ResultType.FAILURE: 911 def testId = descriptor.getClassName() + "." + descriptor.getName() 912 println(" TEST-UNEXPECTED-FAIL | " + testId + " | " + result.getException()) 913 break 914 915 case ResultType.SKIPPED: 916 println(" SKIPPED") 917 break 918 } 919 it.logger.lifecycle("") 920 } 921 } 922 } 923 924 if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopsrcdir')) { 925 if (gradle.hasProperty('localProperties.dependencySubstitutions.geckoviewTopobjdir')) { 926 ext.topobjdir = gradle."localProperties.dependencySubstitutions.geckoviewTopobjdir" 927 } 928 ext.topsrcdir = gradle."localProperties.dependencySubstitutions.geckoviewTopsrcdir" 929 apply from: "${topsrcdir}/substitute-local-geckoview.gradle" 930 } 931 932 android.applicationVariants.configureEach { variant -> 933 tasks.register("apkSize${variant.name.capitalize()}", ApkSizeTask) { 934 variantName = variant.name 935 apks = variant.outputs.collect { output -> output.outputFile.name } 936 dependsOn "package${variant.name.capitalize()}" 937 } 938 } 939 940 def getSupportedLocales() { 941 // This isn't running as a task, instead the array is build when the gradle file is parsed. 942 // https://github.com/mozilla-mobile/fenix/issues/14175 943 def foundLocales = new StringBuilder() 944 def languageCodes = [] 945 foundLocales.append("new String[]{") 946 947 fileTree("src/main/res").visit { FileVisitDetails details -> 948 if (details.file.path.endsWith("${File.separator}torbrowser_strings.xml")) { 949 def languageCode = details.file.parent.tokenize(File.separator).last().replaceAll('values-', '').replaceAll('-r', '-') 950 languageCode = (languageCode == "values") ? "en-US" : languageCode 951 languageCodes.add(languageCode) 952 } 953 } 954 955 // The order of files in a `FileTree` is not stable, even on a single 956 // computer. Thus we need to sort the `languageCode`s. See: fenix#40083. 957 languageCodes.sort() 958 languageCodes.each { 959 foundLocales.append("\"").append(it).append("\"").append(",") 960 } 961 962 foundLocales.append("}") 963 def foundLocalesString = foundLocales.toString().replaceAll(',}', '}') 964 return foundLocalesString 965 } 966 967 // Fix for Gradle 9 strictness: Ensure CleanUp runs before DependencyTask 968 tasks.withType(DependencyTask).configureEach { task -> 969 // The OSS plugin creates a "LicensesCleanUp" task for every "DependencyTask" 970 // e.g., nightlyOssDependencyTask -> nightlyOssLicensesCleanUp 971 def cleanUpTaskName = task.name.replace("DependencyTask", "LicensesCleanUp") 972 973 // make the dependency task depend on its cleanup task. 974 task.dependsOn(tasks.named(cleanUpTaskName)) 975 }