From 10143858cb69ef69d7427596d4be9ea2bf48d298 Mon Sep 17 00:00:00 2001 From: Sebastian Kaspari Date: Fri, 23 Aug 2019 18:10:25 +0200 Subject: [PATCH] Issue #4873: Use Fennec version code mechanism for fennecProduction build type. --- app/build.gradle | 5 +- buildSrc/src/main/java/Config.kt | 96 ++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 568f5347d..aeba9bb7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -197,8 +197,9 @@ android.applicationVariants.all { variant -> def abi = output.getFilter(OutputFile.ABI) def versionCodeOverride - - if (abi == "x86_64") { + if (variant.name.contains("Fennec")) { + versionCodeOverride = Config.generateFennecVersionCode(abi) + } else if (abi == "x86_64") { versionCodeOverride = baseVersionCode + 3 } else if (abi == "x86") { versionCodeOverride = baseVersionCode + 2 diff --git a/buildSrc/src/main/java/Config.kt b/buildSrc/src/main/java/Config.kt index 33bfd2dd3..b14b3e1e7 100644 --- a/buildSrc/src/main/java/Config.kt +++ b/buildSrc/src/main/java/Config.kt @@ -1,4 +1,6 @@ import org.gradle.api.Project +import java.lang.Math.pow +import java.lang.RuntimeException import java.text.SimpleDateFormat import java.time.LocalDateTime @@ -41,4 +43,98 @@ object Config { return "${dateTime.dayOfWeek.toString().toLowerCase().capitalize()} ${dateTime.monthValue}/${dateTime.dayOfMonth} @ ${timeFormatter.format(dateTime)}" } + + private val fennecBaseVersionCode by lazy { + val format = SimpleDateFormat("YYYYMMDDHHMMSS", Locale.US) + val cutoff = format.parse("20150801000000") + 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/source/python/mozbuild/mozbuild/android_version_code.py + */ + @JvmStatic + fun generateFennecVersionCode(abi: String): 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 + // September 1, 2015. (2015090100 in YYYYMMMDDHH 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' is 1 if the build targets a recent API level, which + // is currently always the case, because Firefox no longer ships releases that + // are split by API levels. However, we may reintroduce a split in the future, + // in which case the release that targets an older API level will + // + // 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 > pow(2.0, 17.0) -> throw RuntimeException("Cannot calculate versionCode. Hours overflow.") + base > (pow(2.0, 17.0) - (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 (abi == "x86_64" || abi == "x86") { + version = version or (1 shl 2) + } + + // 'p' bit is 1 for 64-bit architectures. + if (abi == "arm64-v8a" || abi == "x86_64") { + version = version or (1 shl 1) + } + + // 'g' bit is currently always 1, but may depend on `min_sdk` in the future. + version = version or (1 shl 0) + + return version + } }