Retain distinction between compile and runtime deps of optional deps

Previously, the optional configuration was added to the compile and
runtime classpaths of each source set and the the javadoc classpath
as well. This had a few disadvantages, the most notable of which is
that it meant that the configuration was ifrst resolved and then
the outcome of the resolution was added to the compile and runtime
classpaths. As a result, none of the attributes on the compile and
runtime classpaths were considered to influence variant selection.

This commit reworks the optional dependencies plugin so that the
compile and runtime classpaths of each source set are now configured
to extend from the optional configuration. This allows each
classpath configuration's attributes to influence the dependencies
that are selected from the optional configuration during resolution.
For example, when resolving the compile classpath, compile
dependencies (Usage.JAVA_API) will be selected and when resolving the
runtime classpath, runtime dependencies (Usage.JAVA_RUNTIME) will be
selected.

The above-described change means that runtime dependencies of an
optional dependencies will no longer leak into the compile classpath.
As a result of this, our Gradle plugin's test infrastructure has
been updated so that it no longer references runtime dependencies of
the Kotlin Gradle plugin at compile time.

Closes gh-27965
This commit is contained in:
Andy Wilkinson 2021-09-13 10:06:09 +01:00
parent ab81e3c05c
commit 273600bcdd
2 changed files with 22 additions and 22 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -19,19 +19,15 @@ package org.springframework.boot.build.optional;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.attributes.Usage;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.javadoc.Javadoc;
import org.gradle.plugins.ide.eclipse.EclipsePlugin;
import org.gradle.plugins.ide.eclipse.model.EclipseModel;
/**
* A {@code Plugin} that adds support for Maven-style optional dependencies. Creates a new
* {@code optional} configuration. The {@code optional} configuration is part of the
* project's compile and runtime classpath's but does not affect the classpath of
* dependent projects.
* project's compile and runtime classpaths but does not affect the classpath of dependent
* projects.
*
* @author Andy Wilkinson
*/
@ -44,22 +40,19 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
Configuration optional = project.getConfigurations().create(OPTIONAL_CONFIGURATION_NAME);
optional.attributes((attributes) -> attributes.attribute(Usage.USAGE_ATTRIBUTE,
project.getObjects().named(Usage.class, Usage.JAVA_RUNTIME)));
Configuration optional = project.getConfigurations().create("optional");
optional.setCanBeConsumed(false);
optional.setCanBeResolved(false);
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
SourceSetContainer sourceSets = project.getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets();
sourceSets.all((sourceSet) -> {
sourceSet.setCompileClasspath(sourceSet.getCompileClasspath().plus(optional));
sourceSet.setRuntimeClasspath(sourceSet.getRuntimeClasspath().plus(optional));
project.getConfigurations().getByName(sourceSet.getCompileClasspathConfigurationName())
.extendsFrom(optional);
project.getConfigurations().getByName(sourceSet.getRuntimeClasspathConfigurationName())
.extendsFrom(optional);
});
project.getTasks().withType(Javadoc.class)
.all((javadoc) -> javadoc.setClasspath(javadoc.getClasspath().plus(optional)));
});
project.getPlugins().withType(EclipsePlugin.class,
(eclipsePlugin) -> project.getExtensions().getByType(EclipseModel.class)
.classpath((classpath) -> classpath.getPlusConfigurations().add(optional)));
}
}

View File

@ -43,9 +43,6 @@ import org.apache.http.conn.HttpClientConnectionManager;
import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.GradleRunner;
import org.gradle.util.GradleVersion;
import org.jetbrains.kotlin.cli.common.PropertiesKt;
import org.jetbrains.kotlin.compilerRunner.KotlinLogger;
import org.jetbrains.kotlin.daemon.client.KotlinCompilerClient;
import org.jetbrains.kotlin.gradle.model.KotlinProject;
import org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin;
import org.jetbrains.kotlin.gradle.plugin.KotlinPlugin;
@ -107,9 +104,10 @@ public class GradleBuild {
new File("build/resources/main"), new File(pathOfJarContaining(LaunchScript.class)),
new File(pathOfJarContaining(ClassVisitor.class)),
new File(pathOfJarContaining(DependencyManagementPlugin.class)),
new File(pathOfJarContaining(PropertiesKt.class)), new File(pathOfJarContaining(KotlinLogger.class)),
new File(pathOfJarContaining("org.jetbrains.kotlin.cli.common.PropertiesKt")),
new File(pathOfJarContaining("org.jetbrains.kotlin.compilerRunner.KotlinLogger")),
new File(pathOfJarContaining(KotlinPlugin.class)), new File(pathOfJarContaining(KotlinProject.class)),
new File(pathOfJarContaining(KotlinCompilerClient.class)),
new File(pathOfJarContaining("org.jetbrains.kotlin.daemon.client.KotlinCompilerClient")),
new File(pathOfJarContaining(KotlinGradleSubplugin.class)),
new File(pathOfJarContaining(ArchiveEntry.class)), new File(pathOfJarContaining(BuildRequest.class)),
new File(pathOfJarContaining(HttpClientConnectionManager.class)),
@ -119,6 +117,15 @@ public class GradleBuild {
new File(pathOfJarContaining(JsonView.class)), new File(pathOfJarContaining(Platform.class)));
}
private String pathOfJarContaining(String className) {
try {
return pathOfJarContaining(Class.forName(className));
}
catch (ClassNotFoundException ex) {
throw new IllegalArgumentException(ex);
}
}
private String pathOfJarContaining(Class<?> type) {
return type.getProtectionDomain().getCodeSource().getLocation().getPath();
}