From 4cf74da1207b6fe05398bc0fd1e1852f41342594 Mon Sep 17 00:00:00 2001 From: JesseBrault0709 <62299747+JesseBrault0709@users.noreply.github.com> Date: Mon, 6 May 2024 14:43:56 +0200 Subject: [PATCH] More sketching of gradle plugin and related cli. --- cli/build.gradle | 25 +++++++- cli/src/main/java/groowt/cli/Generate.java | 62 +++++++++++++++++++ cli/src/main/java/groowt/cli/GradleUtil.java | 20 ++++++ cli/src/main/java/groowt/cli/GroowtCli.java | 33 ++++++++++ gradle/libs.versions.toml | 1 + groowt-gradle-model/build.gradle | 23 +++++++ .../model/DefaultGroowtGradleModel.java | 34 ++++++++++ .../gradle/model/GroowtGradleModel.java | 13 ++++ groowt-gradle/build.gradle | 9 ++- .../groowt/gradle/DefaultGroowtExtension.java | 11 ++-- .../java/groowt/gradle/GroowtExtension.java | 4 +- .../groowt/gradle/GroowtGradlePlugin.java | 15 +++++ .../model/GroowtGradleModelBuilder.java | 50 +++++++++++++++ settings.gradle | 2 +- 14 files changed, 288 insertions(+), 14 deletions(-) create mode 100644 cli/src/main/java/groowt/cli/Generate.java create mode 100644 cli/src/main/java/groowt/cli/GradleUtil.java create mode 100644 groowt-gradle-model/build.gradle create mode 100644 groowt-gradle-model/src/main/java/groowt/gradle/model/DefaultGroowtGradleModel.java create mode 100644 groowt-gradle-model/src/main/java/groowt/gradle/model/GroowtGradleModel.java create mode 100644 groowt-gradle/src/main/java/groowt/gradle/model/GroowtGradleModelBuilder.java diff --git a/cli/build.gradle b/cli/build.gradle index 9393df6..7069d19 100644 --- a/cli/build.gradle +++ b/cli/build.gradle @@ -1,6 +1,5 @@ plugins { id 'GroowtConventions' - id 'application' id 'com.jessebrault.jbarchiva' version '0.1.0' id 'maven-publish' } @@ -8,14 +7,27 @@ plugins { group = 'groowt' version = '0.1.0' -application { - mainClass = 'groowt.cli.GroowtCli' +repositories { + maven { + url 'https://repo.gradle.org/gradle/libs-releases' + } +} + +dependencies { + implementation libs.gradle.tooling, libs.picocli, project(':groowt-gradle-model') } tasks.named('jar', Jar) { manifest { attributes('Main-Class': 'groowt.cli.GroowtCli') } + from sourceSets.main.runtimeClasspath.filter(File.&exists).collect { it.isDirectory() ? it : zipTree(it) } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + dependsOn ':groowt-gradle-model:jar' +} + +tasks.withType(GenerateModuleMetadata) { + enabled = false } publishing { @@ -23,6 +35,13 @@ publishing { create('groowtCli', MavenPublication) { artifactId = 'groowt-cli' from components.java + pom { + withXml { + def rootNode = asNode() + def dependenciesNode = rootNode.get('dependencies') + rootNode.remove(dependenciesNode) + } + } } } } diff --git a/cli/src/main/java/groowt/cli/Generate.java b/cli/src/main/java/groowt/cli/Generate.java new file mode 100644 index 0000000..63a834b --- /dev/null +++ b/cli/src/main/java/groowt/cli/Generate.java @@ -0,0 +1,62 @@ +package groowt.cli; + +import groowt.gradle.model.GroowtGradleModel; +import picocli.CommandLine; +import picocli.CommandLine.Command; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.Callable; + +@Command(name = "generate", aliases = "gen", description = "Generate a component, template, model, etc.") +public class Generate implements Callable { + + @CommandLine.ParentCommand + private GroowtCli cli; + + @CommandLine.Option( + names = { "-c", "--component" }, + description = "Create a component with the given name." + ) + private String componentName; + + @CommandLine.Option( + names = { "-s", "--sourceSet" }, + description = "The source set in which to generate the component, etc.", + defaultValue = "main" + ) + private String sourceSet; + + @CommandLine.Option( + names = { "-d", "--srcDir", "--sourceDir", "--sourceDirectory" }, + description = "The directory in the source set in which to generate the component, etc." + ) + private File sourceDir; + + @SuppressWarnings("ResultOfMethodCallIgnored") + @Override + public Integer call() { + if (this.componentName != null) { + GradleUtil.doWith(this.cli.getProjectDir(), project -> { + final var model = project.getModel(GroowtGradleModel.class); + if (sourceDir == null) { + this.sourceDir = new File(String.join(File.separator, "src", this.sourceSet, "groovy")); + } + final File packageDir = new File( + this.sourceDir, model.getBasePackage().replace(".", File.separator) + ); + packageDir.mkdirs(); + final File componentFile = new File(packageDir, this.componentName + ".txt"); + try (final OutputStream componentFileOutputStream = new FileOutputStream(componentFile)) { + componentFileOutputStream.write("Hello, Groowt!".getBytes()); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + return 0; + } + +} diff --git a/cli/src/main/java/groowt/cli/GradleUtil.java b/cli/src/main/java/groowt/cli/GradleUtil.java new file mode 100644 index 0000000..e1dba19 --- /dev/null +++ b/cli/src/main/java/groowt/cli/GradleUtil.java @@ -0,0 +1,20 @@ +package groowt.cli; + +import org.gradle.tooling.GradleConnector; +import org.gradle.tooling.ProjectConnection; + +import java.io.File; +import java.util.function.Consumer; + +public final class GradleUtil { + + public static void doWith(File projectDir, Consumer action) { + final var gradleConnector = GradleConnector.newConnector().forProjectDirectory(projectDir); + try (final var projectConnection = gradleConnector.connect()) { + action.accept(projectConnection); + } + } + + private GradleUtil() {} + +} diff --git a/cli/src/main/java/groowt/cli/GroowtCli.java b/cli/src/main/java/groowt/cli/GroowtCli.java index 637b106..7e372d4 100644 --- a/cli/src/main/java/groowt/cli/GroowtCli.java +++ b/cli/src/main/java/groowt/cli/GroowtCli.java @@ -1,9 +1,42 @@ package groowt.cli; +import picocli.CommandLine; + +import java.io.File; + +@CommandLine.Command( + name = "groowt", + description = "The command line interface facilitating development of a Groowt project.", + mixinStandardHelpOptions = true, + version = "0.1.0", + subcommands = { Generate.class } +) public class GroowtCli { + @CommandLine.Option( + names = { "-v", "--verbose" }, + description = "Log verbosely to standard out." + ) + private boolean verbose; + + @CommandLine.Option( + names = { "--projectDir" }, + defaultValue = ".", + description = "The root directory of the groowt project." + ) + private File projectDir; + public static void main(String[] args) { System.out.println("Hello from Groowt!"); + System.exit(new CommandLine(new GroowtCli()).execute(args)); + } + + public boolean isVerbose() { + return this.verbose; + } + + public File getProjectDir() { + return this.projectDir; } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e50a2e0..54ed2ae 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ slf4j = '2.0.12' antlr = { module = 'org.antlr:antlr4', version.ref = 'antlr' } antlr-runtime = { module = 'org.antlr:antlr4-runtime', version.ref = 'antlr' } asm = 'org.ow2.asm:asm:9.7' +gradle-tooling = 'org.gradle:gradle-tooling-api:8.6' groovy = { module = 'org.apache.groovy:groovy', version.ref = 'groovy' } groovy-all = { module = 'org.apache.groovy:groovy-all', version.ref = 'groovy' } groovy-console = { module = 'org.apache.groovy:groovy-console', version.ref = 'groovy' } diff --git a/groowt-gradle-model/build.gradle b/groowt-gradle-model/build.gradle new file mode 100644 index 0000000..f9d0cf8 --- /dev/null +++ b/groowt-gradle-model/build.gradle @@ -0,0 +1,23 @@ +plugins { + id 'GroowtConventions' + id 'java-library' + id 'maven-publish' +} + +group = 'groowt' +version = '0.1.0' + +repositories { + maven { + url 'https://repo.gradle.org/gradle/libs-releases' + } +} + +publishing { + publications { + create('groowtGradleModel', MavenPublication) { + artifactId = 'groowt-gradle-model' + from components.java + } + } +} diff --git a/groowt-gradle-model/src/main/java/groowt/gradle/model/DefaultGroowtGradleModel.java b/groowt-gradle-model/src/main/java/groowt/gradle/model/DefaultGroowtGradleModel.java new file mode 100644 index 0000000..06b2946 --- /dev/null +++ b/groowt-gradle-model/src/main/java/groowt/gradle/model/DefaultGroowtGradleModel.java @@ -0,0 +1,34 @@ +package groowt.gradle.model; + +import java.io.File; +import java.io.Serializable; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class DefaultGroowtGradleModel implements GroowtGradleModel, Serializable { + + private String basePackage; + private Map> sourceSetToTemplatesDirs; + + @Override + public String getBasePackage() { + return this.basePackage; + } + + public void setBasePackage(String basePackage) { + this.basePackage = Objects.requireNonNull(basePackage); + } + + @Override + public Map> getSourceSetToTemplatesDirs() { + return Objects.requireNonNull(this.sourceSetToTemplatesDirs); + } + + public void setSourceFileSets(Map> sourceSetToTemplateDir) { + this.sourceSetToTemplatesDirs = sourceSetToTemplateDir; + } + + + +} diff --git a/groowt-gradle-model/src/main/java/groowt/gradle/model/GroowtGradleModel.java b/groowt-gradle-model/src/main/java/groowt/gradle/model/GroowtGradleModel.java new file mode 100644 index 0000000..b0f191b --- /dev/null +++ b/groowt-gradle-model/src/main/java/groowt/gradle/model/GroowtGradleModel.java @@ -0,0 +1,13 @@ +package groowt.gradle.model; + +import java.io.File; +import java.util.Map; +import java.util.Set; + +public interface GroowtGradleModel { + + String getBasePackage(); + + Map> getSourceSetToTemplatesDirs(); + +} diff --git a/groowt-gradle/build.gradle b/groowt-gradle/build.gradle index 7730b26..30079b2 100644 --- a/groowt-gradle/build.gradle +++ b/groowt-gradle/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'GroowtConventions' id 'java-gradle-plugin' id 'com.jessebrault.jbarchiva' version '0.1.0' id 'maven-publish' @@ -9,10 +10,13 @@ version = '0.1.0' repositories { mavenCentral() + maven { + url 'https://repo.gradle.org/gradle/libs-releases' + } } dependencies { - implementation libs.groovy + implementation libs.groovy, libs.gradle.tooling, project(':groowt-gradle-model') } gradlePlugin { @@ -26,9 +30,10 @@ gradlePlugin { publishing { publications { - groowtGradlePlugin(MavenPublication) { + create('groowtGradlePlugin', MavenPublication) { artifactId = 'groowt-gradle' from components.java } + } } diff --git a/groowt-gradle/src/main/java/groowt/gradle/DefaultGroowtExtension.java b/groowt-gradle/src/main/java/groowt/gradle/DefaultGroowtExtension.java index fedd3ab..ea31757 100644 --- a/groowt-gradle/src/main/java/groowt/gradle/DefaultGroowtExtension.java +++ b/groowt-gradle/src/main/java/groowt/gradle/DefaultGroowtExtension.java @@ -1,23 +1,22 @@ package groowt.gradle; -import org.gradle.api.internal.tasks.DefaultSourceSetContainer; -import org.gradle.api.tasks.SourceSetContainer; import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; import javax.inject.Inject; public class DefaultGroowtExtension implements GroowtExtension { - private final SourceSetContainer sourceSets; + private final Property basePackage; @Inject public DefaultGroowtExtension(ObjectFactory objectFactory) { - this.sourceSets = objectFactory.newInstance(DefaultSourceSetContainer.class); + this.basePackage = objectFactory.property(String.class); } @Override - public SourceSetContainer getSourceSets() { - return this.sourceSets; + public Property getBasePackage() { + return this.basePackage; } } diff --git a/groowt-gradle/src/main/java/groowt/gradle/GroowtExtension.java b/groowt-gradle/src/main/java/groowt/gradle/GroowtExtension.java index a793e7b..4f00135 100644 --- a/groowt-gradle/src/main/java/groowt/gradle/GroowtExtension.java +++ b/groowt-gradle/src/main/java/groowt/gradle/GroowtExtension.java @@ -1,7 +1,7 @@ package groowt.gradle; -import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.api.provider.Property; public interface GroowtExtension { - SourceSetContainer getSourceSets(); + Property getBasePackage(); } diff --git a/groowt-gradle/src/main/java/groowt/gradle/GroowtGradlePlugin.java b/groowt-gradle/src/main/java/groowt/gradle/GroowtGradlePlugin.java index 70cd492..a686d9d 100644 --- a/groowt-gradle/src/main/java/groowt/gradle/GroowtGradlePlugin.java +++ b/groowt-gradle/src/main/java/groowt/gradle/GroowtGradlePlugin.java @@ -1,5 +1,6 @@ package groowt.gradle; +import groowt.gradle.model.GroowtGradleModelBuilder; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; @@ -10,11 +11,21 @@ import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginExtension; import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSetContainer; +import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry; + +import javax.inject.Inject; import static org.gradle.api.internal.lambdas.SerializableLambdas.spec; public class GroowtGradlePlugin implements Plugin { + private final ToolingModelBuilderRegistry modelBuilderRegistry; + + @Inject + public GroowtGradlePlugin(ToolingModelBuilderRegistry modelBuilderRegistry) { + this.modelBuilderRegistry = modelBuilderRegistry; + } + @Override public void apply(Project project) { // Apply java and groovy plugins, if not done already @@ -35,6 +46,7 @@ public class GroowtGradlePlugin implements Plugin { "groowt", DefaultGroowtExtension.class ); + groowtExtension.getBasePackage().convention(""); final JavaPluginExtension javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); final SourceSetContainer javaSourceSets = javaExtension.getSourceSets(); @@ -71,6 +83,9 @@ public class GroowtGradlePlugin implements Plugin { // create init task project.getTasks().create("groowtInit", GroowtInitTask.class, groowtConfigurationProvider); + + // tooling models for cli + this.modelBuilderRegistry.register(new GroowtGradleModelBuilder()); } } diff --git a/groowt-gradle/src/main/java/groowt/gradle/model/GroowtGradleModelBuilder.java b/groowt-gradle/src/main/java/groowt/gradle/model/GroowtGradleModelBuilder.java new file mode 100644 index 0000000..91e05a6 --- /dev/null +++ b/groowt-gradle/src/main/java/groowt/gradle/model/GroowtGradleModelBuilder.java @@ -0,0 +1,50 @@ +package groowt.gradle.model; + +import groowt.gradle.GroowtExtension; +import groowt.gradle.TemplateSourceDirectorySet; +import org.gradle.api.Project; +import org.gradle.api.plugins.JavaPluginExtension; +import org.gradle.api.provider.Property; +import org.gradle.tooling.provider.model.ToolingModelBuilder; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class GroowtGradleModelBuilder implements ToolingModelBuilder { + + @Override + public boolean canBuild(String modelName) { + return modelName.equals(GroowtGradleModel.class.getName()); + } + + @Override + public @NotNull Object buildAll(@NotNull String modelName, Project project) { + final DefaultGroowtGradleModel model = new DefaultGroowtGradleModel(); + final var groowtExtension = project.getExtensions().getByType(GroowtExtension.class); + + // base package + final Property basePackage = groowtExtension.getBasePackage(); + if (!basePackage.isPresent()) { + throw new RuntimeException( + "The property 'basePackage' must be set under the 'groowt' extension in build.gradle" + ); + } + model.setBasePackage(basePackage.get()); + + // templates dirs + final Map> sourceSetToTemplatesDirs = new HashMap<>(); + final var javaExtension = project.getExtensions().getByType(JavaPluginExtension.class); + javaExtension.getSourceSets().forEach(sourceSet -> { + final TemplateSourceDirectorySet templateSourceDirectorySet = + sourceSet.getExtensions().getByType(TemplateSourceDirectorySet.class); + sourceSetToTemplatesDirs.put(sourceSet.getName(), templateSourceDirectorySet.getFiles()); + }); + model.setSourceFileSets(sourceSetToTemplatesDirs); + + return model; + } + +} diff --git a/settings.gradle b/settings.gradle index 478c991..2b4e87e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -26,7 +26,7 @@ pluginManagement { rootProject.name = 'groowt' -include 'cli', 'groowt-gradle', 'views', 'view-components', 'web-views' +include 'cli', 'groowt-gradle', 'groowt-gradle-model', 'views', 'view-components', 'web-views' file('util').eachDir { include it.name