summaryrefslogtreecommitdiffstats
path: root/mobile/android/android-components/plugins/config
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:35:49 +0000
commitd8bbc7858622b6d9c278469aab701ca0b609cddf (patch)
treeeff41dc61d9f714852212739e6b3738b82a2af87 /mobile/android/android-components/plugins/config
parentReleasing progress-linux version 125.0.3-1~progress7.99u1. (diff)
downloadfirefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz
firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/plugins/config')
-rw-r--r--mobile/android/android-components/plugins/config/build.gradle25
-rw-r--r--mobile/android/android-components/plugins/config/settings.gradle19
-rw-r--r--mobile/android/android-components/plugins/config/src/main/java/ConfigPlugin.kt224
3 files changed, 268 insertions, 0 deletions
diff --git a/mobile/android/android-components/plugins/config/build.gradle b/mobile/android/android-components/plugins/config/build.gradle
new file mode 100644
index 0000000000..4efe5b3b2f
--- /dev/null
+++ b/mobile/android/android-components/plugins/config/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("mozac.ConfigPlugin") {
+ id = "mozac.ConfigPlugin"
+ implementationClass = "ConfigPlugin"
+ }
+}
diff --git a/mobile/android/android-components/plugins/config/settings.gradle b/mobile/android/android-components/plugins/config/settings.gradle
new file mode 100644
index 0000000000..16701d4aac
--- /dev/null
+++ b/mobile/android/android-components/plugins/config/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/android-components/plugins/config/src/main/java/ConfigPlugin.kt b/mobile/android/android-components/plugins/config/src/main/java/ConfigPlugin.kt
new file mode 100644
index 0000000000..7261a09328
--- /dev/null
+++ b/mobile/android/android-components/plugins/config/src/main/java/ConfigPlugin.kt
@@ -0,0 +1,224 @@
+/* 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.Project
+import org.gradle.api.initialization.Settings
+import java.io.File
+import java.text.SimpleDateFormat
+import java.time.LocalDateTime
+import java.util.Date
+import java.util.Locale
+import java.util.concurrent.TimeUnit
+
+class ConfigPlugin : Plugin<Settings> {
+ override fun apply(settings: Settings) = Unit
+}
+
+object Config {
+
+ @JvmStatic
+ private fun generateDebugVersionName(): String {
+ val today = Date()
+ // Append the year (2 digits) and week in year (2 digits). This will make it easier to distinguish versions and
+ // identify ancient versions when debugging issues. However this will still keep the same version number during
+ // the week so that we do not end up with a lot of versions in tools like Sentry. As an extra this matches the
+ // sections we use in the changelog (weeks).
+ return SimpleDateFormat("1.0.yyww", Locale.US).format(today)
+ }
+
+ @JvmStatic
+ fun releaseVersionName(project: Project): String? {
+ // Note: release builds must have the `versionName` set. However, the gradle ecosystem makes this hard to
+ // ergonomically validate (sometimes IDEs default to a release variant and mysteriously fail due to the
+ // validation, sometimes devs just need a release build and specifying project properties is annoying in IDEs),
+ // so instead we'll allow the `versionName` to silently default to an empty string.
+ return if (project.hasProperty("versionName")) project.property("versionName").toString() else null
+ }
+
+ @JvmStatic
+ fun nightlyVersionName(project: Project): String {
+ // Nightly versions will use the version from "version.txt".
+ return readVersionFromFile(project)
+ }
+
+ @JvmStatic
+ fun readVersionFromFile(project: Project): String {
+ var versionPath = "../version.txt"
+
+ if (project.findProject(":geckoview") != null) {
+ versionPath = "./mobile/android/version.txt"
+ }
+
+ return File(versionPath).useLines { it.firstOrNull() ?: "" }
+
+ }
+
+ @JvmStatic
+ fun majorVersion(project: Project): String {
+ return readVersionFromFile(project).split(".")[0]
+ }
+
+ /**
+ * Generate a build date that follows the ISO-8601 format
+ */
+ @JvmStatic
+ fun generateBuildDate(): String {
+ return LocalDateTime.now().toString()
+ }
+
+ private val fennecBaseVersionCode by lazy {
+ val format = SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
+ val cutoff = format.parse("20141228000000")
+ val build = Date()
+
+ Math.floor((build.time - cutoff.time) / (1000.0 * 60.0 * 60.0)).toInt()
+ }
+
+ /**
+ * Generates a versionCode that follows the same rules like legacy Fennec builds.
+ * Adapted from:
+ * https://searchfox.org/mozilla-central/rev/34cb8d0a2a324043bcfc2c56f37b31abe7fb23a8/python/mozbuild/mozbuild/android_version_code.py
+ *
+ * There is a discrepancy between the epoch date used here (20141228)
+ * and the epoch used in Fennec (20150801) for historical reasons. We keep
+ * this discrepancy to avoid having Fenix version codes decrease.
+ * Note that the original Fennec implementation also had an inconsistency in
+ * the documented epoch date (20150901) and the effective epoch date (20150801).
+ */
+ @JvmStatic
+ fun generateFennecVersionCode(abi: String, aab: Boolean): Int {
+ // The important consideration is that version codes be monotonically
+ // increasing (per Android package name) for all published builds. The input
+ // build IDs are based on timestamps and hence are always monotonically
+ // increasing.
+ //
+ // The generated v1 version codes look like (in binary):
+ //
+ // 0111 1000 0010 tttt tttt tttt tttt txpg
+ //
+ // The 17 bits labelled 't' represent the number of hours since midnight on
+ // December 28, 2014. (2014122800 in yyyyMMddHH format.) This yields a
+ // little under 15 years worth of hourly build identifiers, since 2**17 / (366
+ // * 24) =~ 14.92.
+ //
+ // The bits labelled 'x', 'p', and 'g' are feature flags.
+ //
+ // The bit labelled 'x' is 1 if the build is for an x86 or x86-64 architecture,
+ // and 0 otherwise, which means the build is for an ARM or ARM64 architecture.
+ // (Fennec no longer supports ARMv6, so ARM is equivalent to ARMv7.
+ //
+ // ARM64 is also known as AArch64; it is logically ARMv8.)
+ //
+ // For the same release, x86 and x86_64 builds have higher version codes and
+ // take precedence over ARM builds, so that they are preferred over ARM on
+ // devices that have ARM emulation.
+ //
+ // The bit labelled 'p' is 1 if the build is for a 64-bit architecture (x86-64
+ // or ARM64), and 0 otherwise, which means the build is for a 32-bit
+ // architecture (x86 or ARM). 64-bit builds have higher version codes so
+ // they take precedence over 32-bit builds on devices that support 64-bit.
+ //
+ // The bit labelled 'g' was once used for APK splits. Today, it is 0 for
+ // APK builds, or 1 for AAB builds.
+ //
+ // We throw an explanatory exception when we are within one calendar year of
+ // running out of build events. This gives lots of time to update the version
+ // scheme. The responsible individual should then bump the range (to allow
+ // builds to continue) and use the time remaining to update the version scheme
+ // via the reserved high order bits.
+ //
+ // N.B.: the reserved 0 bit to the left of the highest order 't' bit can,
+ // sometimes, be used to bump the version scheme. In addition, by reducing the
+ // granularity of the build identifiers (for example, moving to identifying
+ // builds every 2 or 4 hours), the version scheme may be adjusted further still
+ // without losing a (valuable) high order bit.
+
+ val base = fennecBaseVersionCode
+
+ when {
+ base < 0 -> throw RuntimeException("Cannot calculate versionCode. Hours underflow.")
+ base > 0x20000 /* 2^17 */ -> throw RuntimeException("Cannot calculate versionCode. Hours overflow.")
+ base > 0x20000 - (366 * 24) ->
+ // You have one year to update the version scheme...
+ throw RuntimeException("Running out of low order bits calculating versionCode.")
+ }
+
+ var version = 0x78200000 // 1111000001000000000000000000000
+ // We reserve 1 "middle" high order bit for the future, and 3 low order bits
+ // for architecture and APK splits.
+ version = version or (base shl 3)
+
+ // 'x' bit is 1 for x86/x86-64 architectures
+ if (aab || abi == "x86_64" || abi == "x86") {
+ version = version or (1 shl 2)
+ }
+
+ // 'p' bit is 1 for 64-bit architectures.
+ if (aab || abi == "arm64-v8a" || abi == "x86_64") {
+ version = version or (1 shl 1)
+ }
+
+ // 'g' bit is 0 for APK, 1 for AAB
+ if (aab) {
+ version = version or (1 shl 0)
+ }
+
+ return version
+ }
+
+ /**
+ * Returns the git or hg hash of the currently checked out revision. If there are uncommitted changes,
+ * a "+" will be appended to the hash, e.g. "c8ba05ad0+".
+ */
+ @JvmStatic
+ fun getVcsHash(): String {
+ val gitRevision: String
+ try {
+ val revisionCmd = arrayOf("git", "rev-parse", "--short", "HEAD")
+ gitRevision = execReadStandardOutOrThrow(revisionCmd)
+ } catch (e: IllegalStateException) {
+ // hg id already appends "+" if the working directory isn't clean
+ val revisionCmd = arrayOf("hg", "id", "--id")
+ val hgRevision = execReadStandardOutOrThrow(revisionCmd)
+ return "hg-$hgRevision"
+ }
+ // Append "+" if there are uncommitted changes in the working directory.
+ val statusCmd = arrayOf("git", "status", "--porcelain=v2")
+ val status = execReadStandardOutOrThrow(statusCmd)
+ val hasUnstagedChanges = status.isNotBlank()
+ val statusSuffix = if (hasUnstagedChanges) "+" else ""
+
+ return "git-$gitRevision$statusSuffix"
+ }
+
+ /**
+ * Executes the given command with [Runtime.exec], throwing if the command returns a non-zero exit
+ * code or times out. If successful, returns the command's stdout.
+ *
+ * @return stdout of the command
+ * @throws [IllegalStateException] if the command returns a non-zero exit code or times out.
+ */
+ private fun execReadStandardOutOrThrow(cmd: Array<String>, timeoutSeconds: Long = 30): String {
+ val process = Runtime.getRuntime().exec(cmd)
+
+ check(
+ process.waitFor(
+ timeoutSeconds,
+ TimeUnit.SECONDS,
+ ),
+ ) { "command unexpectedly timed out: `$cmd`" }
+ check(process.exitValue() == 0) {
+ val stderr = process.errorStream.bufferedReader().readText().trim()
+
+ """command exited with non-zero exit value: ${process.exitValue()}.
+ |cmd: ${cmd.joinToString(separator = " ")}
+ |stderr:
+ |$stderr"""
+ .trimMargin()
+ }
+
+ return process.inputStream.bufferedReader().readText().trim()
+ }
+}