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-SNAPSHOT
dependencies {
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 oncompileLanguage
and is a dependency ofassemble
.checkLanguage
: Check the Spoofax language. This task depends onarchiveLanguage
and is a dependency ofcheck
. The propertylanguageUnderTest
defines 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 extendscompileLanguage
andsourceLanguage
.
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.