forked from open-telemetry/opentelemetry-java
-
Notifications
You must be signed in to change notification settings - Fork 1
/
otel.japicmp-conventions.gradle.kts
161 lines (147 loc) · 6.67 KB
/
otel.japicmp-conventions.gradle.kts
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
import com.google.auto.value.AutoValue
import japicmp.model.JApiChangeStatus
import japicmp.model.JApiCompatibility
import japicmp.model.JApiCompatibilityChange
import japicmp.model.JApiMethod
import me.champeau.gradle.japicmp.JapicmpTask
import me.champeau.gradle.japicmp.report.Severity
import me.champeau.gradle.japicmp.report.Violation
import me.champeau.gradle.japicmp.report.stdrules.AbstractRecordingSeenMembers
import me.champeau.gradle.japicmp.report.stdrules.RecordSeenMembersSetup
import me.champeau.gradle.japicmp.report.stdrules.SourceCompatibleRule
import me.champeau.gradle.japicmp.report.stdrules.UnchangedMemberRule
plugins {
base
id("me.champeau.gradle.japicmp")
}
/**
* The latest *released* version of the project. Evaluated lazily so the work is only done if necessary.
*/
val latestReleasedVersion: String by lazy {
// hack to find the current released version of the project
val temp: Configuration = configurations.create("tempConfig")
// pick the api, since it's always there.
dependencies.add(temp.name, "io.opentelemetry:opentelemetry-api:latest.release")
val moduleVersion = configurations["tempConfig"].resolvedConfiguration.firstLevelModuleDependencies.elementAt(0).moduleVersion
configurations.remove(temp)
logger.debug("Discovered latest release version: " + moduleVersion)
moduleVersion
}
class AllowDefaultMethodRule : AbstractRecordingSeenMembers() {
override fun maybeAddViolation(member: JApiCompatibility): Violation? {
for (change in member.compatibilityChanges) {
if (isAbstractMethodOnAutoValue(member, change)) {
continue
}
if (isLogEmitterProvider(member)) {
continue
}
if (!change.isSourceCompatible) {
return Violation.error(member, "Not source compatible")
}
if (!change.isBinaryCompatible) {
return Violation.notBinaryCompatible(member, Severity.error)
}
}
return null
}
/**
* Checks if the change is an abstract method on a class annotated with AutoValue.
* AutoValues need to override default interface methods and declare them abstract again.
* It causes METHOD_ABSTRACT_ADDED_TO_CLASS - source-incompatible change. It's
* false-positive since AutoValue will generate implementation anyway.
*/
fun isAbstractMethodOnAutoValue(member: JApiCompatibility, change: JApiCompatibilityChange): Boolean {
return change == JApiCompatibilityChange.METHOD_ABSTRACT_ADDED_TO_CLASS &&
member is JApiMethod &&
member.getjApiClass().newClass.get().getAnnotation(AutoValue::class.java) != null
}
/**
* Check if the change is related to LogEmitterProvider name change to LoggerProvider.
*/
// TODO(jack-berg): remove after 1.19.0.
fun isLogEmitterProvider(member: JApiCompatibility): Boolean {
try {
return member is JApiMethod &&
member.oldMethod.get().methodInfo.name.matches("(getSdk|set|add)LogEmitterProvider(.*)".toRegex())
} catch (e: IllegalStateException) {
return false
}
}
}
/**
* Locate the project's artifact of a particular version.
*/
fun findArtifact(version: String): File {
val existingGroup = group
try {
// Temporarily change the group name because we want to fetch an artifact with the same
// Maven coordinates as the project, which Gradle would not allow otherwise.
group = "virtual_group"
val depModule = "io.opentelemetry:${base.archivesName.get()}:$version@jar"
val depJar = "${base.archivesName.get()}-$version.jar"
val configuration: Configuration = configurations.detachedConfiguration(
dependencies.create(depModule)
)
return files(configuration.files).filter {
it.name.equals(depJar)
}.singleFile
} finally {
group = existingGroup
}
}
// generate the api diff report for any module that is stable and publishes a jar.
if (!project.hasProperty("otel.release") && !project.name.startsWith("bom")) {
afterEvaluate {
tasks {
val jApiCmp by registering(JapicmpTask::class) {
dependsOn("jar")
// the japicmp "new" version is either the user-specified one, or the locally built jar.
val apiNewVersion: String? by project
val newArtifact = apiNewVersion?.let { findArtifact(it) }
?: file(getByName<Jar>("jar").archiveFile)
newClasspath.from(files(newArtifact))
// only output changes, not everything
onlyModified.set(true)
// the japicmp "old" version is either the user-specified one, or the latest release.
val apiBaseVersion: String? by project
val baselineVersion = apiBaseVersion ?: latestReleasedVersion
oldClasspath.from(
try {
files(findArtifact(baselineVersion))
} catch (e: Exception) {
// if we can't find the baseline artifact, this is probably one that's never been published before,
// so publish the whole API. We do that by flipping this flag, and comparing the current against nothing.
onlyModified.set(false)
files()
}
)
// Reproduce defaults from https://github.com/melix/japicmp-gradle-plugin/blob/09f52739ef1fccda6b4310cf3f4b19dc97377024/src/main/java/me/champeau/gradle/japicmp/report/ViolationsGenerator.java#L130
// but allow new default methods on interfaces, adding default implementations to
// interface methods previously abstract, and select additional customizations defined in
// AllowDefaultMethodRule.
compatibilityChangeExcludes.set(listOf("METHOD_NEW_DEFAULT", "METHOD_ABSTRACT_NOW_DEFAULT"))
richReport {
addSetupRule(RecordSeenMembersSetup::class.java)
addRule(JApiChangeStatus.NEW, SourceCompatibleRule::class.java)
addRule(JApiChangeStatus.MODIFIED, SourceCompatibleRule::class.java)
addRule(JApiChangeStatus.UNCHANGED, UnchangedMemberRule::class.java)
addRule(AllowDefaultMethodRule::class.java)
addRule(SourceCompatibleRule::class.java)
}
// this is needed so that we only consider the current artifact, and not dependencies
ignoreMissingClasses.set(true)
packageExcludes.addAll("*.internal", "*.internal.*", "io.opentelemetry.internal.shaded.jctools.*")
val baseVersionString = if (apiBaseVersion == null) "latest" else baselineVersion
txtOutputFile.set(
apiNewVersion?.let { file("$rootDir/docs/apidiffs/${apiNewVersion}_vs_$baselineVersion/${base.archivesName.get()}.txt") }
?: file("$rootDir/docs/apidiffs/current_vs_$baseVersionString/${base.archivesName.get()}.txt")
)
}
// have the check task depend on the api comparison task, to make it more likely it will get used.
named("check") {
dependsOn(jApiCmp)
}
}
}
}