I no longer maintain this repository. This project has been superseded by https://github.com/metaborg/spoofax.gradle/.
The Spoofax Gradle plugin makes it possible to build Spoofax languages with Gradle.
To compile and package the plugin:
gradle assemble
To publish the plugin to your local Maven repository:
gradle pTML
On macOS Catalina, run the build/tests in a Docker container:
cat << EOF > Dockerfile
FROM container-registry.oracle.com/java/serverjre:8
WORKDIR /root
ENTRYPOINT ./gradlew test
EOF
docker build -t spoofax-gradle-plugin .
docker run -v /path/to/spoofax-gradle-plugin:/root spoofax-gradle-plugin:latest
Below is an example build script for building a Spoofax language project.
plugins {
id 'nl.martijndwars.spoofax' version '1.2.5'
}
repositories {
jcenter()
spoofaxRepos()
}
spoofax {
strategoFormat = 'ctree'
languageVersion = '0.1.0-SNAPSHOT'
overrides = []
}To build your Spoofax language:
gradle archiveLanguage
If you get a StackOverflowError or OutOfMemoryError create a gradle.properties with the following content:
org.gradle.jvmargs=-Xms1g -Xmx2g -Xss32m
| Plugin version | Spoofax version |
|---|---|
| 1.2.5 | 2.5.16 |
| 1.2.4 | 2.5.11 |
| 1.2.3 | 2.5.11 |
| 1.2.2 | 2.5.9 |
| 1.2.1 | 2.5.7 |
| 1.2.0 | 2.5.4 |
| 1.0.0 - 1.1.0 | 2.5.1 |
If you have a source dependency on another project in a multi-project build:
dependencies {
sourceLanguage project(':projectB')
}If you have a multi-project build, then you might be able to build projects that do not directly depend upon each other in parallel.
To enable parallel task execution, use the --parallel flag or add the following to the project's gradle.properties:
org.gradle.parallel=true
Assume you have a project foo.lang that defines the language and contains one or more .spt files.
If you add a compile dependency on the SPT language to metaborg.yaml, then the checkLanguage task runs the SPT tests.
For example,
dependencies:
compile:
- org.metaborg:org.metaborg.meta.lang.spt:${metaborgVersion}Assume you have a project foo.lang that defines the language and foo.tests that contains one or more .spt files.
In foo.tests/metaborg.yaml add a compile dependency on SPT and a source dependency on the language under test.
In foo.tests/build.gradle add a project-dependency on :foo.lang and configure the checkLanguage task by specifying the language under test.
---
id: org.example:foo.tests:0.1.0-SNAPSHOT
name: foo.tests
dependencies:
compile:
- org.metaborg:org.metaborg.meta.lang.spt:${metaborgVersion}
source:
- org.example:foo.lang:0.1.0-SNAPSHOTdependencies {
sourceLanguage project(':foo.lang')
}
checkLanguage {
languageUnderTest = "org.example:foo.lang:$version"
}The Spoofax plugin integrates with Gradle's maven-publish plugin. Add the following to your buildscript:
plugins {
id 'maven-publish'
}
publishing {
publications {
simple(MavenPublication) { publication ->
project.spoofax.component(publication)
}
}
repositories {
maven {
url "https://repository.acme.com"
}
}
}This creates a Maven publication based on your Spoofax language specification.
In particular, the publication's groupId, artifactId, and version are based on the language specification, not on the Gradle project.
Run gradle pTML or gradle publish to publish the Spoofax language as Maven publication to your local Maven repository or remote Maven repository, respectively.
To override the version of a source dependency:
spoofax {
overrides = [
'com.acme:foo.lang:1.2.3'
]
}The plugin modifies the build in several ways.
The plugin applies the Java plugin to your project, which in turn applies the Base plugin to your project.
The plugin defines the following tasks:
cleanLanguage: Clean the Spoofax language. This task is a dependency ofclean.compileLanguage: Compile the Spoofax language. This task is a dependency ofassemble.archiveLanguage: Archive the Spoofax language. This task depends oncompileLanguageand is a dependency ofassemble.checkLanguage: Check the Spoofax language. This task depends onarchiveLanguageand is a dependency ofcheck. The propertylanguageUnderTestdefines the language to be tested. If this property is not specified, the current language is tested.
If :projectA has a project lib dependency on :projectB in the sourceLanguage configuration, then :projectA:compileLanguage depends on :projectB:archiveLanguage.
This ensures that all project dependencies are built before the depending project is built.
The plugin defines three dependency configurations:
compileLanguage: a configuration holding all the compile dependencies.sourceLanguage: a configuration holding all the source dependencies.language: a convenience configuration that extendscompileLanguageandsourceLanguage.
The plugin defines one artifact configuration:
spoofaxLanguage
The spoofaxLanguage configuration contains the built Spoofax language (.spoofax-language) artifact.
The assemble configuration is made to extend the spoofaxLanguage configuration.
The compileLanguage task generates Java sources and the archiveLanguage task expects the generated Java sources to be compiled.
Hence, the plugin configures compileJava to depend on compileLanguage and to be a dependency of archiveLanguage.
A moderately large Spoofax project generates many Java files.
In fact, Spoofax generates so many Java files that the default JDK compiler runs out of memory (even with -Xmx4g).
For this reason, the plugin configures compileJava to use the the Eclipse Java Compiler (ECJ).
The generated Java sources need to be compiled against the Spoofax API.
For this reason, the plugin adds a compileOnly dependency on org.metaborg.spoofax.core.
A consequence is that a plugin user needs to add repositories in which all transitive dependencies can be resolved.
The plugin adds a spoofaxRepos() function. This function adds the following repositories:
You will need to add a repository where third-party dependencies can be resolved, e.g. jcenter() or mavenCentral().
It is recommended to add the spoofaxRepos() before any other repository, because:
- The repositories that are added by
spoofaxRepos()are specific for the artifacts they contain. For example, the Metaborg repositories will only be contacted for artifacts with grouporg.metaborg. That is, there is no penalty for addingspoofaxRepos()first. - Third-party repositories do not by default exclude artifacts that are only available in the Spoofax repositories. If a third-party repository is added first, then Gradle will first check if a Spoofax artifact exists in this repository. That is, there is a penalty for adding third-party repositories first.