/
gradlebuild.binary-compatibility.gradle
198 lines (178 loc) · 8.58 KB
/
gradlebuild.binary-compatibility.gradle
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import gradlebuild.EnrichedReportRenderer
import gradlebuild.basics.GradleModuleApiAttribute
import gradlebuild.basics.PublicApi
import gradlebuild.basics.PublicKotlinDslApi
import gradlebuild.binarycompatibility.AcceptedApiChanges
import gradlebuild.binarycompatibility.BinaryCompatibilityHelper
import gradlebuild.binarycompatibility.CleanAcceptedApiChanges
import gradlebuild.binarycompatibility.transforms.ExplodeZipAndFindJars
import gradlebuild.binarycompatibility.transforms.FindGradleClasspath
import gradlebuild.binarycompatibility.transforms.FindGradleJars
plugins {
id("gradlebuild.module-identity")
}
repositories {
['distributions', 'distributions-snapshots'].each { distUrl ->
ivy {
name 'Gradle distributions'
url 'https://services.gradle.org'
patternLayout {
artifact "/${distUrl}/[module]-[revision]-bin(.[ext])"
}
metadataSources {
artifact()
}
content {
includeModule('gradle', 'gradle')
}
}
}
}
def apiChangesJsonFile = layout.projectDirectory.file("src/changes/accepted-public-api-changes.json")
def acceptedViolations = AcceptedApiChanges.parse(providers.fileContents(apiChangesJsonFile).asText.get())
def compatibilityBaselineVersion = moduleIdentity.releasedVersions.get().mostRecentRelease.version
def ARTIFACT_TYPE = Attribute.of('artifactType', String)
def RUNTIME_ATTRIBUTE = objects.named(Usage, Usage.JAVA_RUNTIME)
def DOCUMENTATION_ATTRIBUTE = objects.named(Category, Category.DOCUMENTATION)
def SOURCES_ATTRIBUTE = objects.named(DocsType, "gradle-source-folders")
configurations {
baseline
baselineClasspath {
extendsFrom baseline
attributes.attribute(ARTIFACT_TYPE, 'gradle-classpath')
}
baselineJars {
extendsFrom baseline
attributes.attribute(ARTIFACT_TYPE, 'gradle-baseline-jars')
}
currentClasspath {
canBeConsumed = false
canBeResolved = false
visible = false
}
currentApiClasspath {
canBeConsumed = false
canBeResolved = true
description = "Classpath to check binary compatibility against"
attributes.attribute(GradleModuleApiAttribute.attribute as Attribute<GradleModuleApiAttribute>, GradleModuleApiAttribute.API)
extendsFrom(currentClasspath)
}
currentSources {
canBeConsumed = false
canBeResolved = true
description = "Sources to check binary compatibility against"
attributes.attribute(GradleModuleApiAttribute.attribute as Attribute<GradleModuleApiAttribute>, GradleModuleApiAttribute.API)
attributes.attribute(Usage.USAGE_ATTRIBUTE as Attribute<Usage>, RUNTIME_ATTRIBUTE)
attributes.attribute(Category.CATEGORY_ATTRIBUTE as Attribute<Category>, DOCUMENTATION_ATTRIBUTE)
attributes.attribute(DocsType.DOCS_TYPE_ATTRIBUTE as Attribute<DocsType>, SOURCES_ATTRIBUTE)
extendsFrom(currentClasspath)
}
}
def currentClasspath = configurations.currentApiClasspath.incoming.artifactView { lenient(true) }.files
def currentDistroJars = currentClasspath.filter { it.name.startsWith('gradle-') && it.name.endsWith('.jar') }
def baselineJars = configurations.baselineJars
def baseVersion = moduleIdentity.version.map { it.baseVersion.version }
dependencies {
baseline("gradle:gradle:${compatibilityBaselineVersion}@zip")
// This transform takes the Gradle zip distribution,
// and unzips the Gradle jar files that it contains in a directory
registerTransform(ExplodeZipAndFindJars) {
from.attribute(ARTIFACT_TYPE, 'zip')
to.attribute(ARTIFACT_TYPE, 'gradle-libs-dir')
}
registerTransform(FindGradleClasspath) {
from.attribute(ARTIFACT_TYPE, 'gradle-libs-dir')
to.attribute(ARTIFACT_TYPE, 'gradle-classpath')
}
dependencies.registerTransform(FindGradleJars) {
from.attribute(ARTIFACT_TYPE, 'gradle-libs-dir')
to.attribute(ARTIFACT_TYPE, 'gradle-baseline-jars')
}
}
def currentUpgradedPropertiesFile = layout.buildDirectory.file("gradle-api-info/current-upgraded-properties.json")
def baselineUpgradedPropertiesFile = layout.buildDirectory.file("gradle-api-info/baseline-upgraded-properties.json")
def extractGradleApiInfo = tasks.register("extractGradleApiInfo", gradlebuild.binarycompatibility.ExtractGradleApiInfoTask) {
currentDistributionJars = currentDistroJars
baselineDistributionJars = baselineJars
currentUpgradedProperties = currentUpgradedPropertiesFile
baselineUpgradedProperties = baselineUpgradedPropertiesFile
}
def checkBinaryCompatibility = tasks.register("checkBinaryCompatibility", gradlebuild.binarycompatibility.JapicmpTask) {
def isSnapshot = moduleIdentity.snapshot
inputs.property('acceptedViolations', acceptedViolations.toAcceptedChangesMap())
inputs.property("baseline.version", compatibilityBaselineVersion)
inputs.property("currentVersion", baseVersion)
def apiSourceFolders = configurations.currentSources.incoming.artifactView { lenient(true) }.files
inputs.files("apiSourceFolders", apiSourceFolders)
inputs.files(currentClasspath)
inputs.files(extractGradleApiInfo)
newClasspath.from(currentClasspath)
oldClasspath.from(configurations.baselineClasspath)
newArchives.from(currentDistroJars)
oldArchives.from(baselineJars)
// binary breaking change checking setup
onlyModified = false
failOnModification = false // we rely on the custom report to fail or not
ignoreMissingClasses = true // because of a missing scala.runtime.AbstractFunction0 class
richReport({
it.includedClasses = toPatterns(PublicApi.includes + PublicKotlinDslApi.includes)
it.excludedClasses = toPatterns(PublicApi.excludes + PublicKotlinDslApi.excludes)
it.title = "Binary compatibility report for Gradle ${isSnapshot.get() ? "${baseVersion.get()}-SNAPSHOT" : version} since ${compatibilityBaselineVersion}"
it.destinationDir = file("$buildDir/reports/binary-compatibility")
it.reportName = "report.html"
it.description = """
<p>See the <a href="https://bt-internal-docs.grdev.net/gbt/how-to/release/release-troubleshooting/#binary-compatibility-check-failed-">documentation</a> for more details.</p>
<p>
We check the binary compatibility by comparing the current code’s binary interfaces
against THE LATEST VERSION WHICH IS RELEASED FROM RELEASE BRANCH (from `released-version.json` on this branch)
AND LOWER THAN CURRENT BASE VERSION (from `version.txt` on this branch).
The difference must identically match <a href="${apiChangesJsonFile.asFile.path}">accepted-public-api-changes.json</a>, no more, no less - otherwise the task will fail.
</p>
${writeFilterPreset(project)}
""".stripIndent()
it.renderer.set(EnrichedReportRenderer.class)
} as Action)
BinaryCompatibilityHelper.setupJApiCmpRichReportRules(
delegate,
acceptedViolations,
apiSourceFolders,
baseVersion.get(),
apiChangesJsonFile.asFile,
rootProject.layout.projectDirectory,
currentUpgradedPropertiesFile.get().asFile,
baselineUpgradedPropertiesFile.get().asFile
)
}
tasks.named("check").configure { dependsOn(checkBinaryCompatibility) }
tasks.register("cleanAcceptedApiChanges", CleanAcceptedApiChanges) {
description = 'Cleans up all existing accepted API changes.'
jsonFile = apiChangesJsonFile.asFile
}
static List<String> toPatterns(List<String> packages) {
packages.collect {
it.replaceAll('\\*\\*', '###')
.replaceAll('/\\*', '/[A-Z][a-z_A-Z0-9]+')
.replaceAll('$', '\\$')
.replaceAll('/', '[.]')
.replaceAll('###', '.*?')
}
}
private String writeFilterPreset(Project project) {
def preset = project.hasProperty('bin.cmp.report.severity.filter') ? project.getProperty('bin.cmp.report.severity.filter') : 'All levels'
return """<input id="filter-preset" type="hidden" value="$preset" />"""
}