diff options
Diffstat (limited to 'mobile/android/fenix/plugins')
6 files changed, 356 insertions, 0 deletions
diff --git a/mobile/android/fenix/plugins/apksize/build.gradle b/mobile/android/fenix/plugins/apksize/build.gradle new file mode 100644 index 0000000000..f9d4b5d404 --- /dev/null +++ b/mobile/android/fenix/plugins/apksize/build.gradle @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +plugins { + id "org.gradle.kotlin.kotlin-dsl" version "4.2.1" +} + +repositories { + gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> + maven { + url repository + if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) { + allowInsecureProtocol = true + } + } + } +} + +dependencies { + implementation "org.json:json:20210307" +} + +gradlePlugin { + plugins.register("ApkSizePlugin") { + id = "ApkSizePlugin" + implementationClass = "ApkSizePlugin" + } +} diff --git a/mobile/android/fenix/plugins/apksize/settings.gradle b/mobile/android/fenix/plugins/apksize/settings.gradle new file mode 100644 index 0000000000..16701d4aac --- /dev/null +++ b/mobile/android/fenix/plugins/apksize/settings.gradle @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Prevents gradle builds from looking for a root settings.gradle +pluginManagement { + apply from: file('../../../gradle/mozconfig.gradle') + + repositories { + gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> + maven { + url repository + if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) { + allowInsecureProtocol = true + } + } + } + } +} diff --git a/mobile/android/fenix/plugins/apksize/src/main/java/ApkSizePlugin.kt b/mobile/android/fenix/plugins/apksize/src/main/java/ApkSizePlugin.kt new file mode 100644 index 0000000000..6a70a4da88 --- /dev/null +++ b/mobile/android/fenix/plugins/apksize/src/main/java/ApkSizePlugin.kt @@ -0,0 +1,153 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import org.gradle.api.DefaultTask +import org.gradle.api.Plugin +import org.gradle.api.initialization.Settings +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths + +class ApkSizePlugin : Plugin<Settings> { + override fun apply(settings: Settings) = Unit +} + +/** + * Gradle task for determining the size of APKs and logging them in a perfherder compatible format. + */ +open class ApkSizeTask : DefaultTask() { + /** + * Name of the build variant getting built. + */ + @Input + var variantName: String? = null + + /** + * List of APKs that get build for the build variant. + */ + @Input + var apks: List<String>? = null + + @TaskAction + fun logApkSize() { + val apkSizes = determineApkSizes() + if (apkSizes.isEmpty()) { + println("Couldn't determine APK sizes for perfherder") + return + } + + val json = buildPerfherderJson(apkSizes) ?: return + + println("PERFHERDER_DATA: $json") + } + + private fun determineApkSizes(): Map<String, Long> { + val variantOutputPath = variantName?.removePrefix("fenix")?.lowercase() + val basePath = listOf( + "${project.projectDir}", "build", "outputs", "apk", "fenix", variantOutputPath + ).joinToString(File.separator) + + return requireNotNull(apks).associateWith { apk -> + val rawPath = "$basePath${File.separator}$apk" + + try { + val path = Paths.get(rawPath) + Files.size(path) + } catch (t: Throwable) { + println("Could not determine size of $apk ($rawPath)") + t.printStackTrace() + 0 + } + }.filter { (_, size) -> size > 0 } + } + + /** + * Returns perfherder compatible JSON for tracking the file size of APKs. + * + * ``` + * { + * "framework": { + * "name": "build_metrics" + * }, + * "suites": [ + * { + * "name": "apk-size-[debug,nightly,beta,release]", + * "lowerIsBetter": true, + * "subtests": [ + * { "name": "app-arm64-v8a-debug.apk", "value": 98855735 }, + * { "name": "app-armeabi-v7a-debug.apk", "value": 92300031 }, + * { "name": "app-x86-debug.apk", "value": 103410909 }, + * { "name": "app-x86_64-debug.apk", "value": 102465675 } + * ], + * "value":98855735, + * "shouldAlert":false + * } + * ] + * } + * ``` + */ + private fun buildPerfherderJson(apkSize: Map<String, Long>): JSONObject? { + return try { + val data = JSONObject() + + val framework = JSONObject() + framework.put("name", "build_metrics") + data.put("framework", framework) + + val suites = JSONArray() + + val suite = JSONObject() + suite.put("name", "apk-size-$variantName") + suite.put("value", getSummarySize(apkSize)) + suite.put("lowerIsBetter", true) + suite.put("alertChangeType", "absolute") + suite.put("alertThreshold", 1024 * 1024) + + // Debug variants do not have alerts + if (variantName?.contains("debug", ignoreCase = true) == true) { + suite.put("shouldAlert", false) + } + + val subtests = JSONArray() + apkSize.forEach { (apk, size) -> + val subtest = JSONObject() + subtest.put("name", apk) + subtest.put("value", size) + subtests.put(subtest) + } + suite.put("subtests", subtests) + + suites.put(suite) + + data.put("suites", suites) + + data + } catch (e: JSONException) { + println("Couldn't generate perfherder JSON") + e.printStackTrace() + null + } + } +} + +/** + * Returns a summarized size for the APKs. This is the main value that is getting tracked. The size + * of the individual APKs will be reported as "subtests". + */ +private fun getSummarySize(apkSize: Map<String, Long>): Long { + val arm64size = apkSize.keys.find { it.contains("arm64") }?.let { apk -> apkSize[apk] } + if (arm64size != null) { + // If available we will report the size of the arm64 APK as the summary. This is the most + // important and most installed APK. + return arm64size + } + + // If there's no arm64 APK then we calculate a simple average. + return apkSize.values.sum() / apkSize.size +} diff --git a/mobile/android/fenix/plugins/fenixdependencies/build.gradle b/mobile/android/fenix/plugins/fenixdependencies/build.gradle new file mode 100644 index 0000000000..d377cc4118 --- /dev/null +++ b/mobile/android/fenix/plugins/fenixdependencies/build.gradle @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +plugins { + id "org.gradle.kotlin.kotlin-dsl" version "4.2.1" +} + +repositories { + gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> + maven { + url repository + if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) { + allowInsecureProtocol = true + } + } + } +} + +gradlePlugin { + plugins.register("FenixDependenciesPlugin") { + id = "FenixDependenciesPlugin" + implementationClass = "FenixDependenciesPlugin" + } +} diff --git a/mobile/android/fenix/plugins/fenixdependencies/settings.gradle b/mobile/android/fenix/plugins/fenixdependencies/settings.gradle new file mode 100644 index 0000000000..16701d4aac --- /dev/null +++ b/mobile/android/fenix/plugins/fenixdependencies/settings.gradle @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Prevents gradle builds from looking for a root settings.gradle +pluginManagement { + apply from: file('../../../gradle/mozconfig.gradle') + + repositories { + gradle.mozconfig.substs.GRADLE_MAVEN_REPOSITORIES.each { repository -> + maven { + url repository + if (gradle.mozconfig.substs.ALLOW_INSECURE_GRADLE_REPOSITORIES) { + allowInsecureProtocol = true + } + } + } + } +} diff --git a/mobile/android/fenix/plugins/fenixdependencies/src/main/java/FenixDependenciesPlugin.kt b/mobile/android/fenix/plugins/fenixdependencies/src/main/java/FenixDependenciesPlugin.kt new file mode 100644 index 0000000000..9df93af3fa --- /dev/null +++ b/mobile/android/fenix/plugins/fenixdependencies/src/main/java/FenixDependenciesPlugin.kt @@ -0,0 +1,111 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import org.gradle.api.Plugin +import org.gradle.api.initialization.Settings + +// If you ever need to force a toolchain rebuild (taskcluster) then edit the following comment. +// FORCE REBUILD 2023-05-12 + +class FenixDependenciesPlugin : Plugin<Settings> { + override fun apply(settings: Settings) = Unit +} + +object FenixVersions { + const val osslicenses_plugin = "0.10.4" + + const val falcon = "2.2.0" + const val fastlane = "2.1.1" + + const val androidx_activity = "1.7.2" + const val androidx_benchmark = "1.2.3" + const val androidx_profileinstaller = "1.3.1" + const val androidx_splash_screen = "1.0.1" + const val androidx_transition = "1.4.1" + const val androidx_datastore = "1.0.0" + const val google_accompanist = "0.32.0" + + const val adjust = "4.38.2" + const val installreferrer = "2.2" + + const val junit = "5.10.2" + const val mockk = "1.13.10" + + const val google_ads_id_version = "16.0.0" + + const val google_play_review_version = "2.0.1" + + // keep in sync with the versions used in AS. + const val protobuf = "3.21.10" + const val protobuf_plugin = "0.9.4" +} + +@Suppress("unused") +object FenixDependencies { + const val tools_benchmarkgradle = "androidx.benchmark:benchmark-gradle-plugin:${FenixVersions.androidx_benchmark}" + + const val osslicenses_plugin = "com.google.android.gms:oss-licenses-plugin:${FenixVersions.osslicenses_plugin}" + + const val androidx_benchmark_junit4 = "androidx.benchmark:benchmark-junit4:${FenixVersions.androidx_benchmark}" + const val androidx_benchmark_macro_junit4 = "androidx.benchmark:benchmark-macro-junit4:${FenixVersions.androidx_benchmark}" + const val androidx_core_splashscreen = "androidx.core:core-splashscreen:${FenixVersions.androidx_splash_screen}" + const val androidx_profileinstaller = "androidx.profileinstaller:profileinstaller:${FenixVersions.androidx_profileinstaller}" + const val androidx_activity_ktx = "androidx.activity:activity-ktx:${FenixVersions.androidx_activity}" + const val androidx_transition = "androidx.transition:transition:${FenixVersions.androidx_transition}" + const val androidx_datastore = "androidx.datastore:datastore:${FenixVersions.androidx_datastore}" + + const val google_accompanist_drawablepainter = "com.google.accompanist:accompanist-drawablepainter:${FenixVersions.google_accompanist}" + + const val protobuf_javalite = "com.google.protobuf:protobuf-javalite:${FenixVersions.protobuf}" + const val protobuf_compiler = "com.google.protobuf:protoc:${FenixVersions.protobuf}" + + const val adjust = "com.adjust.sdk:adjust-android:${FenixVersions.adjust}" + const val installreferrer = "com.android.installreferrer:installreferrer:${FenixVersions.installreferrer}" + + const val mockk = "io.mockk:mockk:${FenixVersions.mockk}" + const val mockk_android = "io.mockk:mockk-android:${FenixVersions.mockk}" + const val falcon = "com.jraska:falcon:${FenixVersions.falcon}" + const val fastlane = "tools.fastlane:screengrab:${FenixVersions.fastlane}" + + // --- START AndroidX test dependencies --- // + // N.B.: the versions of these dependencies appear to be pinned together. To avoid bugs, they + // should always be updated together based on the latest version from the Android test releases page: + // https://developer.android.com/jetpack/androidx/releases/test + // For the full IDs of these test dependencies, see: + // https://developer.android.com/training/testing/set-up-project#android-test-dependencies + private const val androidx_test_orchestrator = "1.4.2" + private const val androidx_espresso_version = "3.5.1" + const val espresso_contrib = "androidx.test.espresso:espresso-contrib:$androidx_espresso_version" + const val espresso_idling_resources = "androidx.test.espresso:espresso-idling-resource:$androidx_espresso_version" + const val espresso_intents = "androidx.test.espresso:espresso-intents:$androidx_espresso_version" + // Monitor is unused + const val orchestrator = "androidx.test:orchestrator:$androidx_test_orchestrator" + // Truth is unused + // Test services is unused + // --- END AndroidX test dependencies --- // + + const val google_ads_id = "com.google.android.gms:play-services-ads-identifier:${FenixVersions.google_ads_id_version}" + + // Required for in-app reviews + const val google_play_review = "com.google.android.play:review:${FenixVersions.google_play_review_version}" + const val google_play_review_ktx = "com.google.android.play:review-ktx:${FenixVersions.google_play_review_version}" + + const val junitApi = "org.junit.jupiter:junit-jupiter-api:${FenixVersions.junit}" + const val junitParams = "org.junit.jupiter:junit-jupiter-params:${FenixVersions.junit}" + const val junitEngine = "org.junit.jupiter:junit-jupiter-engine:${FenixVersions.junit}" +} + +/** + * Functionality to limit specific dependencies to specific repositories. These are typically expected to be used by + * dependency group name (i.e. with `include/excludeGroup`). For additional info, see: + * https://docs.gradle.org/current/userguide/declaring_repositories.html#sec::matching_repositories_to_dependencies + * + * Note: I wanted to nest this in Deps but for some reason gradle can't find it so it's top-level now. :| + */ +object RepoMatching { + const val mozilla = "org\\.mozilla\\..*" + const val androidx = "androidx\\..*" + const val comAndroid = "com\\.android.*" + const val comGoogle = "com\\.google\\..*" +} |