Allow arbitrary code snippets to be added in a Gradle build

This commit provides an escape hatch for the DSL that we currently don't
support in a gradle build. Snippets can be added with a callback to an
IndentingWriter. For convenience, imports can be added as well.

Snippets are rendered at the end of the file.

Closes gh-1079
This commit is contained in:
Stephane Nicoll
2022-11-10 17:30:02 +01:00
parent dd7a704d3b
commit 186fdeaa2f
7 changed files with 306 additions and 60 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2019 the original author or authors. * Copyright 2012-2022 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -38,6 +38,8 @@ public class GradleBuild extends Build {
private final GradleTaskContainer tasks = new GradleTaskContainer(); private final GradleTaskContainer tasks = new GradleTaskContainer();
private final GradleSnippetContainer snippets = new GradleSnippetContainer();
private final GradleBuildscript.Builder buildscript = new GradleBuildscript.Builder(); private final GradleBuildscript.Builder buildscript = new GradleBuildscript.Builder();
/** /**
@@ -92,6 +94,15 @@ public class GradleBuild extends Build {
return this.tasks; return this.tasks;
} }
/**
* Return the {@linkplain GradleSnippetContainer snippet container} to use to apply
* snippets.
* @return the {@link GradleSnippetContainer}
*/
public GradleSnippetContainer snippets() {
return this.snippets;
}
/** /**
* Customize the {@code buildscript} of the build using the specified consumer. * Customize the {@code buildscript} of the build using the specified consumer.
* @param buildscript a consumer of the current buildscript * @param buildscript a consumer of the current buildscript

View File

@@ -29,6 +29,7 @@ import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.spring.initializr.generator.buildsystem.BillOfMaterials; import io.spring.initializr.generator.buildsystem.BillOfMaterials;
import io.spring.initializr.generator.buildsystem.Dependency; import io.spring.initializr.generator.buildsystem.Dependency;
@@ -59,7 +60,7 @@ public abstract class GradleBuildWriter {
*/ */
public final void writeTo(IndentingWriter writer, GradleBuild build) { public final void writeTo(IndentingWriter writer, GradleBuild build) {
GradleBuildSettings settings = build.getSettings(); GradleBuildSettings settings = build.getSettings();
writeImports(writer, build.tasks()); writeImports(writer, build.tasks(), build.snippets());
writeBuildscript(writer, build); writeBuildscript(writer, build);
writePlugins(writer, build); writePlugins(writer, build);
writeProperty(writer, "group", settings.getGroup()); writeProperty(writer, "group", settings.getGroup());
@@ -72,10 +73,11 @@ public abstract class GradleBuildWriter {
writeDependencies(writer, build); writeDependencies(writer, build);
writeBoms(writer, build); writeBoms(writer, build);
writeTasks(writer, build.tasks()); writeTasks(writer, build.tasks());
writeSnippets(writer, build.snippets());
} }
private void writeImports(IndentingWriter writer, GradleTaskContainer tasks) { private void writeImports(IndentingWriter writer, GradleTaskContainer tasks, GradleSnippetContainer snippets) {
List<String> imports = tasks.importedTypes().sorted().toList(); List<String> imports = Stream.concat(tasks.importedTypes(), snippets.importedTypes()).sorted().toList();
imports.forEach((importedType) -> writer.println("import " + importedType)); imports.forEach((importedType) -> writer.println("import " + importedType));
if (!imports.isEmpty()) { if (!imports.isEmpty()) {
writer.println(); writer.println();
@@ -202,6 +204,16 @@ public abstract class GradleBuildWriter {
protected abstract String invocationAsString(GradleTask.Invocation invocation); protected abstract String invocationAsString(GradleTask.Invocation invocation);
private void writeSnippets(IndentingWriter writer, GradleSnippetContainer snippets) {
if (!snippets.isEmpty()) {
writer.println();
}
snippets.values().forEach((snippet) -> {
snippet.apply(writer);
writer.println();
});
}
protected final <T> void writeNestedCollection(IndentingWriter writer, String name, Collection<T> collection, protected final <T> void writeNestedCollection(IndentingWriter writer, String name, Collection<T> collection,
Function<T, String> itemToStringConverter) { Function<T, String> itemToStringConverter) {
this.writeNestedCollection(writer, name, collection, itemToStringConverter, null); this.writeNestedCollection(writer, name, collection, itemToStringConverter, null);

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.buildsystem.gradle;
import java.util.Set;
import java.util.function.Consumer;
import io.spring.initializr.generator.io.IndentingWriter;
/**
* A free-form {@code snippet} to add to a Gradle build.
*
* @author Stephane Nicoll
*/
public class GradleSnippet {
private final Set<String> importedTypes;
private final Consumer<IndentingWriter> writer;
GradleSnippet(Set<String> importedTypes, Consumer<IndentingWriter> writer) {
this.importedTypes = Set.copyOf(importedTypes);
this.writer = writer;
}
Set<String> getImportedTypes() {
return this.importedTypes;
}
/**
* Apply the snippet using the specified {@link IndentingWriter}.
* @param indentingWriter the writer to use
*/
public void apply(IndentingWriter indentingWriter) {
this.writer.accept(indentingWriter);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.buildsystem.gradle;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Stream;
import io.spring.initializr.generator.io.IndentingWriter;
/**
* A container for {@linkplain GradleSnippet Gradle snippets}.
*
* @author Stephane Nicoll
*/
public class GradleSnippetContainer {
private final List<GradleSnippet> snippets = new ArrayList<>();
/**
* Specify if this container is empty.
* @return {@code true} if no snippet is registered
*/
public boolean isEmpty() {
return this.snippets.isEmpty();
}
/**
* Return the {@link GradleSnippet Gradle snippets} to apply.
* @return the gradle snippets
*/
public Stream<GradleSnippet> values() {
return this.snippets.stream();
}
/**
* Return the fully qualified name of types to import.
* @return the imported types
*/
public Stream<String> importedTypes() {
return this.snippets.stream().map(GradleSnippet::getImportedTypes).flatMap(Collection::stream);
}
/**
* Register a {@code snippet} with the specified types to import and writer.
* @param importedTypes the types to import
* @param writer the writer to use.
*/
public void add(Set<String> importedTypes, Consumer<IndentingWriter> writer) {
this.snippets.add(new GradleSnippet(importedTypes, writer));
}
/**
* Register a {@code snippet} with no import.
* @param writer the writer to use.
*/
public void add(Consumer<IndentingWriter> writer) {
add(Collections.emptySet(), writer);
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright 2012-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.buildsystem.gradle;
import java.util.Set;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Common tests for {@link GradleBuildWriter} implementations.
*
* @author Stephane Nicoll
*/
public abstract class GradleBuildWriterTests {
@Test
void gradleBuildWithSnippet() {
GradleBuild build = new GradleBuild();
build.snippets().add((writer) -> {
writer.println("custom {");
writer.indented(() -> {
writer.println("first = 1");
writer.println("second = 2");
});
writer.println("}");
});
assertThat(write(build)).contains("""
custom {
first = 1
second = 2
}
""");
}
@Test
void gradleBuildWithSnippetsAreSeparated() {
GradleBuild build = new GradleBuild();
build.snippets().add((writer) -> {
writer.println("custom {");
writer.indented(() -> {
writer.println("first = 1");
writer.println("second = 2");
});
writer.println("}");
});
build.snippets().add((writer) -> {
writer.println("another {");
writer.indented(() -> {
writer.println("third = 3");
writer.println("fourth = 4");
});
writer.println("}");
});
assertThat(write(build)).contains("""
custom {
first = 1
second = 2
}
another {
third = 3
fourth = 4
}
""");
}
@Test
void gradleBuildWithSnippetAndImports() {
GradleBuild build = new GradleBuild();
build.snippets().add(Set.of("com.example.CustomTask"), (writer) -> writer.println("custom { }"));
assertThat(write(build)).containsOnlyOnce("import com.example.CustomTask");
}
protected abstract String write(GradleBuild build);
}

View File

@@ -39,7 +39,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Jean-Baptiste Nizet * @author Jean-Baptiste Nizet
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
class GroovyDslGradleBuildWriterTests { class GroovyDslGradleBuildWriterTests extends GradleBuildWriterTests {
@Test @Test
void gradleBuildWithCoordinates() { void gradleBuildWithCoordinates() {
@@ -556,7 +556,7 @@ class GroovyDslGradleBuildWriterTests {
assertThat(write(build)).contains("version = '1.2.4.RELEASE'"); assertThat(write(build)).contains("version = '1.2.4.RELEASE'");
} }
private String write(GradleBuild build) { protected String write(GradleBuild build) {
return write(new GroovyDslGradleBuildWriter(), build); return write(new GroovyDslGradleBuildWriter(), build);
} }

View File

@@ -43,13 +43,13 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Jean-Baptiste Nizet * @author Jean-Baptiste Nizet
* @author Stephane Nicoll * @author Stephane Nicoll
*/ */
class KotlinDslGradleBuildWriterTests { class KotlinDslGradleBuildWriterTests extends GradleBuildWriterTests {
@Test @Test
void gradleBuildWithCoordinates() { void gradleBuildWithCoordinates() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.settings().group("com.example").version("1.0.1-SNAPSHOT"); build.settings().group("com.example").version("1.0.1-SNAPSHOT");
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
group = "com.example" group = "com.example"
version = "1.0.1-SNAPSHOT" version = "1.0.1-SNAPSHOT"
"""); """);
@@ -60,7 +60,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithSourceCompatibility15(String sourceCompatibility, String javaVersionConstant) { void gradleBuildWithSourceCompatibility15(String sourceCompatibility, String javaVersionConstant) {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.settings().sourceCompatibility(sourceCompatibility); build.settings().sourceCompatibility(sourceCompatibility);
assertThat(generateBuild(build)).contains("java.sourceCompatibility = " + javaVersionConstant); assertThat(write(build)).contains("java.sourceCompatibility = " + javaVersionConstant);
} }
static Stream<Arguments> sourceCompatibilityParameters() { static Stream<Arguments> sourceCompatibilityParameters() {
@@ -86,14 +86,14 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.buildscript((buildscript) -> buildscript build.buildscript((buildscript) -> buildscript
.dependency("org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE")); .dependency("org.springframework.boot:spring-boot-gradle-plugin:2.1.0.RELEASE"));
assertThatIllegalStateException().isThrownBy(() -> generateBuild(build)); assertThatIllegalStateException().isThrownBy(() -> write(build));
} }
@Test @Test
void gradleBuildWithBuildscriptExtProperty() { void gradleBuildWithBuildscriptExtProperty() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.buildscript((buildscript) -> buildscript.ext("kotlinVersion", "\1.2.51\"")); build.buildscript((buildscript) -> buildscript.ext("kotlinVersion", "\1.2.51\""));
assertThatIllegalStateException().isThrownBy(() -> generateBuild(build)); assertThatIllegalStateException().isThrownBy(() -> write(build));
} }
@Test @Test
@@ -101,7 +101,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.plugins().add("java"); build.plugins().add("java");
build.plugins().add("war"); build.plugins().add("war");
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
plugins { plugins {
java java
war war
@@ -113,7 +113,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.plugins().add("org.jetbrains.kotlin.jvm", (plugin) -> plugin.setVersion("1.3.21")); build.plugins().add("org.jetbrains.kotlin.jvm", (plugin) -> plugin.setVersion("1.3.21"));
build.plugins().add("org.jetbrains.kotlin.plugin.spring", (plugin) -> plugin.setVersion("1.3.21")); build.plugins().add("org.jetbrains.kotlin.plugin.spring", (plugin) -> plugin.setVersion("1.3.21"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
plugins { plugins {
kotlin("jvm") version "1.3.21" kotlin("jvm") version "1.3.21"
kotlin("plugin.spring") version "1.3.21" kotlin("plugin.spring") version "1.3.21"
@@ -124,7 +124,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithPluginAndVersion() { void gradleBuildWithPluginAndVersion() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.plugins().add("org.springframework.boot", (plugin) -> plugin.setVersion("2.1.0.RELEASE")); build.plugins().add("org.springframework.boot", (plugin) -> plugin.setVersion("2.1.0.RELEASE"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
plugins { plugins {
id("org.springframework.boot") version "2.1.0.RELEASE" id("org.springframework.boot") version "2.1.0.RELEASE"
}"""); }""");
@@ -134,14 +134,14 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithApplyPlugin() { void gradleBuildWithApplyPlugin() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.plugins().apply("io.spring.dependency-management"); build.plugins().apply("io.spring.dependency-management");
assertThatIllegalStateException().isThrownBy(() -> generateBuild(build)); assertThatIllegalStateException().isThrownBy(() -> write(build));
} }
@Test @Test
void gradleBuildWithMavenCentralRepository() { void gradleBuildWithMavenCentralRepository() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.repositories().add("maven-central"); build.repositories().add("maven-central");
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
repositories { repositories {
mavenCentral() mavenCentral()
}"""); }""");
@@ -151,7 +151,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithRepository() { void gradleBuildWithRepository() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.repositories().add(MavenRepository.withIdAndUrl("spring-milestones", "https://repo.spring.io/milestone")); build.repositories().add(MavenRepository.withIdAndUrl("spring-milestones", "https://repo.spring.io/milestone"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
repositories { repositories {
maven { url = uri("https://repo.spring.io/milestone") } maven { url = uri("https://repo.spring.io/milestone") }
}"""); }""");
@@ -162,7 +162,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.repositories().add( build.repositories().add(
MavenRepository.withIdAndUrl("spring-snapshots", "https://repo.spring.io/snapshot").onlySnapshots()); MavenRepository.withIdAndUrl("spring-snapshots", "https://repo.spring.io/snapshot").onlySnapshots());
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
repositories { repositories {
maven { url = uri("https://repo.spring.io/snapshot") } maven { url = uri("https://repo.spring.io/snapshot") }
}"""); }""");
@@ -173,7 +173,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.pluginRepositories() build.pluginRepositories()
.add(MavenRepository.withIdAndUrl("spring-milestones", "https://repo.spring.io/milestone")); .add(MavenRepository.withIdAndUrl("spring-milestones", "https://repo.spring.io/milestone"));
assertThat(generateBuild(build)).doesNotContain("repositories {"); assertThat(write(build)).doesNotContain("repositories {");
} }
@Test @Test
@@ -184,21 +184,20 @@ class KotlinDslGradleBuildWriterTests {
(kotlinOptions) -> kotlinOptions.attribute("freeCompilerArgs", "listOf(\"-Xjsr305=strict\")"))); (kotlinOptions) -> kotlinOptions.attribute("freeCompilerArgs", "listOf(\"-Xjsr305=strict\")")));
build.tasks().customizeWithType("org.jetbrains.kotlin.gradle.tasks.KotlinCompile", (task) -> task build.tasks().customizeWithType("org.jetbrains.kotlin.gradle.tasks.KotlinCompile", (task) -> task
.nested("kotlinOptions", (kotlinOptions) -> kotlinOptions.attribute("jvmTarget", "\"1.8\""))); .nested("kotlinOptions", (kotlinOptions) -> kotlinOptions.attribute("jvmTarget", "\"1.8\"")));
assertThat(generateBuild(build)).containsOnlyOnce("import org.jetbrains.kotlin.gradle.tasks.KotlinCompile") assertThat(write(build)).containsOnlyOnce("import org.jetbrains.kotlin.gradle.tasks.KotlinCompile").contains("""
.contains(""" tasks.withType<KotlinCompile> {
tasks.withType<KotlinCompile> { kotlinOptions {
kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict")
freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "1.8"
jvmTarget = "1.8" }
} }""");
}""");
} }
@Test @Test
void gradleBuildWithTaskWithTypesAndShortTypes() { void gradleBuildWithTaskWithTypesAndShortTypes() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.tasks().customizeWithType("JavaCompile", (javaCompile) -> javaCompile.attribute("options.fork", "true")); build.tasks().customizeWithType("JavaCompile", (javaCompile) -> javaCompile.attribute("options.fork", "true"));
assertThat(generateBuild(build)).doesNotContain("import JavaCompile").contains(""" assertThat(write(build)).doesNotContain("import JavaCompile").contains("""
tasks.withType<JavaCompile> { tasks.withType<JavaCompile> {
options.fork = true options.fork = true
}"""); }""");
@@ -211,7 +210,7 @@ class KotlinDslGradleBuildWriterTests {
task.invoke("inputs.dir", "snippetsDir"); task.invoke("inputs.dir", "snippetsDir");
task.invoke("dependsOn", "test"); task.invoke("dependsOn", "test");
}); });
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
tasks.asciidoctor { tasks.asciidoctor {
inputs.dir(snippetsDir) inputs.dir(snippetsDir)
dependsOn(test) dependsOn(test)
@@ -225,7 +224,7 @@ class KotlinDslGradleBuildWriterTests {
task.attribute("kotlinOptions.freeCompilerArgs", "listOf(\"-Xjsr305=strict\")"); task.attribute("kotlinOptions.freeCompilerArgs", "listOf(\"-Xjsr305=strict\")");
task.attribute("kotlinOptions.jvmTarget", "\"1.8\""); task.attribute("kotlinOptions.jvmTarget", "\"1.8\"");
}); });
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
tasks.compileKotlin { tasks.compileKotlin {
kotlinOptions.freeCompilerArgs = listOf("-Xjsr305=strict") kotlinOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
kotlinOptions.jvmTarget = "1.8" kotlinOptions.jvmTarget = "1.8"
@@ -240,7 +239,7 @@ class KotlinDslGradleBuildWriterTests {
kotlinOptions.attribute("freeCompilerArgs", "listOf(\"-Xjsr305=strict\")"); kotlinOptions.attribute("freeCompilerArgs", "listOf(\"-Xjsr305=strict\")");
kotlinOptions.attribute("jvmTarget", "\"1.8\""); kotlinOptions.attribute("jvmTarget", "\"1.8\"");
})); }));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
tasks.compileKotlin { tasks.compileKotlin {
kotlinOptions { kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict") freeCompilerArgs = listOf("-Xjsr305=strict")
@@ -253,7 +252,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithExt() { void gradleBuildWithExt() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.properties().property("java.version", "\"1.8\"").property("alpha", "file(\"build/example\")"); build.properties().property("java.version", "\"1.8\"").property("alpha", "file(\"build/example\")");
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
extra["alpha"] = file("build/example") extra["alpha"] = file("build/example")
extra["java.version"] = "1.8" extra["java.version"] = "1.8"
"""); """);
@@ -264,7 +263,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.properties().version(VersionProperty.of("version.property", false), "1.2.3") build.properties().version(VersionProperty.of("version.property", false), "1.2.3")
.version(VersionProperty.of("internal.property", true), "4.5.6").version("external.property", "7.8.9"); .version(VersionProperty.of("internal.property", true), "4.5.6").version("external.property", "7.8.9");
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
extra["external.property"] = "7.8.9" extra["external.property"] = "7.8.9"
extra["internalProperty"] = "4.5.6" extra["internalProperty"] = "4.5.6"
extra["version.property"] = "1.2.3" extra["version.property"] = "1.2.3"
@@ -277,7 +276,7 @@ class KotlinDslGradleBuildWriterTests {
build.dependencies().add("kotlin-stdlib", build.dependencies().add("kotlin-stdlib",
Dependency.withCoordinates("org.jetbrains.kotlin", "kotlin-stdlib-jdk8") Dependency.withCoordinates("org.jetbrains.kotlin", "kotlin-stdlib-jdk8")
.version(VersionReference.ofProperty("kotlin.version")).scope(DependencyScope.COMPILE)); .version(VersionReference.ofProperty("kotlin.version")).scope(DependencyScope.COMPILE));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${property("kotlinVersion")}") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:${property("kotlinVersion")}")
}"""); }""");
@@ -290,7 +289,7 @@ class KotlinDslGradleBuildWriterTests {
Dependency.withCoordinates("com.example", "acme") Dependency.withCoordinates("com.example", "acme")
.version(VersionReference.ofProperty(VersionProperty.of("acme.version", false))) .version(VersionReference.ofProperty(VersionProperty.of("acme.version", false)))
.scope(DependencyScope.COMPILE)); .scope(DependencyScope.COMPILE));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("com.example:acme:${property("acme.version")}") implementation("com.example:acme:${property("acme.version")}")
}"""); }""");
@@ -301,7 +300,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.properties().version(VersionProperty.of("test-version", true), "1.0").version("alpha-version", "0.1") build.properties().version(VersionProperty.of("test-version", true), "1.0").version("alpha-version", "0.1")
.property("myProperty", "42"); .property("myProperty", "42");
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
extra["myProperty"] = 42 extra["myProperty"] = 42
extra["alpha-version"] = "0.1" extra["alpha-version"] = "0.1"
extra["testVersion"] = "1.0" extra["testVersion"] = "1.0"
@@ -312,7 +311,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithConfiguration() { void gradleBuildWithConfiguration() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.configurations().add("developmentOnly"); build.configurations().add("developmentOnly");
assertThat(generateBuild(build)).contains("val developmentOnly by configurations.creating"); assertThat(write(build)).contains("val developmentOnly by configurations.creating");
} }
@Test @Test
@@ -321,7 +320,7 @@ class KotlinDslGradleBuildWriterTests {
build.configurations().add("custom"); build.configurations().add("custom");
build.configurations().customize("runtimeClasspath", (configuration) -> configuration.extendsFrom("custom")); build.configurations().customize("runtimeClasspath", (configuration) -> configuration.extendsFrom("custom"));
build.configurations().customize("runtimeClasspath", (configuration) -> configuration.extendsFrom("builtIn")); build.configurations().customize("runtimeClasspath", (configuration) -> configuration.extendsFrom("builtIn"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
val custom by configurations.creating val custom by configurations.creating
configurations { configurations {
runtimeClasspath { runtimeClasspath {
@@ -338,7 +337,7 @@ class KotlinDslGradleBuildWriterTests {
build.configurations().customize("runtimeClasspath", (configuration) -> configuration.extendsFrom("custom")); build.configurations().customize("runtimeClasspath", (configuration) -> configuration.extendsFrom("custom"));
build.configurations().customize("testRuntimeClasspath", build.configurations().customize("testRuntimeClasspath",
(configuration) -> configuration.extendsFrom("builtIn")); (configuration) -> configuration.extendsFrom("builtIn"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
val custom by configurations.creating val custom by configurations.creating
configurations { configurations {
runtimeClasspath { runtimeClasspath {
@@ -356,7 +355,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("annotation-processor", "org.springframework.boot", build.dependencies().add("annotation-processor", "org.springframework.boot",
"spring-boot-configuration-processor", DependencyScope.ANNOTATION_PROCESSOR); "spring-boot-configuration-processor", DependencyScope.ANNOTATION_PROCESSOR);
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor") annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
}"""); }""");
@@ -366,7 +365,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithCompileDependency() { void gradleBuildWithCompileDependency() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("root", "org.springframework.boot", "spring-boot-starter", DependencyScope.COMPILE); build.dependencies().add("root", "org.springframework.boot", "spring-boot-starter", DependencyScope.COMPILE);
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter")
}"""); }""");
@@ -376,7 +375,7 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithNoScopeDependencyDefaultsToCompile() { void gradleBuildWithNoScopeDependencyDefaultsToCompile() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("root", Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter")); build.dependencies().add("root", Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter")
}"""); }""");
@@ -387,7 +386,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("driver", Dependency.withCoordinates("com.example", "jdbc-driver") build.dependencies().add("driver", Dependency.withCoordinates("com.example", "jdbc-driver")
.version(VersionReference.ofValue("1.0.0")).scope(DependencyScope.RUNTIME)); .version(VersionReference.ofValue("1.0.0")).scope(DependencyScope.RUNTIME));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
runtimeOnly("com.example:jdbc-driver:1.0.0") runtimeOnly("com.example:jdbc-driver:1.0.0")
}"""); }""");
@@ -398,7 +397,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("tomcat", "org.springframework.boot", "spring-boot-starter-tomcat", build.dependencies().add("tomcat", "org.springframework.boot", "spring-boot-starter-tomcat",
DependencyScope.PROVIDED_RUNTIME); DependencyScope.PROVIDED_RUNTIME);
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat") providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
}"""); }""");
@@ -409,7 +408,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("test", "org.springframework.boot", "spring-boot-starter-test", build.dependencies().add("test", "org.springframework.boot", "spring-boot-starter-test",
DependencyScope.TEST_COMPILE); DependencyScope.TEST_COMPILE);
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test") testImplementation("org.springframework.boot:spring-boot-starter-test")
}"""); }""");
@@ -420,7 +419,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("test", "org.springframework.boot", "spring-boot-starter-foobar", build.dependencies().add("test", "org.springframework.boot", "spring-boot-starter-foobar",
DependencyScope.COMPILE_ONLY); DependencyScope.COMPILE_ONLY);
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
compileOnly("org.springframework.boot:spring-boot-starter-foobar") compileOnly("org.springframework.boot:spring-boot-starter-foobar")
}"""); }""");
@@ -431,7 +430,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("embed-mongo", "de.flapdoodle.embed", "de.flapdoodle.embed.mongo", build.dependencies().add("embed-mongo", "de.flapdoodle.embed", "de.flapdoodle.embed.mongo",
DependencyScope.TEST_RUNTIME); DependencyScope.TEST_RUNTIME);
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
testRuntimeOnly("de.flapdoodle.embed:de.flapdoodle.embed.mongo") testRuntimeOnly("de.flapdoodle.embed:de.flapdoodle.embed.mongo")
}"""); }""");
@@ -442,7 +441,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("root", Dependency.withCoordinates("com.example", "acme") build.dependencies().add("root", Dependency.withCoordinates("com.example", "acme")
.scope(DependencyScope.COMPILE).classifier("test-jar")); .scope(DependencyScope.COMPILE).classifier("test-jar"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("com.example:acme:test-jar") implementation("com.example:acme:test-jar")
}"""); }""");
@@ -455,7 +454,7 @@ class KotlinDslGradleBuildWriterTests {
Dependency.withCoordinates("com.example", "test").scope(DependencyScope.COMPILE).exclusions( Dependency.withCoordinates("com.example", "test").scope(DependencyScope.COMPILE).exclusions(
new Exclusion("com.example.legacy", "legacy-one"), new Exclusion("com.example.legacy", "legacy-one"),
new Exclusion("com.example.another", "legacy-two"))); new Exclusion("com.example.another", "legacy-two")));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("com.example:test") { implementation("com.example:test") {
exclude(group = "com.example.legacy", module = "legacy-one") exclude(group = "com.example.legacy", module = "legacy-one")
@@ -470,7 +469,7 @@ class KotlinDslGradleBuildWriterTests {
build.dependencies().add("test", build.dependencies().add("test",
GradleDependency.withCoordinates("org.springframework.boot", "spring-boot-starter-foobar") GradleDependency.withCoordinates("org.springframework.boot", "spring-boot-starter-foobar")
.scope(DependencyScope.RUNTIME).configuration("myRuntime")); .scope(DependencyScope.RUNTIME).configuration("myRuntime"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
myRuntime("org.springframework.boot:spring-boot-starter-foobar") myRuntime("org.springframework.boot:spring-boot-starter-foobar")
}"""); }""");
@@ -481,7 +480,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("root", Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter") build.dependencies().add("root", Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter")
.scope(DependencyScope.COMPILE).type("tar.gz")); .scope(DependencyScope.COMPILE).type("tar.gz"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("org.springframework.boot:spring-boot-starter@tar.gz") implementation("org.springframework.boot:spring-boot-starter@tar.gz")
}"""); }""");
@@ -492,7 +491,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.dependencies().add("root", Dependency.withCoordinates("com.example", "acme") build.dependencies().add("root", Dependency.withCoordinates("com.example", "acme")
.scope(DependencyScope.COMPILE).type("tar.gz").classifier("test-jar")); .scope(DependencyScope.COMPILE).type("tar.gz").classifier("test-jar"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("com.example:acme:test-jar@tar.gz") implementation("com.example:acme:test-jar@tar.gz")
}"""); }""");
@@ -506,7 +505,7 @@ class KotlinDslGradleBuildWriterTests {
build.dependencies().add("web", build.dependencies().add("web",
Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter-web")); Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter-web"));
build.dependencies().add("root", Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter")); build.dependencies().add("root", Dependency.withCoordinates("org.springframework.boot", "spring-boot-starter"));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencies { dependencies {
implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-web")
@@ -530,7 +529,7 @@ class KotlinDslGradleBuildWriterTests {
return Comparator.comparing(Dependency::getArtifactId); return Comparator.comparing(Dependency::getArtifactId);
} }
}; };
assertThat(generateBuild(writer, build)).contains(""" assertThat(write(writer, build)).contains("""
dependencies { dependencies {
implementation("com.example:alpha") implementation("com.example:alpha")
implementation("com.example:beta") implementation("com.example:beta")
@@ -544,7 +543,7 @@ class KotlinDslGradleBuildWriterTests {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.boms().add("test", BillOfMaterials.withCoordinates("com.example", "my-project-dependencies") build.boms().add("test", BillOfMaterials.withCoordinates("com.example", "my-project-dependencies")
.version(VersionReference.ofValue("1.0.0.RELEASE"))); .version(VersionReference.ofValue("1.0.0.RELEASE")));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencyManagement { dependencyManagement {
imports { imports {
mavenBom("com.example:my-project-dependencies:1.0.0.RELEASE") mavenBom("com.example:my-project-dependencies:1.0.0.RELEASE")
@@ -559,7 +558,7 @@ class KotlinDslGradleBuildWriterTests {
.version(VersionReference.ofValue("1.0.0.RELEASE")).order(5)); .version(VersionReference.ofValue("1.0.0.RELEASE")).order(5));
build.boms().add("bom2", BillOfMaterials.withCoordinates("com.example", "root-dependencies") build.boms().add("bom2", BillOfMaterials.withCoordinates("com.example", "root-dependencies")
.version(VersionReference.ofProperty("root.version")).order(2)); .version(VersionReference.ofProperty("root.version")).order(2));
assertThat(generateBuild(build)).contains(""" assertThat(write(build)).contains("""
dependencyManagement { dependencyManagement {
imports { imports {
mavenBom("com.example:my-project-dependencies:1.0.0.RELEASE") mavenBom("com.example:my-project-dependencies:1.0.0.RELEASE")
@@ -572,14 +571,14 @@ class KotlinDslGradleBuildWriterTests {
void gradleBuildWithCustomVersion() { void gradleBuildWithCustomVersion() {
GradleBuild build = new GradleBuild(); GradleBuild build = new GradleBuild();
build.settings().version("1.2.4.RELEASE"); build.settings().version("1.2.4.RELEASE");
assertThat(generateBuild(build)).contains("version = \"1.2.4.RELEASE\""); assertThat(write(build)).contains("version = \"1.2.4.RELEASE\"");
} }
private String generateBuild(GradleBuild build) { protected String write(GradleBuild build) {
return generateBuild(new KotlinDslGradleBuildWriter(), build); return write(new KotlinDslGradleBuildWriter(), build);
} }
private String generateBuild(KotlinDslGradleBuildWriter writer, GradleBuild build) { private String write(KotlinDslGradleBuildWriter writer, GradleBuild build) {
StringWriter out = new StringWriter(); StringWriter out = new StringWriter();
writer.writeTo(new IndentingWriter(out, new SimpleIndentStrategy("\t")), build); writer.writeTo(new IndentingWriter(out, new SimpleIndentStrategy("\t")), build);
return out.toString().replace("\r\n", "\n"); return out.toString().replace("\r\n", "\n");