0%

Kotlin-KCP的应用-修改SDK版本号

Kotlin-KCP的应用-修改SDK版本号

背景

在 SDK 开发中,一般会暴露获取 SDK 版本号的接口,获取的版本号一般为 String 类型,比如:

1
2
3
4
5
6
7
// sdk接口
interface Sdk {
fun getVersion(): String
}

// sdk调用方
sdk.getVersion()

上述方式可以通过在 gradle.properties 中配置版本号,然后在 build.gradle 中读取版本号生成至 BuildConfig.java 中,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// gradle.properties
VERSION=1.0.0.0

// builde.gradle
android {
defaultConfig {
buildConfigField("String", "SDK_VERSION", "\"$VERSION\"")
}
}

// SdkImple.kt
class SdkImpl : Sdk {
override fun getVersion(): String {
// 返回 BuildConfig 中的 SDK_VERSION
return BuildConfig.SDK_VERSION
}
}

上述方式在 SDK 发版时只需修改 gradle.properties 中的版本号即可

但是上述方式有一个弊端:SDK 提供的版本号为 String 类型,第三方根据版本号进行适配开发时不太方便,第三方需要自己实现版本号大小的判断,笔者希望 SDK 自身可以暴露判断版本号大小的接口

方案

基于上述需求,SDK 暴露的获取版本号接口就不能返回 String 类型了,Sdk 接口修改如下:

1
2
3
4
5
6
7
8
9
10
11
interface Sdk {
// 返回一个 Version 对象
fun getVersion(): Version
}

class SdkImpl : Sdk {
override fun getVersion(): Version {
// 返回 Version 中的 CURRENT
return Version.CURRENT
}
}

下面是 Version 对象的定义[1],版本号规则不尽相同,以下是示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Version internal constructor(
private val major: Int, // 主版本 1
private val minor: Int, // 次版本 0
private val patch: Int, // 补丁版本 0
private val extra: Int, // 保留版本 0
private val suffix: String?, // 后缀版本, 比如:alpha01、beta01
) : Comparable<Version> {

private val version = versionOf(major, minor, patch, extra)

// 版本校验
private fun versionOf(major: Int, minor: Int, patch: Int, extra: Int): Int {
require(
major in 0..MAX_COMPONENT_VALUE &&
minor in 0..MAX_COMPONENT_VALUE &&
patch in 0..MAX_COMPONENT_VALUE &&
extra in 0..MAX_COMPONENT_VALUE
) {
"Version components are out of range: $major.$minor.$patch.$extra"
}
return major.shl(24) + minor.shl(16) + patch.shl(8) + extra
}

override fun toString(): String =
if (suffix.isNullOrEmpty()) "$major.$minor.$patch.$extra" else "$major.$minor.$patch.$extra-$suffix"

override fun equals(other: Any?): Boolean {
if (this === other) return true
val otherVersion = (other as? Version) ?: return false
return this.version == otherVersion.version
}

override fun hashCode(): Int = version

// 版本比较1
override fun compareTo(other: Version): Int = version - other.version

// 版本比较2
fun isAtLeast(major: Int, minor: Int): Boolean =
this.major > major || (this.major == major &&
this.minor >= minor)

// 版本比较2
fun isAtLeast(major: Int, minor: Int, patch: Int): Boolean =
this.major > major || (this.major == major &&
(this.minor > minor || this.minor == minor &&
this.patch >= patch))

// 版本比较2
fun isAtLeast(major: Int, minor: Int, patch: Int, extra: Int): Boolean =
this.major > major || (this.major == major &&
(this.minor > minor || this.minor == minor &&
(this.patch > patch || this.patch == patch &&
this.extra >= extra)))

companion object {
internal const val MAX_COMPONENT_VALUE = 255

// 当前版本
@JvmField
val CURRENT: Version = VersionCurrentValue.get()
}
}

private object VersionCurrentValue {
@JvmStatic
fun get(): Version =
Version(0, 0, 0, 0, null) // value is written here automatically during build
}

第三方进行版本适配开发时,可以如下操作,就比较方便了:

1
2
3
4
5
6
7
8
9
10
val version = sdk.getVersion()
println("version = $version")

if (version.isAtLeast(1, 2)) {
// 当前版本大于等于 1.2.0.0
// do something
} else {
// 当前版本小于 1.2.0.0
// do something
}

上述方案是不是比较友好了?:happy:,不知道读者有没有发现,在哪里修改版本号呢?

细心的读者可能已经发现,Version.CURRENT 是调用的 VersionCurrentValue#get() 方法,VersionCurrentValue#get() 方法会创建 Version 对象的实例,只需要修改 VersionCurrentValue#get() 方法传入版本号即可。等下,每次发版时都要修改 VersionCurrentValue#get() 方法?

隐隐感觉到一丝不妥,要是哪次发版时忘记修改 VersionCurrentValue#get() 方法,这不惨了😢

“人非圣贤孰能无过” 呢,还是让程序帮我们生成版本号吧,同时兼容方案一:只修改 gradle.properties 即可

使用 KCP 在编译阶段修改 VersionCurrentValue#get() 方法

实现

在上篇 Kotlin-KCP的应用-第二篇 中笔者记录了搭建 KCP 环境的基本步骤,这里不再赘述,有兴趣的读者可以先看下上篇文章

image-20220522224118965

上图是本项目的组织架构,简单介绍下:

  • sample:包含 Version 及测试类

  • version-plugin-gradle:kcp 中的 gradle plugin 部分

  • version-plugin-kotlin:kcp 中的 kotlin compiler plugin 部分

sample 模块不做介绍,下面主要实现其他两个模块

build.gradle.kts - project level

在项目级别的 build.gradle.kts 脚本中配置插件依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
buildscript {
// 配置 Kotlin 插件唯一ID
extra["kotlin_plugin_id"] = "com.guodong.android.version.kcp"
}

plugins {
kotlin("jvm") version "1.5.31" apply false

// 配置 Gradle 发布插件,可以不再写 META-INF
id("com.gradle.plugin-publish") version "0.16.0" apply false

// 配置生成 BuildConfig 插件
id("com.github.gmazzo.buildconfig") version "3.0.3" apply false
}

allprojects {
// 配置 Kotlin 插件版本
version = "0.0.1"
}

version-plugin-gradle

首先配置下 build.gradle.kts 脚本

build.gradle.kts - module level

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
plugins {
id("java-gradle-plugin")
kotlin("jvm")
id("com.github.gmazzo.buildconfig")
}

dependencies {
implementation(kotlin("gradle-plugin-api"))
}

buildConfig {
// 配置 BuildConfig 的包名
packageName("com.guodong.android.version.kcp.plugin.gradle")

// 设置 Kotlin 插件唯一 ID
buildConfigField("String", "KOTLIN_PLUGIN_ID", "\"${rootProject.extra["kotlin_plugin_id"]}\"")

// 设置 Kotlin 插件 GroupId
buildConfigField("String", "KOTLIN_PLUGIN_GROUP", "\"com.guodong.android\"")

// 设置 Kotlin 插件 ArtifactId
buildConfigField("String", "KOTLIN_PLUGIN_NAME", "\"version-kcp-kotlin-plugin\"")

// 设置 Kotlin 插件 Version
buildConfigField("String", "KOTLIN_PLUGIN_VERSION", "\"${project.version}\"")
}

gradlePlugin {
plugins {
create("Version") {
id = rootProject.extra["kotlin_plugin_id"] as String // `apply plugin: "com.guodong.android.version.kcp"`
displayName = "Version Kcp"
description = "Version Kcp"
implementationClass = "com.guodong.android.version.kcp.gradle.VersionGradlePlugin" // 插件入口类
}
}
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

VersionGradlePlugin

创建 VersionGradlePlugin 实现 KotlinCompilerPluginSupportPlugin 接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class VersionGradlePlugin : KotlinCompilerPluginSupportPlugin {

override fun apply(target: Project): Unit = with(target) {
logger.error("Welcome to guodongAndroid-version kcp gradle plugin.")

// 此处配置 Gradle 插件扩展
extensions.create("version", VersionExtension::class.java)
}

// 是否适用, 默认True
override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = true

// 获取 Kotlin 插件唯一ID
override fun getCompilerPluginId(): String = BuildConfig.KOTLIN_PLUGIN_ID

// 获取 Kotlin 插件 Maven 坐标信息
override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact(
groupId = BuildConfig.KOTLIN_PLUGIN_GROUP,
artifactId = BuildConfig.KOTLIN_PLUGIN_NAME,
version = BuildConfig.KOTLIN_PLUGIN_VERSION
)

// 读取 Gradle 插件扩展信息并写入 SubpluginOption
override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider<List<SubpluginOption>> {
val project = kotlinCompilation.target.project
val extension = project.extensions.getByType(VersionExtension::class.java)
return project.provider {
listOf(
SubpluginOption(key = "version", value = extension.version)
)
}
}
}

因为版本号需要在外部配置传入 Gradle Plugin,这里需要创建 VersionExtension:

1
2
3
4
5
6
7
8
open class VersionExtension {

var version: String = "0.0.0.0"

override fun toString(): String {
return "VersionExtension(version=$version)"
}
}

至此 Gradle 插件编写完成

version-plugin-kotlin

接下来编写 Kotlin 编译器插件,首先配置下 build.gradle.kts 脚本

build.gradle.kts - module level

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
plugins {
kotlin("jvm")
kotlin("kapt")
id("com.github.gmazzo.buildconfig")
}

dependencies {
// 依赖 Kotlin 编译器库
compileOnly("org.jetbrains.kotlin:kotlin-compiler-embeddable")

// 依赖 Google auto service
kapt("com.google.auto.service:auto-service:1.0")
compileOnly("com.google.auto.service:auto-service-annotations:1.0")
}

buildConfig {
// 配置 BuildConfig 的包名
packageName("com.guodong.android.version.kcp.plugin.kotlin")

// 设置 Kotlin 插件唯一 ID
buildConfigField("String", "KOTLIN_PLUGIN_ID", "\"${rootProject.extra["kotlin_plugin_id"]}\"")
}

tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

VersionCommandLineProcessor

实现 CommandLineProcessor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@AutoService(CommandLineProcessor::class)
class VersionCommandLineProcessor : CommandLineProcessor {

companion object {
// OptionName 对应 VersionGradlePlugin#applyToCompilation() 传入的 Key
private const val OPTION_VERSION = "version"

// ConfigurationKey
val ARG_VERSION = CompilerConfigurationKey<String>(OPTION_VERSION)
}

// 配置 Kotlin 插件唯一 ID
override val pluginId: String = BuildConfig.KOTLIN_PLUGIN_ID

// 读取 `SubpluginOptions` 参数,并写入 `CliOption`
override val pluginOptions: Collection<AbstractCliOption> = listOf(
CliOption(
optionName = OPTION_VERSION,
valueDescription = "string",
description = "version string",
required = true,
)
)

// 处理 `CliOption` 写入 `CompilerConfiguration`
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
when (option.optionName) {
OPTION_VERSION -> configuration.put(ARG_VERSION, value)
else -> throw IllegalArgumentException("Unexpected config option ${option.optionName}")
}
}
}

VersionComponentRegistrar

实现 ComponentRegistrar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@AutoService(ComponentRegistrar::class)
class VersionComponentRegistrar(
private val defaultVersion: String,
) : ComponentRegistrar {

companion object {
internal const val DEFAULT_VERSION = "0.0.0.0"
}

@Suppress("unused") // Used by service loader
constructor() : this(DEFAULT_VERSION)

override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
// 获取日志收集器
val messageCollector = configuration.get(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)

// 获取传入的版本号
val version = configuration.get(VersionCommandLineProcessor.ARG_VERSION, defaultVersion)

// 输出日志,查看是否执行
// CompilerMessageSeverity.INFO - 没有看到日志输出
// CompilerMessageSeverity.ERROR - 编译过程停止执行
messageCollector.report(CompilerMessageSeverity.STRONG_WARNING, "Welcome to guodongAndroid-version kcp kotlin plugin")

// 此处在 `ClassBuilderInterceptorExtension` 中注册扩展
ClassBuilderInterceptorExtension.registerExtension(
project,
VersionClassGenerationInterceptor(
messageCollector = messageCollector,
// 传入版本号
version = version
)
)
}
}

VersionClassGenerationInterceptor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class VersionClassGenerationInterceptor(
private val messageCollector: MessageCollector,
private val version: String,
) : ClassBuilderInterceptorExtension {

// 拦截 ClassBuilderFactory
override fun interceptClassBuilderFactory(
interceptedFactory: ClassBuilderFactory,
bindingContext: BindingContext,
diagnostics: DiagnosticSink
// 自定义 ClassBuilderFactory 委托给 源ClassBuilderFactory
): ClassBuilderFactory = object : ClassBuilderFactory by interceptedFactory {

// 复写 newClassBuilder
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
// 自定义 ClassBuilder
return VersionClassBuilder(
messageCollector = messageCollector,
// 传入版本号
version = version,
// 传入源ClassBuilder
delegate = interceptedFactory.newClassBuilder(origin),
)
}
}
}

VersionClassBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
class VersionClassBuilder(
private val messageCollector: MessageCollector,
private val version: String,
private val delegate: ClassBuilder,
) : DelegatingClassBuilder() {

companion object {
private const val VERSION_NAME = "com/guodong/android/VersionCurrentValue"
private const val MIN_COMPONENT_VALUE = 0
private const val MAX_COMPONENT_VALUE = 255
}

override fun getDelegate(): ClassBuilder {
return delegate
}

override fun newMethod(
origin: JvmDeclarationOrigin,
access: Int,
name: String,
desc: String,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {

val original = super.newMethod(origin, access, name, desc, signature, exceptions)

val thisName = delegate.thisName

// 校验VersionCurrentValue的完全限定名
if (thisName != VERSION_NAME) {
return original
}

// 校验是否在`build.gradle`中设置了版本号
if (version == VersionComponentRegistrar.DEFAULT_VERSION) {
messageCollector.report(
CompilerMessageSeverity.ERROR,
"Missing version, need to set version in build.gradle, like this:\n" +
"version {\n" +
"\tversion = \"1.0.0.0\"\n" +
"}"
)
}

// 结构版本号
val (major, minor, patch, extra, suffix) = parseVersion()

// 返回ASM MethodVisitor
return VersionMethodVisitor(Opcodes.ASM9, original, major, minor, patch, extra, suffix)
}

// 解析版本号为`Multiple`
private fun parseVersion(): Multiple<Int, Int, Int, Int, String?> {
if (version.isEmpty()) {
throw IllegalArgumentException("Version must not be empty.")
}

val major: Int
val minor: Int
val patch: Int
val extra: Int
val suffix: String?

if (version.contains("-")) {
val split = version.split("-")
if (split.size != 2) {
throw IllegalArgumentException("Version components must be only contains one `-`.")
}

val versions = split[0].split(".")
val length = versions.size
if (length != 4) {
throw IllegalArgumentException("Version components must be four digits, it is [ $version ] now.")
}

try {
major = versions[0].toInt()
minor = versions[1].toInt()
patch = versions[2].toInt()
extra = versions[3].toInt()
suffix = split[1]
} catch (e: NumberFormatException) {
val errMsg = "Version components must consist of numbers."
val exception = IllegalArgumentException(errMsg)
exception.addSuppressed(e)
throw exception
}
} else {
val versions = version.split(".")
val length = versions.size
if (length != 4) {
throw IllegalArgumentException("Version components must be four digits, it is [ $version ] now.")
}

try {
major = versions[0].toInt()
minor = versions[1].toInt()
patch = versions[2].toInt()
extra = versions[3].toInt()
suffix = null
} catch (e: NumberFormatException) {
val errMsg = "Version components must consist of numbers."
val exception = IllegalArgumentException(errMsg)
exception.addSuppressed(e)
throw exception
}
}

if (suffix.isNullOrEmpty()) {
messageCollector.report(
CompilerMessageSeverity.WARNING,
String.format(Locale.CHINA, "version = %d.%d.%d.%d", major, minor, patch, extra)
)
} else {
messageCollector.report(
CompilerMessageSeverity.WARNING,
String.format(Locale.CHINA, "version = %d.%d.%d.%d-%s", major, minor, patch, extra, suffix)
)
}

if (checkVersion(major) || checkVersion(minor) || checkVersion(patch) || checkVersion(extra)) {
val msg = String.format(
Locale.CHINA,
"Version components are out of range: %d.%d.%d.%d.",
major,
minor,
patch,
extra
)
throw IllegalArgumentException(msg)
}

return Multiple(major, minor, patch, extra, suffix)
}

private fun checkVersion(version: Int): Boolean {
return version < MIN_COMPONENT_VALUE || version > MAX_COMPONENT_VALUE
}
}

Multiple

1
2
3
4
5
6
7
8
9
10
data class Multiple<out A, out B, out C, out D, out E>(
val first: A,
val second: B,
val third: C,
val fourth: D,
val fifth: E?
) : Serializable {

override fun toString(): String = "($first, $second, $third, $fourth, $fifth)"
}

VersionMethodVisitor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
class VersionMethodVisitor(
api: Int,
mv: MethodVisitor,
private val major: Int,
private val minor: Int,
private val patch: Int,
private val extra: Int,
private val suffix: String?
) : MethodPatternAdapter(api, mv) {

companion object {
// 状态
private const val SEEN_ICONST_0 = 1
private const val SEEN_ICONST_0_ICONST_0 = 2
private const val SEEN_ICONST_0_ICONST_0_ICONST_0 = 3
private const val SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0 = 4
private const val SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0_ACONST_NULL = 5

// Version完全限定名
private const val OWNER = "com/guodong/android/Version"
private const val METHOD_NAME = "<init>"
private const val METHOD_DESCRIPTOR = "(IIIILjava/lang/String;)V"
}

/**
* val version = Version(0, 0, 0, 0, null)
* ICONST_0
* ICONST_0
* ICONST_0
* ICONST_0
* ACONST_NULL
*/
override fun visitInsn(opcode: Int) {
// 状态机
when (state) {
SEEN_NOTHING -> {
if (opcode == Opcodes.ICONST_0) {
state = SEEN_ICONST_0
return
}
}
SEEN_ICONST_0 -> {
if (opcode == Opcodes.ICONST_0) {
state = SEEN_ICONST_0_ICONST_0
return
}
}
SEEN_ICONST_0_ICONST_0 -> {
if (opcode == Opcodes.ICONST_0) {
state = SEEN_ICONST_0_ICONST_0_ICONST_0
return
}
}
SEEN_ICONST_0_ICONST_0_ICONST_0 -> {
if (opcode == Opcodes.ICONST_0) {
state = SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0
return
}
}
SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0 -> {
if (opcode == Opcodes.ACONST_NULL) {
state = SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0_ACONST_NULL
return
}
}
SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0_ACONST_NULL -> {
if (opcode == Opcodes.ACONST_NULL) {
mv.visitInsn(opcode)
return
}
}
}

super.visitInsn(opcode)
}

override fun visitMethodInsn(
opcode: Int,
owner: String,
name: String,
descriptor: String,
isInterface: Boolean
) {

val flag = opcode == Opcodes.INVOKESPECIAL
&& OWNER == owner
&& METHOD_NAME == name
&& METHOD_DESCRIPTOR == descriptor

when (state) {
SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0_ACONST_NULL -> {
if (flag) {
weaveCode(major)
weaveCode(minor)
weaveCode(patch)
weaveCode(extra)
weaveSuffix()
state = SEEN_NOTHING
}
}
}

super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
}

// 补发
override fun visitInsn() {
when (state) {
SEEN_ICONST_0 -> {
mv.visitInsn(Opcodes.ICONST_0)
}
SEEN_ICONST_0_ICONST_0 -> {
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
}
SEEN_ICONST_0_ICONST_0_ICONST_0 -> {
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
}
SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0 -> {
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
}
SEEN_ICONST_0_ICONST_0_ICONST_0_ICONST_0_ACONST_NULL -> {
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ICONST_0)
mv.visitInsn(Opcodes.ACONST_NULL)
}
}
state = SEEN_NOTHING
}

// 织入版本号
private fun weaveCode(code: Int) {
when {
code <= 5 -> {
val opcode = when (code) {
0 -> Opcodes.ICONST_0
1 -> Opcodes.ICONST_1
2 -> Opcodes.ICONST_2
3 -> Opcodes.ICONST_3
4 -> Opcodes.ICONST_4
5 -> Opcodes.ICONST_5
else -> Opcodes.ICONST_0
}
mv.visitInsn(opcode)
}
code <= 127 -> {
mv.visitIntInsn(Opcodes.BIPUSH, code)
}
else -> {
mv.visitIntInsn(Opcodes.SIPUSH, code)
}
}
}

// 织入后缀
private fun weaveSuffix() {
if (suffix.isNullOrEmpty()) {
mv.visitInsn(Opcodes.ACONST_NULL)
} else {
mv.visitLdcInsn(suffix)
}
}
}

应用

sample - build.gradle.kts

1
2
3
4
5
6
7
8
plugins {
kotlin("jvm")
id("com.guodong.android.version.kcp")
}

version {
version = "1.0.0.1"
}

Test

1
2
3
4
5
6
fun main() {
println("version = ${Version.CURRENT}")
}

// output
version = 1.0.0.1

happy~

参考


  1. 参考 KotlinVersion.kt ↩︎

欢迎关注我的其它发布渠道