diff --git a/app/build.gradle b/app/build.gradle index 3d0f277d8..bf1162814 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -409,7 +409,22 @@ dependencies { testImplementation 'org.apache.maven:maven-ant-tasks:2.1.3' implementation Deps.fragment_testing - testImplementation Deps.places_forUnitTests + // For production builds, the native code for all `org.mozilla.appservices` + // dependencies gets compiled together into a single "megazord" build, and + // different megazords are published for different subsets of features. Ref + // https://mozilla.github.io/application-services/docs/applications/consuming-megazord-libraries.html + // We want to use the one that's specifically designed for Fenix. + implementation Deps.fenix_megazord + testImplementation Deps.fenix_megazord_forUnitTests + implementation Deps.mozilla_support_rusthttp + modules { + module('org.mozilla.appservices:full-megazord') { + replacedBy('org.mozilla.appservices:fenix-megazord', 'prefer the fenix megazord, to reduce final application size') + } + module('org.mozilla.appservices:fenix-megazord') { + replacedBy('org.mozilla.appservices:fenix-megazord-forUnitTests', 'prefer the forUnitTests variant if present') + } + } testImplementation Deps.mockito_core androidTestImplementation Deps.mockito_android @@ -421,6 +436,10 @@ dependencies { releaseImplementation Deps.flipper_noop } +if (project.hasProperty("raptor")) { + android.defaultConfig.manifestPlaceholders.isRaptorEnabled = "true" +} + // ------------------------------------------------------------------------------------------------- // Task for printing all build variants to build variants in parallel in automation // ------------------------------------------------------------------------------------------------- @@ -448,41 +467,8 @@ apply from: 'https://github.com/mozilla-mobile/android-components/raw/' + glean_ // into a single "megazord" build, and different megazords are published for different subsets of features. // Ref https://mozilla.github.io/application-services/docs/applications/consuming-megazord-libraries.html // Substitute all appservices dependencies with an appropriate megazord. + afterEvaluate { - def megazord = "fenix-megazord" - def appServicesGroup = "org.mozilla.appservices" - def appServicesVersion = null - configurations.each { configuration -> - configuration.resolutionStrategy.eachDependency { DependencyResolveDetails dependency -> - if (dependency.requested.group == appServicesGroup) { - // Ensure that we only depend on a single, consistent version of appservices. - if (appServicesVersion == null) { - appServicesVersion = dependency.requested.version - } else { - if (dependency.requested.version != appServicesVersion) { - logger.lifecycle("In ${configuration}: mismatched '${appServicesGroup}` dependency version:" + - " '${dependency.requested.group}:${dependency.requested.name}:${dependency.requested.version}'" + - " does not have expected version ${appServicesVersion}") - throw new RuntimeException("mismatched '${appServicesGroup}` dependency version") - } - } - // Check whether it was already megazorded. - // Hack: sync15 is pure kotlin, and thus not part of the megazord. - if (! dependency.requested.name.endsWith("-withoutLib") && dependency.requested.name != "sync15") { - // Use either the -forUnitTests megazord, or the default one. - def substitution - if (dependency.requested.name.endsWith("-forUnitTests")) { - substitution = "org.mozilla.appservices:${megazord}-forUnitTests:${appServicesVersion}" - } else { - substitution = "org.mozilla.appservices:${megazord}:${appServicesVersion}" - } - logger.lifecycle("In ${configuration}: substituting megazord module '$substitution' for component module" + - " '${dependency.requested.group}:${dependency.requested.name}:${dependency.requested.version}'") - dependency.useTarget(substitution) - } - } - } - } // Format test output. Ported from AC #2401 tasks.matching {it instanceof Test}.all { diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index fc9b567c5..fb55fa491 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -16,7 +16,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking -import mozilla.components.concept.fetch.Client +import mozilla.appservices.Megazord import mozilla.components.concept.push.PushProcessor import mozilla.components.service.fretboard.Fretboard import mozilla.components.service.fretboard.source.kinto.KintoExperimentSource @@ -26,6 +26,7 @@ import mozilla.components.support.base.log.logger.Logger import mozilla.components.support.base.log.sink.AndroidLogSink import mozilla.components.support.ktx.android.content.isMainProcess import mozilla.components.support.ktx.android.content.runOnlyInMainProcess +import mozilla.components.support.rusthttp.RustHttpConfig import mozilla.components.support.rustlog.RustLog import org.mozilla.fenix.components.Components import org.mozilla.fenix.utils.Settings @@ -49,8 +50,9 @@ open class FenixApplication : Application() { open fun setupApplication() { setupCrashReporting() setDayNightTheme() - val megazordEnabled = setupMegazord() - setupLogging(megazordEnabled) + + setupMegazord() + setupLogging() registerRxExceptionHandling() enableStrictMode() @@ -129,16 +131,12 @@ open class FenixApplication : Application() { // no-op, LeakCanary is disabled by default } - private fun setupLogging(megazordEnabled: Boolean) { + private fun setupLogging() { // We want the log messages of all builds to go to Android logcat Log.addSink(AndroidLogSink()) - - if (megazordEnabled) { - // We want rust logging to go through the log sinks. - // This has to happen after initializing the megazord, and - // it's only worth doing in the case that we are a megazord. - RustLog.enable() - } + // We want rust logging to go through the log sinks. + // This has to happen after initializing the megazord. + RustLog.enable() } private fun loadExperiments(): Deferred { @@ -171,34 +169,18 @@ open class FenixApplication : Application() { /** * Initiate Megazord sequence! Megazord Battle Mode! * - * Mozilla Application Services publishes many native (Rust) code libraries that stand alone: each published Android - * ARchive (AAR) contains managed code (classes.jar) and multiple .so library files (one for each supported - * architecture). That means consuming multiple such libraries entails at least two .so libraries, and each of those - * libraries includes the entire Rust standard library as well as (potentially many) duplicated dependencies. To - * save space and allow cross-component native-code Link Time Optimization (LTO, i.e., inlining, dead code - * elimination, etc). - * Application Services also publishes composite libraries -- so called megazord libraries or just megazords -- that - * compose multiple Rust components into a single optimized .so library file. - * - * @return Boolean indicating if we're in a megazord. + * The application-services combined libraries are known as the "megazord". The default megazord + * contains several features that fenix doesn't need, and so we swap out with a customized fenix-specific + * version of the megazord. The best explanation for what this is, and why it's done is the a-s + * documentation on the topic: + * - https://github.com/mozilla/application-services/blob/master/docs/design/megazords.md + * - https://mozilla.github.io/application-services/docs/applications/consuming-megazord-libraries.html */ - private fun setupMegazord(): Boolean { - // mozilla.appservices.FenixMegazord will be missing if we're doing an application-services - // dependency substitution locally. That class is supplied dynamically by the org.mozilla.appservices - // gradle plugin, and that won't happen if we're not megazording. We won't megazord if we're - // locally substituting every module that's part of the megazord's definition, which is what - // happens during a local substitution of application-services. - // As a workaround, use reflections to conditionally initialize the megazord in case it's present. - return try { - val megazordClass = Class.forName("mozilla.appservices.FenixMegazord") - val megazordInitMethod = megazordClass.getDeclaredMethod("init", Lazy::class.java) - val client: Lazy = lazy { components.core.client } - megazordInitMethod.invoke(megazordClass, client) - true - } catch (e: ClassNotFoundException) { - Logger.info("mozilla.appservices.FenixMegazord not found; skipping megazord init.") - false - } + private fun setupMegazord() { + // Note: This must be called as soon as possible + Megazord.init() + // This (and enabling RustLog) may be delayed if needed for performance reasons + RustHttpConfig.setClient(lazy { components.core.client }) } override fun onTrimMemory(level: Int) { diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt index 6bab89648..382d2fe79 100644 --- a/buildSrc/src/main/java/Dependencies.kt +++ b/buildSrc/src/main/java/Dependencies.kt @@ -37,9 +37,10 @@ object Versions { // Note that android-components also depends on application-services, // and in fact is our main source of appservices-related functionality. // The version number below tracks the application-services version - // that we depend on directly for tests, and it's important that it - // be kept in sync with the version used by android-components above. - const val mozilla_appservices = "0.34.0" + // that we depend on directly for the fenix-megazord (and for it's + // forUnitTest variant), and it's important that it be kept in + // sync with the version used by android-components above. + const val mozilla_appservices = "0.36.0" const val autodispose = "1.1.0" const val adjust = "4.11.4" @@ -139,6 +140,7 @@ object Deps { const val mozilla_support_base = "org.mozilla.components:support-base:${Versions.mozilla_android_components}" const val mozilla_support_ktx = "org.mozilla.components:support-ktx:${Versions.mozilla_android_components}" + const val mozilla_support_rusthttp = "org.mozilla.components:support-rusthttp:${Versions.mozilla_android_components}" const val mozilla_support_rustlog = "org.mozilla.components:support-rustlog:${Versions.mozilla_android_components}" const val mozilla_support_utils = "org.mozilla.components:support-utils:${Versions.mozilla_android_components}" const val mozilla_support_test = "org.mozilla.components:support-test:${Versions.mozilla_android_components}" @@ -200,7 +202,9 @@ object Deps { const val robolectric = "org.robolectric:robolectric:${Versions.robolectric}" const val fragment_testing = "androidx.fragment:fragment-testing:${Versions.androidx_testing}" const val androidx_junit = "androidx.test.ext:junit:${Versions.androidx_test_ext}" - const val places_forUnitTests = "org.mozilla.appservices:places-forUnitTests:${Versions.mozilla_appservices}" + + const val fenix_megazord = "org.mozilla.appservices:fenix-megazord:${Versions.mozilla_appservices}" + const val fenix_megazord_forUnitTests = "org.mozilla.appservices:fenix-megazord-forUnitTests:${Versions.mozilla_appservices}" const val google_ads_id = "com.google.android.gms:play-services-ads-identifier:${Versions.google_ads_id_version}" } diff --git a/settings.gradle b/settings.gradle index aa011bd8d..f3e75aa4b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,16 +20,8 @@ if (localProperties != null) { if (appServicesLocalPath != null) { logger.lifecycle("Local configuration: substituting application-services modules from path: $appServicesLocalPath") - - includeBuild(appServicesLocalPath) { - dependencySubstitution { - substitute module('org.mozilla.appservices:fxaclient') with project(':fxa-client-library') - substitute module('org.mozilla.appservices:logins') with project(':logins-library') - substitute module('org.mozilla.appservices:places') with project(':places-library') - substitute module('org.mozilla.appservices:rustlog') with project(':rustlog-library') - } - } - + // XXX doesn't quite work yet. + includeBuild(appServicesLocalPath) } else { logger.lifecycle("Local configuration: application-services substitution path missing. Specify it via '$settingAppServicesPath' setting.") }