diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrAllTask.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrAllTask.java deleted file mode 100644 index 7b14bcf..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrAllTask.java +++ /dev/null @@ -1,5 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.plugins.antlr.AntlrTask; - -public class GroowtAntlrAllTask extends AntlrTask {} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrExecTask.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrExecTask.java new file mode 100644 index 0000000..3134e87 --- /dev/null +++ b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrExecTask.java @@ -0,0 +1,95 @@ +package groowt.gradle.antlr; + +import org.gradle.api.DefaultTask; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.file.RegularFileProperty; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; +import org.gradle.api.provider.Provider; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.gradle.process.ExecOperations; +import org.gradle.process.ExecResult; + +import javax.inject.Inject; +import java.io.File; + +public class GroowtAntlrExecTask extends DefaultTask { + + private final ExecOperations execOperations; + private final Provider configurationProvider; + private final RegularFileProperty antlrSourceFile; + private final DirectoryProperty outputDirectory; + private final Property packageName; + private final Property visitor; + + @Inject + public GroowtAntlrExecTask( + ObjectFactory objectFactory, + ExecOperations execOperations, + Provider configurationProvider + ) { + this.execOperations = execOperations; + this.configurationProvider = configurationProvider; + this.antlrSourceFile = objectFactory.fileProperty(); + this.outputDirectory = objectFactory.directoryProperty(); + this.packageName = objectFactory.property(String.class); + this.visitor = objectFactory.property(Boolean.class); + this.visitor.convention(false); + } + + @InputFile + public RegularFileProperty getAntlrSourceFile() { + return this.antlrSourceFile; + } + + @OutputDirectory + public DirectoryProperty getOutputDirectory() { + return this.outputDirectory; + } + + @Option(option = "packageName", description = "The packageName argument for Antlr.") + @Input + public Property getPackageName() { + return this.packageName; + } + + @Option(option = "visitor", description = "Whether Antlr should generate visitor classes or not.") + @Input + public Property getVisitor() { + return this.visitor; + } + + public File resolveOutputFile(String name) { + return new File(this.outputDirectory.get().getAsFile(), name); + } + + @TaskAction + public void doGenerate() { + final Configuration antlrConfiguration = configurationProvider.get(); + + final ExecResult result = this.execOperations.javaexec(javaExecSpec -> { + javaExecSpec.classpath(antlrConfiguration); + javaExecSpec.getMainClass().set("org.antlr.v4.Tool"); + + final String visitorArg = this.visitor.map(isVisitor -> isVisitor ? "-visitor" : "").get(); + if (!visitorArg.isEmpty()) { + javaExecSpec.args(visitorArg); + } + final String packageNameArg = this.packageName.getOrElse(""); + if (!packageNameArg.isEmpty()) { + javaExecSpec.args("-package", packageNameArg); + } + javaExecSpec.args("-o", this.outputDirectory.get().getAsFile().toString()); + + javaExecSpec.args(this.antlrSourceFile.getAsFile().get().toString()); + }); + + result.assertNormalExitValue(); + } + +} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrExtension.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrExtension.java deleted file mode 100644 index ae028a2..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrExtension.java +++ /dev/null @@ -1,34 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.Action; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; - -import javax.inject.Inject; - -public abstract class GroowtAntlrExtension { - - private final SourceSpecContainer sourceSpecs; - - @Inject - public GroowtAntlrExtension(ObjectFactory objectFactory) { - this.sourceSpecs = objectFactory.newInstance(SourceSpecContainer.class, (Action) sourceSpec -> { - sourceSpec.getPackageName().convention(this.getPackageName()); - sourceSpec.getVisitor().convention(this.getVisitor()); - sourceSpec.getIsCompileDependency().convention(true); - sourceSpec.getDebug().convention(false); - }); - } - - public abstract Property getPackageName(); - public abstract Property getVisitor(); - - public SourceSpecContainer getSourceSpecs() { - return this.sourceSpecs; - } - - public void sourceSpecs(Action configure) { - configure.execute(this.getSourceSpecs()); - } - -} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrPlugin.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrPlugin.java index 622022a..8b91c82 100644 --- a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrPlugin.java +++ b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrPlugin.java @@ -1,216 +1,76 @@ package groowt.gradle.antlr; -import org.gradle.api.Action; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.file.FileTree; -import org.gradle.api.model.ObjectFactory; import org.gradle.api.plugins.JavaPluginExtension; -import org.gradle.api.plugins.antlr.AntlrPlugin; -import org.gradle.api.plugins.antlr.AntlrSourceDirectorySet; -import org.gradle.api.plugins.antlr.AntlrTask; -import org.gradle.api.provider.Provider; -import org.gradle.api.tasks.SourceSet; -import org.gradle.api.tasks.TaskContainer; -import org.gradle.api.tasks.compile.JavaCompile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.inject.Inject; import java.io.File; -import java.util.*; -import java.util.stream.Collectors; - -import static groowt.gradle.antlr.GroowtAntlrUtil.*; public final class GroowtAntlrPlugin implements Plugin { - public static final String taskGroup = "groowtAntlr"; + public static final String GROOWT_ANTLR = "groowtAntlr"; - private static final String packageArg = "-package"; - private static final String traceArg = "-trace"; - private static final String visitorArg = "-visitor"; + @Override + public void apply(Project project) { + // register configuration + final var configurationProvider = project.getConfigurations().register(GROOWT_ANTLR, configuration -> { + configuration.setCanBeConsumed(false); + configuration.setCanBeResolved(true); + }); - private final ObjectFactory objectFactory; + // register extension + final var extension = project.getExtensions().create(GROOWT_ANTLR, GroowtAntlrSimpleExtension.class); - @Inject - public GroowtAntlrPlugin(ObjectFactory objectFactory) { - this.objectFactory = objectFactory; - } - - private static NullableProviderList getArguments(SourceSpec sourceSpec) { - final NullableProviderList arguments = new NullableProviderList<>(); - arguments.addCollectionProvider(sourceSpec.getPackageName().map(packageName -> List.of(packageArg, packageName))); - arguments.addProvider(sourceSpec.getVisitor().map(isVisitor -> isVisitor ? visitorArg : null)); - arguments.addProvider(sourceSpec.getDebug().map(isDebug -> isDebug ? traceArg : null)); - return arguments; - } - - // For afterEvaluate! - private static List createAndRegister(Project project, GroowtAntlrExtension extension) { - final TaskContainer taskContainer = project.getTasks(); - return extension.getSourceSpecs().stream().map(sourceSpec -> { - return taskContainer.create( - getGenerateTaskName(sourceSpec), - GroowtAntlrTask.class, - sourceSpec, - (Action) task -> { - task.setGroup(taskGroup); - task.setArguments(getArguments(sourceSpec)); - task.setSource(sourceSpec.getResolvedSource().getSourceFile()); - task.setOutputDirectory( - getOutputDirectory( - project, - sourceSpec.getResolvedSource().getSourceSet(), - sourceSpec.getPackageName() - ) - ); - } + // register tasks for each source file + project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().forEach(sourceSet -> { + // register sourceDirectorySet + final var antlrSourceDirectorySet = project.getObjects().sourceDirectorySet(GROOWT_ANTLR, GROOWT_ANTLR); + antlrSourceDirectorySet.srcDir( + String.join(File.separator, "src", sourceSet.getName(), "antlr") ); - }).toList(); - } + sourceSet.getAllSource().source(antlrSourceDirectorySet); - // For afterEvaluate! - private static void addCompileDependencies(Project project, List tasks) { - tasks.forEach(task -> { - // if it is a compile dependency, add it as input to java source set - final var isCompileDependency = task.getSourceSpec().getIsCompileDependency().get(); - if (isCompileDependency) { - project.getTasks().withType(JavaCompile.class).configureEach(javaCompile -> { - javaCompile.dependsOn(task); + final var baseOutputDir = project.getLayout().getBuildDirectory().dir(String.join( + File.separator, + "generated-src", + "antlr", + sourceSet.getName() + )); + + sourceSet.getJava().srcDir(baseOutputDir); + + final var sourceSetTasks = antlrSourceDirectorySet.getFiles().stream() + .filter(GroowtAntlrUtil::isAntlrFile) + .map(file -> { + final var taskProvider = project.getTasks().register( + GroowtAntlrUtil.getGenerateTaskName(sourceSet, file), + GroowtAntlrExecTask.class, + configurationProvider + ); + taskProvider.configure(task -> { + task.setGroup(GROOWT_ANTLR); + task.getAntlrSourceFile().set(file); + task.getOutputDirectory().convention( + baseOutputDir.flatMap(base -> base.dir( + task.getPackageName().map(packageName -> + packageName.replace(".", File.separator) + ) + )) + ); + task.getVisitor().convention(extension.getVisitor()); + task.getPackageName().convention(extension.getPackageName()); + }); + return taskProvider; + }) + .toList(); + + if (!sourceSetTasks.isEmpty()) { + project.getTasks().register(sourceSet.getTaskName("generate", "AllAntlr"), task -> { + task.dependsOn(sourceSetTasks); + task.setGroup(GROOWT_ANTLR); }); } }); } - private record GenerateAllSpec( - @Nullable FileTree source, - @NotNull List args, - @NotNull Provider packageName - ) {} - - private GenerateAllSpec getBlankGenerateAllSpec() { - return new GenerateAllSpec(null, List.of(), this.objectFactory.property(String.class)); - } - - private static GenerateAllSpec getGenerateAllSpecFromTask(GroowtAntlrTask task) { - return new GenerateAllSpec(task.getSource(), task.getArguments(), task.getSourceSpec().getPackageName()); - } - - private static @Nullable FileTree combineFileTrees(@Nullable FileTree f0, @Nullable FileTree f1) { - if (f0 != null && f1 != null) { - return f0.plus(f1); - } else if (f0 != null) { - return f0; - } else { - return f1; // null - } - } - - private static List combineArguments(List a0, List a1) { - final List result = new ArrayList<>(a0); - final Iterator a1Iter = a1.iterator(); - while (a1Iter.hasNext()) { - final String arg = a1Iter.next(); - if (arg.equals(packageArg) && result.contains(packageArg)) { - if (!a1Iter.hasNext()) { - throw new IllegalStateException("shouldn't get here"); - } - final String a0PackageName = result.get(result.indexOf(arg) + 1); - final String a1PackageName = a1Iter.next(); - if (!a0PackageName.equals(a1PackageName)) { - throw new IllegalArgumentException("Cannot have separate package arguments for two files from the same source set."); - } - } else if (!result.contains(arg)) { - result.add(arg); - } - } - return result; - } - - private static Provider combinePackageNames(Provider p0, Provider p1) { - return p0.zip(p1, (pn0, pn1) -> { - if (!pn0.equals(pn1)) { - throw new IllegalArgumentException("Cannot have separate package names for two files from the same source set."); - } - return pn0; - }); - } - - private static GenerateAllSpec combineGenerateAllSpecs(GenerateAllSpec s0, GenerateAllSpec s1) { - return new GenerateAllSpec( - combineFileTrees(s0.source(), s1.source()), - combineArguments(s0.args(), s1.args()), - combinePackageNames(s0.packageName(), s1.packageName()) - ); - } - - // For afterEvaluate! - private static void addGenerateAllTasks(Project project, List tasks) { - final Map> sourceSetToTasks = tasks.stream().collect(Collectors.groupingBy( - task -> task.getSourceSpec().getResolvedSource().getSourceSet() - )); - - final Map sourceSetToSpec = new HashMap<>(); - sourceSetToTasks.forEach((sourceSet, sourceSetTasks) -> { - List specs = sourceSetTasks.stream().map(task -> - new GenerateAllSpec(task.getSource(), task.getArguments(), task.getSourceSpec().getPackageName()) - ).toList(); - specs.stream().reduce(GroowtAntlrPlugin::combineGenerateAllSpecs).ifPresent(allSpec -> { - sourceSetToSpec.put(sourceSet, allSpec); - }); - }); - - sourceSetToSpec.forEach((sourceSet, spec) -> { - project.getTasks().register( - getGenerateAllTaskName(sourceSet), - GroowtAntlrAllTask.class, - task -> { - task.setGroup(taskGroup); - if (spec.source() != null) { - task.setSource(spec.source()); - } - task.setArguments(spec.args()); - task.setOutputDirectory( - getOutputDirectory(project, sourceSet, spec.packageName()).get().getAsFile() - ); - } - ); - }); - } - - @Override - public void apply(Project project) { - project.getPluginManager().apply(AntlrPlugin.class); - - // undo the antlr plugin creating its own tasks - project.getTasks().withType(AntlrTask.class, antlrTask -> { - if (!(antlrTask instanceof GroowtAntlrTask || antlrTask instanceof GroowtAntlrAllTask)) { - antlrTask.setEnabled(false); - } - }); - - // create extension - final GroowtAntlrExtension extension = project.getExtensions().create("groowtAntlr", GroowtAntlrExtension.class); - extension.getPackageName().convention(""); - extension.getVisitor().convention(false); - - // find all antlr files first and add them to extension - project.getExtensions().getByType(JavaPluginExtension.class).getSourceSets().forEach(sourceSet -> { - final Set antlrFiles = sourceSet.getExtensions().getByType(AntlrSourceDirectorySet.class).getFiles(); - for (final File antlrFile : antlrFiles) { - if (isAntlrSourceFile(antlrFile)) { - extension.getSourceSpecs().register(sourceSet, antlrFile); - } - } - }); - - // after evaluate, generate tasks for each registered sourceSpec - project.afterEvaluate(postEvaluateProject -> { - final List tasks = createAndRegister(postEvaluateProject, extension); - addCompileDependencies(project, tasks); - addGenerateAllTasks(project, tasks); - }); - } - } diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrSimpleExtension.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrSimpleExtension.java new file mode 100644 index 0000000..68a7a80 --- /dev/null +++ b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrSimpleExtension.java @@ -0,0 +1,8 @@ +package groowt.gradle.antlr; + +import org.gradle.api.provider.Property; + +public interface GroowtAntlrSimpleExtension { + Property getPackageName(); + Property getVisitor(); +} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrTask.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrTask.java deleted file mode 100644 index a255954..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrTask.java +++ /dev/null @@ -1,70 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.Action; -import org.gradle.api.file.Directory; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.antlr.AntlrTask; -import org.gradle.api.provider.Provider; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Nested; -import org.gradle.api.tasks.OutputDirectory; -import org.jetbrains.annotations.NotNull; - -import javax.inject.Inject; -import java.io.File; -import java.util.List; - -public class GroowtAntlrTask extends AntlrTask { - - private final ObjectFactory objectFactory; - private final SourceSpec sourceSpec; - - private Provider outputDirectory; - private NullableProviderList arguments; - - @Inject - public GroowtAntlrTask(ObjectFactory objectFactory, SourceSpec sourceSpec, Action configure) { - this.objectFactory = objectFactory; - this.sourceSpec = sourceSpec; - configure.execute(this); - } - - @Nested - public SourceSpec getSourceSpec() { - return this.sourceSpec; - } - - @Override - public @NotNull File getOutputDirectory() { - return this.outputDirectory.get().getAsFile(); - } - - @Override - public void setOutputDirectory(@NotNull File outputDirectory) { - final DirectoryProperty directoryProperty = this.objectFactory.directoryProperty(); - directoryProperty.set(outputDirectory); - this.outputDirectory = directoryProperty; - } - - public void setOutputDirectory(Provider outputDirectoryProvider) { - this.outputDirectory = outputDirectoryProvider; - } - - @Override - @Internal - public @NotNull List getArguments() { - return this.arguments.getElements(); - } - - @Override - public void setArguments(@NotNull List arguments) { - this.arguments = new NullableProviderList<>(); - this.arguments.addAllElements(arguments); - } - - public void setArguments(@NotNull NullableProviderList arguments) { - this.arguments = arguments; - } - -} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrUtil.java b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrUtil.java index 370c351..083a963 100644 --- a/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrUtil.java +++ b/buildSrc/src/main/java/groowt/gradle/antlr/GroowtAntlrUtil.java @@ -1,147 +1,35 @@ package groowt.gradle.antlr; -import org.gradle.api.Project; -import org.gradle.api.file.Directory; -import org.gradle.api.file.SourceDirectorySet; -import org.gradle.api.provider.Provider; import org.gradle.api.tasks.SourceSet; import java.io.File; -import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; public final class GroowtAntlrUtil { - public static final List antlrFileExtensions = List.of("g4", "g"); + public static final List antlrExtensions = List.of("g4", "g"); - private static final Pattern extensionPattern = Pattern.compile("(?.*)\\.(?.*)$"); + private static final Pattern nameAndExtension = Pattern.compile("(?.*)\\.(?.*)$"); - public static File resolve(File from, File to) { - return from.toPath().resolve(to.toPath()).toFile(); - } - - public static File relativize(File from, File to) { - return from.toPath().relativize(to.toPath()).toFile(); - } - - public static boolean isAntlrSourceFile(File file) { - final var m = extensionPattern.matcher(file.getName()); - if (m.matches()) { - return antlrFileExtensions.contains(m.group("ext")); - } else { - throw new IllegalArgumentException("Cannot determine extension of file: " + file); - } - } - - public static List sourceFileToIdentifierParts(File sourceDir, File sourceFile) { - final var relative = getRelativePathToSourceFile(sourceDir, sourceFile); - final List result = new ArrayList<>(); - for (int i = 0; i < relative.getNameCount(); i++) { - final var name = relative.getName(i); - final var m = extensionPattern.matcher(name.toString()); - if (m.matches()) { - result.add(m.group("name")); - } else { - result.add(name.toString()); + public static boolean isAntlrFile(File file) { + for (final var antlrExtension : antlrExtensions) { + if (file.getName().endsWith(antlrExtension)) { + return true; } } - return result; + return false; } - private static Path getRelativePathToSourceFile(File sourceDir, File sourceFile) { - if (!sourceDir.isAbsolute()) { - throw new IllegalArgumentException("sourceDir must be absolute, given: " + sourceDir); - } - if (sourceFile.isAbsolute()) { - final var sourceDirPath = sourceDir.toPath(); - final var sourceFilePath = sourceFile.toPath(); - return sourceDirPath.relativize(sourceFilePath); + public static String getGenerateTaskName(SourceSet sourceSet, File sourceFile) { + final var matcher = nameAndExtension.matcher(sourceFile.getName()); + if (matcher.matches()) { + return sourceSet.getTaskName("generate", matcher.group("name")); } else { - return sourceFile.toPath(); + throw new IllegalArgumentException("Cannot determine source name for " + sourceFile); } } - public static String getSourceIdentifier(ResolvedSource resolvedSource) { - final List parts = new ArrayList<>(); - if (!resolvedSource.getSourceSet().getName().equals("main")) { - parts.add(resolvedSource.getSourceSet().getName()); - } - parts.addAll(sourceFileToIdentifierParts(resolvedSource.getSourceDir(), resolvedSource.getSourceFile())); - final List capitalizedParts = parts.stream() - .map(part -> { - final var first = part.substring(0, 1); - final var rest = part.substring(1); - return first.toUpperCase() + rest; - }) - .toList(); - return String.join("", capitalizedParts); - } - - public static Provider getOutputDirectory( - Project project, - SourceSet sourceSet, - Provider packageNameProvider - ) { - return project.getLayout().getBuildDirectory().flatMap(buildDir -> { - return buildDir.dir(packageNameProvider.map(packageName -> { - return String.join(File.separator, List.of( - "generated-src", - "antlr", - sourceSet.getName(), - packageName.replace(".", File.separator) - )); - })); - }); - } - - public static String getGenerateTaskName(SourceSpec sourceSpec) { - return sourceSpec.getResolvedSource().getSourceSet().getTaskName("generate", sourceSpec.getIdentifier()); - } - - public static String getGenerateAllTaskName(SourceSet sourceSet) { - return sourceSet.getTaskName("generate", "AllAntlr"); - } - - public static ResolvedSource resolveSource( - Project project, - SourceSet sourceSet, - SourceDirectorySet sourceDirectorySet, - File sourceFile - ) { - if (!isAntlrSourceFile(sourceFile)) { - throw new IllegalArgumentException( - "The given source file " + sourceFile + " is not a recognized antlr file (bad extension)." - ); - } - - final List potentialSrcDirs = new ArrayList<>(); - - if (sourceFile.isAbsolute()) { - for (final File srcDir : sourceDirectorySet.getSrcDirs()) { - if (sourceFile.getPath().startsWith(srcDir.getPath())) { - potentialSrcDirs.add(srcDir); - } - } - } else { - for (final File srcDir : sourceDirectorySet.getSrcDirs()) { - if (resolve(srcDir, sourceFile).exists()) { - potentialSrcDirs.add(srcDir); - } - } - } - - if (potentialSrcDirs.size() > 1) { - throw new IllegalArgumentException("Multiple source directories in " + sourceDirectorySet.getName() + " contain a source file " + sourceFile); - } else if (potentialSrcDirs.isEmpty()) { - throw new IllegalArgumentException("No directories in " + sourceDirectorySet.getName() + " contain a source file " + sourceFile); - } - - final File srcDir = potentialSrcDirs.getFirst(); - return new ResolvedSource(project, sourceSet, srcDir, resolve(srcDir, sourceFile)); - } - private GroowtAntlrUtil() {} } diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/NullableProviderList.java b/buildSrc/src/main/java/groowt/gradle/antlr/NullableProviderList.java deleted file mode 100644 index 8caf2ec..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/NullableProviderList.java +++ /dev/null @@ -1,160 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.provider.Provider; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.function.Supplier; - -public final class NullableProviderList { - - private sealed interface Element extends Iterable permits BareElement, ElementProvider, CollectionProvider { - boolean isPresent(); - } - - private static final class BareElement implements Element { - - private final T element; - - public BareElement(T element) { - this.element = element; - } - - @Override - public boolean isPresent() { - return this.element != null; - } - - @NotNull - @Override - public Iterator iterator() { - return new Iterator<>() { - private boolean hasNext = true; - - @Override - public boolean hasNext() { - return this.hasNext; - } - - @Override - public T next() { - if (!this.hasNext) throw new IllegalStateException(); - this.hasNext = false; - return BareElement.this.element; - } - }; - } - - } - - private static final class ElementProvider implements Element { - - private final Provider provider; - - public ElementProvider(Provider provider) { - this.provider = provider; - } - - @Override - public boolean isPresent() { - return this.provider.isPresent(); - } - - @NotNull - @Override - public Iterator iterator() { - return new Iterator() { - private boolean hasNext = true; - - @Override - public boolean hasNext() { - return this.hasNext; - } - - @Override - public T next() { - if (!this.hasNext) throw new IllegalStateException(); - this.hasNext = false; - return ElementProvider.this.provider.get(); - } - }; - } - - } - - private static final class CollectionProvider implements Element { - - private final Provider> collectionProvider; - - public CollectionProvider(Provider> collectionProvider) { - this.collectionProvider = collectionProvider; - } - - @Override - public boolean isPresent() { - return this.collectionProvider.isPresent(); - } - - @NotNull - @Override - public Iterator iterator() { - return this.collectionProvider.get().iterator(); - } - - } - - private final List> elements = new ArrayList<>(); - - public void addElement(@Nullable T element) { - this.elements.add(new BareElement<>(element)); - } - - public void addProvider(Provider elementProvider) { - this.elements.add(new ElementProvider<>(elementProvider)); - } - - public void addCollectionProvider(Provider> collectionProvider) { - this.elements.add(new CollectionProvider<>(collectionProvider)); - } - - public void addAllElements(Collection elements) { - for (final T element : elements) { - this.elements.add(new BareElement<>(element)); - } - } - - public void addAllProviders(Collection> providers) { - for (final Provider provider : providers) { - this.elements.add(new ElementProvider<>(provider)); - } - } - - public void addAllCollectionProviders(Collection>> collectionProviders) { - for (final Provider> collectionProvider : collectionProviders) { - this.elements.add(new CollectionProvider<>(collectionProvider)); - } - } - - public List getElements() { - return this.getElements(null); - } - - public List getElements(@Nullable Supplier onNullElement) { - final List result = new ArrayList<>(); - for (final Element element : this.elements) { - if (element.isPresent()) { - for (final T t : element) { - result.add(t); - } - } else if (onNullElement != null) { - result.add(onNullElement.get()); - } - } - return result; - } - -} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/ResolvedSource.java b/buildSrc/src/main/java/groowt/gradle/antlr/ResolvedSource.java deleted file mode 100644 index a178c90..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/ResolvedSource.java +++ /dev/null @@ -1,90 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.Project; -import org.gradle.api.tasks.InputDirectory; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.SourceSet; - -import java.io.File; -import java.util.List; - -import static groowt.gradle.antlr.GroowtAntlrUtil.relativize; - -public final class ResolvedSource { - - private final Project project; - private final SourceSet sourceSet; - private final File sourceDir; - private final File sourceFile; - - public ResolvedSource(Project project, SourceSet sourceSet, File sourceDir, File sourceFile) { - this.project = project; - this.sourceSet = sourceSet; - this.sourceDir = sourceDir; - this.sourceFile = sourceFile; - } - - @Internal - public Project getProject() { - return this.project; - } - - @Internal - public SourceSet getSourceSet() { - return this.sourceSet; - } - - @InputDirectory - public File getSourceDir() { - return this.sourceDir; - } - - @InputFile - public File getSourceFile() { - return this.sourceFile; - } - - private String relativizeSourceDir() { - return relativize(this.project.getProjectDir(), this.sourceDir).toString(); - } - - private String relativeSourceFile() { - return relativize(this.sourceDir, this.sourceFile).toString(); - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } else if (obj instanceof ResolvedSource other) { - return this.project.equals(other.project) - && this.sourceSet.equals(other.sourceSet) - && this.sourceDir.equals(other.sourceDir) - && this.sourceFile.equals(other.sourceFile); - } else { - return false; - } - } - - @Override - public int hashCode() { - int result = this.project.hashCode(); - result = 31 * result + this.sourceSet.hashCode(); - result = 31 * result + this.sourceDir.hashCode(); - result = 31 * result + this.sourceFile.hashCode(); - return result; - } - - @Override - public String toString() { - return "ResolvedSource(" - + String.join(", ", List.of( - "sourceSet: " + this.sourceSet.getName(), - "sourceDir: " + this.relativizeSourceDir(), - "sourceFile: " + this.relativeSourceFile() - )) - + ")"; - } - -} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/SourceSpec.java b/buildSrc/src/main/java/groowt/gradle/antlr/SourceSpec.java deleted file mode 100644 index b8075a0..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/SourceSpec.java +++ /dev/null @@ -1,43 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.provider.Property; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.Nested; - -import javax.inject.Inject; - -public abstract class SourceSpec { - - private final String identifier; - private final ResolvedSource resolvedSource; - - @Inject - public SourceSpec(String identifier, ResolvedSource resolvedSource) { - this.identifier = identifier; - this.resolvedSource = resolvedSource; - } - - @Internal - public String getIdentifier() { - return this.identifier; - } - - @Nested - public ResolvedSource getResolvedSource() { - return this.resolvedSource; - } - - @Input - public abstract Property getIsCompileDependency(); - - @Input - public abstract Property getDebug(); - - @Input - public abstract Property getPackageName(); - - @Input - public abstract Property getVisitor(); - -} diff --git a/buildSrc/src/main/java/groowt/gradle/antlr/SourceSpecContainer.java b/buildSrc/src/main/java/groowt/gradle/antlr/SourceSpecContainer.java deleted file mode 100644 index ceca4ff..0000000 --- a/buildSrc/src/main/java/groowt/gradle/antlr/SourceSpecContainer.java +++ /dev/null @@ -1,89 +0,0 @@ -package groowt.gradle.antlr; - -import org.gradle.api.Action; -import org.gradle.api.Project; -import org.gradle.api.internal.CollectionCallbackActionDecorator; -import org.gradle.api.internal.DefaultDomainObjectSet; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.antlr.AntlrSourceDirectorySet; -import org.gradle.api.tasks.SourceSet; - -import javax.inject.Inject; -import java.io.File; - -import static groowt.gradle.antlr.GroowtAntlrUtil.getSourceIdentifier; -import static groowt.gradle.antlr.GroowtAntlrUtil.resolveSource; - -public class SourceSpecContainer extends DefaultDomainObjectSet { - - private final Project project; - private final ObjectFactory objectFactory; - private final Action applyConventions; - - @Inject - public SourceSpecContainer(Project project, ObjectFactory objectFactory, Action applyConventions) { - super(SourceSpec.class, CollectionCallbackActionDecorator.NOOP); - this.project = project; - this.objectFactory = objectFactory; - this.applyConventions = applyConventions; - } - - /** - * @param sourceFilePaths instances of File or String - */ - public void ignore(SourceSet sourceSet, Object... sourceFilePaths) { - for (final Object sourceFilePath : sourceFilePaths) { - switch (sourceFilePath) { - case File f -> this.ignoreFile(sourceSet, f); - case String s -> this.ignoreFile(sourceSet, new File(s)); - default -> throw new IllegalArgumentException("Can only ignore Files or Strings, given: " + sourceFilePath); - } - } - } - - private void ignoreFile(SourceSet sourceSet, File target) { - this.removeIf(potentialSourceSpec -> { - final SourceSet potentialSourceSet = potentialSourceSpec.getResolvedSource().getSourceSet(); - if (!sourceSet.equals(potentialSourceSet)) { - return false; - } - final var antlrSourceDirectorySet = sourceSet.getExtensions().getByType(AntlrSourceDirectorySet.class); - final ResolvedSource toIgnore = resolveSource(this.project, sourceSet, antlrSourceDirectorySet, target); - final File potentialSourceFile = potentialSourceSpec.getResolvedSource().getSourceFile(); - return toIgnore.getSourceFile().equals(potentialSourceFile); - }); - } - - public void register(SourceSet sourceSet, String sourceFilePath) { - this.register(sourceSet, new File(sourceFilePath), sourceSpec -> {}); // no-op action - } - - public void register(SourceSet sourceSet, File sourceFile) { - this.register(sourceSet, sourceFile, sourceSpec -> {}); - } - - public void register(SourceSet sourceSet, String sourceFilePath, Action action) { - this.register(sourceSet, new File(sourceFilePath), action); - } - - public void register(SourceSet sourceSet, File sourceFile, Action action) { - final var antlrSourceDirectorySet = sourceSet.getExtensions().getByType(AntlrSourceDirectorySet.class); - final var resolvedSource = resolveSource(this.project, sourceSet, antlrSourceDirectorySet, sourceFile); - - final String identifier = getSourceIdentifier(resolvedSource); - final var specOptional = this.stream().filter(sourceSpec -> sourceSpec.getIdentifier().equals(identifier)).findFirst(); - - if (specOptional.isPresent()) { - // we already have one, so find and run the action against it - final var spec = specOptional.get(); - action.execute(spec); - } else { - // create a new one - final var spec = this.objectFactory.newInstance(SourceSpec.class, identifier, resolvedSource); - this.applyConventions.execute(spec); - action.execute(spec); - this.add(spec); - } - } - -} diff --git a/buildSrc/src/test/groovy/groowt/gradle/antlr/GroowtAntlrPluginTests.groovy b/buildSrc/src/test/groovy/groowt/gradle/antlr/GroowtAntlrPluginTests.groovy deleted file mode 100644 index 3636493..0000000 --- a/buildSrc/src/test/groovy/groowt/gradle/antlr/GroowtAntlrPluginTests.groovy +++ /dev/null @@ -1,53 +0,0 @@ -//file:noinspection ConfigurationAvoidance -package groowt.gradle.antlr - -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.plugins.antlr.AntlrPlugin -import org.gradle.testfixtures.ProjectBuilder -import org.junit.jupiter.api.Disabled -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.function.Executable - -import static org.junit.jupiter.api.Assertions.assertDoesNotThrow - -class GroowtAntlrPluginTests { - - @Disabled('TODO: figure out why register(SourceSet, String) is not working.') - @Test - void smokeScreen() { - def projectDir = File.createTempDir() - new FileTreeBuilder(projectDir).tap { - dir(['src', 'main', 'antlr'].join(File.separator)) { - file('MyGrammar.g4') { - write("parser grammar MyGrammar;") - } - } - } - def project = ProjectBuilder.builder().with { - withProjectDir(projectDir) - build() - } - - project.pluginManager.with { - apply(JavaPlugin) - apply(AntlrPlugin) - apply(GroowtAntlrPlugin) - } - - def mainSourceSet = project.extensions.getByType(JavaPluginExtension).sourceSets.findByName('main') - - project.extensions.getByType(GroowtAntlrExtension).sourceSpecs.with { - register(mainSourceSet, 'MyGrammar.g4') - } - - def findTask = { - project.tasks.named('generateMyGrammar', GroowtAntlrTask) - } as Executable - - assertDoesNotThrow(findTask) { - "Could not find task 'generateMyGrammar' (all tasks: ${project.tasks*.name.join(', ')})" - } - } - -} diff --git a/buildSrc/src/test/groovy/groowt/gradle/antlr/GroowtAntlrUtilTests.groovy b/buildSrc/src/test/groovy/groowt/gradle/antlr/GroowtAntlrUtilTests.groovy deleted file mode 100644 index 46fd2ba..0000000 --- a/buildSrc/src/test/groovy/groowt/gradle/antlr/GroowtAntlrUtilTests.groovy +++ /dev/null @@ -1,132 +0,0 @@ -//file:noinspection ConfigurationAvoidance -package groowt.gradle.antlr - -import groovy.transform.TupleConstructor -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.plugins.antlr.AntlrPlugin -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.SourceSet -import org.gradle.testfixtures.ProjectBuilder -import org.junit.jupiter.api.DynamicTest -import org.junit.jupiter.api.TestFactory - -import java.nio.file.Path - -import static groowt.gradle.antlr.GroowtAntlrUtil.* -import static org.junit.jupiter.api.Assertions.assertEquals -import static org.junit.jupiter.api.Assertions.assertIterableEquals -import static org.junit.jupiter.api.DynamicTest.dynamicTest - -class GroowtAntlrUtilTests { - - @TupleConstructor - private static class SourceFileToIdentifierTestSpec { - File srcDir - File srcFile - List expected - } - - @TestFactory - Collection sourceFileToIdentifierPartsTests() { - def srcDir = File.createTempDir() - def getSpec = { String path, List expected -> - new SourceFileToIdentifierTestSpec(srcDir, new File(path), expected) - } - - def specs = [ - getSpec('MyGrammar.g4', ['MyGrammar']), - getSpec('subDir/MyGrammar.g4', ['subDir', 'MyGrammar']), - getSpec('subDir/subSubDir/MyGrammar.g4', ['subDir', 'subSubDir', 'MyGrammar']), - getSpec('My.grammar.g4', ['My.grammar']) - ] - - return specs.collect { spec -> - dynamicTest(spec.srcFile.toString()) { - def actual = sourceFileToIdentifierParts(spec.srcDir, spec.srcFile) - assertIterableEquals(spec.expected, actual) { - "Unexpected result: ${actual}" - } - } - } - } - - @TestFactory - Collection getSourceIdentifierTests() { - def projectDir = File.createTempDir() - File srcDir - new FileTreeBuilder(projectDir).with { - srcDir = dir(['src', 'main', 'antlr'].join(File.separator)) - } - - def project = ProjectBuilder.builder() - .withProjectDir(projectDir) - .build() - project.pluginManager.with { - apply(JavaPlugin) - apply(AntlrPlugin) - } - def mainSourceSet = project.extensions.getByType(JavaPluginExtension) - .sourceSets.getByName('main') - - def getResolvedSource = { String path -> - new ResolvedSource(project, mainSourceSet, srcDir, new File(path)) - } - - Closure> getSpec = { String expected, String path -> - new Tuple2(expected, getResolvedSource(path)) - } - - def specs = [ - getSpec('MyGrammar', 'MyGrammar.g4'), - getSpec('SubDirMyGrammar', 'subDir/MyGrammar.g4') - ] - - return specs.collect { spec -> - dynamicTest(spec.v2.sourceFile.toString()) { - def actual = getSourceIdentifier(spec.v2) - assertEquals(spec.v1, actual) - } - } - } - - @TestFactory - Collection getOutputDirTests() { - def project = ProjectBuilder.builder().build() - project.pluginManager.tap { - apply(JavaPlugin) - apply(AntlrPlugin) - } - project.layout.buildDirectory.set(new File('build')) - - def mainSourceSet = project.extensions.getByType(JavaPluginExtension) - .sourceSets.getByName('main') - - Closure>> getSpec = { String givenPackageName -> - def expectedPackagePath = givenPackageName.replace('.', File.separator) - def expected = Path.of('build', ['generated-src', 'antlr', mainSourceSet.name, expectedPackagePath] as String[]) - def packageProperty = project.objects.property(String).tap { set(givenPackageName) } - new Tuple3(expected, mainSourceSet, packageProperty) - } - - List>> specs = [ - getSpec('antlr.one.two.three'), // build/generated-src/antlr/main/antlr/one/two/three - getSpec('test.antlr'), // build/generated-src/antlr/main/test/antlr - getSpec('antlr'), // build/generated-src/antlr/main/antlr - getSpec('') // build/generated-src/antlr/main - ] - - def projectPath = project.layout.projectDirectory.asFile.toPath() - - return specs.collect { spec -> - def givenPackageName = spec.v3.get() - dynamicTest(givenPackageName.empty ? '' : givenPackageName) { - def actualPath = getOutputDirectory(project, spec.v2, spec.v3) - .get().asFile.toPath() - def result = projectPath.relativize(actualPath) - assertEquals(spec.v1, result) - } - } - } - -} diff --git a/web-views/build.gradle b/web-views/build.gradle index 83146cb..855d169 100644 --- a/web-views/build.gradle +++ b/web-views/build.gradle @@ -1,11 +1,10 @@ import groovy.transform.NullCheck -import groowt.gradle.antlr.GroowtAntlrTask +import groowt.gradle.antlr.GroowtAntlrExecTask plugins { id 'java' id 'java-library' id 'groovy' - id 'antlr' id 'GroowtAntlrPlugin' id 'org.jetbrains.kotlin.jvm' id 'java-test-fixtures' @@ -49,7 +48,7 @@ dependencies { project(':di'), project(':extensible') ) - antlr libs.antlr + groowtAntlr libs.antlr runtimeOnly libs.log4j.slf4jBinding def testLibs = [ @@ -82,23 +81,19 @@ groowtAntlr { visitor = true } -// Must be temporarily afterEvaluate unless we switch the GroowtAntlr plugin -// to eagerly create the tasks upon plugin application -afterEvaluate { - tasks.named('generateWebViewComponentsLexerBase', GroowtAntlrTask) { task -> - doLast { - def pattern = ~/public class WebViewComponentsLexerBase(.*)/ - def lexerSource = new File(task.outputDirectory, 'WebViewComponentsLexerBase.java') - def outLines = lexerSource.readLines().collect { - def matcher = pattern.matcher(it) - if (matcher.matches()) { - return 'public abstract class WebViewComponentsLexerBase' + matcher.group(1) - } else { - return it - } +tasks.named('generateWebViewComponentsLexerBase', GroowtAntlrExecTask) { task -> + doLast { + def pattern = ~/public class WebViewComponentsLexerBase(.*)/ + def lexerSource = task.resolveOutputFile 'WebViewComponentsLexerBase.java' + def outLines = lexerSource.readLines().collect { + def matcher = pattern.matcher(it) + if (matcher.matches()) { + return 'public abstract class WebViewComponentsLexerBase' + matcher.group(1) + } else { + return it } - lexerSource.write(outLines.join('\n')) } + lexerSource.write(outLines.join('\n')) } } @@ -178,11 +173,6 @@ tasks.register('cleanBin', Delete) { delete file('bin') } -// Hacky -tasks.named('compileKotlin').configure { - dependsOn 'generateLexerFragments', 'generateWebViewComponentsLexerBase', 'generateWebViewComponentsParser' -} - test { jvmArgs '-XX:+EnableDynamicAgentLoading' // for mockito/bytebuddy testLogging.showStandardStreams = true