Skip to content

Commit

Permalink
Gradle JDK automanagement integration for toolchains (#2783)
Browse files Browse the repository at this point in the history
Gradle JDK automanagement integration for toolchains
  • Loading branch information
crogoz authored May 2, 2024
1 parent 8bffa16 commit 1215264
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 112 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
.settings
.factorypath
.apt_generated_tests/
.profileconfig.json
bin/
build
out/
Expand Down
5 changes: 5 additions & 0 deletions changelog/@unreleased/pr-2783.v2.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
type: improvement
improvement:
description: Gradle JDK automanagement integration for toolchains
links:
- https://github.com/palantir/gradle-baseline/pull/2783
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;
import org.gradle.api.plugins.JavaPluginExtension;
import org.gradle.api.plugins.quality.Checkstyle;
import org.gradle.api.provider.Property;
Expand All @@ -40,13 +42,16 @@
import org.gradle.api.tasks.scala.ScalaDoc;
import org.gradle.api.tasks.testing.Test;
import org.gradle.external.javadoc.CoreJavadocOptions;
import org.gradle.jvm.toolchain.JavaLauncher;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.jvm.toolchain.JavaToolchainSpec;
import org.gradle.process.CommandLineArgumentProvider;
import org.gradle.util.GradleVersion;

public final class BaselineJavaVersion implements Plugin<Project> {

public static final String EXTENSION_NAME = "javaVersion";
public static final Logger log = Logging.getLogger(BaselineJavaVersion.class.getName());

@Override
public void apply(Project project) {
Expand All @@ -56,6 +61,8 @@ public void apply(Project project) {
project.getPluginManager().withPlugin("java", unused -> {
JavaPluginExtension javaPluginExtension = project.getExtensions().getByType(JavaPluginExtension.class);

JavaToolchainService toolchainService = project.getExtensions().getByType(JavaToolchainService.class);

// Set the default project toolchain to the runtime target version, this indirectly
// sets the value returned by 'getTargetCompatibility', which is used by sls-packaging
// to request a specific java feature release.
Expand All @@ -68,23 +75,24 @@ public void execute(JavaToolchainSpec javaToolchainSpec) {
}
});

JavaToolchains javaToolchains = new JavaToolchains(
project, project.getRootProject().getExtensions().getByType(BaselineJavaVersionsExtension.class));
BaselineJavaVersionsExtension rootExtension =
project.getRootProject().getExtensions().getByType(BaselineJavaVersionsExtension.class);

JavaToolchains baselineConfiguredJavaToolchains = new JavaToolchains(project, rootExtension);

// Compilation tasks (using target version)
configureCompilationTasks(project, extension.target(), javaToolchains.forVersion(extension.target()));
configureCompilationTasks(
project, extension.target(), baselineConfiguredJavaToolchains, rootExtension, toolchainService);

// Execution tasks (using the runtime version)
configureExecutionTasks(project, extension.runtime(), javaToolchains.forVersion(extension.runtime()));
configureExecutionTasks(
project, extension.runtime(), baselineConfiguredJavaToolchains, rootExtension, toolchainService);

// Validation
TaskProvider<CheckJavaVersionsTask> checkJavaVersions = project.getTasks()
.register("checkJavaVersions", CheckJavaVersionsTask.class, new Action<CheckJavaVersionsTask>() {
@Override
public void execute(CheckJavaVersionsTask task) {
task.getTargetVersion().set(extension.target());
task.getRuntimeVersion().set(extension.runtime());
}
.register("checkJavaVersions", CheckJavaVersionsTask.class, task -> {
task.getTargetVersion().set(extension.target());
task.getRuntimeVersion().set(extension.runtime());
});

TaskProvider<CheckClasspathCompatible> checkRuntimeClasspathCompatible = project.getTasks()
Expand All @@ -101,127 +109,170 @@ public void execute(CheckJavaVersionsTask task) {
}

private static void configureCompilationTasks(
Project project, Property<ChosenJavaVersion> target, Provider<BaselineJavaToolchain> javaToolchain) {
project.getTasks().withType(JavaCompile.class).configureEach(new Action<JavaCompile>() {
@Override
public void execute(JavaCompile javaCompileTask) {
javaCompileTask.getJavaCompiler().set(javaToolchain.flatMap(BaselineJavaToolchain::javaCompiler));
javaCompileTask
.getOptions()
.getCompilerArgumentProviders()
.add(new EnablePreviewArgumentProvider(target));

// Set sourceCompatibility to opt out of '-release', allowing opens/exports to be used.
javaCompileTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
((JavaCompile) task)
.setSourceCompatibility(
target.get().javaLanguageVersion().toString());
}
});
}
Project project,
Property<ChosenJavaVersion> target,
JavaToolchains baselineConfiguredJavaToolchains,
BaselineJavaVersionsExtension rootExtension,
JavaToolchainService javaToolchainService) {

project.getTasks().withType(JavaCompile.class).configureEach(javaCompileTask -> {
setJavaCompiler(
javaCompileTask, rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, target);
javaCompileTask.getOptions().getCompilerArgumentProviders().add(new EnablePreviewArgumentProvider(target));
// Set sourceCompatibility to opt out of '-release', allowing opens/exports to be used.
javaCompileTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
((JavaCompile) task)
.setSourceCompatibility(
target.get().javaLanguageVersion().toString());
}
});
});

project.getTasks().withType(Javadoc.class).configureEach(new Action<Javadoc>() {
@Override
public void execute(Javadoc javadocTask) {
javadocTask.getJavadocTool().set(javaToolchain.flatMap(BaselineJavaToolchain::javadocTool));

// javadocTask doesn't allow us to add a CommandLineArgumentProvider, so we do it just in time
javadocTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
CoreJavadocOptions options = (CoreJavadocOptions) ((Javadoc) task).getOptions();
if (target.get().enablePreview()) {
// yes, javadoc truly takes a single-dash where everyone else takes a double dash
options.addBooleanOption("-enable-preview", true);
options.setSource(target.get().javaLanguageVersion().toString());
}
project.getTasks().withType(Javadoc.class).configureEach(javadocTask -> {
setJavaDocTool(javadocTask, rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, target);

// javadocTask doesn't allow us to add a CommandLineArgumentProvider, so we do it just in time
javadocTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
CoreJavadocOptions options = (CoreJavadocOptions) ((Javadoc) task).getOptions();
if (target.get().enablePreview()) {
// yes, javadoc truly takes a single-dash where everyone else takes a double dash
options.addBooleanOption("-enable-preview", true);
options.setSource(target.get().javaLanguageVersion().toString());
}
});
}
}
});
});

// checkstyle.getJavaLauncher() was added in Gradle 7.5
if (GradleVersion.current().compareTo(GradleVersion.version("7.5")) >= 0) {
project.getTasks().withType(Checkstyle.class).configureEach(new Action<Checkstyle>() {
project.getTasks().withType(Checkstyle.class).configureEach(checkstyle -> checkstyle
.getJavaLauncher()
.set(getJavaLauncher(
rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, target)));
}

project.getTasks().withType(GroovyCompile.class).configureEach(groovyCompileTask -> {
groovyCompileTask
.getJavaLauncher()
.set(getJavaLauncher(
rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, target));
groovyCompileTask
.getOptions()
.getCompilerArgumentProviders()
.add(new EnablePreviewArgumentProvider(target));

// Set sourceCompatibility to opt out of '-release', allowing opens/exports to be used.
groovyCompileTask.doFirst(new Action<Task>() {
@Override
public void execute(Checkstyle checkstyle) {
checkstyle.getJavaLauncher().set(javaToolchain.flatMap(BaselineJavaToolchain::javaLauncher));
public void execute(Task task) {
((GroovyCompile) task)
.setSourceCompatibility(
target.get().javaLanguageVersion().toString());
}
});
}

project.getTasks().withType(GroovyCompile.class).configureEach(new Action<GroovyCompile>() {
@Override
public void execute(GroovyCompile groovyCompileTask) {
groovyCompileTask.getJavaLauncher().set(javaToolchain.flatMap(BaselineJavaToolchain::javaLauncher));
groovyCompileTask
.getOptions()
.getCompilerArgumentProviders()
.add(new EnablePreviewArgumentProvider(target));

// Set sourceCompatibility to opt out of '-release', allowing opens/exports to be used.
groovyCompileTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
((GroovyCompile) task)
.setSourceCompatibility(
target.get().javaLanguageVersion().toString());
}
});
}
});

project.getTasks().withType(ScalaCompile.class).configureEach(new Action<ScalaCompile>() {
@Override
public void execute(ScalaCompile scalaCompileTask) {
scalaCompileTask.getJavaLauncher().set(javaToolchain.flatMap(BaselineJavaToolchain::javaLauncher));
scalaCompileTask
.getOptions()
.getCompilerArgumentProviders()
.add(new EnablePreviewArgumentProvider(target));

// Set sourceCompatibility to opt out of '-release', allowing opens/exports to be used.
scalaCompileTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
((ScalaCompile) task)
.setSourceCompatibility(
target.get().javaLanguageVersion().toString());
}
});
}
});
project.getTasks().withType(ScalaCompile.class).configureEach(scalaCompileTask -> {
scalaCompileTask
.getJavaLauncher()
.set(getJavaLauncher(
rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, target));
scalaCompileTask.getOptions().getCompilerArgumentProviders().add(new EnablePreviewArgumentProvider(target));

project.getTasks().withType(ScalaDoc.class).configureEach(new Action<ScalaDoc>() {
@Override
public void execute(ScalaDoc scalaDoc) {
scalaDoc.getJavaLauncher().set(javaToolchain.flatMap(BaselineJavaToolchain::javaLauncher));
}
// Set sourceCompatibility to opt out of '-release', allowing opens/exports to be used.
scalaCompileTask.doFirst(new Action<Task>() {
@Override
public void execute(Task task) {
((ScalaCompile) task)
.setSourceCompatibility(
target.get().javaLanguageVersion().toString());
}
});
});

project.getTasks().withType(ScalaDoc.class).configureEach(scalaDoc -> scalaDoc.getJavaLauncher()
.set(getJavaLauncher(rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, target)));
}

private static void configureExecutionTasks(
Project project, Provider<ChosenJavaVersion> runtime, Provider<BaselineJavaToolchain> javaToolchain) {
project.getTasks().withType(JavaExec.class).configureEach(new Action<JavaExec>() {
@Override
public void execute(JavaExec javaExec) {
javaExec.getJavaLauncher().set(javaToolchain.flatMap(BaselineJavaToolchain::javaLauncher));
javaExec.getJvmArgumentProviders().add(new EnablePreviewArgumentProvider(runtime));
}
Project project,
Property<ChosenJavaVersion> runtime,
JavaToolchains baselineConfiguredJavaToolchains,
BaselineJavaVersionsExtension rootExtension,
JavaToolchainService javaToolchainService) {

project.getTasks().withType(JavaExec.class).configureEach(javaExec -> {
javaExec.getJavaLauncher()
.set(getJavaLauncher(
rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, runtime));
javaExec.getJvmArgumentProviders().add(new EnablePreviewArgumentProvider(runtime));
});

project.getTasks().withType(Test.class).configureEach(new Action<Test>() {
@Override
public void execute(Test test) {
test.getJavaLauncher().set(javaToolchain.flatMap(BaselineJavaToolchain::javaLauncher));
test.getJvmArgumentProviders().add(new EnablePreviewArgumentProvider(runtime));
}
project.getTasks().withType(Test.class).configureEach(test -> {
test.getJavaLauncher()
.set(getJavaLauncher(
rootExtension, baselineConfiguredJavaToolchains, javaToolchainService, runtime));
test.getJvmArgumentProviders().add(new EnablePreviewArgumentProvider(runtime));
});
}

private static void setJavaCompiler(
JavaCompile javaCompileTask,
BaselineJavaVersionsExtension rootExtension,
JavaToolchains baselineConfiguredJavaToolchains,
JavaToolchainService javaToolchainService,
Property<ChosenJavaVersion> version) {
if (rootExtension.getSetupJdkToolchains().get()) {
log.debug("Using baselineConfiguredJavaToolchains to configure the javaCompileTask");
javaCompileTask
.getJavaCompiler()
.set(baselineConfiguredJavaToolchains
.forVersion(version)
.flatMap(BaselineJavaToolchain::javaCompiler));
return;
}
log.debug("Using detected javaToolchains to configure the javaCompileTask");
javaCompileTask.getJavaCompiler().set(javaToolchainService.compilerFor(spec -> spec.getLanguageVersion()
.set(version.map(ChosenJavaVersion::javaLanguageVersion))));
}

private static void setJavaDocTool(
Javadoc javadocTask,
BaselineJavaVersionsExtension rootExtension,
JavaToolchains baselineConfiguredJavaToolchains,
JavaToolchainService javaToolchainService,
Property<ChosenJavaVersion> version) {
if (rootExtension.getSetupJdkToolchains().get()) {
log.debug("Using baselineConfiguredJavaToolchains to configure javaDocTool");
javadocTask
.getJavadocTool()
.set(baselineConfiguredJavaToolchains
.forVersion(version)
.flatMap(BaselineJavaToolchain::javadocTool));
return;
}
log.debug("Using detected javaToolchains to configure javaDocTool");
javadocTask.getJavadocTool().set(javaToolchainService.javadocToolFor(spec -> spec.getLanguageVersion()
.set(version.map(ChosenJavaVersion::javaLanguageVersion))));
}

private static Provider<JavaLauncher> getJavaLauncher(
BaselineJavaVersionsExtension rootExtension,
JavaToolchains baselineConfiguredJavaToolchains,
JavaToolchainService javaToolchainService,
Property<ChosenJavaVersion> version) {
if (rootExtension.getSetupJdkToolchains().get()) {
log.debug("Using baselineConfiguredJavaToolchains to configure JavaLauncher");
return baselineConfiguredJavaToolchains.forVersion(version).flatMap(BaselineJavaToolchain::javaLauncher);
}
log.debug("Using detected javaToolchains to configure JavaLauncher");
return javaToolchainService.launcherFor(
spec -> spec.getLanguageVersion().set(version.map(ChosenJavaVersion::javaLanguageVersion)));
}

@CacheableTask
@SuppressWarnings("checkstyle:DesignForExtension")
public static class CheckJavaVersionsTask extends DefaultTask {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,15 @@ public class BaselineJavaVersionsExtension implements BaselineJavaVersionsExtens
private final Property<ChosenJavaVersion> runtime;
private final LazilyConfiguredMapping<JavaLanguageVersion, AtomicReference<JavaInstallationMetadata>, Project>
jdks = new LazilyConfiguredMapping<>(AtomicReference::new);
private final Property<Boolean> setupJdkToolchains;

@Inject
public BaselineJavaVersionsExtension(Project project) {
this.libraryTarget = project.getObjects().property(JavaLanguageVersion.class);
this.distributionTarget = project.getObjects().property(ChosenJavaVersion.class);
this.runtime = project.getObjects().property(ChosenJavaVersion.class);
this.setupJdkToolchains = project.getObjects().property(Boolean.class);
this.setupJdkToolchains.convention(true);

// distribution defaults to the library value
distributionTarget.convention(libraryTarget.map(ChosenJavaVersion::of));
Expand Down Expand Up @@ -126,4 +129,11 @@ public final void jdks(LazyJdks lazyJdks) {
public interface LazyJdks {
Optional<JavaInstallationMetadata> jdkFor(JavaLanguageVersion javaLanguageVersion, Project project);
}

/**
* Enables the setup of JDK toolchains for all subprojects.
*/
public final Property<Boolean> getSetupJdkToolchains() {
return setupJdkToolchains;
}
}
Loading

0 comments on commit 1215264

Please sign in to comment.