Restructure the code to enforce separation of plugin logic and tasks

pull/8686/merge
Andy Wilkinson 8 years ago
parent 5f27ecc6d9
commit 8d55801c4d

@ -1,6 +1,7 @@
plugins {
id 'java'
id 'eclipse'
id 'org.sonarqube' version '2.2.1'
}
repositories {

@ -1,174 +0,0 @@
/*
* Copyright 2012-2017 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
*
* http://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.gradle.bundling;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ModuleDependency;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.maven.MavenResolver;
import org.gradle.api.attributes.Usage;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
import org.gradle.api.internal.attributes.Usages;
import org.gradle.api.internal.component.SoftwareComponentInternal;
import org.gradle.api.internal.component.UsageContext;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.plugins.WarPlugin;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.Upload;
import org.springframework.boot.gradle.MainClassResolver;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} for the bundling of an application.
*
* @author Andy Wilkinson
*/
public class BundlingPluginFeatures implements PluginFeatures {
private SinglePublishedArtifact singlePublishedArtifact;
@Override
public void apply(Project project) {
this.singlePublishedArtifact = new SinglePublishedArtifact(
project.getConfigurations().create("bootArchives").getArtifacts());
project.getPlugins().withType(JavaPlugin.class,
(javaPlugin) -> configureBootJarTask(project));
project.getPlugins().withType(WarPlugin.class,
(warPlugin) -> configureBootWarTask(project));
project.afterEvaluate(this::configureBootArchivesUpload);
}
private void configureBootWarTask(Project project) {
BootWar bootWar = project.getTasks().create("bootWar", BootWar.class);
bootWar.providedClasspath(providedRuntimeConfiguration(project));
ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar);
this.singlePublishedArtifact.addCandidate(artifact);
project.getComponents().add(new BootSoftwareComponent(artifact, "bootWeb"));
bootWar.conventionMapping("mainClass",
mainClassConvention(project, bootWar::getClasspath));
}
private void configureBootJarTask(Project project) {
BootJar bootJar = project.getTasks().create("bootJar", BootJar.class);
bootJar.classpath((Callable<FileCollection>) () -> {
JavaPluginConvention convention = project.getConvention()
.getPlugin(JavaPluginConvention.class);
SourceSet mainSourceSet = convention.getSourceSets()
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
return mainSourceSet.getRuntimeClasspath();
});
ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar);
this.singlePublishedArtifact.addCandidate(artifact);
project.getComponents().add(new BootSoftwareComponent(artifact, "bootJava"));
bootJar.conventionMapping("mainClass",
mainClassConvention(project, bootJar::getClasspath));
}
private Callable<Object> mainClassConvention(Project project,
Supplier<FileCollection> classpathSupplier) {
return () -> {
if (project.hasProperty("mainClassName")) {
return project.property("mainClassName");
}
return new MainClassResolver(classpathSupplier.get()).resolveMainClass();
};
}
private void configureBootArchivesUpload(Project project) {
Upload upload = project.getTasks().withType(Upload.class)
.findByName("uploadBootArchives");
if (upload == null) {
return;
}
clearConfigurationMappings(upload);
}
private void clearConfigurationMappings(Upload upload) {
upload.getRepositories().withType(MavenResolver.class, (resolver) -> {
resolver.getPom().getScopeMappings().getMappings().clear();
});
}
private Configuration providedRuntimeConfiguration(Project project) {
return project.getConfigurations()
.getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME);
}
/**
* {@link SofwareComponent} for a Spring Boot fat jar or war.
*/
private static final class BootSoftwareComponent
implements SoftwareComponentInternal {
private final PublishArtifact artifact;
private final String name;
private BootSoftwareComponent(PublishArtifact artifact, String name) {
this.artifact = artifact;
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public Set<UsageContext> getUsages() {
return Collections.singleton(new BootUsageContext(this.artifact));
}
private static final class BootUsageContext implements UsageContext {
private static final Usage USAGE = Usages.usage("master");
private final PublishArtifact artifact;
private BootUsageContext(PublishArtifact artifact) {
this.artifact = artifact;
}
@Override
public Usage getUsage() {
return USAGE;
}
@Override
public Set<PublishArtifact> getArtifacts() {
return Collections.singleton(this.artifact);
}
@Override
public Set<ModuleDependency> getDependencies() {
return Collections.emptySet();
}
}
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle;
package org.springframework.boot.gradle.dsl;
import java.io.File;
import java.util.concurrent.Callable;
@ -26,10 +26,10 @@ import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.bundling.Jar;
import org.springframework.boot.gradle.buildinfo.BuildInfo;
import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo;
/**
* Gradle DSL extension that provides the entry point to Spring Boot's DSL.
* Entry point to Spring Boot's Gradle DSL.
*
* @author Andy Wilkinson
*/
@ -73,30 +73,39 @@ public class SpringBootExtension {
public void buildInfo(Action<BuildInfo> configurer) {
BuildInfo bootBuildInfo = this.project.getTasks().create("bootBuildInfo",
BuildInfo.class);
this.project.getPlugins().withType(JavaPlugin.class, (plugin) -> {
this.project.getPlugins().withType(JavaPlugin.class, plugin -> {
this.project.getTasks().getByName(JavaPlugin.CLASSES_TASK_NAME)
.dependsOn(bootBuildInfo);
bootBuildInfo.getConventionMapping().map("projectArtifact",
(Callable<String>) () -> {
Jar artifactTask = (Jar) this.project.getTasks().findByName("bootWar");
if (artifactTask == null) {
artifactTask = (Jar) this.project.getTasks().findByName("bootJar");
}
String result = artifactTask == null ? null : artifactTask.getBaseName();
return result;
});
bootBuildInfo.getConventionMapping().map("destinationDir",
(Callable<File>) () -> {
return new File(
this.project.getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME)
.getOutput().getResourcesDir(),
"META-INF");
});
(Callable<String>) () -> determineArtifactBaseName());
bootBuildInfo.getConventionMapping()
.map("destinationDir",
(Callable<File>) () -> new File(
determineMainSourceSetResourcesOutputDir(),
"META-INF"));
});
if (configurer != null) {
configurer.execute(bootBuildInfo);
}
}
private File determineMainSourceSetResourcesOutputDir() {
return this.project.getConvention().getPlugin(JavaPluginConvention.class)
.getSourceSets().getByName(SourceSet.MAIN_SOURCE_SET_NAME).getOutput()
.getResourcesDir();
}
private String determineArtifactBaseName() {
Jar artifactTask = findArtifactTask();
return artifactTask == null ? null : artifactTask.getBaseName();
}
private Jar findArtifactTask() {
Jar artifactTask = (Jar) this.project.getTasks().findByName("bootWar");
if (artifactTask != null) {
return artifactTask;
}
return (Jar) this.project.getTasks().findByName("bootJar");
}
}

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 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
*
* http://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.
*/
/**
* Spring Boot Gradle DSL.
*/
package org.springframework.boot.gradle.dsl;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.application;
package org.springframework.boot.gradle.plugin;
import java.io.File;
import java.io.IOException;
@ -23,6 +23,7 @@ import java.io.StringWriter;
import java.util.concurrent.Callable;
import org.gradle.api.GradleException;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.distribution.Distribution;
import org.gradle.api.distribution.DistributionContainer;
@ -32,22 +33,17 @@ import org.gradle.api.plugins.ApplicationPlugin;
import org.gradle.api.plugins.ApplicationPluginConvention;
import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator;
import org.springframework.boot.gradle.PluginFeatures;
import org.springframework.boot.gradle.tasks.application.CreateBootStartScripts;
/**
* Features that are configured when the application plugin is applied.
* Action that is executed in response to the {@link ApplicationPlugin} being applied.
*
* @author Andy Wilkinson
*/
public class ApplicationPluginFeatures implements PluginFeatures {
final class ApplicationPluginAction implements PluginApplicationAction {
@Override
public void apply(Project project) {
project.getPlugins().withType(ApplicationPlugin.class,
(plugin) -> configureDistribution(project));
}
public void configureDistribution(Project project) {
public void execute(Project project) {
ApplicationPluginConvention applicationConvention = project.getConvention()
.getPlugin(ApplicationPluginConvention.class);
DistributionContainer distributions = project.getExtensions()
@ -61,12 +57,12 @@ public class ApplicationPluginFeatures implements PluginFeatures {
((TemplateBasedScriptGenerator) bootStartScripts.getWindowsStartScriptGenerator())
.setTemplate(project.getResources().getText()
.fromString(loadResource("/windowsStartScript.txt")));
project.getConfigurations().all((configuration) -> {
project.getConfigurations().all(configuration -> {
if ("bootArchives".equals(configuration.getName())) {
distribution.getContents().with(project.copySpec().into("lib")
.from((Callable<FileCollection>) () -> {
return configuration.getArtifacts().getFiles();
}));
distribution.getContents()
.with(project.copySpec().into("lib")
.from((Callable<FileCollection>) () -> configuration
.getArtifacts().getFiles()));
bootStartScripts.setClasspath(configuration.getArtifacts().getFiles());
}
});
@ -75,10 +71,15 @@ public class ApplicationPluginFeatures implements PluginFeatures {
bootStartScripts.getConventionMapping().map("applicationName",
() -> applicationConvention.getApplicationName());
CopySpec binCopySpec = project.copySpec().into("bin").from(bootStartScripts);
binCopySpec.setFileMode(0755);
binCopySpec.setFileMode(0x755);
distribution.getContents().with(binCopySpec);
}
@Override
public Class<? extends Plugin<Project>> getPluginClass() {
return ApplicationPlugin.class;
}
private String loadResource(String name) {
InputStreamReader reader = new InputStreamReader(
getClass().getResourceAsStream(name));

@ -14,48 +14,37 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.dependencymanagement;
package org.springframework.boot.gradle.plugin;
import io.spring.gradle.dependencymanagement.DependencyManagementPlugin;
import io.spring.gradle.dependencymanagement.dsl.DependencyManagementExtension;
import io.spring.gradle.dependencymanagement.dsl.ImportsHandler;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} to configure dependency management.
* {@link Action} that is performed in response to the {@link DependencyManagementPlugin}
* being applied.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 1.3.0
*/
public class DependencyManagementPluginFeatures implements PluginFeatures {
final class DependencyManagementPluginAction implements PluginApplicationAction {
private static final String SPRING_BOOT_VERSION = DependencyManagementPluginFeatures.class
private static final String SPRING_BOOT_VERSION = DependencyManagementPluginAction.class
.getPackage().getImplementationVersion();
private static final String SPRING_BOOT_BOM = "org.springframework.boot:spring-boot-dependencies:"
+ SPRING_BOOT_VERSION;
@Override
public void apply(Project project) {
project.getPlugins().withType(DependencyManagementPlugin.class,
(plugin) -> configureDependencyManagement(project));
public void execute(Project project) {
project.getExtensions().findByType(DependencyManagementExtension.class)
.imports(importsHandler -> importsHandler.mavenBom(SPRING_BOOT_BOM));
}
private void configureDependencyManagement(Project project) {
DependencyManagementExtension dependencyManagement = project.getExtensions()
.findByType(DependencyManagementExtension.class);
dependencyManagement.imports(new Action<ImportsHandler>() {
@Override
public void execute(ImportsHandler importsHandler) {
importsHandler.mavenBom(SPRING_BOOT_BOM);
}
});
public Class<? extends Plugin<Project>> getPluginClass() {
return DependencyManagementPlugin.class;
}
}

@ -0,0 +1,111 @@
/*
* Copyright 2012-2017 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
*
* http://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.gradle.plugin;
import java.util.Collections;
import java.util.concurrent.Callable;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.compile.JavaCompile;
import org.springframework.boot.gradle.tasks.bundling.BootJar;
import org.springframework.boot.gradle.tasks.run.BootRun;
/**
* {@link Action} that is executed in response to the {@link JavaPlugin} being applied.
*
* @author Andy Wilkinson
*/
final class JavaPluginAction implements PluginApplicationAction {
private final SinglePublishedArtifact singlePublishedArtifact;
JavaPluginAction(SinglePublishedArtifact singlePublishedArtifact) {
this.singlePublishedArtifact = singlePublishedArtifact;
}
@Override
public Class<? extends Plugin<? extends Project>> getPluginClass() {
return JavaPlugin.class;
}
@Override
public void execute(Project project) {
BootJar bootJar = configureBootJarTask(project);
configureArtifactPublication(project, bootJar);
configureBootRunTask(project);
configureUtf8Encoding(project);
}
private BootJar configureBootJarTask(Project project) {
BootJar bootJar = project.getTasks().create(SpringBootPlugin.BOOT_JAR_TASK_NAME,
BootJar.class);
bootJar.classpath((Callable<FileCollection>) () -> {
JavaPluginConvention convention = project.getConvention()
.getPlugin(JavaPluginConvention.class);
SourceSet mainSourceSet = convention.getSourceSets()
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
return mainSourceSet.getRuntimeClasspath();
});
bootJar.conventionMapping("mainClass",
new MainClassConvention(project, bootJar::getClasspath));
return bootJar;
}
private void configureArtifactPublication(Project project, BootJar bootJar) {
ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootJar);
this.singlePublishedArtifact.addCandidate(artifact);
project.getComponents().add(new SpringBootSoftwareComponent(artifact,
SpringBootPlugin.BOOT_JAVA_SOFTWARE_COMPONENT_NAME));
}
private void configureBootRunTask(Project project) {
JavaPluginConvention javaConvention = project.getConvention()
.getPlugin(JavaPluginConvention.class);
BootRun run = project.getTasks().create("bootRun", BootRun.class);
run.setDescription("Run the project with support for "
+ "auto-detecting main class and reloading static resources");
run.setGroup("application");
run.classpath(javaConvention.getSourceSets()
.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath());
run.getConventionMapping().map("jvmArgs", () -> {
if (project.hasProperty("applicationDefaultJvmArgs")) {
return project.property("applicationDefaultJvmArgs");
}
return Collections.emptyList();
});
run.conventionMapping("main",
new MainClassConvention(project, run::getClasspath));
}
private void configureUtf8Encoding(Project project) {
project.getTasks().withType(JavaCompile.class,
compile -> compile.doFirst(task -> {
if (compile.getOptions().getEncoding() == null) {
compile.getOptions().setEncoding("UTF-8");
}
}));
}
}

@ -14,54 +14,55 @@
* limitations under the License.
*/
package org.springframework.boot.gradle;
package org.springframework.boot.gradle.plugin;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import org.gradle.api.Project;
import org.gradle.api.file.FileCollection;
import org.springframework.boot.loader.tools.MainClassFinder;
/**
* Resolves the main class for an application.
* A {@link Callable} that provide a convention for the project's main class name.
*
* @author Andy Wilkinson
*/
public class MainClassResolver {
final class MainClassConvention implements Callable<Object> {
private static final String SPRING_BOOT_APPLICATION_CLASS_NAME = "org.springframework.boot.autoconfigure.SpringBootApplication";
private final FileCollection classpath;
private final Project project;
/**
* Creates a new {@code MainClassResolver} that will search
* directories in the given {@code classpath} for
* the application's main class.
*
* @param classpath the classpath
*/
public MainClassResolver(FileCollection classpath) {
this.classpath = classpath;
private final Supplier<FileCollection> classpathSupplier;
MainClassConvention(Project project, Supplier<FileCollection> classpathSupplier) {
this.project = project;
this.classpathSupplier = classpathSupplier;
}
/**
* Resolves the main class.
*
* @return the main class or {@code null}
*/
public String resolveMainClass() {
return this.classpath.filter(File::isDirectory).getFiles().stream()
@Override
public Object call() throws Exception {
if (this.project.hasProperty("mainClassName")) {
return this.project.property("mainClassName");
}
return resolveMainClass();
}
private String resolveMainClass() {
return this.classpathSupplier.get().filter(File::isDirectory).getFiles().stream()
.map(this::findMainClass).filter(Objects::nonNull).findFirst()
.orElse(null);
}
private String findMainClass(File file) {
try {
String result = MainClassFinder.findSingleMainClass(file,
return MainClassFinder.findSingleMainClass(file,
SPRING_BOOT_APPLICATION_CLASS_NAME);
return result;
}
catch (IOException ex) {
return null;

@ -0,0 +1,58 @@
/*
* Copyright 2012-2017 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
*
* http://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.gradle.plugin;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.maven.MavenResolver;
import org.gradle.api.plugins.MavenPlugin;
import org.gradle.api.tasks.Upload;
/**
* {@link Action} that is executed in response to the {@link MavenPlugin} being applied.
*
* @author Andy Wilkinson
*/
final class MavenPluginAction implements PluginApplicationAction {
private final String uploadTaskName;
MavenPluginAction(String uploadTaskName) {
this.uploadTaskName = uploadTaskName;
}
@Override
public Class<? extends Plugin<? extends Project>> getPluginClass() {
return MavenPlugin.class;
}
@Override
public void execute(Project project) {
project.getTasks().withType(Upload.class, upload -> {
if (this.uploadTaskName.equals(upload.getName())) {
project.afterEvaluate(evaluated -> clearConfigurationMappings(upload));
}
});
}
private void clearConfigurationMappings(Upload upload) {
upload.getRepositories().withType(MavenResolver.class,
resolver -> resolver.getPom().getScopeMappings().getMappings().clear());
}
}

@ -14,23 +14,20 @@
* limitations under the License.
*/
package org.springframework.boot.gradle;
package org.springframework.boot.gradle.plugin;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
/**
* A specific set of {@code org.gradle.api.Plugin} features applied via the
* {@code SpringBootPlugin}.
* An {@link Action} to be executed on a {@link Project} in response to a particular type
* of {@link Plugin} being applied.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
@FunctionalInterface
public interface PluginFeatures {
interface PluginApplicationAction extends Action<Project> {
/**
* Apply the features to the specified project.
* @param project the project to apply features to
*/
void apply(Project project);
Class<? extends Plugin<? extends Project>> getPluginClass();
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.plugin;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.artifacts.PublishArtifactSet;
@ -25,7 +25,7 @@ import org.gradle.api.artifacts.PublishArtifactSet;
*
* @author Andy Wilkinson
*/
class SinglePublishedArtifact {
final class SinglePublishedArtifact {
private final PublishArtifactSet artifacts;
@ -37,7 +37,7 @@ class SinglePublishedArtifact {
void addCandidate(PublishArtifact candidate) {
if (this.currentArtifact == null || "war".equals(candidate.getExtension())) {
this.artifacts.clear();
this.artifacts.remove(this.currentArtifact);
this.artifacts.add(candidate);
this.currentArtifact = candidate;
}

@ -16,20 +16,20 @@
package org.springframework.boot.gradle.plugin;
import org.gradle.api.Action;
import java.util.Arrays;
import java.util.List;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.tasks.compile.JavaCompile;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.component.SoftwareComponent;
import org.springframework.boot.gradle.SpringBootExtension;
import org.springframework.boot.gradle.application.ApplicationPluginFeatures;
import org.springframework.boot.gradle.bundling.BundlingPluginFeatures;
import org.springframework.boot.gradle.dependencymanagement.DependencyManagementPluginFeatures;
import org.springframework.boot.gradle.run.RunPluginFeatures;
import org.springframework.boot.gradle.dsl.SpringBootExtension;
import org.springframework.boot.gradle.tasks.bundling.BootJar;
import org.springframework.boot.gradle.tasks.bundling.BootWar;
/**
* Gradle 'Spring Boot' {@link Plugin}.
* Gradle plugin for Spring Boot.
*
* @author Phillip Webb
* @author Dave Syer
@ -37,33 +37,47 @@ import org.springframework.boot.gradle.run.RunPluginFeatures;
*/
public class SpringBootPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
project.getExtensions().create("springBoot", SpringBootExtension.class,
project);
new ApplicationPluginFeatures().apply(project);
new BundlingPluginFeatures().apply(project);
new RunPluginFeatures().apply(project);
new DependencyManagementPluginFeatures().apply(project);
project.getTasks().withType(JavaCompile.class).all(new SetUtf8EncodingAction());
}
/**
* The name of the {@link Configuration} that contains Spring Boot archives.
*/
public static final String BOOT_ARCHIVES_CONFIURATION_NAME = "bootArchives";
private static class SetUtf8EncodingAction implements Action<JavaCompile> {
/**
* The name of the {@link SoftwareComponent} for a Spring Boot Java application.
*/
public static final String BOOT_JAVA_SOFTWARE_COMPONENT_NAME = "bootJava";
@Override
public void execute(final JavaCompile compile) {
compile.doFirst(new Action<Task>() {
/**
* The name of the {@link SoftwareComponent} for a Spring Boot Web application.
*/
public static final String BOOT_WEB_SOFTWARE_COMPONENT_NAME = "bootWeb";
@Override
public void execute(Task t) {
if (compile.getOptions().getEncoding() == null) {
compile.getOptions().setEncoding("UTF-8");
}
}
/**
* The name of the default {@link BootJar} task.
*/
public static final String BOOT_JAR_TASK_NAME = "bootJar";
});
}
/**
* The name of the default {@link BootWar} task.
*/
public static final String BOOT_WAR_TASK_NAME = "bootWar";
@Override
public void apply(Project project) {
project.getExtensions().create("springBoot", SpringBootExtension.class, project);
Configuration bootArchives = project.getConfigurations()
.create(BOOT_ARCHIVES_CONFIURATION_NAME);
SinglePublishedArtifact singlePublishedArtifact = new SinglePublishedArtifact(
bootArchives.getArtifacts());
List<PluginApplicationAction> actions = Arrays.asList(
new JavaPluginAction(singlePublishedArtifact),
new WarPluginAction(singlePublishedArtifact),
new MavenPluginAction(bootArchives.getUploadTaskName()),
new DependencyManagementPluginAction(), new ApplicationPluginAction());
for (PluginApplicationAction action : actions) {
project.getPlugins().withType(action.getPluginClass(),
plugin -> action.execute(project));
}
}
}

@ -0,0 +1,82 @@
/*
* Copyright 2012-2017 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
*
* http://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.gradle.plugin;
import java.util.Collections;
import java.util.Set;
import org.gradle.api.artifacts.ModuleDependency;
import org.gradle.api.artifacts.PublishArtifact;
import org.gradle.api.attributes.Usage;
import org.gradle.api.internal.attributes.Usages;
import org.gradle.api.internal.component.SoftwareComponentInternal;
import org.gradle.api.internal.component.UsageContext;
/**
* {@link org.gradle.api.component.SoftwareComponent} for a Spring Boot fat jar or war.
*
* @author Andy Wilkinson
*/
final class SpringBootSoftwareComponent implements SoftwareComponentInternal {
private final PublishArtifact artifact;
private final String name;
SpringBootSoftwareComponent(PublishArtifact artifact, String name) {
this.artifact = artifact;
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public Set<UsageContext> getUsages() {
return Collections.singleton(new BootUsageContext(this.artifact));
}
private static final class BootUsageContext implements UsageContext {
private static final Usage USAGE = Usages.usage("master");
private final PublishArtifact artifact;
private BootUsageContext(PublishArtifact artifact) {
this.artifact = artifact;
}
@Override
public Usage getUsage() {
return USAGE;
}
@Override
public Set<PublishArtifact> getArtifacts() {
return Collections.singleton(this.artifact);
}
@Override
public Set<ModuleDependency> getDependencies() {
return Collections.emptySet();
}
}
}

@ -0,0 +1,64 @@
/*
* Copyright 2012-2017 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
*
* http://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.gradle.plugin;
import org.gradle.api.Action;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact;
import org.gradle.api.plugins.WarPlugin;
import org.springframework.boot.gradle.tasks.bundling.BootWar;
/**
* {@link Action} that is executed in response to the {@link WarPlugin} being applied.
*
* @author Andy Wilkinson
*/
class WarPluginAction implements PluginApplicationAction {
private final SinglePublishedArtifact singlePublishedArtifact;
WarPluginAction(SinglePublishedArtifact singlePublishedArtifact) {
this.singlePublishedArtifact = singlePublishedArtifact;
}
@Override
public Class<? extends Plugin<? extends Project>> getPluginClass() {
return WarPlugin.class;
}
@Override
public void execute(Project project) {
BootWar bootWar = project.getTasks().create(SpringBootPlugin.BOOT_WAR_TASK_NAME,
BootWar.class);
bootWar.providedClasspath(providedRuntimeConfiguration(project));
ArchivePublishArtifact artifact = new ArchivePublishArtifact(bootWar);
this.singlePublishedArtifact.addCandidate(artifact);
project.getComponents().add(new SpringBootSoftwareComponent(artifact,
SpringBootPlugin.BOOT_WEB_SOFTWARE_COMPONENT_NAME));
bootWar.conventionMapping("mainClass",
new MainClassConvention(project, bootWar::getClasspath));
}
private Configuration providedRuntimeConfiguration(Project project) {
return project.getConfigurations()
.getByName(WarPlugin.PROVIDED_RUNTIME_CONFIGURATION_NAME);
}
}

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 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
*
* http://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.
*/
/**
* Central classes for the Spring Boot Gradle plugin.
*/
package org.springframework.boot.gradle.plugin;

@ -1,70 +0,0 @@
/*
* Copyright 2012-2017 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
*
* http://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.gradle.run;
import java.util.Collections;
import java.util.concurrent.Callable;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
import org.springframework.boot.gradle.MainClassResolver;
import org.springframework.boot.gradle.PluginFeatures;
/**
* {@link PluginFeatures} to add run support.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class RunPluginFeatures implements PluginFeatures {
private static final String RUN_APP_TASK_NAME = "bootRun";
@Override
public void apply(Project project) {
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
addBootRunTask(project);
});
}
private void addBootRunTask(Project project) {
JavaPluginConvention javaConvention = project.getConvention()
.getPlugin(JavaPluginConvention.class);
BootRun run = project.getTasks().create(RUN_APP_TASK_NAME, BootRun.class);
run.setDescription("Run the project with support for "
+ "auto-detecting main class and reloading static resources");
run.setGroup("application");
run.classpath(javaConvention.getSourceSets()
.findByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath());
run.getConventionMapping().map("jvmArgs", ((Callable<Object>) () -> {
if (project.hasProperty("applicationDefaultJvmArgs")) {
return project.property("applicationDefaultJvmArgs");
}
return Collections.emptyList();
}));
run.conventionMapping("main", () -> {
if (project.hasProperty("mainClassName")) {
return project.property("mainClassName");
}
return new MainClassResolver(run.getClasspath()).resolveMainClass();
});
}
}

@ -1,54 +0,0 @@
/*
* Copyright 2012-2015 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
*
* http://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.gradle.run;
import java.util.Collections;
import org.gradle.api.Project;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
/**
* Utilities for working with {@link SourceSet}s.
*
* @author Dave Syer
* @author Phillip Webb
*/
final class SourceSets {
private SourceSets() {
}
public static SourceSet findMainSourceSet(Project project) {
for (SourceSet sourceSet : getJavaSourceSets(project)) {
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
return sourceSet;
}
}
return null;
}
private static Iterable<SourceSet> getJavaSourceSets(Project project) {
JavaPluginConvention plugin = project.getConvention()
.getPlugin(JavaPluginConvention.class);
if (plugin == null) {
return Collections.emptyList();
}
return plugin.getSourceSets();
}
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.application;
package org.springframework.boot.gradle.tasks.application;
import org.gradle.api.tasks.Optional;
import org.gradle.jvm.application.tasks.CreateStartScripts;
@ -33,9 +33,4 @@ public class CreateBootStartScripts extends CreateStartScripts {
return super.getMainClassName();
}
@Override
public void setMainClassName(String mainClassName) {
super.setMainClassName(mainClassName);
}
}

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 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
*
* http://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.
*/
/**
* Classes related to Gradle's application features.
*/
package org.springframework.boot.gradle.tasks.application;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.buildinfo;
package org.springframework.boot.gradle.tasks.buildinfo;
import java.io.File;
import java.io.IOException;

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 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
*
* http://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.
*/
/**
* Support for producing build info for consumption by Spring Boot's actuator.
*/
package org.springframework.boot.gradle.tasks.buildinfo;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import org.gradle.api.Action;
import org.gradle.api.Project;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.util.Collections;
import java.util.HashSet;
@ -138,11 +138,10 @@ class BootArchiveSupport {
@Override
public WorkResult execute(CopyActionProcessingStream stream) {
return this.delegate.execute((action) -> {
return this.delegate.execute(action -> {
Map<RelativePath, FileCopyDetailsInternal> detailsByPath = new TreeMap<>();
stream.process((details) -> {
detailsByPath.put(details.getRelativePath(), details);
});
stream.process(
details -> detailsByPath.put(details.getRelativePath(), details));
detailsByPath.values().stream().forEach(action::processFile);
});
}

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.util.Collections;
@ -51,12 +51,10 @@ public class BootJar extends Jar implements BootArchive {
}
private Action<CopySpec> classpathFiles(Spec<File> filter) {
return (copySpec) -> {
copySpec.from((Callable<Iterable<File>>) () -> {
return this.classpath == null ? Collections.emptyList()
: this.classpath.filter(filter);
});
};
return copySpec -> copySpec
.from((Callable<Iterable<File>>) () -> this.classpath == null
? Collections.emptyList() : this.classpath.filter(filter));
}
@Override

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.util.Collections;
@ -45,12 +45,10 @@ public class BootWar extends War implements BootArchive {
private FileCollection providedClasspath;
public BootWar() {
getWebInf().into("lib-provided", (copySpec) -> {
copySpec.from((Callable<Iterable<File>>) () -> {
return this.providedClasspath == null ? Collections.emptyList()
: this.providedClasspath;
});
});
getWebInf().into("lib-provided",
copySpec -> copySpec
.from((Callable<Iterable<File>>) () -> this.providedClasspath == null
? Collections.emptyList() : this.providedClasspath));
}
@Override

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.FileOutputStream;
@ -101,9 +101,7 @@ class BootZipCopyAction implements CopyAction {
// Continue
}
}
return () -> {
return true;
};
return () -> true;
}
private void writeLoaderClassesIfNecessary(ZipOutputStream out) {
@ -116,9 +114,20 @@ class BootZipCopyAction implements CopyAction {
ZipEntry entry;
try (ZipInputStream in = new ZipInputStream(getClass()
.getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
byte[] buffer = new byte[4096];
while ((entry = in.getNextEntry()) != null) {
if (entry.getName().endsWith((".class"))) {
if (entry.getName().endsWith(".class")) {
writeClass(entry, in, out);
}
}
}
catch (IOException ex) {
throw new GradleException("Failed to write loader classes", ex);
}
}
private void writeClass(ZipEntry entry, ZipInputStream in, ZipOutputStream out)
throws IOException {
byte[] buffer = new byte[4096];
if (!this.preserveFileTimestamps) {
entry.setTime(GUtil.CONSTANT_TIME_FOR_ZIP_ENTRIES);
}
@ -129,12 +138,6 @@ class BootZipCopyAction implements CopyAction {
}
out.closeEntry();
}
}
}
catch (IOException ex) {
throw new GradleException("Failed to write loader classes", ex);
}
}
private void writeLaunchScriptIfNecessary(FileOutputStream fileStream) {
try {

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.IOException;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.util.zip.ZipEntry;

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 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
*
* http://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.
*/
/**
* Support for creating executable jars and wars.
*/
package org.springframework.boot.gradle.tasks.bundling;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.run;
package org.springframework.boot.gradle.tasks.run;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.tasks.JavaExec;
@ -39,7 +39,7 @@ public class BootRun extends JavaExec {
public void sourceResources(SourceSet sourceSet) {
setClasspath(getProject()
.files(sourceSet.getResources().getSrcDirs(), getClasspath())
.filter((file) -> !file.equals(sourceSet.getOutput().getResourcesDir())));
.filter(file -> !file.equals(sourceSet.getOutput().getResourcesDir())));
}
@Override

@ -0,0 +1,20 @@
/*
* Copyright 2012-2017 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
*
* http://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.
*/
/**
* Support for running Spring Boot applications.
*/
package org.springframework.boot.gradle.tasks.run;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.buildinfo;
package org.springframework.boot.gradle.dsl;
import java.io.File;
import java.io.FileReader;
@ -25,13 +25,14 @@ import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.gradle.tasks.buildinfo.BuildInfo;
import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link BuildInfo} created using the
* {@link org.springframework.boot.gradle.SpringBootExtension DSL}.
* {@link org.springframework.boot.gradle.dsl.SpringBootExtension DSL}.
*
* @author Andy Wilkinson
*/

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.dependencymanagement;
package org.springframework.boot.gradle.plugin;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
@ -25,11 +25,12 @@ import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for the plugin's dependency management features.
* Integration tests for the configuration applied by
* {@link DependencyManagementPluginAction}.
*
* @author Andy Wilkinson
*/
public class DependencyManagementIntegrationTests {
public class DependencyManagementPluginActionIntegrationTests {
@Rule
public GradleBuild gradleBuild = new GradleBuild();

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.buildinfo;
package org.springframework.boot.gradle.tasks.buildinfo;
import org.gradle.testkit.runner.TaskOutcome;
import org.junit.Rule;
@ -25,7 +25,7 @@ import org.springframework.boot.gradle.testkit.GradleBuild;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link BuildInfo}.
* Integration tests for the {@link BuildInfo} task.
*
* @author Andy Wilkinson
*/

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.IOException;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.IOException;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
/**
* Integration tests for {@link BootJar}.

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
/**
* Tests for {@link BootJar}.

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
/**
* Integration tests for {@link BootJar}.

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.IOException;
import java.util.jar.JarFile;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.FileNotFoundException;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.FileNotFoundException;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.bundling;
package org.springframework.boot.gradle.tasks.bundling;
import java.io.File;
import java.io.FileReader;

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.gradle.run;
package org.springframework.boot.gradle.tasks.run;
import java.io.File;
import java.io.IOException;
@ -30,7 +30,7 @@ import org.springframework.util.FileSystemUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link BootRun}.
* Integration tests for the {@link BootRun} task.
*
* @author Andy Wilkinson
*/

@ -8,7 +8,7 @@ def property(String name, Object defaultValue) {
project.hasProperty(name) ? project.getProperty(name) : defaultValue
}
task buildInfo(type: org.springframework.boot.gradle.buildinfo.BuildInfo) {
task buildInfo(type: org.springframework.boot.gradle.tasks.buildinfo.BuildInfo) {
destinationDir file(property('buildInfoDestinationDir', project.buildDir))
projectArtifact property('buildInfoProjectArtifact', 'foo')
projectVersion property('buildInfoProjectVersion', '1.0')
Loading…
Cancel
Save