diff --git a/CHANGELOG.md b/CHANGELOG.md index e4da41b..17bc445 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Version 1.3 * [New] [#18](/~https://github.com/gradlex-org/build-parameters/issues/18) Fail the build when it's running on an unsupported Gradle version +* [New] [#52](/~https://github.com/gradlex-org/build-parameters/issues/52) Groups should have a description +* [New] [#53](/~https://github.com/gradlex-org/build-parameters/issues/53) Render descriptions into getters JavaDoc ## Version 1.2 * [New] [#42](/~https://github.com/gradlex-org/build-parameters/issues/42) Boolean parameters: empty string maps to 'true' and invalid value fails the build (instead of silently mapping to 'false') diff --git a/src/main/java/org/gradlex/buildparameters/BuildParameterGroup.java b/src/main/java/org/gradlex/buildparameters/BuildParameterGroup.java index 6b7887d..5f13756 100644 --- a/src/main/java/org/gradlex/buildparameters/BuildParameterGroup.java +++ b/src/main/java/org/gradlex/buildparameters/BuildParameterGroup.java @@ -19,8 +19,10 @@ import org.gradle.api.Action; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; import javax.inject.Inject; @@ -35,6 +37,13 @@ public BuildParameterGroup(Identifier identifier) { this.id = identifier; } + /** + * @since 1.3 + */ + @Input + @Optional + public abstract Property getDescription(); + /** * @since 1.2 */ diff --git a/src/main/java/org/gradlex/buildparameters/CodeGeneratingBuildParameter.java b/src/main/java/org/gradlex/buildparameters/CodeGeneratingBuildParameter.java index 260887e..47bac25 100644 --- a/src/main/java/org/gradlex/buildparameters/CodeGeneratingBuildParameter.java +++ b/src/main/java/org/gradlex/buildparameters/CodeGeneratingBuildParameter.java @@ -16,6 +16,10 @@ package org.gradlex.buildparameters; +import org.gradle.api.provider.Property; + +import java.util.Arrays; +import java.util.Collections; import java.util.function.Function; interface CodeGeneratingBuildParameter { @@ -26,6 +30,8 @@ interface CodeGeneratingBuildParameter { Identifier getId(); + Property getDescription(); + static CodeGeneratingBuildParameter from(BuildParameter parameter, BuildParameterGroup containingGroup) { ParameterType type; if (parameter instanceof IntegerBuildParameter) { @@ -78,6 +84,11 @@ private String getDefaultValue() { public Identifier getId() { return parameter.id; } + + @Override + public Property getDescription() { + return parameter.getDescription(); + } } class ParameterWithoutDefault implements CodeGeneratingBuildParameter { @@ -109,6 +120,11 @@ public String getValue() { public Identifier getId() { return parameter.id; } + + @Override + public Property getDescription() { + return parameter.getDescription(); + } } class ParameterType { diff --git a/src/main/java/org/gradlex/buildparameters/PluginCodeGeneration.java b/src/main/java/org/gradlex/buildparameters/PluginCodeGeneration.java index 3704965..ecf5f71 100644 --- a/src/main/java/org/gradlex/buildparameters/PluginCodeGeneration.java +++ b/src/main/java/org/gradlex/buildparameters/PluginCodeGeneration.java @@ -30,6 +30,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; import static org.gradlex.buildparameters.Constants.GENERATED_EXTENSION_CLASS_NAME; import static org.gradlex.buildparameters.Constants.GENERATED_EXTENSION_NAME; @@ -105,11 +106,13 @@ private void generateGroupClass(BuildParameterGroup group) { } lines.add(" }"); for (BuildParameterGroup subGroup : subGroups) { + renderJavaDoc(lines, subGroup.getDescription()); lines.add(" public " + subGroup.id.toFullQualifiedTypeName() + " get" + subGroup.id.toSimpleTypeName() + "() {"); lines.add(" return this." + subGroup.id.toFieldName() + ";"); lines.add(" }"); } for (CodeGeneratingBuildParameter parameter : parameters) { + renderJavaDoc(lines, parameter.getDescription()); lines.add(" public " + parameter.getType() + " get" + capitalize(parameter.getId().toFieldName()) + "() {"); lines.add(" return this." + parameter.getId().toFieldName() + ";"); lines.add(" }"); @@ -120,6 +123,20 @@ private void generateGroupClass(BuildParameterGroup group) { write(groupSource, lines); } + private void renderJavaDoc(List lines, Property description) { + if (description.isPresent()) { + List descriptionLines = Arrays.stream(description.get().split(System.lineSeparator())) + .map(String::trim) + .collect(Collectors.toCollection(ArrayList::new)); + + StringLists.dropLeadingAndTrailingEmptyLines(descriptionLines); + + lines.add(" /**"); + descriptionLines.forEach(l -> lines.add(" * " + l.trim())); + lines.add(" */"); + } + } + private void generateEnumClass(EnumBuildParameter enumBuildParameter) { getOutputDirectory().get().dir(enumBuildParameter.id.toPackageFolderPath()).getAsFile().mkdirs(); Path enumSource = getOutputDirectory().get().file(enumBuildParameter.id.toPackageFolderPath() + "/" + enumBuildParameter.id.toSimpleTypeName() + ".java").getAsFile().toPath(); diff --git a/src/main/java/org/gradlex/buildparameters/StringLists.java b/src/main/java/org/gradlex/buildparameters/StringLists.java new file mode 100644 index 0000000..e4c0db1 --- /dev/null +++ b/src/main/java/org/gradlex/buildparameters/StringLists.java @@ -0,0 +1,58 @@ +/* + * Copyright 2022 the GradleX team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradlex.buildparameters; + +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +class StringLists { + + private StringLists() { + } + + static void dropLeadingAndTrailingEmptyLines(List input) { + dropLeadingEmptyLines(input); + dropTrailingEmptyLines(input); + } + + static void dropLeadingEmptyLines(List input) { + Iterator it = input.iterator(); + boolean nonEmptyLineReached = false; + while (it.hasNext() && !nonEmptyLineReached) { + String line = it.next(); + if (line.isEmpty()) { + it.remove(); + } else { + nonEmptyLineReached = true; + } + } + } + + public static void dropTrailingEmptyLines(List input) { + ListIterator it = input.listIterator(input.size()); + boolean nonEmptyLineReached = false; + while (it.hasPrevious() && !nonEmptyLineReached) { + String line = it.previous(); + if (line.isEmpty()) { + it.remove(); + } else { + nonEmptyLineReached = true; + } + } + } +} diff --git a/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginBasicFuncTest.groovy b/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginBasicFuncTest.groovy deleted file mode 100644 index f8d95d9..0000000 --- a/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginBasicFuncTest.groovy +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2022 the GradleX team. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.gradlex.buildparameters - -import org.gradlex.buildparameters.fixture.GradleBuild -import org.gradle.testkit.runner.TaskOutcome -import spock.lang.AutoCleanup -import spock.lang.Specification - -class BuildParametersPluginBasicFuncTest extends Specification { - - @Delegate - @AutoCleanup - GradleBuild build = new GradleBuild() - - def "build depends on generatePluginCode"() { - given: - buildFile << """ - plugins { - id 'org.gradlex.build-parameters' - } - - buildParameters { - string("myParameter") { - description = "A simple string parameter" - defaultValue = "foo" - } - } - """ - - when: - def result = build("build") - - then: - result.task(":generatePluginCode").outcome == TaskOutcome.SUCCESS - } - -} diff --git a/src/test/groovy/org/gradlex/buildparameters/CodeGenerationFuncTest.groovy b/src/test/groovy/org/gradlex/buildparameters/CodeGenerationFuncTest.groovy new file mode 100644 index 0000000..3139048 --- /dev/null +++ b/src/test/groovy/org/gradlex/buildparameters/CodeGenerationFuncTest.groovy @@ -0,0 +1,200 @@ +/* + * Copyright 2022 the GradleX team. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.gradlex.buildparameters + +import org.gradlex.buildparameters.fixture.GradleBuild +import org.gradle.testkit.runner.TaskOutcome +import spock.lang.AutoCleanup +import spock.lang.Specification + +class CodeGenerationFuncTest extends Specification { + + @Delegate + @AutoCleanup + GradleBuild build = new GradleBuild() + + void setup() { + buildFile << """ + plugins { + id 'org.gradlex.build-parameters' + } + + buildParameters { + string("myParameter") { + description = "A simple string parameter" + defaultValue = "foo" + } + group("myGroup") { + description = "Group description" + } + } + """ + } + + def "build depends on generatePluginCode"() { + when: + def result = build("build") + + then: + result.task(":generatePluginCode").outcome == TaskOutcome.SUCCESS + } + + def "task is up to date when nothing changes"() { + given: + build("build") + + when: + def result = build("build") + + then: + result.task(":pluginDescriptors").outcome == TaskOutcome.UP_TO_DATE + result.task(":generatePluginCode").outcome == TaskOutcome.UP_TO_DATE + } + + def "changing the pluginId does not cause the task to become out of date"() { + given: + build("build") + + when: + buildFile << """ + buildParameters.pluginId("custom-plugin-id") + """ + + and: + def result = build("build") + + then: + result.task(":pluginDescriptors").outcome == TaskOutcome.SUCCESS + result.task(":generatePluginCode").outcome == TaskOutcome.UP_TO_DATE + } + + def "adding a new parameter causes to task to become out of date"() { + given: + build("build") + + when: + buildFile << """ + buildParameters { + string("another") + } + """ + + and: + def result = build("build") + + then: + result.task(":pluginDescriptors").outcome == TaskOutcome.UP_TO_DATE + result.task(":generatePluginCode").outcome == TaskOutcome.SUCCESS + } + + def "task is not cacheable"() { + given: + build("build", "--build-cache") + + when: + def result = build("clean", "build", "--build-cache") + + then: + result.task(":generatePluginCode").outcome == TaskOutcome.SUCCESS + } + + def "task configuration cache compatible"() { + given: + build("build", "--configuration-cache") + + when: + def result = build("build", "--configuration-cache") + + then: + result.output.contains("Configuration cache entry reused.") + } + + def "it renders descriptions of parameters and groups as JavaDocs of getters"() { + when: + build("build") + + then: + def parameterJavaDoc = + """ | /** + | * A simple string parameter + | */ + | public String getMyParameter()""".stripMargin("|") + generatedFile("buildparameters/BuildParametersExtension.java").text.contains(parameterJavaDoc) + + and: + def groupJavaDoc = + """ | /** + | * Group description + | */ + | public buildparameters.MyGroup getMyGroup()""".stripMargin("|") + generatedFile("buildparameters/BuildParametersExtension.java").text.contains(groupJavaDoc) + } + + def "it renders multiline descriptions as multi line JavaDoc comments"() { + given: + buildFile << """ + buildParameters { + integer("myInt") { + description = ${'"""'} + A multi line + description for + + an int parameter. + ${'"""'} + defaultValue = 42 + } + group("someGroup") { + description = ${'"""'} + A multi line + description for + + a group. + ${'"""'} + } + } + """ + + when: + build("build") + + then: + def parameterJavaDoc = + """ | /** + | * A multi line + | * description for + | * + | * an int parameter. + | */ + | public int getMyInt()""".stripMargin("|") + generatedFile("buildparameters/BuildParametersExtension.java").text.contains(parameterJavaDoc) + + and: + def groupJavaDoc = + """ | /** + | * A multi line + | * description for + | * + | * a group. + | */ + | public buildparameters.SomeGroup getSomeGroup()""".stripMargin("|") + generatedFile("buildparameters/BuildParametersExtension.java").text.contains(groupJavaDoc) + } + + private File generatedFile(String path) { + projectDir.file("build/generated/sources/build-parameters-plugin/java/main/$path") + } +} diff --git a/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginFuncTest.groovy b/src/test/groovy/org/gradlex/buildparameters/GeneratedPluginFuncTest.groovy similarity index 99% rename from src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginFuncTest.groovy rename to src/test/groovy/org/gradlex/buildparameters/GeneratedPluginFuncTest.groovy index 5a832e8..54e3665 100644 --- a/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginFuncTest.groovy +++ b/src/test/groovy/org/gradlex/buildparameters/GeneratedPluginFuncTest.groovy @@ -21,7 +21,7 @@ import spock.lang.AutoCleanup import spock.lang.Issue import spock.lang.Specification -class BuildParametersPluginFuncTest extends Specification { +class GeneratedPluginFuncTest extends Specification { @Delegate @AutoCleanup diff --git a/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginCrossVersionTest.groovy b/src/test/groovy/org/gradlex/buildparameters/GradleCrossVersionTest.groovy similarity index 98% rename from src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginCrossVersionTest.groovy rename to src/test/groovy/org/gradlex/buildparameters/GradleCrossVersionTest.groovy index b45bd6d..af916d6 100644 --- a/src/test/groovy/org/gradlex/buildparameters/BuildParametersPluginCrossVersionTest.groovy +++ b/src/test/groovy/org/gradlex/buildparameters/GradleCrossVersionTest.groovy @@ -20,7 +20,7 @@ import org.gradlex.buildparameters.fixture.GradleBuild import spock.lang.AutoCleanup import spock.lang.Specification -class BuildParametersPluginCrossVersionTest extends Specification { +class GradleCrossVersionTest extends Specification { @Delegate @AutoCleanup diff --git a/src/test/groovy/org/gradlex/buildparameters/BuildParametersSettingsPluginFuncTest.groovy b/src/test/groovy/org/gradlex/buildparameters/SettingsPluginApplicationFuncTest.groovy similarity index 97% rename from src/test/groovy/org/gradlex/buildparameters/BuildParametersSettingsPluginFuncTest.groovy rename to src/test/groovy/org/gradlex/buildparameters/SettingsPluginApplicationFuncTest.groovy index c2058e2..34fde60 100644 --- a/src/test/groovy/org/gradlex/buildparameters/BuildParametersSettingsPluginFuncTest.groovy +++ b/src/test/groovy/org/gradlex/buildparameters/SettingsPluginApplicationFuncTest.groovy @@ -20,7 +20,7 @@ import org.gradlex.buildparameters.fixture.GradleBuild import spock.lang.AutoCleanup import spock.lang.Specification -class BuildParametersSettingsPluginFuncTest extends Specification { +class SettingsPluginApplicationFuncTest extends Specification { @Delegate @AutoCleanup