Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for multi module gradle builds #1288

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ public class GradleBuildProjectContributor implements BuildWriter, ProjectContri

@Override
public void contribute(Path projectRoot) throws IOException {
Path buildGradle = Files.createFile(projectRoot.resolve(this.buildFileName));
Path buildGradle = projectRoot.resolve(this.buildFileName);
if (!Files.exists(buildGradle)) {
Files.createDirectories(buildGradle.getParent());
Files.createFile(buildGradle);
}
writeBuild(Files.newBufferedWriter(buildGradle));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,28 @@ BuildCustomizer<GradleBuild> springBootPluginContributor(ProjectDescription desc
@Bean
@ConditionalOnBuildSystem(id = GradleBuildSystem.ID, dialect = GradleBuildSystem.DIALECT_GROOVY)
public GradleBuildProjectContributor gradleBuildProjectContributor(GroovyDslGradleBuildWriter buildWriter,
GradleBuild build) {
return new GradleBuildProjectContributor(buildWriter, build, this.indentingWriterFactory, "build.gradle");
GradleBuild build, ProjectDescription description) {
String buildFileName = gradleBuildFilePath(description, "build.gradle");
return new GradleBuildProjectContributor(buildWriter, build, this.indentingWriterFactory, buildFileName);
}

@Bean
@ConditionalOnBuildSystem(id = GradleBuildSystem.ID, dialect = GradleBuildSystem.DIALECT_KOTLIN)
public GradleBuildProjectContributor gradleKtsBuildProjectContributor(KotlinDslGradleBuildWriter buildWriter,
GradleBuild build) {
return new GradleBuildProjectContributor(buildWriter, build, this.indentingWriterFactory, "build.gradle.kts");
GradleBuild build, ProjectDescription description) {
String buildFileName = gradleBuildFilePath(description, "build.gradle.kts");
return new GradleBuildProjectContributor(buildWriter, build, this.indentingWriterFactory, buildFileName);
}

private String gradleBuildFilePath(ProjectDescription description, String buildFileName) {
String module = description.getBuildSystem().getApplicationModuleName();
return (module != null) ? module + "/" + buildFileName : buildFileName;
}

@Bean
@ConditionalOnBuildSystem(id = GradleBuildSystem.ID, multiModule = "true")
BuildCustomizer<GradleBuild> addSubmodulesToGradleBuild(ProjectDescription description) {
return (build) -> build.settings().submodule(description.getBuildSystem().getApplicationModuleName());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package io.spring.initializr.generator.spring.configuration;

import io.spring.initializr.generator.buildsystem.Build;
import io.spring.initializr.generator.project.ProjectDescription;
import io.spring.initializr.generator.project.ProjectGenerationConfiguration;
import io.spring.initializr.metadata.InitializrMetadata;

Expand All @@ -31,13 +32,20 @@
public class ApplicationConfigurationProjectGenerationConfiguration {

@Bean
public ApplicationPropertiesContributor applicationPropertiesContributor() {
return new ApplicationPropertiesContributor();
public ApplicationPropertiesContributor applicationPropertiesContributor(
ResourcesPathProvider resourcesPathProvider) {
return new ApplicationPropertiesContributor(resourcesPathProvider);
}

@Bean
public WebFoldersContributor webFoldersContributor(Build build, InitializrMetadata metadata) {
return new WebFoldersContributor(build, metadata);
public WebFoldersContributor webFoldersContributor(Build build, InitializrMetadata metadata,
ResourcesPathProvider resourcesPathProvider) {
return new WebFoldersContributor(build, metadata, resourcesPathProvider);
}

@Bean
public ResourcesPathProvider resourcesPathProvider(ProjectDescription description) {
return ResourcesPathProvider.forProject(description);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,22 @@
*/
public class ApplicationPropertiesContributor extends SingleResourceProjectContributor {

private static final String defaultResourcePattern = "classpath:configuration/application.properties";

public ApplicationPropertiesContributor() {
this("classpath:configuration/application.properties");
this(ResourcesPathProvider.DEFAULT, defaultResourcePattern);
}

public ApplicationPropertiesContributor(String resourcePattern) {
super("src/main/resources/application.properties", resourcePattern);
this(ResourcesPathProvider.DEFAULT, resourcePattern);
}

public ApplicationPropertiesContributor(ResourcesPathProvider resourcesPathProvider) {
this(resourcesPathProvider, defaultResourcePattern);
}

public ApplicationPropertiesContributor(ResourcesPathProvider resourcesPathProvider, String resourcePattern) {
super((root) -> resourcesPathProvider.get(root).resolve("application.properties"), resourcePattern);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.spring.configuration;

import java.nio.file.Path;

import io.spring.initializr.generator.project.ProjectDescription;

@FunctionalInterface
interface ResourcesPathProvider {

ResourcesPathProvider DEFAULT = (root) -> root.resolve("src/main/resources");

static ResourcesPathProvider forProject(ProjectDescription description) {
return (root) -> description.getBuildSystem().getMainSource(root, description.getLanguage())
.getResourcesDirectory();
}

Path get(Path projectRoot);

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,25 @@ public class WebFoldersContributor implements ProjectContributor {

private final BuildMetadataResolver buildMetadataResolver;

private final ResourcesPathProvider resourcesPathProvider;

public WebFoldersContributor(Build build, InitializrMetadata metadata) {
this(build, metadata, ResourcesPathProvider.DEFAULT);
}

public WebFoldersContributor(Build build, InitializrMetadata metadata,
ResourcesPathProvider resourcesPathProvider) {
this.build = build;
this.buildMetadataResolver = new BuildMetadataResolver(metadata);
this.resourcesPathProvider = resourcesPathProvider;
}

@Override
public void contribute(Path projectRoot) throws IOException {
if (this.buildMetadataResolver.hasFacet(this.build, "web")) {
Files.createDirectories(projectRoot.resolve("src/main/resources/templates"));
Files.createDirectories(projectRoot.resolve("src/main/resources/static"));
Path resources = this.resourcesPathProvider.get(projectRoot);
Files.createDirectories(resources.resolve("templates"));
Files.createDirectories(resources.resolve("static"));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ void groovyDslGradleBuildIsContributedInProjectStructure(@TempDir Path projectDi
assertThat(buildGradle).isRegularFile();
}

@Test
void groovyDslGradleBuildIsContributedInMultiProjectStructure(@TempDir Path projectDir) throws IOException {
String path = "sub/build.gradle";
GradleBuild build = new GradleBuild();
new GradleBuildProjectContributor(new GroovyDslGradleBuildWriter(), build,
IndentingWriterFactory.withDefaultSettings(), path).contribute(projectDir);
Path buildGradle = projectDir.resolve(path);
assertThat(buildGradle).isRegularFile();
}

@Test
void groovyDslGradleBuildIsContributedToProject() throws IOException {
GradleBuild build = new GradleBuild();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,23 @@ void buildDotGradleIsContributedWhenGeneratingGradleProject() {
"}"); // @formatter:on
}

static Stream<Arguments> gradleBuildSystemDialects() {
return Stream.of(Arguments.arguments(GradleBuildSystem.DIALECT_GROOVY, "build.gradle"),
Arguments.arguments(GradleBuildSystem.DIALECT_KOTLIN, "build.gradle.kts"));
}

@ParameterizedTest(name = "Dialect {0}")
@MethodSource("gradleBuildSystemDialects")
void buildScriptIsContributedWhenGeneratingMultiModuleGradleProject(String dialect, String buildFileName) {
MutableProjectDescription description = new MutableProjectDescription();
description.setPlatformVersion(Version.parse("2.4.0"));
description.setLanguage(new JavaLanguage("11"));
ProjectStructure project = this.projectTester
.withDescriptionCustomizer((d) -> d.setBuildSystem(new GradleBuildSystem(dialect, "service")))
.generate(description);
assertThat(project).containsFiles("service/" + buildFileName);
}

@Test
void warPluginIsAppliedWhenBuildingProjectThatUsesWarPackaging() {
MutableProjectDescription description = new MutableProjectDescription();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright 2012-2019 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 io.spring.initializr.generator.spring.configuration;

import java.nio.file.Path;

import io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystem;
import io.spring.initializr.generator.language.java.JavaLanguage;
import io.spring.initializr.generator.project.MutableProjectDescription;
import io.spring.initializr.generator.spring.build.gradle.GradleProjectGenerationConfiguration;
import io.spring.initializr.generator.test.InitializrMetadataTestBuilder;
import io.spring.initializr.generator.test.project.ProjectAssetTester;
import io.spring.initializr.generator.test.project.ProjectStructure;
import io.spring.initializr.generator.version.Version;
import io.spring.initializr.metadata.InitializrMetadata;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.assertj.core.api.Assertions.assertThat;

/**
* Tests for {@link ApplicationConfigurationProjectGenerationConfiguration}
*
* @author Linda Navarette
*/
class ApplicationConfigurationProjectGenerationConfigurationTests {

private ProjectAssetTester projectTester;

@BeforeEach
void setup(@TempDir Path directory) {
this.projectTester = new ProjectAssetTester().withIndentingWriterFactory()
.withConfiguration(ApplicationConfigurationProjectGenerationConfiguration.class,
GradleProjectGenerationConfiguration.class)
.withDirectory(directory)
.withBean(InitializrMetadata.class, () -> InitializrMetadataTestBuilder.withDefaults().build());
}

@Test
void applicationConfigurationResourcesAreIncludedInSingleModuleBuild() {
MutableProjectDescription description = new MutableProjectDescription();
description.setPlatformVersion(Version.parse("2.4.0"));
description.setLanguage(new JavaLanguage());
description.setBuildSystem(new GradleBuildSystem());
ProjectStructure project = this.projectTester.generate(description);
assertThat(project).containsFiles("src/main/resources/application.properties");
}

@Test
void applicationConfigurationResourcesAreIncludedInMultiModuleBuild() {
MutableProjectDescription description = new MutableProjectDescription();
description.setPlatformVersion(Version.parse("2.4.0"));
description.setLanguage(new JavaLanguage());
description.setBuildSystem(new GradleBuildSystem(GradleBuildSystem.DIALECT_GROOVY, "foo"));
ProjectStructure project = this.projectTester.generate(description);
assertThat(project).containsFiles("foo/src/main/resources/application.properties");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,11 @@ void applicationConfigurationWithDefaultSettings() throws IOException {
.isEmpty();
}

@Test
void applicationConfigurationWithCustomResourcesDirectory() throws IOException {
Path projectDir = Files.createTempDirectory(this.directory, "project-");
new ApplicationPropertiesContributor((root) -> root.resolve("foo")).contribute(projectDir);
assertThat(new ProjectStructure(projectDir)).textFile("foo/application.properties").lines().isEmpty();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ private Build createBuild(InitializrMetadata metadata) {
}

private Path contribute(Build build, InitializrMetadata metadata) throws IOException {
new WebFoldersContributor(build, metadata).contribute(this.projectDir);
new WebFoldersContributor(build, metadata, (dir) -> dir.resolve("src/main/resources"))
.contribute(this.projectDir);
return this.projectDir;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@

package io.spring.initializr.generator.buildsystem;

import java.util.ArrayList;
import java.util.List;

/**
* General build settings.
*
Expand All @@ -29,10 +32,13 @@ public class BuildSettings {

private final String version;

private final List<String> submodules;

protected BuildSettings(Builder<?> builder) {
this.group = builder.group;
this.artifact = builder.artifact;
this.version = builder.version;
this.submodules = builder.submodules;
}

/**
Expand All @@ -59,6 +65,14 @@ public String getVersion() {
return this.version;
}

/**
* Return the submodules of the project.
* @return the submodules or {@code null}
*/
public List<String> getSubmodules() {
return this.submodules;
}

/**
* Builder for build settings.
*
Expand All @@ -72,6 +86,8 @@ public abstract static class Builder<B extends Builder<B>> {

private String version = "0.0.1-SNAPSHOT";

private final List<String> submodules = new ArrayList<>();

protected Builder() {
}

Expand Down Expand Up @@ -105,6 +121,16 @@ public B version(String version) {
return self();
}

/**
* Adds a submodule to the project.
* @param moduleName the name of the submodule
* @return this for method chaining
*/
public B submodule(String moduleName) {
this.submodules.add(moduleName);
return self();
}

@SuppressWarnings("unchecked")
protected B self() {
return (B) this;
Expand Down
Loading