Changes to the Gradle build
With MontiCore version 7.6.0,
the monticore
gradle plugin has been replaced with the de.monticore.generator
plugin.
It supports the default use case for the MontiCore generator out of the box.
Default Project Setup
plugins {
id "java-library"
id "de.monticore.generator" version "$mc_version"
}
dependencies {
grammar "de.monticore:monticore-grammar:$mc_version"
}
// And thats it - no further task definitions or dependencies required (except repositories)!
For every Gradle source set,
such as main or test, a MCGenTask
is automatically added which builds all grammars
of the source set.
For example, generateMCGrammars
builds all grammars in the src/main/grammars
folder.
Unlike with the old MCTask
, which required one task per grammar,
the new MCGenTask
task builds all grammars incrementally.
The generated java files are automatically added to the source sets java
sources.
A work action is created for every grammar which is required to be worked on and thus, multiple grammars can be build in parallel.
Example Project Setup
The following project setup combines the default project setup with the SE codestyle plugin and sets up jUnit tests:
plugins {
id "java-library" // Java, while providing specific knowledge about Java libraries
id "de.monticore.generator" version "$mc_version" // MontiCore plugin
id "de.se_rwth.codestyle" version "$mc_version" // Enforces & Provides SE CodeStyle
}
java {
// Configure the java toolchain to use Java 11 (overriding the locally installed JDK)
// https://docs.gradle.org/current/userguide/toolchains.html
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}
dependencies {
// Depend on the MontiCore language library (which in term depends on the runtime)
grammar "de.monticore:monticore-grammar:$mc_version"
// and depend on the junit dependencies
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit_version"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junit_version"
}
// Where can we find the dependencies?
repositories {
if (("true").equals(getProperty('useLocalRepo'))) {
mavenLocal() // in our local maven repository
}
maven { // in the se-nexus
credentials.username mavenUser
credentials.password mavenPassword
url repo
}
}
test {
// Detect junit 5 tests
useJUnitPlatform()
}
The following gradle.properties file can be used:
mavenUser=${user}
mavenPassword=${pass}
repo=https://nexus.se.rwth-aachen.de/content/groups/public
useLocalRepo=false
mc_version=REPLACE-ME
Configuration
The automatically added generateMCGrammars
task is configured with some defaults,
which can be modified via the build file.
For example, the following build file snippet changes the output directory:
generateMCGrammars {
// manually change the output directory
outputDir = project.layout.buildDirectory.dir("mc")
}
The following table shows the main configuration options
Option (, alias) | Description | Type | Default |
---|---|---|---|
grammar (, input) | The grammar files | ConfigurableFileCollection | src/${sourceSet}/grammars |
handWrittenCodeDir (, hwc, handcodedPath ) | Directories for detecting hand-written code that needs to be integrated. | ConfigurableFileCollection | src/${sourceSet}/java |
handWrittenGrammarDir (, hwg) | Directories for detecting hand-written grammar modifications that need to be integrated. | ConfigurableFileCollection | src/${sourceSet}/grammarsHC |
templatePath (, tmplDir) | Directories for detecting hand-written templates to integrate. | ConfigurableFileCollection | src/${sourceSet}/resources |
configTemplate | template to configure the integration of hand-written templates. Thus, it can only be used in conjunction with a valid templatePath. | String | absent |
script | Groovy script to control the generation workflow. | String | monticore_standard.groovy |
modelPath | Directories in which symbols or grammars are searched. | ConfigurableFileCollection | absent |
outputDir | Directory to which the output should be written to | Directory | $buildDir/generated-sources/monticore/${sourceSet}-sourcecode |
reportDir | Directory to which the reports should be written to | Directory | $buildDir/mc_reports/task_$taskname |
debug | Enables developer logging and | boolean | false |
genDST | Whether domain specific transformation infrastructure should be generated (only suitable for TR grammars) | boolean | false |
genTag | Whether tagging infrastructure should be generated (only suitable for TagDefinition grammars) | boolean | false |
groovyHook1 | Groovy script that is hooked into the workflow of the standard script at hook point one, which is called after initialization, before the actual workflow begins. | File | absent |
groovyHook2 | Groovy script that is hooked into the workflow of the standard script at hook point two, which is called before the generation step. | File | absent |
moreArgs | Additional arguments passed to the Tool. | List<String> |
[] |
Grammar Dependencies
To depend on another language project,
simply use the grammar
configuration:
dependencies {
grammar "de.monticore.lang:mydsl:$mc_version"
testGrammar "de.monticore.lang:mytestdsl:$mc_version"
}
This adds the java-library variant (aka implementation) to the java classpath, and the grammar files to the symbol path.
Note: Grammar dependencies are transitive, which includes the implementation/java dependencies as well (such as the monticore-runtime).
Examples
In most cases, adding the plugin should be enough. The following snippets show examples, in which the default configuration is modified:
Different ModelPath
generateMCGrammars {
modelPath("$projectDir/src/main/grammars", "$projectDir/src/notquitemain/grammars")
}
Note: We have introduced a breaking change to the modelPath()
behavior:
It is no longer possible to specify multiple directories by calling modelPath(...)
multiple times,
instead a comma-seperated list is to be used.
The legacy usage now aborts with a descriptive error.
Different workflow script
generateMCGrammars {
script = "de/monticore/monticore_noreports.groovy"
}
Specify Groovy Hooks
generateMCGrammars {
groovyHook1 = file "$projectDir/gs1.groovy"
groovyHook2 = file "$projectDir/gs2.groovy"
}
ConfigTemplate
generateMCGrammars {
configTemplate = "ct.ftl"
templatePath "$projectDir/src/main/tpl"
}
Note: Incremental building after changes to the templatePath directory contents is not supported. Instead, a full-rebuild is enforced.
Transformation Grammars
Simply use the de.monticore.generator-withtr
gradle plugin.
You can set a genTR
property to any value different than true
to disable the trafo generation.
To further exclude grammars, you can filter on the extractTRGrammars
task:
if (("true").equals(getProperty('genTR'))) {
// Further exclude the grammar and basic tf grammars from being included in the TR grammar input set
extractTRGrammars {
exclude(["**/de/monticore/tf/*", "**/de/monticore/grammar/*", "**/de/monticore/siunit/*"])
}
}
Custom MCGenTask
A generateModifiedGrammars
task to generate all grammars generated by another task
(and with an additional filter, only matching grammar files starting with a captital A
.)
// register a new MCGenTask task
tasks.register("generateModifiedGrammars", MCGenTask) {
grammar.setFrom(tasks.named("modifyGrammars").get()
.getOutputs().getFiles().getAsFileTree()
.matching(patternFilterable -> patternFilterable.include( "**/A*.mc4")))
outputDir = file myModOutDir
modelPath.from(file modifyGrammarsOutD) // the directory hierarchy is required here
if (findProperty("ci") != null) {
script = "de/monticore/monticore_noreports.groovy"
}
}
// Note: The output directory has to manually be added to the java sources, if desired
sourceSets {
// we explicitly use the outputDir property instead of the myModOutDir variable
// to give Gradle further insights into the build setup
main.java.srcDirs += tasks.generateModifiedGrammars.getOutputDir()
}
Custom SourceSet
Additional source sets for MontiCore grammars, such as trafos and tagging, can be defined and published. Note: Trafos and Tagging can be set up using a specific plugin.
sourceSets {
variantB {
grammars {
srcDir("src/main/variantBGrammars")
}
}
}
dependencies {
// add grammar dependencies to variant B
variantBGrammar "de.monticore:monticore-grammar:$mc_version"
}
monticoreGenerator {
// publish the MC artifacts of the "variantB" source set
publishSourceSet(project.sourceSets.variantB)
}
Improvements over the old MCTask
- Improved incremental build support
- Added Gradle build cache support
- Added parallel task execution support
- Using Gradle task definitions which can be used for Gradle plugins of MontiCore language tools
- Transitive grammar dependencies
- Handling of test grammars (and source sets)
Added build elements
- For every source set, the MontiCore generator plugin adds:
- A
grammar
SourceSetDirectory extension (e.g.sourceSets.main.grammars
) to the source set - A
generate${sourceSetName}MCGrammars
task building (by default) all grammars of the source set - Two configurations:
grammar
(or${sourceSetName}Grammar
for non-main source sets): Used to declare dependencies to other MontiCore language projects. This configuration will not be resolved, instead theimplementation
andgrammarSymbolDependencies
extend this configuration and resolve their respective variants as dependencies.- (internal)
grammarSymbolDependencies
(or${sourceSetName}GrammarSymbolDependencies
): Dependencies (jars containing the .mc4 files) will be added to the ModelPath. This configuration extends the previous dependency-defining grammar. Do not declare dependencies using this configuration.
- In case publishing is enabled (by default, only for the main source set):
- A
grammarSymbolOutElements
(or${sourceSetName}GrammarSymbolOutElements
) configuration & variant:- Contains the grammars jar as an artifact.
- Three
Jar
tasks (with publishing their artifacts):${sourceSetName}GrammarsJar
${sourceSetName}Jar
(only for non-main source sets)${sourceSetName}sourcesJar
(only for non-main source sets)
- An ad-hoc component
grammars${sourceSetName}
- An alias MavenPublication named identical to the source set (only for non-main source sets)
- A
- A
- Grammars declared in the main source set are also available in the test source sets.
Implementation specifics
MontiCore suffers from a memory leak caused by the GroovyInterpreter.
The parallel, isolated execution of the generator intensifies this memory leak
(which is why we limit it using --max-workers=4
).
This might be fixed by 8078641 in jdk
18+.
Migration Guide: Using the new plugin
- Replace plugins
id "monticore"
withid "de.monticore.generator"
- Remove the creation of individual MCTasks
- Modelpath adding: see above using the source sets
- Ensure the generation target is NOT added to the java sources (otherwise the TOP mechanism will fail)
- Ensure no resources.srcDirs += grammarOutDir is used (otherwise your build will depend on itself)
Using legacy-dependencies with the new plugin
Due to the diverging capabilities/variants of the new plugin, the resolution of legacy built grammars requires additional effort:
configurations {
legacyGrammar // configuration for legacy-MC-projects
}
dependencies {
// resolve the grammars of myolddsl
legacyGrammar "de.monticore.languages:myolddsl:7.7.0-SNAPSHOT:grammars"
// we also have to add the implementation/java classes
implementation "de.monticore.languages:myolddsl:7.7.0-SNAPSHOT"
}
tasks.generateMCGrammars.configure {
// Add the legacy-grammars to the symbol/model path of the generation
symbolPathConfiguration.from(configurations.legacyGrammar)
}