diff --git a/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java b/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java index 1831393168e..697eed64d32 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/AntoraConventions.java @@ -35,7 +35,9 @@ import org.antora.gradle.AntoraPlugin; import org.antora.gradle.AntoraTask; import org.gradle.StartParameter; import org.gradle.api.Project; +import org.gradle.api.artifacts.Configuration; import org.gradle.api.file.Directory; +import org.gradle.api.file.FileCollection; import org.gradle.api.logging.LogLevel; import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.provider.Provider; @@ -46,7 +48,7 @@ import org.gradle.api.tasks.TaskProvider; import org.springframework.boot.build.antora.AntoraAsciidocAttributes; import org.springframework.boot.build.antora.GenerateAntoraPlaybook; import org.springframework.boot.build.bom.BomExtension; -import org.springframework.boot.build.constraints.ExtractVersionConstraints; +import org.springframework.boot.build.bom.ResolvedBom; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -76,7 +78,10 @@ public class AntoraConventions { } private void apply(Project project, AntoraPlugin antoraPlugin) { - TaskProvider dependencyVersionsTask = addDependencyVersionsTask(project); + Configuration resolvedBom = project.getConfigurations().create("resolveBom"); + project.getDependencies() + .add(resolvedBom.getName(), project.getDependencies() + .project(Map.of("path", DEPENDENCIES_PATH, "configuration", "resolvedBom"))); project.getPlugins().apply(GenerateAntoraYmlPlugin.class); TaskContainer tasks = project.getTasks(); TaskProvider generateAntoraPlaybookTask = tasks.register( @@ -86,8 +91,8 @@ public class AntoraConventions { (task) -> configureCopyAntoraPackageJsonTask(project, task)); TaskProvider npmInstallTask = tasks.register("antoraNpmInstall", NpmInstallTask.class, (task) -> configureNpmInstallTask(project, task, copyAntoraPackageJsonTask)); - tasks.withType(GenerateAntoraYmlTask.class, (generateAntoraYmlTask) -> configureGenerateAntoraYmlTask(project, - generateAntoraYmlTask, dependencyVersionsTask)); + tasks.withType(GenerateAntoraYmlTask.class, + (generateAntoraYmlTask) -> configureGenerateAntoraYmlTask(project, generateAntoraYmlTask, resolvedBom)); tasks.withType(AntoraTask.class, (antoraTask) -> configureAntoraTask(project, antoraTask, npmInstallTask, generateAntoraPlaybookTask)); project.getExtensions() @@ -118,21 +123,15 @@ public class AntoraConventions { npmInstallTask.getNpmCommand().set(List.of("ci", "--silent", "--no-progress")); } - private TaskProvider addDependencyVersionsTask(Project project) { - return project.getTasks() - .register("dependencyVersions", ExtractVersionConstraints.class, - (task) -> task.enforcedPlatform(DEPENDENCIES_PATH)); - } - private void configureGenerateAntoraYmlTask(Project project, GenerateAntoraYmlTask generateAntoraYmlTask, - TaskProvider dependencyVersionsTask) { + Configuration resolvedBom) { generateAntoraYmlTask.getOutputs().doNotCacheIf("getAsciidocAttributes() changes output", (task) -> true); - generateAntoraYmlTask.dependsOn(dependencyVersionsTask); + generateAntoraYmlTask.dependsOn(resolvedBom); generateAntoraYmlTask.setProperty("componentName", "boot"); generateAntoraYmlTask.setProperty("outputFile", project.getLayout().getBuildDirectory().file("generated/docs/antora-yml/antora.yml")); generateAntoraYmlTask.setProperty("yml", getDefaultYml(project)); - generateAntoraYmlTask.getAsciidocAttributes().putAll(getAsciidocAttributes(project, dependencyVersionsTask)); + generateAntoraYmlTask.getAsciidocAttributes().putAll(getAsciidocAttributes(project, resolvedBom)); } private Map getDefaultYml(Project project) { @@ -151,11 +150,11 @@ public class AntoraConventions { return defaultYml; } - private Provider> getAsciidocAttributes(Project project, - TaskProvider dependencyVersionsTask) { - return dependencyVersionsTask.map((task) -> task.getVersionConstraints()).map((constraints) -> { + private Provider> getAsciidocAttributes(Project project, FileCollection resolvedBoms) { + return project.provider(() -> { BomExtension bom = (BomExtension) project.project(DEPENDENCIES_PATH).getExtensions().getByName("bom"); - return new AntoraAsciidocAttributes(project, bom, constraints).get(); + ResolvedBom resolvedBom = ResolvedBom.readFrom(resolvedBoms.getSingleFile()); + return new AntoraAsciidocAttributes(project, bom, resolvedBom).get(); }); } diff --git a/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java b/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java index d32004c8794..b5b19a4d57d 100644 --- a/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java +++ b/buildSrc/src/main/java/org/springframework/boot/build/antora/AntoraAsciidocAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2024 the original author or authors. + * Copyright 2012-2025 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. @@ -20,7 +20,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -32,6 +34,10 @@ import org.gradle.api.Project; import org.springframework.boot.build.artifacts.ArtifactRelease; import org.springframework.boot.build.bom.BomExtension; import org.springframework.boot.build.bom.Library; +import org.springframework.boot.build.bom.ResolvedBom; +import org.springframework.boot.build.bom.ResolvedBom.Bom; +import org.springframework.boot.build.bom.ResolvedBom.Id; +import org.springframework.boot.build.bom.ResolvedBom.ResolvedLibrary; import org.springframework.boot.build.properties.BuildProperties; import org.springframework.boot.build.properties.BuildType; import org.springframework.util.Assert; @@ -59,17 +65,48 @@ public class AntoraAsciidocAttributes { private final Map projectProperties; - public AntoraAsciidocAttributes(Project project, BomExtension dependencyBom, - Map dependencyVersions) { + public AntoraAsciidocAttributes(Project project, BomExtension dependencyBom, ResolvedBom resolvedBom) { this.version = String.valueOf(project.getVersion()); this.latestVersion = Boolean.parseBoolean(String.valueOf(project.findProperty("latestVersion"))); this.buildType = BuildProperties.get(project).buildType(); this.artifactRelease = ArtifactRelease.forProject(project); this.libraries = dependencyBom.getLibraries(); - this.dependencyVersions = dependencyVersions; + this.dependencyVersions = dependencyVersionsOf(resolvedBom); this.projectProperties = project.getProperties(); } + private static Map dependencyVersionsOf(ResolvedBom resolvedBom) { + Map dependencyVersions = new HashMap<>(); + for (ResolvedLibrary library : resolvedBom.libraries()) { + dependencyVersions.putAll(dependencyVersionsOf(library.managedDependencies())); + for (Bom importedBom : library.importedBoms()) { + dependencyVersions.putAll(dependencyVersionsOf(importedBom)); + } + } + return dependencyVersions; + } + + private static Map dependencyVersionsOf(Bom bom) { + Map dependencyVersions = new HashMap<>(); + if (bom != null) { + dependencyVersions.putAll(dependencyVersionsOf(bom.managedDependencies())); + dependencyVersions.putAll(dependencyVersionsOf(bom.parent())); + for (Bom importedBom : bom.importedBoms()) { + dependencyVersions.putAll(dependencyVersionsOf(importedBom)); + } + } + return dependencyVersions; + } + + private static Map dependencyVersionsOf(Collection managedDependencies) { + Map dependencyVersions = new HashMap<>(); + for (Id managedDependency : managedDependencies) { + dependencyVersions.put(managedDependency.groupId() + ":" + managedDependency.artifactId(), + managedDependency.version()); + } + return dependencyVersions; + } + AntoraAsciidocAttributes(String version, boolean latestVersion, BuildType buildType, List libraries, Map dependencyVersions, Map projectProperties) { this.version = version; diff --git a/buildSrc/src/main/java/org/springframework/boot/build/constraints/ExtractVersionConstraints.java b/buildSrc/src/main/java/org/springframework/boot/build/constraints/ExtractVersionConstraints.java deleted file mode 100644 index e1147c672d3..00000000000 --- a/buildSrc/src/main/java/org/springframework/boot/build/constraints/ExtractVersionConstraints.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright 2012-2024 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 - * - * https://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. - */ - -package org.springframework.boot.build.constraints; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.TreeSet; - -import org.gradle.api.DefaultTask; -import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.artifacts.ComponentMetadataDetails; -import org.gradle.api.artifacts.Configuration; -import org.gradle.api.artifacts.DependencyConstraint; -import org.gradle.api.artifacts.DependencyConstraintMetadata; -import org.gradle.api.artifacts.DependencyConstraintSet; -import org.gradle.api.artifacts.dsl.DependencyHandler; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.TaskAction; -import org.gradle.platform.base.Platform; - -import org.springframework.boot.build.bom.BomExtension; -import org.springframework.boot.build.bom.BomPlugin; -import org.springframework.boot.build.bom.Library; - -/** - * {@link Task} to extract constraints from a {@link Platform}. The platform's own - * constraints and those in any boms upon which it depends are extracted. - * - * @author Andy Wilkinson - */ -public abstract class ExtractVersionConstraints extends DefaultTask { - - private final Configuration configuration; - - private final Map versionConstraints = new TreeMap<>(); - - private final Set constrainedVersions = new TreeSet<>(); - - private final Set versionProperties = new TreeSet<>(); - - private final List dependencyConstraintSets = new ArrayList<>(); - - private final List boms = new ArrayList<>(); - - private final DependencyHandler dependencies; - - public ExtractVersionConstraints() { - this.dependencies = getProject().getDependencies(); - this.configuration = getProject().getConfigurations().create(getName()); - this.dependencies.getComponents().all(this::processMetadataDetails); - } - - public void enforcedPlatform(String projectPath) { - this.configuration.getDependencies() - .add(this.dependencies - .enforcedPlatform(this.dependencies.project(Collections.singletonMap("path", projectPath)))); - Project project = getProject().project(projectPath); - project.getPlugins().withType(BomPlugin.class).all((plugin) -> { - this.boms.add(project.getExtensions().getByType(BomExtension.class)); - this.dependencyConstraintSets - .add(project.getConfigurations().getByName("apiElements").getAllDependencyConstraints()); - }); - } - - @Internal - public Map getVersionConstraints() { - return Collections.unmodifiableMap(this.versionConstraints); - } - - @Internal - public Set getConstrainedVersions() { - return this.constrainedVersions; - } - - @Internal - public Set getVersionProperties() { - return this.versionProperties; - } - - @TaskAction - void extractVersionConstraints() { - this.configuration.resolve(); - this.boms.forEach(this::extractVersionProperties); - for (DependencyConstraintSet constraints : this.dependencyConstraintSets) { - for (DependencyConstraint constraint : constraints) { - this.versionConstraints.put(constraint.getGroup() + ":" + constraint.getName(), - constraint.getVersionConstraint().toString()); - this.constrainedVersions.add(new ConstrainedVersion(constraint.getGroup(), constraint.getName(), - constraint.getVersionConstraint().toString())); - } - } - } - - private void extractVersionProperties(BomExtension bomExtension) { - for (Library lib : bomExtension.getLibraries()) { - String versionProperty = lib.getVersionProperty(); - if (versionProperty != null) { - this.versionProperties.add(new VersionProperty(lib.getName(), versionProperty)); - } - } - } - - private void processMetadataDetails(ComponentMetadataDetails details) { - details.allVariants((variantMetadata) -> variantMetadata.withDependencyConstraints((dependencyConstraints) -> { - for (DependencyConstraintMetadata constraint : dependencyConstraints) { - this.versionConstraints.put(constraint.getGroup() + ":" + constraint.getName(), - constraint.getVersionConstraint().toString()); - this.constrainedVersions.add(new ConstrainedVersion(constraint.getGroup(), constraint.getName(), - constraint.getVersionConstraint().toString())); - } - })); - } - - public static final class ConstrainedVersion implements Comparable, Serializable { - - private final String group; - - private final String artifact; - - private final String version; - - private ConstrainedVersion(String group, String artifact, String version) { - this.group = group; - this.artifact = artifact; - this.version = version; - } - - public String getGroup() { - return this.group; - } - - public String getArtifact() { - return this.artifact; - } - - public String getVersion() { - return this.version; - } - - @Override - public int compareTo(ConstrainedVersion other) { - int groupComparison = this.group.compareTo(other.group); - if (groupComparison != 0) { - return groupComparison; - } - return this.artifact.compareTo(other.artifact); - } - - } - - public static final class VersionProperty implements Comparable, Serializable { - - private final String libraryName; - - private final String versionProperty; - - public VersionProperty(String libraryName, String versionProperty) { - this.libraryName = libraryName; - this.versionProperty = versionProperty; - } - - public String getLibraryName() { - return this.libraryName; - } - - public String getVersionProperty() { - return this.versionProperty; - } - - @Override - public int compareTo(VersionProperty other) { - int groupComparison = this.libraryName.compareToIgnoreCase(other.libraryName); - if (groupComparison != 0) { - return groupComparison; - } - return this.versionProperty.compareTo(other.versionProperty); - } - - } - -}