diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuild.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuild.java new file mode 100644 index 00000000..dc3dfc8e --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuild.java @@ -0,0 +1,241 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Consumer; + +import io.spring.initializr.generator.buildsystem.Build; +import io.spring.initializr.generator.buildsystem.BuildItemResolver; + +/** + * Gradle build configuration for a project. + * + * @author Andy Wilkinson + */ +public class GradleBuild extends Build { + + private String sourceCompatibility; + + private final Map ext = new TreeMap<>(); + + private final List plugins = new ArrayList<>(); + + private final List appliedPlugins = new ArrayList<>(); + + private final Map configurationCustomizations = new LinkedHashMap<>(); + + private final Map taskCustomizations = new LinkedHashMap<>(); + + private final Buildscript buildscript = new Buildscript(); + + public GradleBuild(BuildItemResolver buildItemResolver) { + super(buildItemResolver); + } + + public GradleBuild() { + this(null); + } + + public void setSourceCompatibility(String sourceCompatibility) { + this.sourceCompatibility = sourceCompatibility; + } + + public String getSourceCompatibility() { + return this.sourceCompatibility; + } + + public GradleBuild ext(String key, String value) { + this.ext.put(key, value); + return this; + } + + public Map getExt() { + return Collections.unmodifiableMap(this.ext); + } + + public GradlePlugin addPlugin(String id) { + return this.addPlugin(id, null); + } + + public GradlePlugin addPlugin(String id, String version) { + GradlePlugin plugin = new GradlePlugin(id, version); + this.plugins.add(plugin); + return plugin; + } + + public void applyPlugin(String id) { + this.appliedPlugins.add(id); + } + + public List getPlugins() { + return Collections.unmodifiableList(this.plugins); + } + + public List getAppliedPlugins() { + return Collections.unmodifiableList(this.appliedPlugins); + } + + public void buildscript(Consumer customizer) { + customizer.accept(this.buildscript); + } + + public Buildscript getBuildscript() { + return this.buildscript; + } + + public void customizeConfiguration(String configurationName, + Consumer customizer) { + customizer.accept(this.configurationCustomizations.computeIfAbsent( + configurationName, (name) -> new ConfigurationCustomization())); + } + + public void addConfiguration(String configurationName) { + customizeConfiguration(configurationName, (configuration) -> { + }); + } + + public Map getConfigurationCustomizations() { + return Collections.unmodifiableMap(this.configurationCustomizations); + } + + public void customizeTask(String taskName, Consumer customizer) { + customizer.accept(this.taskCustomizations.computeIfAbsent(taskName, + (name) -> new TaskCustomization())); + } + + public Map getTaskCustomizations() { + return Collections.unmodifiableMap(this.taskCustomizations); + } + + /** + * The {@code buildscript} block in the {@code build.gradle} file. + */ + public static class Buildscript { + + private final List dependencies = new ArrayList<>(); + + private final Map ext = new LinkedHashMap<>(); + + public Buildscript dependency(String coordinates) { + this.dependencies.add(coordinates); + return this; + } + + public Buildscript ext(String key, String value) { + this.ext.put(key, value); + return this; + } + + public List getDependencies() { + return Collections.unmodifiableList(this.dependencies); + } + + public Map getExt() { + return Collections.unmodifiableMap(this.ext); + } + + } + + /** + * Customization of a configuration in a Gradle build. + */ + public static class ConfigurationCustomization { + + private final Set extendsFrom = new LinkedHashSet<>(); + + public void extendsFrom(String configurationName) { + this.extendsFrom.add(configurationName); + } + + public Set getExtendsFrom() { + return Collections.unmodifiableSet(this.extendsFrom); + } + + } + + /** + * Customization of a task in a Gradle build. + */ + public static class TaskCustomization { + + private final List invocations = new ArrayList<>(); + + private final Map assignments = new LinkedHashMap<>(); + + private final Map nested = new LinkedHashMap<>(); + + public void nested(String property, Consumer customizer) { + customizer.accept(this.nested.computeIfAbsent(property, + (name) -> new TaskCustomization())); + } + + public Map getNested() { + return this.nested; + } + + public void invoke(String target, String... arguments) { + this.invocations.add(new Invocation(target, Arrays.asList(arguments))); + } + + public List getInvocations() { + return Collections.unmodifiableList(this.invocations); + } + + public void set(String target, String value) { + this.assignments.put(target, value); + } + + public Map getAssignments() { + return Collections.unmodifiableMap(this.assignments); + } + + /** + * An invocation of a method that customizes a task. + */ + public static class Invocation { + + private final String target; + + private final List arguments; + + Invocation(String target, List arguments) { + this.target = target; + this.arguments = arguments; + } + + public String getTarget() { + return this.target; + } + + public List getArguments() { + return this.arguments; + } + + } + + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildSystem.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildSystem.java new file mode 100644 index 00000000..6698d936 --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildSystem.java @@ -0,0 +1,43 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import io.spring.initializr.generator.buildsystem.BuildSystem; + +/** + * Gradle {@link BuildSystem}. + * + * @author Andy Wilkinson + */ +public final class GradleBuildSystem implements BuildSystem { + + /** + * Gradle {@link BuildSystem} identifier. + */ + public static final String ID = "gradle"; + + @Override + public String id() { + return ID; + } + + @Override + public String toString() { + return id(); + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildSystemFactory.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildSystemFactory.java new file mode 100644 index 00000000..f3acc6e5 --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildSystemFactory.java @@ -0,0 +1,37 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import io.spring.initializr.generator.buildsystem.BuildSystem; +import io.spring.initializr.generator.buildsystem.BuildSystemFactory; + +/** + * {@link BuildSystemFactory Factory} for {@link GradleBuildSystem}. + * + * @author Andy Wilkinson + */ +class GradleBuildSystemFactory implements BuildSystemFactory { + + @Override + public BuildSystem createBuildSystem(String id) { + if (GradleBuildSystem.ID.equals(id)) { + return new GradleBuildSystem(); + } + return null; + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildWriter.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildWriter.java new file mode 100644 index 00000000..d5b7d37a --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildWriter.java @@ -0,0 +1,367 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +import io.spring.initializr.generator.buildsystem.BillOfMaterials; +import io.spring.initializr.generator.buildsystem.Dependency; +import io.spring.initializr.generator.buildsystem.DependencyComparator; +import io.spring.initializr.generator.buildsystem.DependencyContainer; +import io.spring.initializr.generator.buildsystem.DependencyScope; +import io.spring.initializr.generator.buildsystem.MavenRepository; +import io.spring.initializr.generator.buildsystem.gradle.GradleBuild.ConfigurationCustomization; +import io.spring.initializr.generator.buildsystem.gradle.GradleBuild.TaskCustomization; +import io.spring.initializr.generator.io.IndentingWriter; +import io.spring.initializr.generator.version.VersionProperty; +import io.spring.initializr.generator.version.VersionReference; + +/** + * A {@link GradleBuild} writer for {@code build.gradle}. + * + * @author Andy Wilkinson + * @author Stephane Nicoll + */ +public class GradleBuildWriter { + + public void writeTo(IndentingWriter writer, GradleBuild build) throws IOException { + boolean buildScriptWritten = writeBuildscript(writer, build); + writePlugins(writer, build, buildScriptWritten); + writeProperty(writer, "group", build.getGroup()); + writeProperty(writer, "version", build.getVersion()); + writeProperty(writer, "sourceCompatibility", build.getSourceCompatibility()); + writeConfigurations(writer, build); + writeRepositories(writer, build, writer::println); + writeProperties(writer, build); + writeDependencies(writer, build); + writeBoms(writer, build); + writeTaskCustomizations(writer, build); + } + + private boolean writeBuildscript(IndentingWriter writer, GradleBuild build) { + List dependencies = build.getBuildscript().getDependencies(); + Map ext = build.getBuildscript().getExt(); + if (dependencies.isEmpty() && ext.isEmpty()) { + return false; + } + writer.println("buildscript {"); + writer.indented(() -> { + writeBuildscriptExt(writer, build); + writeBuildscriptRepositories(writer, build); + writeBuildscriptDependencies(writer, build); + }); + writer.println("}"); + return true; + } + + private void writeBuildscriptExt(IndentingWriter writer, GradleBuild build) { + writeNestedMap(writer, "ext", build.getBuildscript().getExt(), + (key, value) -> key + " = " + value); + } + + private void writeBuildscriptRepositories(IndentingWriter writer, GradleBuild build) { + writeRepositories(writer, build); + } + + private void writeBuildscriptDependencies(IndentingWriter writer, GradleBuild build) { + writeNestedCollection(writer, "dependencies", + build.getBuildscript().getDependencies(), + (dependency) -> "classpath '" + dependency + "'"); + } + + private void writePlugins(IndentingWriter writer, GradleBuild build, + boolean buildScriptWritten) { + writeNestedCollection(writer, "plugins", build.getPlugins(), this::pluginAsString, + determineBeforeWriting(buildScriptWritten, writer)); + writeCollection(writer, build.getAppliedPlugins(), + (plugin) -> "apply plugin: '" + plugin + "'", writer::println); + writer.println(); + } + + private Runnable determineBeforeWriting(boolean buildScriptWritten, + IndentingWriter writer) { + if (buildScriptWritten) { + return writer::println; + } + return null; + } + + private String pluginAsString(GradlePlugin plugin) { + String string = "id '" + plugin.getId() + "'"; + if (plugin.getVersion() != null) { + string += " version '" + plugin.getVersion() + "'"; + } + return string; + } + + private void writeConfigurations(IndentingWriter writer, GradleBuild build) { + Map configurationCustomizations = build + .getConfigurationCustomizations(); + if (configurationCustomizations.isEmpty()) { + return; + } + writer.println(); + writer.println("configurations {"); + writer.indented(() -> { + configurationCustomizations.forEach((name, customization) -> { + writeConfiguration(writer, name, customization); + }); + }); + writer.println("}"); + } + + private void writeConfiguration(IndentingWriter writer, String configurationName, + ConfigurationCustomization configurationCustomization) { + if (configurationCustomization.getExtendsFrom().isEmpty()) { + writer.println(configurationName); + } + else { + writer.println(configurationName + " {"); + writer.indented(() -> writer.println(String.format("extendsFrom %s", + String.join(",", configurationCustomization.getExtendsFrom())))); + writer.println("}"); + } + } + + private void writeRepositories(IndentingWriter writer, GradleBuild build) { + writeRepositories(writer, build, null); + } + + private void writeRepositories(IndentingWriter writer, GradleBuild build, + Runnable beforeWriting) { + writeNestedCollection(writer, "repositories", + build.repositories().items().collect(Collectors.toList()), + this::repositoryAsString, beforeWriting); + } + + private String repositoryAsString(MavenRepository repository) { + if (MavenRepository.MAVEN_CENTRAL.equals(repository)) { + return "mavenCentral()"; + } + return "maven { url '" + repository.getUrl() + "' }"; + } + + private void writeProperties(IndentingWriter writer, GradleBuild build) { + if (build.getExt().isEmpty() && build.getVersionProperties().isEmpty()) { + return; + } + Map allProperties = new LinkedHashMap<>(build.getExt()); + build.getVersionProperties().entrySet().forEach((entry) -> allProperties + .put(getVersionPropertyKey(entry), entry.getValue())); + writeNestedCollection(writer, "ext", allProperties.entrySet(), + (e) -> getFormattedProperty(e.getKey(), e.getValue()), writer::println); + } + + private String getVersionPropertyKey(Entry entry) { + return entry.getKey().isInternal() ? entry.getKey().toCamelCaseFormat() + : entry.getKey().toStandardFormat(); + } + + private String getFormattedProperty(String key, String value) { + return String.format("set('%s', '%s')", key, value); + } + + private void writeDependencies(IndentingWriter writer, GradleBuild build) { + Set sortedDependencies = new LinkedHashSet<>(); + DependencyContainer dependencies = build.dependencies(); + sortedDependencies + .addAll(filterDependencies(dependencies, DependencyScope.COMPILE)); + sortedDependencies + .addAll(filterDependencies(dependencies, DependencyScope.COMPILE_ONLY)); + sortedDependencies + .addAll(filterDependencies(dependencies, DependencyScope.RUNTIME)); + sortedDependencies.addAll( + filterDependencies(dependencies, DependencyScope.ANNOTATION_PROCESSOR)); + sortedDependencies.addAll( + filterDependencies(dependencies, DependencyScope.PROVIDED_RUNTIME)); + sortedDependencies + .addAll(filterDependencies(dependencies, DependencyScope.TEST_COMPILE)); + sortedDependencies + .addAll(filterDependencies(dependencies, DependencyScope.TEST_RUNTIME)); + writeNestedCollection(writer, "dependencies", sortedDependencies, + this::dependencyAsString, writer::println); + } + + private String dependencyAsString(Dependency dependency) { + String quoteStyle = determineQuoteStyle(dependency.getVersion()); + String version = determineVersion(dependency.getVersion()); + String type = dependency.getType(); + return configurationForScope(dependency.getScope()) + " " + quoteStyle + + dependency.getGroupId() + ":" + dependency.getArtifactId() + + ((version != null) ? ":" + version : "") + + ((type != null) ? "@" + type : "") + quoteStyle; + } + + private void writeBoms(IndentingWriter writer, GradleBuild build) { + if (build.boms().isEmpty()) { + return; + } + List boms = build.boms().items() + .sorted(Comparator.comparingInt(BillOfMaterials::getOrder).reversed()) + .collect(Collectors.toList()); + writer.println(); + writer.println("dependencyManagement {"); + writer.indented( + () -> writeNestedCollection(writer, "imports", boms, this::bomAsString)); + writer.println("}"); + } + + private String bomAsString(BillOfMaterials bom) { + String quoteStyle = determineQuoteStyle(bom.getVersion()); + String version = determineVersion(bom.getVersion()); + return "mavenBom " + quoteStyle + bom.getGroupId() + ":" + bom.getArtifactId() + + ":" + version + quoteStyle; + } + + private String determineQuoteStyle(VersionReference versionReference) { + return (versionReference != null && versionReference.isProperty()) ? "\"" : "'"; + } + + private String determineVersion(VersionReference versionReference) { + if (versionReference != null) { + if (versionReference.isProperty()) { + VersionProperty property = versionReference.getProperty(); + return "${" + + (property.isInternal() ? property.toCamelCaseFormat() + : "property('" + property.toStandardFormat() + "')") + + "}"; + } + return versionReference.getValue(); + } + return null; + } + + private void writeTaskCustomizations(IndentingWriter writer, GradleBuild build) { + Map taskCustomizations = build.getTaskCustomizations(); + taskCustomizations.forEach((name, customization) -> { + writer.println(); + writer.println(name + " {"); + writer.indented(() -> writeTaskCustomization(writer, customization)); + writer.println("}"); + }); + } + + private void writeTaskCustomization(IndentingWriter writer, + TaskCustomization customization) { + writeCollection(writer, customization.getInvocations(), + (invocation) -> invocation.getTarget() + " " + + String.join(", ", invocation.getArguments())); + writeMap(writer, customization.getAssignments(), + (key, value) -> key + " = " + value); + customization.getNested().forEach((property, nestedCustomization) -> { + writer.println(property + " {"); + writer.indented(() -> writeTaskCustomization(writer, nestedCustomization)); + writer.println("}"); + }); + } + + private void writeNestedCollection(IndentingWriter writer, String name, + Collection collection, Function itemToStringConverter) { + this.writeNestedCollection(writer, name, collection, itemToStringConverter, null); + } + + private void writeNestedCollection(IndentingWriter writer, String name, + Collection collection, Function converter, + Runnable beforeWriting) { + if (!collection.isEmpty()) { + if (beforeWriting != null) { + beforeWriting.run(); + } + writer.println(name + " {"); + writer.indented(() -> writeCollection(writer, collection, converter)); + writer.println("}"); + + } + } + + private void writeCollection(IndentingWriter writer, Collection collection, + Function converter) { + writeCollection(writer, collection, converter, null); + } + + private void writeCollection(IndentingWriter writer, Collection collection, + Function itemToStringConverter, Runnable beforeWriting) { + if (!collection.isEmpty()) { + if (beforeWriting != null) { + beforeWriting.run(); + } + collection.stream().map(itemToStringConverter).forEach(writer::println); + } + } + + private void writeNestedMap(IndentingWriter writer, String name, Map map, + BiFunction converter) { + if (!map.isEmpty()) { + writer.println(name + " {"); + writer.indented(() -> writeMap(writer, map, converter)); + writer.println("}"); + } + } + + private void writeMap(IndentingWriter writer, Map map, + BiFunction converter) { + map.forEach((key, value) -> writer.println(converter.apply(key, value))); + } + + private void writeProperty(IndentingWriter writer, String name, String value) { + if (value != null) { + writer.println(String.format("%s = '%s'", name, value)); + } + } + + private static Collection filterDependencies( + DependencyContainer dependencies, DependencyScope... types) { + List candidates = Arrays.asList(types); + return dependencies.items().filter((dep) -> candidates.contains(dep.getScope())) + .sorted(DependencyComparator.INSTANCE).collect(Collectors.toList()); + } + + private String configurationForScope(DependencyScope type) { + switch (type) { + case ANNOTATION_PROCESSOR: + return "annotationProcessor"; + case COMPILE: + return "implementation"; + case COMPILE_ONLY: + return "compileOnly"; + case PROVIDED_RUNTIME: + return "providedRuntime"; + case RUNTIME: + return "runtimeOnly"; + case TEST_COMPILE: + return "testImplementation"; + case TEST_RUNTIME: + return "testRuntimeOnly"; + default: + throw new IllegalStateException( + "Unrecognized dependency type '" + type + "'"); + } + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradlePlugin.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradlePlugin.java new file mode 100644 index 00000000..a12a90ed --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradlePlugin.java @@ -0,0 +1,43 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +/** + * A plugin in a {@link GradleBuild}. + * + * @author Andy Wilkinson + */ +public class GradlePlugin { + + private final String id; + + private final String version; + + public GradlePlugin(String id, String version) { + this.id = id; + this.version = version; + } + + public String getId() { + return this.id; + } + + public String getVersion() { + return this.version; + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleSettingsWriter.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleSettingsWriter.java new file mode 100644 index 00000000..b2793491 --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/GradleSettingsWriter.java @@ -0,0 +1,82 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import java.io.IOException; + +import io.spring.initializr.generator.buildsystem.MavenRepository; +import io.spring.initializr.generator.io.IndentingWriter; + +/** + * A {@link GradleBuild} writer for {@code settings.gradle}. + * + * @author Andy Wilkinson + */ +public class GradleSettingsWriter { + + public void writeTo(IndentingWriter writer, GradleBuild build) throws IOException { + writePluginManagement(writer, build); + writer.println("rootProject.name = '" + build.getArtifact() + "'"); + } + + private void writePluginManagement(IndentingWriter writer, GradleBuild build) { + writer.println("pluginManagement {"); + writer.indented(() -> { + writeRepositories(writer, build); + writeResolutionStrategyIfNecessary(writer, build); + }); + writer.println("}"); + } + + private void writeRepositories(IndentingWriter writer, GradleBuild build) { + writer.println("repositories {"); + writer.indented(() -> { + build.pluginRepositories().items().map(this::repositoryAsString) + .forEach(writer::println); + writer.println("gradlePluginPortal()"); + }); + writer.println("}"); + } + + private void writeResolutionStrategyIfNecessary(IndentingWriter writer, + GradleBuild build) { + if (build.pluginRepositories().items() + .allMatch(MavenRepository.MAVEN_CENTRAL::equals)) { + return; + } + writer.println("resolutionStrategy {"); + writer.indented(() -> { + writer.println("eachPlugin {"); + writer.indented(() -> { + writer.println("if(requested.id.id == 'org.springframework.boot') {"); + writer.indented(() -> writer.println( + "useModule(\"org.springframework.boot:spring-boot-gradle-plugin:${requested.version}\")")); + writer.println("}"); + }); + writer.println("}"); + }); + writer.println("}"); + } + + private String repositoryAsString(MavenRepository repository) { + if (MavenRepository.MAVEN_CENTRAL.equals(repository)) { + return "mavenCentral()"; + } + return "maven { url '" + repository.getUrl() + "' }"; + } + +} diff --git a/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/package-info.java b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/package-info.java new file mode 100644 index 00000000..298b972c --- /dev/null +++ b/initializr-generator/src/main/java/io/spring/initializr/generator/buildsystem/gradle/package-info.java @@ -0,0 +1,21 @@ +/* + * 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 + * + * 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. + */ + +/** + * Gradle build system. Provides a {@link io.spring.initializr.generator.buildsystem.Build + * Build} abstraction and a writer for {@code build.gradle} and {@code settings.gradle}. + */ +package io.spring.initializr.generator.buildsystem.gradle; diff --git a/initializr-generator/src/main/resources/META-INF/spring.factories b/initializr-generator/src/main/resources/META-INF/spring.factories index 47cf070d..8f2d87c0 100644 --- a/initializr-generator/src/main/resources/META-INF/spring.factories +++ b/initializr-generator/src/main/resources/META-INF/spring.factories @@ -1,3 +1,6 @@ +io.spring.initializr.generator.buildsystem.BuildSystemFactory=\ +io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystemFactory + io.spring.initializr.generator.language.LanguageFactory=\ io.spring.initializr.generator.language.groovy.GroovyLanguageFactory,\ io.spring.initializr.generator.language.java.JavaLanguageFactory,\ diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/BuildSystemTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/BuildSystemTests.java index ecafa119..efbff2cf 100644 --- a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/BuildSystemTests.java +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/BuildSystemTests.java @@ -16,8 +16,15 @@ package io.spring.initializr.generator.buildsystem; -import org.junit.jupiter.api.Test; +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.language.kotlin.KotlinLanguage; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** @@ -27,6 +34,28 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; */ class BuildSystemTests { + @Test + void gradleBuildSystem() { + BuildSystem gradle = BuildSystem.forId("gradle"); + assertThat(gradle).isInstanceOf(GradleBuildSystem.class); + assertThat(gradle.id()).isEqualTo("gradle"); + assertThat(gradle.toString()).isEqualTo("gradle"); + } + + @Test + void defaultMainDirectory(@TempDir Path directory) { + Path mainDirectory = BuildSystem.forId("gradle").getMainDirectory(directory, + new JavaLanguage()); + assertThat(mainDirectory).isEqualTo(directory.resolve("src/main/java")); + } + + @Test + void defaultTestDirectory(@TempDir Path directory) { + Path mainDirectory = BuildSystem.forId("gradle").getTestDirectory(directory, + new KotlinLanguage()); + assertThat(mainDirectory).isEqualTo(directory.resolve("src/test/kotlin")); + } + @Test void unknownBuildSystem() { assertThatIllegalStateException().isThrownBy(() -> BuildSystem.forId("unknown")) diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildWriterTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildWriterTests.java new file mode 100644 index 00000000..ee523f93 --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/gradle/GradleBuildWriterTests.java @@ -0,0 +1,403 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; + +import io.spring.initializr.generator.buildsystem.DependencyScope; +import io.spring.initializr.generator.io.IndentingWriter; +import io.spring.initializr.generator.version.VersionProperty; +import io.spring.initializr.generator.version.VersionReference; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GradleBuildWriter} + * + * @author Andy Wilkinson + */ +class GradleBuildWriterTests { + + @Test + void gradleBuildWithCoordinates() throws IOException { + GradleBuild build = new GradleBuild(); + build.setGroup("com.example"); + build.setVersion("0.0.1-SNAPSHOT"); + List lines = generateBuild(build); + assertThat(lines).contains("group = 'com.example'", "version = '0.0.1-SNAPSHOT'"); + } + + @Test + void gradleBuildWithSourceCompatibility() throws IOException { + GradleBuild build = new GradleBuild(); + build.setSourceCompatibility("11"); + List lines = generateBuild(build); + assertThat(lines).contains("sourceCompatibility = '11'"); + } + + @Test + void gradleBuildWithBuildscriptDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.repositories().add("maven-central"); + build.buildscript((buildscript) -> buildscript.dependency( + "org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE")); + List lines = generateBuild(build); + assertThat(lines).containsSequence("buildscript {", " repositories {", + " mavenCentral()", " }", " dependencies {", + " classpath 'org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE'", + " }", "}"); + } + + @Test + void gradleBuildWithBuildscriptExtProperty() throws IOException { + GradleBuild build = new GradleBuild(); + build.repositories().add("maven-central"); + build.buildscript((buildscript) -> buildscript.ext("kotlinVersion", "'1.2.51'")); + List lines = generateBuild(build); + assertThat(lines).containsSequence("buildscript {", " ext {", + " kotlinVersion = '1.2.51'", " }"); + } + + @Test + void gradleBuildWithPlugin() throws IOException { + GradleBuild build = new GradleBuild(); + build.addPlugin("java"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("plugins {", " id 'java'", "}"); + } + + @Test + void gradleBuildWithPluginAndVersion() throws IOException { + GradleBuild build = new GradleBuild(); + build.addPlugin("org.springframework.boot", "2.1.0.RELEASE"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("plugins {", + " id 'org.springframework.boot' version '2.1.0.RELEASE'", "}"); + } + + @Test + void gradleBuildWithApplyPlugin() throws IOException { + GradleBuild build = new GradleBuild(); + build.applyPlugin("io.spring.dependency-management"); + List lines = generateBuild(build); + assertThat(lines) + .containsSequence("apply plugin: 'io.spring.dependency-management'"); + } + + @Test + void gradleBuildWithMavenCentralRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.repositories().add("maven-central"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("repositories {", " mavenCentral()", "}"); + } + + @Test + void gradleBuildWithRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.repositories().add("spring-milestones", "Spring Milestones", + "https://repo.spring.io/milestone"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("repositories {", + " maven { url 'https://repo.spring.io/milestone' }", "}"); + } + + @Test + void gradleBuildWithSnapshotRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.repositories().add("spring-snapshots", "Spring Snapshots", + "https://repo.spring.io/snapshot", true); + List lines = generateBuild(build); + assertThat(lines).containsSequence("repositories {", + " maven { url 'https://repo.spring.io/snapshot' }", "}"); + } + + @Test + void gradleBuildWithPluginRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.pluginRepositories().add("spring-milestones", "Spring Milestones", + "https://repo.spring.io/milestone"); + List lines = generateBuild(build); + assertThat(lines).doesNotContain("repositories {"); + } + + @Test + void gradleBuildWithTaskCustomizedWithInvocations() throws IOException { + GradleBuild build = new GradleBuild(); + build.customizeTask("asciidoctor", (task) -> { + task.invoke("inputs.dir", "snippetsDir"); + task.invoke("dependsOn", "test"); + }); + List lines = generateBuild(build); + assertThat(lines).containsSequence("asciidoctor {", " inputs.dir snippetsDir", + " dependsOn test", "}"); + } + + @Test + void gradleBuildWithTaskCustomizedWithAssignments() throws IOException { + GradleBuild build = new GradleBuild(); + build.customizeTask("compileKotlin", (task) -> { + task.set("kotlinOptions.freeCompilerArgs", "['-Xjsr305=strict']"); + task.set("kotlinOptions.jvmTarget", "'1.8'"); + }); + List lines = generateBuild(build); + assertThat(lines).containsSequence("compileKotlin {", + " kotlinOptions.freeCompilerArgs = ['-Xjsr305=strict']", + " kotlinOptions.jvmTarget = '1.8'", "}"); + } + + @Test + void gradleBuildWithTaskCustomizedWithNestedCustomization() throws IOException { + GradleBuild build = new GradleBuild(); + build.customizeTask("compileKotlin", (compileKotlin) -> compileKotlin + .nested("kotlinOptions", (kotlinOptions) -> { + kotlinOptions.set("freeCompilerArgs", "['-Xjsr305=strict']"); + kotlinOptions.set("jvmTarget", "'1.8'"); + })); + List lines = generateBuild(build); + assertThat(lines).containsSequence("compileKotlin {", " kotlinOptions {", + " freeCompilerArgs = ['-Xjsr305=strict']", + " jvmTarget = '1.8'", " }", "}"); + } + + @Test + void gradleBuildWithExt() throws Exception { + GradleBuild build = new GradleBuild(); + build.setGroup("com.example.demo"); + build.setArtifact("demo"); + build.ext("java.version", "1.8").ext("alpha", "a"); + List lines = generateBuild(build); + assertThat(lines).containsSequence(" set('alpha', 'a')", + " set('java.version', '1.8')"); + } + + @Test + void gradleBuildWithVersionProperties() throws IOException { + GradleBuild build = new GradleBuild(); + build.addVersionProperty(VersionProperty.of("version.property"), "1.2.3"); + build.addInternalVersionProperty("internal.property", "4.5.6"); + build.addExternalVersionProperty("external.property", "7.8.9"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("ext {", + " set('external.property', '7.8.9')", + " set('internalProperty', '4.5.6')", + " set('versionProperty', '1.2.3')", "}"); + } + + @Test + void gradleBuildWithVersionedDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("kotlin-stdlib", "org.jetbrains.kotlin", + "kotlin-stdlib-jdk8", VersionReference.ofProperty("kotlin.version"), + DependencyScope.COMPILE); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlinVersion}\"", + "}"); + } + + @Test + void gradleBuildWithExternalVersionedDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("acme", "com.example", "acme", + VersionReference.ofProperty(VersionProperty.of("acme.version", false)), + DependencyScope.COMPILE); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " implementation \"com.example:acme:${property('acme.version')}\"", + "}"); + } + + @Test + void gradleBuildWithExtAndVersionProperties() throws Exception { + GradleBuild build = new GradleBuild(); + build.setGroup("com.example.demo"); + build.setArtifact("demo"); + build.addInternalVersionProperty("test-version", "1.0"); + build.addExternalVersionProperty("alpha-version", "0.1"); + build.ext("myProperty", "42"); + List lines = generateBuild(build); + assertThat(lines).containsSequence(" set('myProperty', '42')", + " set('alpha-version', '0.1')", " set('testVersion', '1.0')"); + } + + @Test + void gradleBuildWithConfiguration() throws Exception { + GradleBuild build = new GradleBuild(); + build.addConfiguration("developmentOnly"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("configurations {", " developmentOnly", + "}"); + } + + @Test + void gradleBuildWithConfigurationCustomization() throws Exception { + GradleBuild build = new GradleBuild(); + build.customizeConfiguration("developmentOnly", + (configuration) -> configuration.extendsFrom("compile")); + build.customizeConfiguration("developmentOnly", + (configuration) -> configuration.extendsFrom("testCompile")); + List lines = generateBuild(build); + assertThat(lines).containsSequence("configurations {", " developmentOnly {", + " extendsFrom compile,testCompile", " }", "}"); + } + + @Test + void gradleBuildWithConfigurationCustomizations() throws Exception { + GradleBuild build = new GradleBuild(); + build.customizeConfiguration("developmentOnly", + (configuration) -> configuration.extendsFrom("compile")); + build.customizeConfiguration("testOnly", + (configuration) -> configuration.extendsFrom("testCompile")); + List lines = generateBuild(build); + assertThat(lines).containsSequence("configurations {", " developmentOnly {", + " extendsFrom compile", " }", " testOnly {", + " extendsFrom testCompile", " }", "}"); + } + + @Test + void gradleBuildWithAnnotationProcessorDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("annotation-processor", "org.springframework.boot", + "spring-boot-configuration-processor", + DependencyScope.ANNOTATION_PROCESSOR); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'", + "}"); + } + + @Test + void gradleBuildWithCompileDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("root", "org.springframework.boot", + "spring-boot-starter", DependencyScope.COMPILE); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " implementation 'org.springframework.boot:spring-boot-starter'", "}"); + } + + @Test + void gradleBuildWithRuntimeDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("driver", "com.example", "jdbc-driver", + VersionReference.ofValue("1.0.0"), DependencyScope.RUNTIME); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " runtimeOnly 'com.example:jdbc-driver:1.0.0'", "}"); + } + + @Test + void gradleBuildWithProvidedRuntimeDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("tomcat", "org.springframework.boot", + "spring-boot-starter-tomcat", DependencyScope.PROVIDED_RUNTIME); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'", + "}"); + } + + @Test + void gradleBuildWithTestCompileDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("test", "org.springframework.boot", + "spring-boot-starter-test", DependencyScope.TEST_COMPILE); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " testImplementation 'org.springframework.boot:spring-boot-starter-test'", + "}"); + } + + @Test + void gradleBuildWithCompileOnlyDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("test", "org.springframework.boot", + "spring-boot-starter-foobar", DependencyScope.COMPILE_ONLY); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " compileOnly 'org.springframework.boot:spring-boot-starter-foobar'", + "}"); + } + + @Test + void gradleBuildWithTestRuntimeDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("embed-mongo", "de.flapdoodle.embed", + "de.flapdoodle.embed.mongo", DependencyScope.TEST_RUNTIME); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " testRuntimeOnly 'de.flapdoodle.embed:de.flapdoodle.embed.mongo'", + "}"); + } + + @Test + void gradleBuildWithNonNullArtifactTypeDependency() throws IOException { + GradleBuild build = new GradleBuild(); + build.dependencies().add("root", "org.springframework.boot", + "spring-boot-starter", null, DependencyScope.COMPILE, "tar.gz"); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencies {", + " implementation 'org.springframework.boot:spring-boot-starter@tar.gz'", + "}"); + } + + @Test + void gradleBuildWithBom() throws IOException { + GradleBuild build = new GradleBuild(); + build.boms().add("test", "com.example", "my-project-dependencies", + VersionReference.ofValue("1.0.0.RELEASE")); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencyManagement {", " imports {", + " mavenBom 'com.example:my-project-dependencies:1.0.0.RELEASE'", + " }", "}"); + } + + @Test + void gradleBuildWithOrderedBoms() throws IOException { + GradleBuild build = new GradleBuild(); + build.boms().add("bom1", "com.example", "my-project-dependencies", + VersionReference.ofValue("1.0.0.RELEASE"), 5); + build.boms().add("bom2", "com.example", "root-dependencies", + VersionReference.ofProperty("root.version"), 2); + List lines = generateBuild(build); + assertThat(lines).containsSequence("dependencyManagement {", " imports {", + " mavenBom 'com.example:my-project-dependencies:1.0.0.RELEASE'", + " mavenBom \"com.example:root-dependencies:${rootVersion}\"", + " }", "}"); + } + + @Test + void gradleBuildWithCustomVersion() throws IOException { + GradleBuild build = new GradleBuild(); + build.setVersion("1.2.4.RELEASE"); + List lines = generateBuild(build); + assertThat(lines).contains("version = '1.2.4.RELEASE'"); + } + + private List generateBuild(GradleBuild build) throws IOException { + GradleBuildWriter writer = new GradleBuildWriter(); + StringWriter out = new StringWriter(); + writer.writeTo(new IndentingWriter(out), build); + return Arrays.asList(out.toString().split("\\r?\\n")); + } + +} diff --git a/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/gradle/GradleSettingsWriterTests.java b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/gradle/GradleSettingsWriterTests.java new file mode 100644 index 00000000..f074f5d8 --- /dev/null +++ b/initializr-generator/src/test/java/io/spring/initializr/generator/buildsystem/gradle/GradleSettingsWriterTests.java @@ -0,0 +1,90 @@ +/* + * 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 + * + * 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 io.spring.initializr.generator.buildsystem.gradle; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.Arrays; +import java.util.List; + +import io.spring.initializr.generator.io.IndentingWriter; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link GradleSettingsWriter}. + * + * @author Andy Wilkinson + */ +class GradleSettingsWriterTests { + + @Test + void gradleBuildWithMavenCentralPluginRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.pluginRepositories().add("maven-central"); + List lines = generateSettings(build); + assertThat(lines).containsSequence("pluginManagement {", " repositories {", + " mavenCentral()", " gradlePluginPortal()", " }", "}"); + } + + @Test + void gradleBuildWithPluginRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.pluginRepositories().add("spring-milestones", "Spring Milestones", + "https://repo.spring.io/milestone"); + List lines = generateSettings(build); + assertThat(lines).containsSequence("pluginManagement {", " repositories {", + " maven { url 'https://repo.spring.io/milestone' }", + " gradlePluginPortal()", " }", " resolutionStrategy {", + " eachPlugin {", + " if(requested.id.id == 'org.springframework.boot') {", + " useModule(\"org.springframework.boot:spring-boot-gradle-plugin:${requested.version}\")", + " }", " }", " }", "}"); + } + + @Test + void gradleBuildWithSnapshotPluginRepository() throws IOException { + GradleBuild build = new GradleBuild(); + build.pluginRepositories().add("spring-snapshots", "Spring Snapshots", + "https://repo.spring.io/snapshot", true); + List lines = generateSettings(build); + assertThat(lines).containsSequence("pluginManagement {", " repositories {", + " maven { url 'https://repo.spring.io/snapshot' }", + " gradlePluginPortal()", " }", " resolutionStrategy {", + " eachPlugin {", + " if(requested.id.id == 'org.springframework.boot') {", + " useModule(\"org.springframework.boot:spring-boot-gradle-plugin:${requested.version}\")", + " }", " }", " }", "}"); + } + + @Test + void artifactIdShouldBeUsedAsTheRootProjectName() throws Exception { + GradleBuild build = new GradleBuild(); + build.setArtifact("my-application"); + List lines = generateSettings(build); + assertThat(lines).containsSequence("rootProject.name = 'my-application'"); + } + + private List generateSettings(GradleBuild build) throws IOException { + GradleSettingsWriter writer = new GradleSettingsWriter(); + StringWriter out = new StringWriter(); + writer.writeTo(new IndentingWriter(out), build); + return Arrays.asList(out.toString().split("\\r?\\n")); + } + +}