Access classpath lazily to allow later changes to be picked up
Previously, the classpath of bootJar, bootWar, and bootRun was configured directly as a FileCollection derived from the main source set's runtime classpath. This direct configuration meant that subsequent changes to the main source set's runtime classpath may not have been picked up. This commit changes the configuration of the classpath to use a Callable. This indirection allows subsequent changes to the main source set's runtime classpath to be picked up as long as they occur before Gradle calls the callable. Closes gh-29672
This commit is contained in:
parent
52f1799c20
commit
43ca2d2cb0
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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,6 +20,7 @@ import java.io.File;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
|
|
@ -105,7 +106,7 @@ final class JavaPluginAction implements PluginApplicationAction {
|
|||
.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
|
||||
Configuration productionRuntimeClasspath = project.getConfigurations()
|
||||
.getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
FileCollection classpath = mainSourceSet.getRuntimeClasspath()
|
||||
Callable<FileCollection> classpath = () -> mainSourceSet.getRuntimeClasspath()
|
||||
.minus((developmentOnly.minus(productionRuntimeClasspath))).filter(new JarTypeFileSpec());
|
||||
TaskProvider<ResolveMainClassName> resolveMainClassName = ResolveMainClassName
|
||||
.registerForTask(SpringBootPlugin.BOOT_JAR_TASK_NAME, project, classpath);
|
||||
|
|
@ -137,7 +138,7 @@ final class JavaPluginAction implements PluginApplicationAction {
|
|||
}
|
||||
|
||||
private void configureBootRunTask(Project project) {
|
||||
FileCollection classpath = javaPluginConvention(project).getSourceSets()
|
||||
Callable<FileCollection> classpath = () -> javaPluginConvention(project).getSourceSets()
|
||||
.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath().filter(new JarTypeFileSpec());
|
||||
TaskProvider<ResolveMainClassName> resolveProvider = ResolveMainClassName.registerForTask("bootRun", project,
|
||||
classpath);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -23,6 +23,7 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.InvalidUserDataException;
|
||||
|
|
@ -86,7 +87,17 @@ public class ResolveMainClassName extends DefaultTask {
|
|||
* @param classpath the classpath
|
||||
*/
|
||||
public void setClasspath(FileCollection classpath) {
|
||||
this.classpath = classpath;
|
||||
setClasspath((Object) classpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the classpath to include in the archive. The given {@code classpath} is
|
||||
* evaluated as per {@link Project#files(Object...)}.
|
||||
* @param classpath the classpath
|
||||
* @since 2.5.9
|
||||
*/
|
||||
public void setClasspath(Object classpath) {
|
||||
this.classpath = getProject().files(classpath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,7 +153,7 @@ public class ResolveMainClassName extends DefaultTask {
|
|||
}
|
||||
|
||||
static TaskProvider<ResolveMainClassName> registerForTask(String taskName, Project project,
|
||||
FileCollection classpath) {
|
||||
Callable<FileCollection> classpath) {
|
||||
TaskProvider<ResolveMainClassName> resolveMainClassNameProvider = project.getTasks()
|
||||
.register(taskName + "MainClassName", ResolveMainClassName.class, (resolveMainClassName) -> {
|
||||
Convention convention = project.getConvention();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.gradle.plugin;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.gradle.api.Action;
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
|
|
@ -71,7 +73,7 @@ class WarPluginAction implements PluginApplicationAction {
|
|||
.getByName(SpringBootPlugin.DEVELOPMENT_ONLY_CONFIGURATION_NAME);
|
||||
Configuration productionRuntimeClasspath = project.getConfigurations()
|
||||
.getByName(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
FileCollection classpath = project.getConvention().getByType(SourceSetContainer.class)
|
||||
Callable<FileCollection> classpath = () -> project.getConvention().getByType(SourceSetContainer.class)
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath()
|
||||
.minus(providedRuntimeConfiguration(project)).minus((developmentOnly.minus(productionRuntimeClasspath)))
|
||||
.filter(new JarTypeFileSpec());
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -462,6 +462,30 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
assertExtractedLayers(layerNames, indexedLayers);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void classesFromASecondarySourceSetCanBeIncludedInTheArchive() throws IOException {
|
||||
writeMainClass();
|
||||
File examplePackage = new File(this.gradleBuild.getProjectDir(), "src/secondary/java/example");
|
||||
examplePackage.mkdirs();
|
||||
File main = new File(examplePackage, "Secondary.java");
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(main))) {
|
||||
writer.println("package example;");
|
||||
writer.println();
|
||||
writer.println("public class Secondary {}");
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
BuildResult build = this.gradleBuild.build(this.taskName);
|
||||
assertThat(build.task(":" + this.taskName).getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
try (JarFile jarFile = new JarFile(new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0])) {
|
||||
Stream<String> classesEntryNames = jarFile.stream().filter((entry) -> !entry.isDirectory())
|
||||
.map(JarEntry::getName).filter((name) -> name.startsWith(this.classesPath));
|
||||
assertThat(classesEntryNames).containsExactly(this.classesPath + "example/Main.class",
|
||||
this.classesPath + "example/Secondary.class");
|
||||
}
|
||||
}
|
||||
|
||||
private void copyMainClassApplication() throws IOException {
|
||||
copyApplication("main");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2012-2021 the original author or authors.
|
||||
* Copyright 2012-2022 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.
|
||||
|
|
@ -138,6 +138,16 @@ class BootRunIntegrationTests {
|
|||
assertThat(result.getOutput()).contains("standard.jar").doesNotContain("starter.jar");
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void classesFromASecondarySourceSetCanBeOnTheClasspath() throws IOException {
|
||||
File output = new File(this.gradleBuild.getProjectDir(), "src/secondary/java/com/example/bootrun/main");
|
||||
output.mkdirs();
|
||||
FileSystemUtils.copyRecursively(new File("src/test/java/com/example/bootrun/main"), output);
|
||||
BuildResult result = this.gradleBuild.build("bootRun");
|
||||
assertThat(result.task(":bootRun").getOutcome()).isEqualTo(TaskOutcome.SUCCESS);
|
||||
assertThat(result.getOutput()).contains("com.example.bootrun.main.CustomMainClass");
|
||||
}
|
||||
|
||||
private void copyMainClassApplication() throws IOException {
|
||||
copyApplication("main");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
secondary
|
||||
main {
|
||||
runtimeClasspath += secondary.output
|
||||
}
|
||||
}
|
||||
|
||||
bootJar {
|
||||
mainClass = 'com.example.Application'
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
id 'war'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
secondary
|
||||
main {
|
||||
runtimeClasspath += secondary.output
|
||||
}
|
||||
}
|
||||
|
||||
bootWar {
|
||||
mainClass = 'com.example.Application'
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '{version}'
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
secondary
|
||||
main {
|
||||
runtimeClasspath += secondary.output
|
||||
}
|
||||
}
|
||||
|
||||
springBoot {
|
||||
mainClass = 'com.example.bootrun.main.CustomMainClass'
|
||||
}
|
||||
Loading…
Reference in New Issue