Skip to content

Commit 6008c61

Browse files
committed
Use JApiCmp Gradle plugin for API diffs
This commit removes JDiff from the Spring Framework build and instead, adds a Gradle plugin that configure JApiCmp tasks on the framework modules. Fixes gh-22942 See gh-23282
1 parent 4d10249 commit 6008c61

17 files changed

+142
-1690
lines changed

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ configure(rootProject) {
237237

238238
apply plugin: "groovy"
239239
apply plugin: "io.spring.nohttp"
240-
apply from: "${gradleScriptDir}/jdiff.gradle"
240+
apply plugin: 'org.springframework.build.api-diff'
241241
apply from: "${gradleScriptDir}/docs.gradle"
242242

243243
nohttp {

buildSrc/README.md

+16-1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,19 @@ configurations are preferred.
2929

3030
The `org.springframework.build.test-sources` updates `testCompile` dependencies to include
3131
the test source sets of `project()` dependencies. This plugin is used in the Spring Framework build
32-
to share test utilities and fixtures amongst modules.
32+
to share test utilities and fixtures amongst modules.
33+
34+
## API Diff
35+
36+
This plugin uses the [Gradle JApiCmp](https://github.com/melix/japicmp-gradle-plugin) plugin
37+
to generate API Diff reports for each Spring Framework module. This plugin is applied once on the root
38+
project and create tasks in each framework module. Unlike previous versions of this part of the build,
39+
there is no need for checking out a specific tag. The plugin will fetch the JARs we want to compare the
40+
current working version with. You can generate the reports for all modules or a single module:
41+
42+
```
43+
./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE
44+
./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE
45+
```
46+
47+
The reports are located under `build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/`.

buildSrc/build.gradle

+10
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,20 @@ plugins {
44

55
repositories {
66
mavenCentral()
7+
gradlePluginPortal()
8+
}
9+
10+
dependencies {
11+
implementation "me.champeau.gradle:japicmp-gradle-plugin:0.2.8"
12+
713
}
814

915
gradlePlugin {
1016
plugins {
17+
apiDiffPlugin {
18+
id = "org.springframework.build.api-diff"
19+
implementationClass = "org.springframework.build.api.ApiDiffPlugin"
20+
}
1121
compileConventionsPlugin {
1222
id = "org.springframework.build.compile"
1323
implementationClass = "org.springframework.build.compile.CompilerConventionsPlugin"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2002-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.build.api;
17+
18+
import java.io.File;
19+
import java.nio.file.Path;
20+
import java.nio.file.Paths;
21+
import java.util.Collections;
22+
import java.util.List;
23+
24+
import me.champeau.gradle.japicmp.JapicmpPlugin;
25+
import me.champeau.gradle.japicmp.JapicmpTask;
26+
import org.gradle.api.Plugin;
27+
import org.gradle.api.Project;
28+
import org.gradle.api.artifacts.Configuration;
29+
import org.gradle.api.artifacts.Dependency;
30+
import org.gradle.api.plugins.JavaBasePlugin;
31+
import org.gradle.api.plugins.JavaPlugin;
32+
import org.gradle.api.tasks.TaskProvider;
33+
import org.gradle.jvm.tasks.Jar;
34+
35+
/**
36+
* {@link Plugin} that applies the {@code "japicmp-gradle-plugin"}
37+
* and create tasks for all subprojects, diffing the public API one by one
38+
* and creating the reports in {@code "build/reports/api-diff/$OLDVERSION_to_$NEWVERSION/"}.
39+
* <p>{@code "./gradlew apiDiff -PbaselineVersion=5.1.0.RELEASE"} will output the
40+
* reports for the API diff between the baseline version and the current one for all modules.
41+
* You can limit the report to a single module with
42+
* {@code "./gradlew :spring-core:apiDiff -PbaselineVersion=5.1.0.RELEASE"}.
43+
*
44+
* @author Brian Clozel
45+
*/
46+
public class ApiDiffPlugin implements Plugin<Project> {
47+
48+
public static final String TASK_NAME = "apiDiff";
49+
50+
private static final String BASELINE_VERSION_PROPERTY = "baselineVersion";
51+
52+
private static final List<String> PACKAGE_INCLUDES = Collections.singletonList("org.springframework.*");
53+
54+
@Override
55+
public void apply(Project project) {
56+
if (project.hasProperty(BASELINE_VERSION_PROPERTY) && project.equals(project.getRootProject())) {
57+
project.getPluginManager().apply(JapicmpPlugin.class);
58+
project.getPlugins().withType(JapicmpPlugin.class,
59+
plugin -> applyApiDiffConventions(project));
60+
}
61+
}
62+
63+
private void applyApiDiffConventions(Project project) {
64+
String baselineVersion = project.property(BASELINE_VERSION_PROPERTY).toString();
65+
project.subprojects(subProject -> createApiDiffTask(baselineVersion, subProject));
66+
}
67+
68+
private void createApiDiffTask(String baselineVersion, Project project) {
69+
if (isProjectEligible(project)) {
70+
JapicmpTask apiDiff = project.getTasks().create(TASK_NAME, JapicmpTask.class);
71+
apiDiff.setDescription("Generates an API diff report with japicmp");
72+
apiDiff.setGroup(JavaBasePlugin.DOCUMENTATION_GROUP);
73+
74+
apiDiff.setOldClasspath(project.files(createBaselineConfiguration(baselineVersion, project)));
75+
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
76+
apiDiff.setNewArchives(project.getLayout().files(jar.get().getArchiveFile().get().getAsFile()));
77+
apiDiff.setNewClasspath(getRuntimeClassPath(project));
78+
apiDiff.setPackageIncludes(PACKAGE_INCLUDES);
79+
apiDiff.setOnlyModified(true);
80+
apiDiff.setIgnoreMissingClasses(true);
81+
// Ignore Kotlin metadata annotations since they contain
82+
// illegal HTML characters and fail the report generation
83+
apiDiff.setAnnotationExcludes(Collections.singletonList("@kotlin.Metadata"));
84+
85+
apiDiff.setHtmlOutputFile(getOutputFile(baselineVersion, project));
86+
87+
apiDiff.dependsOn(project.getTasks().getByName("jar"));
88+
}
89+
}
90+
91+
private boolean isProjectEligible(Project project) {
92+
return project.getPlugins().hasPlugin(JavaPlugin.class)
93+
&& !project.getName().equals("spring-core-coroutines")
94+
&& !project.getName().equals("spring-framework-bom");
95+
}
96+
97+
private Configuration createBaselineConfiguration(String baselineVersion, Project project) {
98+
String baseline = String.join(":",
99+
project.getGroup().toString(), project.getName(), baselineVersion);
100+
Dependency baselineDependency = project.getDependencies().create(baseline + "@jar");
101+
return project.getRootProject().getConfigurations().detachedConfiguration(baselineDependency);
102+
}
103+
104+
private Configuration getRuntimeClassPath(Project project) {
105+
return project.getConfigurations().getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
106+
}
107+
108+
private File getOutputFile(String baseLineVersion, Project project) {
109+
Path outDir = Paths.get(project.getRootProject().getBuildDir().getAbsolutePath(),
110+
"reports", "api-diff",
111+
baseLineVersion + "_to_" + project.getRootProject().getVersion());
112+
return project.file(outDir.resolve(project.getName() + ".html").toString());
113+
}
114+
115+
}

gradle/jdiff.gradle

-76
This file was deleted.

0 commit comments

Comments
 (0)