ConfigPlugin.kt (7814B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import org.gradle.api.Plugin 6 import org.gradle.api.Project 7 import org.gradle.api.provider.ProviderFactory 8 import org.gradle.kotlin.dsl.extra 9 import org.gradle.process.ExecOutput 10 import java.nio.file.Paths 11 import java.text.SimpleDateFormat 12 import java.time.LocalDateTime 13 import java.util.Date 14 import java.util.Locale 15 16 class ConfigPlugin : Plugin<Project> { 17 override fun apply(project: Project) = Unit 18 } 19 20 object Config { 21 22 @JvmStatic 23 private fun generateDebugVersionName(): String { 24 val today = if (System.getenv("MOZ_BUILD_DATE") != null) { 25 val format = SimpleDateFormat("yyyyMMddHHmmss", Locale.US) 26 format.parse(System.getenv("MOZ_BUILD_DATE")) 27 } else { 28 Date() 29 } 30 // Append the year (2 digits) and week in year (2 digits). This will make it easier to distinguish versions and 31 // identify ancient versions when debugging issues. However this will still keep the same version number during 32 // the week so that we do not end up with a lot of versions in tools like Sentry. As an extra this matches the 33 // sections we use in the changelog (weeks). 34 return SimpleDateFormat("1.0.yyww", Locale.US).format(today) 35 } 36 37 @JvmStatic 38 fun releaseVersionName(project: Project): String? { 39 // Note: release builds must have the `versionName` set. However, the gradle ecosystem makes this hard to 40 // ergonomically validate (sometimes IDEs default to a release variant and mysteriously fail due to the 41 // validation, sometimes devs just need a release build and specifying project properties is annoying in IDEs), 42 // so instead we'll allow the `versionName` to silently default to an empty string. 43 return if (project.hasProperty("versionName")) project.property("versionName").toString() else null 44 } 45 46 @JvmStatic 47 fun nightlyVersionName(project: Project): String { 48 // Nightly versions will use the version from "version.txt". 49 return readVersionFromFile(project) 50 } 51 52 @JvmStatic 53 fun readVersionFromFile(project: Project): String { 54 var mozconfig = project.gradle.extensions.extraProperties.get("mozconfig") as Map<*, *>; 55 var topsrcdir = mozconfig.get("topsrcdir") as String; 56 var versionPath = Paths.get(topsrcdir, "mobile/android/version.txt"); 57 return project.file(versionPath).useLines { it.firstOrNull() ?: "" } 58 } 59 60 @JvmStatic 61 fun majorVersion(project: Project): String { 62 return readVersionFromFile(project).split(".")[0] 63 } 64 65 private val fennecBaseVersionCode by lazy { 66 val format = SimpleDateFormat("yyyyMMddHHmmss", Locale.US) 67 val cutoff = format.parse("20141228000000") 68 val build = if (System.getenv("MOZ_BUILD_DATE") != null) format.parse(System.getenv("MOZ_BUILD_DATE")) else Date() 69 70 Math.floor((build.time - cutoff.time) / (1000.0 * 60.0 * 60.0)).toInt() 71 } 72 73 /** 74 * Generates a versionCode that follows the same rules like legacy Fennec builds. 75 * Adapted from: 76 * https://searchfox.org/mozilla-central/rev/34cb8d0a2a324043bcfc2c56f37b31abe7fb23a8/python/mozbuild/mozbuild/android_version_code.py 77 * 78 * There is a discrepancy between the epoch date used here (20141228) 79 * and the epoch used in Fennec (20150801) for historical reasons. We keep 80 * this discrepancy to avoid having Fenix version codes decrease. 81 * Note that the original Fennec implementation also had an inconsistency in 82 * the documented epoch date (20150901) and the effective epoch date (20150801). 83 */ 84 @JvmStatic 85 fun generateFennecVersionCode(abi: String): Int { 86 // The important consideration is that version codes be monotonically 87 // increasing (per Android package name) for all published builds. The input 88 // build IDs are based on timestamps and hence are always monotonically 89 // increasing. 90 // 91 // The generated v1 version codes look like (in binary): 92 // 93 // 0111 1000 0010 tttt tttt tttt tttt txpg 94 // 95 // The 17 bits labelled 't' represent the number of hours since midnight on 96 // December 28, 2014. (2014122800 in yyyyMMddHH format.) This yields a 97 // little under 15 years worth of hourly build identifiers, since 2**17 / (366 98 // * 24) =~ 14.92. 99 // 100 // The bits labelled 'x', 'p', and 'g' are feature flags. 101 // 102 // The bit labelled 'x' is 1 if the build is for an x86 or x86-64 architecture, 103 // and 0 otherwise, which means the build is for an ARM or ARM64 architecture. 104 // (Fennec no longer supports ARMv6, so ARM is equivalent to ARMv7. 105 // 106 // ARM64 is also known as AArch64; it is logically ARMv8.) 107 // 108 // For the same release, x86 and x86_64 builds have higher version codes and 109 // take precedence over ARM builds, so that they are preferred over ARM on 110 // devices that have ARM emulation. 111 // 112 // The bit labelled 'p' is 1 if the build is for a 64-bit architecture (x86-64 113 // or ARM64), and 0 otherwise, which means the build is for a 32-bit 114 // architecture (x86 or ARM). 64-bit builds have higher version codes so 115 // they take precedence over 32-bit builds on devices that support 64-bit. 116 // 117 // The bit labelled 'g' was once used for APK splits. Today, it is 0 for 118 // APK builds, or 1 for AAB/Universal builds. 119 // 120 // We throw an explanatory exception when we are within one calendar year of 121 // running out of build events. This gives lots of time to update the version 122 // scheme. The responsible individual should then bump the range (to allow 123 // builds to continue) and use the time remaining to update the version scheme 124 // via the reserved high order bits. 125 // 126 // N.B.: the reserved 0 bit to the left of the highest order 't' bit can, 127 // sometimes, be used to bump the version scheme. In addition, by reducing the 128 // granularity of the build identifiers (for example, moving to identifying 129 // builds every 2 or 4 hours), the version scheme may be adjusted further still 130 // without losing a (valuable) high order bit. 131 132 val base = fennecBaseVersionCode 133 134 when { 135 base < 0 -> throw RuntimeException("Cannot calculate versionCode. Hours underflow.") 136 base > 0x20000 /* 2^17 */ -> throw RuntimeException("Cannot calculate versionCode. Hours overflow.") 137 base > 0x20000 - (366 * 24) -> 138 // You have one year to update the version scheme... 139 throw RuntimeException("Running out of low order bits calculating versionCode.") 140 } 141 142 var version = 0x78200000 // 1111000001000000000000000000000 143 // We reserve 1 "middle" high order bit for the future, and 3 low order bits 144 // for architecture and APK splits. 145 version = version or (base shl 3) 146 147 // 'x' bit is 1 for x86/x86-64 architectures 148 if (abi == "universal" || abi == "x86_64" || abi == "x86") { 149 version = version or (1 shl 2) 150 } 151 152 // 'p' bit is 1 for 64-bit architectures. 153 if (abi == "universal" || abi == "arm64-v8a" || abi == "x86_64") { 154 version = version or (1 shl 1) 155 } 156 157 // 'g' bit is 0 for APK, 1 for AAB/Universal 158 if (abi == "universal") { 159 version = version or (1 shl 0) 160 } 161 162 return version 163 } 164 }