From dc1daa53bf5719e9906d65cccaf42950ff9fab78 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 17 Sep 2026 18:29:12 -0700 Subject: [PATCH] Don't use raw cached filenames in AggregatedJavadoc Update `AggregatedJavadoc` so that jar names are constructed from resolved artifact IDs rather than using the raw cache filename. Without this change, javadoc generation fails when consuming SNAPSHOT dependencies. See gh-51456 --- .../boot/build/bom/BomResolver.java | 4 +- .../boot/build/docs/AggregatedJavadoc.java | 43 ++++++++++++++++--- documentation/spring-boot-docs/build.gradle | 4 +- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java index e3b4579ac51..26532b11017 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/bom/BomResolver.java @@ -220,8 +220,8 @@ class BomResolver { private void addOfflineJavadocLink(Map> offlineJavadocLinks, Link link, Object version, Stream moduleNames) { URI uri = URI.create(link.url(new LinkedVersion(version))); - offlineJavadocLinks.computeIfAbsent(uri, (key) -> new ArrayList<>()) - .addAll(moduleNames.map((name) -> "%s-%s-javadoc.jar".formatted(name, version)).toList()); + List jars = moduleNames.map((name) -> "%s-%s-javadoc.jar".formatted(name, version)).toList(); + offlineJavadocLinks.computeIfAbsent(uri, (key) -> new ArrayList<>()).addAll(jars); } private static final class Node { diff --git a/buildSrc/src/main/java/org/springframework/boot/build/docs/AggregatedJavadoc.java b/buildSrc/src/main/java/org/springframework/boot/build/docs/AggregatedJavadoc.java index 7382b46690a..adc3f522076 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/docs/AggregatedJavadoc.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/docs/AggregatedJavadoc.java @@ -31,10 +31,16 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import org.gradle.api.UncheckedIOException; +import org.gradle.api.artifacts.component.ComponentIdentifier; +import org.gradle.api.artifacts.component.ModuleComponentIdentifier; +import org.gradle.api.artifacts.result.ResolvedArtifactResult; import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.FileCollection; import org.gradle.api.logging.LogLevel; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.Classpath; +import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.javadoc.Javadoc; import org.gradle.external.javadoc.StandardJavadocDocletOptions; @@ -72,13 +78,36 @@ public abstract class AggregatedJavadoc extends Javadoc { IGNORED_PACKAGES = Collections.unmodifiableList(ignoredPackages); } + public void javadocJars(Provider> artifacts) { + getJavadocJarFiles().set(artifacts.map(this::asJavadocJarFiles)); + getJavadocJarNames().set(artifacts.map(this::asJavadocJarNames)); + } + + private List asJavadocJarFiles(Set artifacts) { + return artifacts.stream().map(ResolvedArtifactResult::getFile).toList(); + } + + private List asJavadocJarNames(Set artifacts) { + return artifacts.stream().map(this::asJavadocJarName).toList(); + } + + private String asJavadocJarName(ResolvedArtifactResult artifact) { + ComponentIdentifier identifier = artifact.getId().getComponentIdentifier(); + if (identifier instanceof ModuleComponentIdentifier moduleIdentifier) { + return "%s-%s-javadoc.jar".formatted(moduleIdentifier.getModule(), moduleIdentifier.getVersion()); + } + return identifier.getDisplayName(); + } + @Classpath @InputFiles public abstract ConfigurableFileCollection getResolvedBom(); - @Classpath @InputFiles - public abstract ConfigurableFileCollection getJavadocJars(); + abstract ListProperty getJavadocJarFiles(); + + @Input + abstract ListProperty getJavadocJarNames(); @Override protected void generate() { @@ -147,11 +176,13 @@ public abstract class AggregatedJavadoc extends Javadoc { } private void extractJavdocListFiles(File javadocListsDir) { - getJavadocJars().forEach((javadocJar) -> { - FileCollection source = getProject().zipTree(javadocJar).filter(this::isJavadocListFile); - File destination = new File(javadocListsDir, javadocJar.getName()); + List files = getJavadocJarFiles().get(); + List names = getJavadocJarNames().get(); + for (int i = 0; i < files.size(); i++) { + FileCollection source = getProject().zipTree(files.get(i)).filter(this::isJavadocListFile); + File destination = new File(javadocListsDir, names.get(i)); getProject().copy((copy) -> copy.from(source).into(destination)); - }); + } } private boolean isJavadocListFile(File file) { diff --git a/documentation/spring-boot-docs/build.gradle b/documentation/spring-boot-docs/build.gradle index fe262b81497..240591d6abe 100644 --- a/documentation/spring-boot-docs/build.gradle +++ b/documentation/spring-boot-docs/build.gradle @@ -165,7 +165,7 @@ configurations { def aggregatedJavadoc = tasks.register('aggregatedJavadoc', AggregatedJavadoc) { resolvedBom.from(configurations.resolvedBom) - javadocJars.from(configurations.runtimeClasspath.incoming.artifactView { + javadocJars(configurations.runtimeClasspath.incoming.artifactView { withVariantReselection() attributes { lenient = true @@ -174,7 +174,7 @@ def aggregatedJavadoc = tasks.register('aggregatedJavadoc', AggregatedJavadoc) { attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL)); attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, "implicitjavadoc")); } - }.files) + }.artifacts.resolvedArtifacts) destinationDir = project.file(project.layout.buildDirectory.dir("docs/javadoc")) source = configurations.javadocSource classpath = configurations.runtimeClasspath