fixes for Antlr tasks

This commit is contained in:
Steve Ebersole 2021-08-20 18:27:46 -05:00
parent 524b2982cf
commit 6803c09645
11 changed files with 460 additions and 206 deletions

View File

@ -195,10 +195,6 @@ xjc {
}
//sourceSets.main.sourceGeneratorsTask.dependsOn xjc
//sourceSets.main.sourceGeneratorsTask.dependsOn antlr
tasks.compile.dependsOn antlr
task copyBundleResources (type: Copy) {
ext {
bundlesTargetDir = file( "${buildDir}/bundles" )

View File

@ -32,7 +32,7 @@ gradlePlugin {
}
antlrPlugin {
id = 'org.hibernate.orm.antlr'
implementationClass = 'org.hibernate.orm.antlr.Antlr4Plugin'
implementationClass = 'org.hibernate.orm.antlr.AntlrPlugin'
}
jakartaPlugin {
id = 'org.hibernate.orm.jakarta'

View File

@ -1,81 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
/**
* Custom Antlr v4 Plugin
*
* The Gradle-supplied Antlr plugin attempts to simultaneously support multiple
* versions of Antlr which leads to many difficulties. This custom plugin provides
* dedicated and simplified support for Antlr v4
*
* @author Steve Ebersole
*/
public class Antlr4Plugin implements Plugin<Project> {
public static final String HQL_PKG = "org.hibernate.grammars.hql";
public static final String IMPORT_SQL_PKG = "org.hibernate.grammars.importsql";
public static final String GRAPH_PKG = "org.hibernate.grammars.graph";
public static final String ORDER_PKG = "org.hibernate.grammars.ordering";
public static final String ANTLR = "antlr";
public final GrammarDescriptor[] grammarDescriptors = new GrammarDescriptor[] {
new GrammarDescriptor( "HqlLexer", HQL_PKG ),
new GrammarDescriptor( "HqlParser", HQL_PKG ),
new GrammarDescriptor( "SqlScriptLexer", IMPORT_SQL_PKG ),
new GrammarDescriptor( "SqlScriptParser", IMPORT_SQL_PKG ),
new GrammarDescriptor( "GraphLanguageLexer", GRAPH_PKG ),
new GrammarDescriptor( "GraphLanguageParser", GRAPH_PKG ),
new GrammarDescriptor( "OrderingLexer", ORDER_PKG ),
new GrammarDescriptor( "OrderingParser", ORDER_PKG )
};
@Override
public void apply(Project project) {
final Antlr4Spec antlr4Spec = project.getExtensions().create(
Antlr4Spec.REGISTRATION_NAME,
Antlr4Spec.class
);
final Configuration antlrDependencies = project.getConfigurations().maybeCreate( ANTLR );
final Task groupingTask = project.getTasks().create( ANTLR );
groupingTask.setDescription( "Performs all defined Antlr grammar generations" );
groupingTask.setGroup( ANTLR );
for ( GrammarDescriptor grammarDescriptor : grammarDescriptors ) {
final GeneratorTask generatorTask = project.getTasks().create(
"generate" + grammarDescriptor.getGrammarName() + "Grammar",
GeneratorTask.class,
grammarDescriptor,
antlr4Spec
);
generatorTask.setDescription( "Performs Antlr grammar generation for `" + grammarDescriptor.getGrammarName() + "`" );
generatorTask.setGroup( ANTLR );
groupingTask.dependsOn( generatorTask );
}
final SourceSet mainSourceSet = project.getConvention()
.getPlugin( JavaPluginConvention.class )
.getSourceSets()
.getByName( SourceSet.MAIN_SOURCE_SET_NAME );
mainSourceSet.setCompileClasspath( mainSourceSet.getCompileClasspath().plus( antlrDependencies ) );
mainSourceSet.getJava().srcDir( antlr4Spec.getOutputBaseDirectory() );
final Task compileTask = project.getTasks().getByName( mainSourceSet.getCompileJavaTaskName() );
compileTask.dependsOn( groupingTask );
// SourceSet testSourceSet = project.convention.getPlugin( JavaPluginConvention ).sourceSets.getByName( SourceSet.TEST_SOURCE_SET_NAME );
// testSourceSet.compileClasspath += configurations.antlr
}
}

View File

@ -0,0 +1,79 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import org.gradle.api.Project;
/**
* @author Steve Ebersole
*/
public class AntlrHelper {
private AntlrHelper() {
// disallow direct instantiation
}
public static void stripSillyGeneratedFromLines(File outputDirectory, Project project) {
// https://github.com/antlr/antlr4/issues/2634
// :shrug:
final File[] generatedJavaFiles = outputDirectory.listFiles( (dir, name) -> name.endsWith( ".java" ) );
if ( generatedJavaFiles == null ) {
// warn?
return;
}
for ( int i = 0; i < generatedJavaFiles.length; i++ ) {
stripSillyGeneratedFromLineFromFile( generatedJavaFiles[i], project );
}
}
private static void stripSillyGeneratedFromLineFromFile(File generatedJavaFile, Project project) {
try {
final File tmpFile = project.getLayout()
.getBuildDirectory()
.get()
.dir( "tmp" )
.file( generatedJavaFile.getName() )
.getAsFile();
tmpFile.getParentFile().mkdirs();
tmpFile.createNewFile();
final BufferedReader reader = new BufferedReader( new FileReader( generatedJavaFile ) );
final BufferedWriter writer = new BufferedWriter( new FileWriter( tmpFile ) );
boolean found = false;
String currentLine;
while ( ( currentLine = reader.readLine() ) != null ) {
if ( ! found && currentLine.startsWith( "// Generated from" ) ) {
found = true;
continue;
}
writer.write( currentLine + System.lineSeparator() );
}
writer.close();
reader.close();
generatedJavaFile.delete();
tmpFile.renameTo( generatedJavaFile );
}
catch (IOException e) {
project.getLogger().lifecycle( "Unable to remove the generated-from line added by Antlr to the generated file" );
}
}
}

View File

@ -0,0 +1,99 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;
/**
* Custom Antlr v4 Plugin
*
* The Gradle-supplied Antlr plugin attempts to simultaneously support multiple
* versions of Antlr which leads to many difficulties. This custom plugin provides
* dedicated and simplified support for Antlr v4
*
* @author Steve Ebersole
*/
@SuppressWarnings("unused")
public class AntlrPlugin implements Plugin<Project> {
public static final String ANTLR = "antlr";
public static final String HQL_PKG = "org.hibernate.grammars.hql";
public static final String SQL_PKG = "org.hibernate.grammars.importsql";
public static final String GRAPH_PKG = "org.hibernate.grammars.graph";
public static final String ORDER_PKG = "org.hibernate.grammars.ordering";
@Override
public void apply(Project project) {
final Task groupingTask = project.getTasks().create( "generateParsers" );
groupingTask.setDescription( "Performs all defined Antlr grammar generations" );
groupingTask.setGroup( ANTLR );
final AntlrSpec antlrSpec = project.getExtensions().create(
AntlrSpec.REGISTRATION_NAME,
AntlrSpec.class,
project,
groupingTask
);
final Configuration antlrDependencies = project.getConfigurations().maybeCreate( ANTLR );
final SourceSet mainSourceSet = project.getConvention()
.getPlugin( JavaPluginConvention.class )
.getSourceSets()
.getByName( SourceSet.MAIN_SOURCE_SET_NAME );
mainSourceSet.setCompileClasspath( mainSourceSet.getCompileClasspath().plus( antlrDependencies ) );
mainSourceSet.getJava().srcDir( antlrSpec.getOutputBaseDirectory() );
final Task compileTask = project.getTasks().getByName( mainSourceSet.getCompileJavaTaskName() );
compileTask.dependsOn( groupingTask );
populateGrammars( antlrSpec );
}
private void populateGrammars(AntlrSpec antlrSpec) {
antlrSpec.getGrammarDescriptors().create(
"hql",
(grammarDescriptor) -> {
grammarDescriptor.getPackageName().set( HQL_PKG );
grammarDescriptor.getLexerFileName().set( "HqlLexer.g4" );
grammarDescriptor.getParserFileName().set( "HqlParser.g4" );
}
);
antlrSpec.getGrammarDescriptors().create(
"graph",
(grammarDescriptor) -> {
grammarDescriptor.getPackageName().set( GRAPH_PKG );
grammarDescriptor.getLexerFileName().set( "GraphLanguageLexer.g4" );
grammarDescriptor.getParserFileName().set( "GraphLanguageParser.g4" );
}
);
antlrSpec.getGrammarDescriptors().create(
"sqlScript",
(grammarDescriptor) -> {
grammarDescriptor.getPackageName().set( SQL_PKG );
grammarDescriptor.getLexerFileName().set( "SqlScriptLexer.g4" );
grammarDescriptor.getParserFileName().set( "SqlScriptParser.g4" );
}
);
antlrSpec.getGrammarDescriptors().create(
"ordering",
(grammarDescriptor) -> {
grammarDescriptor.getPackageName().set( ORDER_PKG );
grammarDescriptor.getLexerFileName().set( "OrderingLexer.g4" );
grammarDescriptor.getParserFileName().set( "OrderingParser.g4" );
}
);
}
}

View File

@ -6,7 +6,11 @@
*/
package org.hibernate.orm.antlr;
import javax.inject.Inject;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.model.ObjectFactory;
@ -14,23 +18,30 @@ import org.gradle.api.model.ObjectFactory;
/**
* @author Steve Ebersole
*/
public class Antlr4Spec {
public class AntlrSpec {
public static final String REGISTRATION_NAME = "antlr4";
private final DirectoryProperty grammarBaseDirectory;
private final DirectoryProperty outputBaseDirectory;
private final NamedDomainObjectContainer<GrammarDescriptor> grammarDescriptors;
private final NamedDomainObjectContainer<SplitGrammarDescriptor> grammarDescriptors;
@Inject
@SuppressWarnings("UnstableApiUsage")
public Antlr4Spec(ObjectFactory objectFactory, ProjectLayout layout) {
public AntlrSpec(Project project, Task groupingTask) {
final ObjectFactory objectFactory = project.getObjects();
final ProjectLayout layout = project.getLayout();
grammarBaseDirectory = objectFactory.directoryProperty();
grammarBaseDirectory.convention( layout.getProjectDirectory().dir( "src/main/antlr" ) );
outputBaseDirectory = objectFactory.directoryProperty();
outputBaseDirectory.convention( layout.getBuildDirectory().dir( "generated/sources/antlr/main" ) );
grammarDescriptors = objectFactory.domainObjectContainer( GrammarDescriptor.class );
grammarDescriptors = objectFactory.domainObjectContainer(
SplitGrammarDescriptor.class,
new GrammarDescriptorFactory( this, groupingTask, project )
);
}
public DirectoryProperty getGrammarBaseDirectory() {
@ -41,7 +52,7 @@ public class Antlr4Spec {
return outputBaseDirectory;
}
public NamedDomainObjectContainer<GrammarDescriptor> getGrammarDescriptors() {
public NamedDomainObjectContainer<SplitGrammarDescriptor> getGrammarDescriptors() {
return grammarDescriptors;
}
}

View File

@ -1,78 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import java.io.File;
import javax.inject.Inject;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.Directory;
import org.gradle.api.file.RegularFile;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.CacheableTask;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
/**
* @author Steve Ebersole
*/
@CacheableTask
public abstract class GeneratorTask extends DefaultTask {
private final Provider<RegularFile> grammarFile;
private final Provider<Directory> outputDirectory;
@Inject
public GeneratorTask(GrammarDescriptor grammarDescriptor, Antlr4Spec antlrSpec) {
final String relativePackagePath = grammarDescriptor.getPackageName().replace( '.', '/' );
grammarFile = antlrSpec.getGrammarBaseDirectory().file( relativePackagePath + "/" + grammarDescriptor.getGrammarName() + ".g4" );
outputDirectory = antlrSpec.getOutputBaseDirectory().dir( relativePackagePath );
}
@InputFile
@PathSensitive( PathSensitivity.RELATIVE )
public Provider<RegularFile> getGrammarFile() {
return grammarFile;
}
@OutputDirectory
public Provider<Directory> getOutputDirectory() {
return outputDirectory;
}
@TaskAction
public void generate() {
final File grammarFileAsFile = grammarFile.get().getAsFile();
final File outputDirectoryAsFile = outputDirectory.get().getAsFile();
getProject().getLogger().info(
"Starting Antlr grammar generation `{}` -> `{}`",
grammarFileAsFile.getName(),
outputDirectoryAsFile.getAbsolutePath()
);
outputDirectoryAsFile.mkdirs();
getProject().javaexec(
(javaExecSpec) -> {
javaExecSpec.setMain( "org.antlr.v4.Tool" );
javaExecSpec.classpath( getProject().getConfigurations().getByName( "antlr" ) );
javaExecSpec.args(
"-o", outputDirectoryAsFile.getAbsolutePath(),
"-long-messages",
"-listener",
"-visitor",
grammarFileAsFile.getAbsolutePath()
);
}
);
}
}

View File

@ -1,37 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import org.gradle.api.Named;
/**
* Describes a grammar for generation
*
* @author Steve Ebersole
*/
public class GrammarDescriptor implements Named {
private final String grammarName;
private final String packageName;
GrammarDescriptor(String grammarName, String packageName) {
this.grammarName = grammarName;
this.packageName = packageName;
}
@Override
public String getName() {
return getGrammarName();
}
public String getPackageName() {
return packageName;
}
public String getGrammarName() {
return grammarName;
}
}

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import org.gradle.api.NamedDomainObjectFactory;
import org.gradle.api.Project;
import org.gradle.api.Task;
/**
* @author Steve Ebersole
*/
public class GrammarDescriptorFactory implements NamedDomainObjectFactory<SplitGrammarDescriptor> {
private final AntlrSpec antlrSpec;
private final Task groupingTask;
private final Project project;
public GrammarDescriptorFactory(AntlrSpec antlrSpec, Task groupingTask, Project project) {
this.antlrSpec = antlrSpec;
this.groupingTask = groupingTask;
this.project = project;
}
@Override
public SplitGrammarDescriptor create(String name) {
final SplitGrammarDescriptor descriptor = new SplitGrammarDescriptor( name, antlrSpec, project.getObjects() );
final SplitGrammarGenerationTask generatorTask = project.getTasks().create(
determineTaskName( name ),
SplitGrammarGenerationTask.class,
descriptor,
antlrSpec
);
generatorTask.setDescription( "Performs Antlr grammar generation for the `" + name + "` grammar" );
generatorTask.setGroup( "antlr" );
groupingTask.dependsOn( generatorTask );
return descriptor;
}
private String determineTaskName(String grammarName) {
final String titularGrammarName = Character.toTitleCase( grammarName.charAt(0) ) + grammarName.substring(1);
return "generate" + titularGrammarName + "Parser";
}
}

View File

@ -0,0 +1,77 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import javax.inject.Inject;
import org.gradle.api.Named;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.Property;
/**
* @author Steve Ebersole
*/
public class SplitGrammarDescriptor implements Named {
private final String name;
private final AntlrSpec antlrSpec;
private final Property<String> packageName;
private final Property<String> lexerFileName;
private final Property<String> parserFileName;
private final Property<Boolean> generateVisitor;
private final Property<Boolean> generateListener;
@Inject
public SplitGrammarDescriptor(String name, AntlrSpec antlrSpec, ObjectFactory objectFactory) {
this.name = name;
this.antlrSpec = antlrSpec;
packageName = objectFactory.property( String.class );
lexerFileName = objectFactory.property( String.class );
parserFileName = objectFactory.property( String.class );
generateVisitor = objectFactory.property( Boolean.class );
generateVisitor.convention( true );
generateListener = objectFactory.property( Boolean.class );
generateListener.convention( true );
}
@Override
public String getName() {
return name;
}
public Property<String> getPackageName() {
return packageName;
}
public Property<String> getLexerFileName() {
return lexerFileName;
}
public Property<String> getParserFileName() {
return parserFileName;
}
public Property<Boolean> getGenerateVisitor() {
return generateVisitor;
}
public Property<Boolean> getGenerateListener() {
return generateListener;
}
public Property<Boolean> generateVisitor() {
return generateListener;
}
public Property<Boolean> generateListener() {
return generateListener;
}
}

View File

@ -0,0 +1,140 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.orm.antlr;
import java.io.File;
import javax.inject.Inject;
import org.gradle.api.DefaultTask;
import org.gradle.api.file.Directory;
import org.gradle.api.file.RegularFile;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.InputFile;
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.PathSensitive;
import org.gradle.api.tasks.PathSensitivity;
import org.gradle.api.tasks.TaskAction;
import static org.hibernate.orm.antlr.AntlrHelper.stripSillyGeneratedFromLines;
/**
* @author Steve Ebersole
*/
public abstract class SplitGrammarGenerationTask extends DefaultTask {
private final SplitGrammarDescriptor grammarDescriptor;
private final Provider<RegularFile> lexerGrammarFile;
private final Provider<RegularFile> parserGrammarFile;
private final Provider<Directory> outputDirectory;
@Inject
public SplitGrammarGenerationTask(SplitGrammarDescriptor grammarDescriptor, AntlrSpec antlrSpec) {
this.grammarDescriptor = grammarDescriptor;
lexerGrammarFile = getProject().provider( () -> {
final Directory grammarBaseDirectory = antlrSpec.getGrammarBaseDirectory().get();
final Directory grammarDirectory = grammarBaseDirectory.dir( grammarDescriptor.getPackageName().get().replace( '.', '/' ) );
return grammarDirectory.file( grammarDescriptor.getLexerFileName().get() );
} );
parserGrammarFile = getProject().provider( () -> {
final Directory grammarBaseDirectory = antlrSpec.getGrammarBaseDirectory().get();
final Directory grammarDirectory = grammarBaseDirectory.dir( grammarDescriptor.getPackageName().get().replace( '.', '/' ) );
return grammarDirectory.file( grammarDescriptor.getParserFileName().get() );
} );
outputDirectory = getProject().provider( () -> {
final Directory outputBaseDirectory = antlrSpec.getOutputBaseDirectory().get();
return outputBaseDirectory.dir( grammarDescriptor.getPackageName().get().replace( '.', '/' ) );
} );
}
@InputFile
@PathSensitive( PathSensitivity.RELATIVE )
public Provider<RegularFile> getLexerGrammarFile() {
return lexerGrammarFile;
}
@InputFile
@PathSensitive( PathSensitivity.RELATIVE )
public Provider<RegularFile> getParserGrammarFile() {
return parserGrammarFile;
}
@OutputDirectory
public Provider<Directory> getOutputDirectory() {
return outputDirectory;
}
@TaskAction
public void generateLexerAndParser() {
final File outputDir = outputDirectory.get().getAsFile();
outputDir.mkdirs();
generateLexer( outputDir );
generateParser( outputDir );
stripSillyGeneratedFromLines( outputDir, getProject() );
}
private void generateLexer(File outputDir) {
final File lexerFile = getLexerGrammarFile().get().getAsFile();
getProject().getLogger().info(
"Starting Antlr lexer grammar generation `{}` : `{}` -> `{}`",
grammarDescriptor.getName(),
lexerFile.getAbsolutePath(),
outputDir.getAbsolutePath()
);
getProject().javaexec(
(javaExecSpec) -> {
javaExecSpec.setMain( "org.antlr.v4.Tool" );
javaExecSpec.classpath( getProject().getConfigurations().getByName( "antlr" ) );
javaExecSpec.args(
"-o", getProject().relativePath( outputDir.getAbsolutePath() ),
"-long-messages",
lexerFile.getAbsolutePath()
);
}
);
}
private void generateParser(File outputDir) {
final File parserFile = getParserGrammarFile().get().getAsFile();
getProject().getLogger().info(
"Starting Antlr parser grammar generation `{}` : `{}` -> `{}`",
grammarDescriptor.getName(),
parserFile.getAbsolutePath(),
outputDir.getAbsolutePath()
);
getProject().javaexec(
(javaExecSpec) -> {
javaExecSpec.setMain( "org.antlr.v4.Tool" );
javaExecSpec.classpath( getProject().getConfigurations().getByName( "antlr" ) );
javaExecSpec.args(
"-o", getProject().relativePath( outputDir.getAbsolutePath() ),
"-long-messages",
parserFile.getAbsolutePath()
);
if ( grammarDescriptor.generateListener().get() ) {
javaExecSpec.args( "-listener" );
}
if ( grammarDescriptor.generateVisitor().get() ) {
javaExecSpec.args( "-visitor" );
}
}
);
}
}