Skip to content

Simplify Gradle scripts that use AntlrKotlinTask #221

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 22 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,17 @@ To start using ANTLR Kotlin:
Depending on `cleanGenerateKotlinGrammarSource` ensures the `.tokens` files are always fresh,
and we do not end up with out-of-sync lexers and parsers.

5. Instruct the Kotlin compilation tasks to depend on the grammar generation.

```kotlin
tasks.withType<KotlinCompile<*>> {
dependsOn(generateKotlinGrammarSource)
}

```

6. Register the `build/generatedAntlr` directory as part of the common source set.
5. Use the `generateKotlinGrammarSource` task to provide an additional source directory
Copy link
Member

@ftomassetti ftomassetti Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we specify such source directory? Do we use a standard path or should it be expressly defined by the user?

Copy link
Contributor Author

@lppedd lppedd Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ftomassetti I've recently learnt that Gradle is able to infer the directory path automatically thanks to the @OutputDirectory-annotated property in the custom task. So inputting the task instance to srcDir is all what's needed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but the way in which it is phrased I interpreted to mean that when using the generateKotlinGrammarSource we will specify the source directory we want. While instead I think that what happens is that the generateKotlinGrammarSource target will generate Kotlin code for the lexer, parser, or combined grammars found under src/main/antlr and all the generated code will be produced in a certain directory (which is pre-defined in the gradle plugin and the user cannot pick). What the user needs to do is to instruct gradle to consider that directory where the code is generated as a source directory.

By the way, I wonder if we should do that automatically by default.

So instead of:

Use the generateKotlinGrammarSource task to provide an additional source directory to the commonMain source set. Gradle will then infer the task dependency automatically.

I would write:

Specify that the output directory of the `generateKotlinGrammarSource` task should be included as a source directory in the commonMain source set. In Gradle, you can do that by simply reference the task itself, and Gradle will automatically infer the associated output directory and include such directory in the source set.

What do you think?

Copy link
Contributor Author

@lppedd lppedd Jun 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ftomassetti makes sense to update that sentence in the readme. Your version sounds better, indeed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, then I will merge it and then change the sentence

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thanks!

to the `commonMain` source set. Gradle will then infer the task dependency automatically.

```kotlin
kotlin {
sourceSets {
commonMain {
kotlin {
srcDir(layout.buildDirectory.dir("generatedAntlr"))
srcDir(generateKotlinGrammarSource)
}
...
}
}
}
Expand All @@ -146,33 +139,25 @@ To start using ANTLR Kotlin:
## Benchmarks

The [antlr-kotlin-benchmarks](./antlr-kotlin-benchmarks) module contains benchmarking code
targeting JVM, JS and WebAssembly.
The scenario has been adapted from [antlr4ng][2].

- JVM benchmarks use [kotlinx-benchmark][3], which under the hood uses JMH.

To run benchmarks, use:

```
./gradlew :antlr-kotlin-benchmarks:jvmBenchmark
```

- JS, WebAssembly and Native benchmarks cannot use kotlinx-benchmark currently.
Instead, they use a test case which re-uses the benchmark code.
for JVM, JS, WebAssembly and Native targets.

To run benchmarks, remove the `@Ignore` annotation on `ManualMySQLBenchmarks`, and use:

```
./gradlew :antlr-kotlin-benchmarks:jsTest
```
or
```
./gradlew :antlr-kotlin-benchmarks:wasmJsTest
```
or
```
./gradlew :antlr-kotlin-benchmarks:mingwX64Test
```
The benchmark scenario has been adapted from [antlr4ng][2].
To run benchmarks, use:
```
./gradlew :antlr-kotlin-benchmarks:jvmBenchmark
```
or
```
./gradlew :antlr-kotlin-benchmarks:jsBenchmark
```
or
```
./gradlew :antlr-kotlin-benchmarks:wasmJsBenchmark
```
or
```
./gradlew :antlr-kotlin-benchmarks:mingwX64Benchmark
```

## Maven Central Publication

Expand Down
69 changes: 22 additions & 47 deletions antlr-kotlin-benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import com.strumenta.antlrkotlin.gradle.AntlrKotlinTask
import com.strumenta.antlrkotlin.gradle.ext.targetsNative
import kotlinx.benchmark.gradle.JvmBenchmarkTarget
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask

plugins {
id("strumenta.multiplatform")
Expand Down Expand Up @@ -36,6 +34,27 @@ strumentaMultiplatform {
}
}

val generateKotlinGrammarSource = tasks.register<AntlrKotlinTask>("generateKotlinGrammarSource") {
dependsOn("cleanGenerateKotlinGrammarSource")

// Only include *.g4 files. This allows tools (e.g., IDE plugins)
// to generate temporary files inside the base path
source = fileTree(layout.projectDirectory.dir("antlr")) {
include("**/*.g4")
}

val pkgName = "com.strumenta.antlrkotlin.benchmarks.generated"
packageName = pkgName

// We want visitors alongside listeners.
// The Kotlin target language is implicit, as is the file encoding (UTF-8)
arguments = listOf("-visitor")

// Generated files are outputted inside build/generatedAntlr
val outDir = "generatedAntlr/${pkgName.replace(".", "/")}"
outputDirectory = layout.buildDirectory.dir(outDir).get().asFile
}

kotlin {
js {
nodejs()
Expand All @@ -60,7 +79,7 @@ kotlin {
sourceSets {
commonMain {
kotlin {
srcDir(layout.buildDirectory.dir("generatedAntlr"))
srcDir(generateKotlinGrammarSource)
}

dependencies {
Expand Down Expand Up @@ -99,47 +118,3 @@ benchmark {
register("mingwX64")
}
}

tasks {
val generateKotlinGrammarSource = register<AntlrKotlinTask>("generateKotlinGrammarSource") {
dependsOn("cleanGenerateKotlinGrammarSource")

// Only include *.g4 files. This allows tools (e.g., IDE plugins)
// to generate temporary files inside the base path
source = fileTree(layout.projectDirectory.dir("antlr")) {
include("**/*.g4")
}

val pkgName = "com.strumenta.antlrkotlin.benchmarks.generated"
packageName = pkgName

// We want visitors alongside listeners.
// The Kotlin target language is implicit, as is the file encoding (UTF-8)
arguments = listOf("-visitor")

// Generated files are outputted inside build/generatedAntlr
val outDir = "generatedAntlr/${pkgName.replace(".", "/")}"
outputDirectory = layout.buildDirectory.dir(outDir).get().asFile
}

withType<KotlinCompilationTask<*>> {
dependsOn(generateKotlinGrammarSource)
}

//
// The source JAR tasks must explicitly depend on the grammar generation
// to avoid Gradle complaining and erroring out
//

sourcesJar {
dependsOn(generateKotlinGrammarSource)
}

kotlin.targets.configureEach {
if (publishable) {
named<Jar>("${targetName}SourcesJar") {
dependsOn(generateKotlinGrammarSource)
}
}
}
}
79 changes: 22 additions & 57 deletions antlr-kotlin-tests/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import com.strumenta.antlrkotlin.gradle.AntlrKotlinTask
import com.strumenta.antlrkotlin.gradle.ext.targetsNative
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilationTask
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile

plugins {
id("strumenta.multiplatform")
Expand Down Expand Up @@ -32,11 +29,32 @@ strumentaMultiplatform {
}
}

val generateKotlinGrammarSource = tasks.register<AntlrKotlinTask>("generateKotlinGrammarSource") {
dependsOn("cleanGenerateKotlinGrammarSource")

// Only include *.g4 files. This allows tools (e.g., IDE plugins)
// to generate temporary files inside the base path
source = fileTree(layout.projectDirectory.dir("antlr")) {
include("**/*.g4")
}

val pkgName = "com.strumenta.antlrkotlin.test.generated"
packageName = pkgName

// We want visitors alongside listeners.
// The Kotlin target language is implicit, as is the file encoding (UTF-8)
arguments = listOf("-visitor")

// Generated files are outputted inside build/generatedAntlr
val outDir = "generatedAntlr/${pkgName.replace(".", "/")}"
outputDirectory = layout.buildDirectory.dir(outDir).get().asFile
}

kotlin {
sourceSets {
commonMain {
kotlin {
srcDir(layout.buildDirectory.dir("generatedAntlr"))
srcDir(generateKotlinGrammarSource)
}

dependencies {
Expand All @@ -52,56 +70,3 @@ kotlin {
}
}
}

tasks {
// The TSQL grammar test fails because of a compiler error
// when targeting the JVM (MethodTooLargeException), even
// if we never test on the JVM.
// Here we simply filter out the generated TSQL Kotlin files
// from the compilation phase
withType<KotlinJvmCompile> {
exclude("**/test/generated/TSql*.kt")
}

val generateKotlinGrammarSource = register<AntlrKotlinTask>("generateKotlinGrammarSource") {
dependsOn("cleanGenerateKotlinGrammarSource")

// Only include *.g4 files. This allows tools (e.g., IDE plugins)
// to generate temporary files inside the base path
source = fileTree(layout.projectDirectory.dir("antlr")) {
include("**/*.g4")
}

val pkgName = "com.strumenta.antlrkotlin.test.generated"
packageName = pkgName

// We want visitors alongside listeners.
// The Kotlin target language is implicit, as is the file encoding (UTF-8)
arguments = listOf("-visitor")

// Generated files are outputted inside build/generatedAntlr
val outDir = "generatedAntlr/${pkgName.replace(".", "/")}"
outputDirectory = layout.buildDirectory.dir(outDir).get().asFile
}

withType<KotlinCompilationTask<*>> {
dependsOn(generateKotlinGrammarSource)
}

//
// The source JAR tasks must explicitly depend on the grammar generation
// to avoid Gradle complaining and erroring out
//

sourcesJar {
dependsOn(generateKotlinGrammarSource)
}

kotlin.targets.configureEach {
if (publishable) {
named<Jar>("${targetName}SourcesJar") {
dependsOn(generateKotlinGrammarSource)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,8 @@ import org.antlr.v4.kotlinruntime.CharStream
import org.antlr.v4.kotlinruntime.ParserRuleContext
import org.antlr.v4.kotlinruntime.TokenStream

// Note(Edoardo): move the test back to commonTest once
// the MethodTooLargeException has been solved on the JVM
// by the Kotlin team.
// FQN: org.jetbrains.org.objectweb.asm.MethodTooLargeException
@Suppress("unused")
class TSqlGrammarJsTest : GrammarTest<TSqlLexer, TSqlParser>() {
class TSqlGrammarTest : GrammarTest<TSqlLexer, TSqlParser>() {
override fun createLexer(input: CharStream): TSqlLexer =
TSqlLexer(input)

Expand Down

This file was deleted.

Loading