maven #1

Merged
johan merged 4 commits from maven into main 2025-05-04 15:07:47 +02:00
16 changed files with 406 additions and 36 deletions
Showing only changes of commit 46da5c5019 - Show all commits

View file

@ -0,0 +1,8 @@
# References
## Maven
https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#generatedsourcesdirectory
### For executing the plugin several times
See executions
https://maven.apache.org/guides/mini/guide-configuring-plugins.html

View file

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>nu.zoom.dsl</groupId>
<artifactId>endgen</artifactId>
<version>1.2-SNAPSHOT</version>
</parent>
<artifactId>endgen-maven-plugin</artifactId>
<packaging>maven-plugin</packaging>
<properties>
<maven-plugin-tools.version>3.15.1</maven-plugin-tools.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.9.9</version>
<scope>provided</scope>
</dependency>
<!-- dependency on annotations -->
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>${maven-plugin-tools.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>nu.zoom.dsl</groupId>
<artifactId>parser</artifactId>
<version>${project.parent.version}</version>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${maven-plugin-tools.version}</version>
<executions>
<execution>
<id>help-mojo</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>nu.zoom.dsl</groupId>
<artifactId>endgen-maven-plugin</artifactId>
<version>1.2-SNAPSHOT</version>
<executions>
<execution>
<configuration>
<templates>${project.build.sourceDirectory}/main/endgen-templates</templates>
<output>${project.build.sourceDirectory}/generated-sources/endgen endpoints-output</output>
<dsl>${project.basedir}/../test01.endpoints</dsl>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

View file

@ -0,0 +1,64 @@
package nu.zoom.dsl.maven;
import nu.zoom.dsl.run.Runner;
import nu.zoom.dsl.run.ValidationException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import java.io.File;
import java.util.Optional;
@Mojo(
name = "endgen",
defaultPhase = LifecyclePhase.GENERATE_SOURCES
)
public class EndgenMojo extends AbstractMojo {
@Parameter(defaultValue = "${project.build.sourceDirectory}/main/endgen-templates")
File templates;
@Parameter(defaultValue = "${project.build.outputDirectory}/generated-sources/endgen")
File output;
@Parameter
File dsl;
@Parameter
String parser;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
getLog().info("Running endgen");
getLog().info("Using dsl: " + dsl);
try {
Runner.run(
optional(dsl).map(File::toPath).orElseThrow(),
optional(templates).map(File::toPath),
optional(output).map(File::toPath),
getParserType(parser),
new MavenLogger(getLog())
);
} catch (Exception e) {
throw new MojoExecutionException(e.getMessage(), e);
}
}
private Optional<Runner.ParserType> getParserType(final String type) throws ValidationException {
if (type == null) {
return Optional.empty();
}
try {
return Optional.of(Runner.ParserType.valueOf(type));
} catch (IllegalArgumentException e) {
throw new ValidationException(e);
}
}
private <T> Optional<T> optional(T arg) {
return arg == null ? Optional.empty() : Optional.of(arg);
}
}

View file

@ -0,0 +1,17 @@
package nu.zoom.dsl.maven;
import nu.zoom.dsl.run.Logger;
import org.apache.maven.plugin.logging.Log;
public class MavenLogger implements Logger {
private final Log delegate;
public MavenLogger(Log delegate) {
this.delegate = delegate;
}
@Override
public void println(String message) {
this.delegate.debug(message);
}
}

View file

@ -0,0 +1,52 @@
package nu.zoom.dsl.maven;
import java.io.File;
public class Run {
private File templates;
private File output;
private File dsl;
private String parser;
public File getTemplates() {
return templates;
}
public void setTemplates(File templates) {
this.templates = templates;
}
public File getOutput() {
return output;
}
public void setOutput(File output) {
this.output = output;
}
public File getDsl() {
return dsl;
}
public void setDsl(File dsl) {
this.dsl = dsl;
}
public String getParser() {
return parser;
}
public void setParser(String parser) {
this.parser = parser;
}
@Override
public String toString() {
return "Run{" +
"templates=" + templates +
", output=" + output +
", dsl=" + dsl +
", parser='" + parser + '\'' +
'}';
}
}

View file

@ -14,7 +14,7 @@
package nu.zoom.dsl.cli; package nu.zoom.dsl.cli;
import nu.zoom.dsl.ast.DocumentNode; import nu.zoom.dsl.ast.DocumentNode;
import nu.zoom.dsl.ast.ParserWrapper; import nu.zoom.dsl.run.*;
import nu.zoom.dsl.freemarker.Generator; import nu.zoom.dsl.freemarker.Generator;
import picocli.CommandLine; import picocli.CommandLine;
import picocli.CommandLine.Command; import picocli.CommandLine.Command;
@ -24,8 +24,8 @@ import picocli.CommandLine.Parameters;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@Command( @Command(
@ -34,20 +34,16 @@ import java.util.concurrent.Callable;
description = "Generate source code from an endpoints specification file." description = "Generate source code from an endpoints specification file."
) )
public class EndpointsCLI implements Callable<Integer> { public class EndpointsCLI implements Callable<Integer> {
public enum ParserType {
Endpoints,
States
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Parameters(index = "0", description = "The source endpoints DSL file.") @Parameters(index = "0", description = "The source endpoints DSL file.")
private Path file; private Path file;
@SuppressWarnings("CanBeFinal") @SuppressWarnings("CanBeFinal")
@Option(names = {"-t", "--template"}, defaultValue = "endpoints-template", description = "The template directory. Default is ${DEFAULT-VALUE}") @Option(names = {"-t", "--template"}, defaultValue = Runner.DEFAULT_TEMPLATE_DIRECTORY_NAME, description = "The template directory. Default is ${DEFAULT-VALUE}")
private Path templateDir ; private Path templateDir ;
@SuppressWarnings("CanBeFinal") @SuppressWarnings("CanBeFinal")
@Option(names = {"-o", "--output"}, defaultValue = "endpoints-output", description = "The directory to write the generated code to. Default is ${DEFAULT-VALUE}") @Option(names = {"-o", "--output"}, defaultValue = Runner.DEFAULT_OUTPUT_DIRECTORY_NAME, description = "The directory to write the generated code to. Default is ${DEFAULT-VALUE}")
private Path outputDir ; private Path outputDir ;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -55,7 +51,7 @@ public class EndpointsCLI implements Callable<Integer> {
private Boolean verbose = false; private Boolean verbose = false;
@Option(names = {"-p", "--parser"}, description = "Force use of a specific parser instead of determining from filename. Valid values: ${COMPLETION-CANDIDATES}.") @Option(names = {"-p", "--parser"}, description = "Force use of a specific parser instead of determining from filename. Valid values: ${COMPLETION-CANDIDATES}.")
private ParserType parser = null; private Runner.ParserType parser = null;
public static void main(String[] args) { public static void main(String[] args) {
int exitCode = new CommandLine(new EndpointsCLI()).execute(args); int exitCode = new CommandLine(new EndpointsCLI()).execute(args);
@ -65,32 +61,14 @@ public class EndpointsCLI implements Callable<Integer> {
@Override @Override
public Integer call() { public Integer call() {
try { try {
validateTemplateDirectory(); final Logger logger = this.verbose ? new StdoutLogger() : new NullLogger() ;
validateInputFile(); Runner.run(
validateOutputDirectory(); this.file,
verbose("Parsing: " + file.toAbsolutePath()); Optional.of(this.templateDir),
if (parser == null) { Optional.of(this.outputDir),
if (file.getFileName().toString().endsWith(".states")) { parser == null ? Optional.empty() : Optional.of(parser),
parser = ParserType.States; logger
} );
}
final DocumentNode rootNode ;
if (parser == ParserType.States) {
verbose("using state grammar.") ;
rootNode = ParserWrapper.parseStates(file);
} else {
verbose("using endpoints grammar.") ;
rootNode = ParserWrapper.parseEndpoints(file);
}
verbose("AST: " + rootNode);
verbose("Generating from templates in: " + templateDir.toAbsolutePath());
Generator generator = new Generator(templateDir, rootNode, outputDir);
List<Path> generatedPaths = generator.generate();
if (generatedPaths.isEmpty()) {
System.out.println("No generated paths found.");
} else {
generatedPaths.forEach(p -> verbose("Generated: " + p.toAbsolutePath()));
}
return 0; return 0;
} catch (Exception e) { } catch (Exception e) {
System.err.println(e.getMessage()); System.err.println(e.getMessage());

View file

@ -0,0 +1,15 @@
package nu.zoom.dsl.run;
public abstract class EndgenException extends Exception {
public EndgenException(String message, Throwable cause) {
super(message, cause);
}
public EndgenException(Throwable cause) {
super(cause);
}
public EndgenException(String message) {
super(message);
}
}

View file

@ -0,0 +1,15 @@
package nu.zoom.dsl.run;
public class GeneratorException extends EndgenException {
public GeneratorException(String message) {
super(message);
}
public GeneratorException(String message, Throwable cause) {
super(message, cause);
}
public GeneratorException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,5 @@
package nu.zoom.dsl.run;
public interface Logger {
void println(String message);
}

View file

@ -0,0 +1,8 @@
package nu.zoom.dsl.run;
public class NullLogger implements Logger {
@Override
public void println(String message) {
}
}

View file

@ -0,0 +1,15 @@
package nu.zoom.dsl.run;
public class ParserException extends EndgenException {
public ParserException(String message) {
super(message);
}
public ParserException(String message, Throwable cause) {
super(message, cause);
}
public ParserException(Throwable cause) {
super(cause);
}
}

View file

@ -11,8 +11,11 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package nu.zoom.dsl.ast; package nu.zoom.dsl.run;
import nu.zoom.dsl.ast.DocumentNode;
import nu.zoom.dsl.ast.EndpointsVisitorTransformer;
import nu.zoom.dsl.ast.StatesVisitorTransformer;
import nu.zoom.dsl.parser.*; import nu.zoom.dsl.parser.*;
import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.CommonTokenStream;

View file

@ -0,0 +1,96 @@
package nu.zoom.dsl.run;
import freemarker.template.TemplateException;
import nu.zoom.dsl.ast.DocumentNode;
import nu.zoom.dsl.freemarker.Generator;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
public final class Runner {
public static final String DEFAULT_TEMPLATE_DIRECTORY_NAME = "endpoints-template";
public static final String DEFAULT_OUTPUT_DIRECTORY_NAME = "endpoints-output";
public static void run(
Path dsl,
Optional<Path> templates,
Optional<Path> output,
Optional<ParserType> maybeParser,
Logger logger
) throws ValidationException, IOException, GeneratorException {
Path templatesDir = templates.orElse(Paths.get(DEFAULT_TEMPLATE_DIRECTORY_NAME));
Path outputDir = output.orElse(Paths.get(DEFAULT_OUTPUT_DIRECTORY_NAME));
validateOutputDirectory(outputDir);
validateTemplateDirectory(templatesDir);
validateInputFile(dsl);
logger.println("Parsing: " + dsl.toAbsolutePath());
final ParserType parser =
maybeParser.orElseGet(
() -> {
if (dsl.getFileName().toString().endsWith(".states")) {
return ParserType.States;
} else {
return ParserType.Endpoints;
}
}
);
final DocumentNode rootNode;
if (parser == ParserType.States) {
logger.println("using state grammar.");
rootNode = ParserWrapper.parseStates(dsl);
} else {
logger.println("using endpoints grammar.");
rootNode = ParserWrapper.parseEndpoints(dsl);
}
logger.println("AST: " + rootNode);
logger.println("Generating from templates in: " + templatesDir.toAbsolutePath());
Generator generator = new Generator(templatesDir, rootNode, outputDir);
List<Path> generatedPaths = null;
try {
generatedPaths = generator.generate();
} catch (TemplateException e) {
throw new GeneratorException(e);
}
if (generatedPaths.isEmpty()) {
System.out.println("No generated paths found.");
} else {
generatedPaths.forEach(p -> logger.println("Generated: " + p.toAbsolutePath()));
}
}
private static void validateOutputDirectory(Path outputDir) throws IOException, ValidationException {
if (Files.notExists(outputDir)) {
Files.createDirectories(outputDir);
}
if (!Files.isDirectory(outputDir)) {
throw new ValidationException("Output directory: '" + outputDir + " 'is not a directory.");
}
}
private static void validateTemplateDirectory(Path templateDir) throws ValidationException {
if (!Files.isDirectory(templateDir)) {
throw new ValidationException("Template directory '" + templateDir + "' is not a directory.");
}
}
private static void validateInputFile(Path file) throws ValidationException {
if (Files.notExists(file)) {
throw new ValidationException("Input file '" + file + "' does not exist.");
}
if (!Files.isReadable(file) || !Files.isRegularFile(file)) {
throw new ValidationException("Input file '" + file + "' is not a readable file.");
}
}
public enum ParserType {
Endpoints,
States
}
}

View file

@ -0,0 +1,9 @@
package nu.zoom.dsl.run;
public class StdoutLogger implements Logger {
@Override
public void println(String message) {
System.out.println(message);
}
}

View file

@ -0,0 +1,15 @@
package nu.zoom.dsl.run;
public class ValidationException extends EndgenException {
public ValidationException(String message) {
super(message);
}
public ValidationException(String message, Throwable cause) {
super(message, cause);
}
public ValidationException(Throwable cause) {
super(cause);
}
}

View file

@ -65,6 +65,7 @@
<modules> <modules>
<module>parser</module> <module>parser</module>
<module>endgen-dist</module> <module>endgen-dist</module>
<module>endgen-maven-plugin</module>
</modules> </modules>
</project> </project>