Building Groovy with Mill

Mill Groovy support is currently still under active development. It is expected to continue evolving over time.

Declarative Module Config

This is a basic Mill build for a single GroovyModule, with one third-party dependency and a test suite using the JUnit framework.

This example project uses two third-party dependencies - Groovy-Cli-Commons for CLI argument parsing - Groovy-Xml for HTML templating and escaping and uses them to wrap a given input string in HTML templates with proper escaping.

build.mill.yaml (download, browse)
extends: mill.groovylib.GroovyModule
groovyVersion: 5.0.3
mvnDeps:
  - org.apache.groovy:groovy-cli-commons
  - org.apache.groovy:groovy-xml
mainClass: foo.Foo
groovyCompileTargetBytecode: 11
test/package.mill.yaml (download, browse)
extends: [build.GroovyTests, TestModule.Junit5]
jupiterVersion: 5.13.4
mvnDeps:
  - org.apache.groovy:groovy-test
> ./mill resolve _ # List what tasks are available to run
assembly
...
compile
...
run
...
test
> ./mill run --text hello
<h1>hello</h1>
> ./mill test
Running Test Class foo.FooTest
Test foo.FooTest#generate html created properly() finished...
Test foo.FooTest#generated html is properly escaped() finished...
Test foo.FooTest finished...
> ./mill assembly # bundle classfiles and libraries into a jar for deployment

> ./mill show assembly # show the output of the assembly task
".../out/assembly.dest/out.jar"

> java -jar ./out/assembly.dest/out.jar --text hello
<h1>hello</h1>

Config-Based Modules

This example project uses two third-party dependencies - groovy-cli-commons for CLI argument parsing, and groovy-xml - uses them to wrap a given input string in HTML templates with proper escaping. The test/ submodule contains its own mvnDeps for dependencies that are only used in the test suite.

build.mill.yaml
src/
    foo/Foo.groovy
resources/
    ...
test/
    package.mill.yaml
    src/
        foo/FooTest.groovy
out/
    compile.json
    compile.dest/
    ...
    test/
        compile.json
        compile.dest/
        ...
The default Mill source folder layout src/ differs from that of Maven/Gradle’s src/main/java/. If you wish to use the Maven source folder layout, e.g. for migrating an existing codebase, you should use Maven-Compatible Modules
> ./mill inspect compile # Show documentation and inputs of a task
compile(GroovyModule.scala:...)
    Compiles the current module to generate compiled classfiles/bytecode.
    ...
Inputs:
    upstreamCompileOutput
    allSources
    compileClasspath

> ./mill compile # compile sources into classfiles
...
Compiling 1 Groovy sources to...

> ./mill test
Test foo.FooTest#generate html created properly() finished, ...
Test foo.FooTest#generated html is properly escaped() finished, ...
Test foo.FooTest finished, ...

Multi-Module Projects

build.mill (download, browse)
package build

import mill.*, groovylib.*

object foo extends GroovyModule {

  def groovyVersion = "5.0.3"

  def moduleDeps = Seq(build.bar)
  def mvnDeps = Seq(
    mvn"org.apache.groovy:groovy-cli-commons"
  )
}

object bar extends GroovyModule {

  def groovyVersion = "5.0.3"

  def mvnDeps = Seq(
    mvn"org.apache.groovy:groovy-xml"
  )

  object test extends GroovyTests with TestModule.Junit5 {}
}
build.mill
foo/
    src/
        foo/Foo.groovy
bar/
    src/
        bar/Bar.groovy
    test/
        src/
            bar/BarTests.groovy
out/
    foo/
        compile.json
        compile.dest/
        ...
    bar/
        compile.json
        compile.dest/
        ...
        test/
            compile.json
            compile.dest/
            ...

Maven Compatible Modules

Mill’s default folder layout of foo/src/ and foo/test/src differs from that of Maven or Gradle’s foo/src/main/groovy/ and foo/src/test/groovy/. If you are migrating an existing codebase, you can use Mill’s GroovyMavenModule and GroovyMavenTests as shown below to preserve filesystem compatibility with an existing Maven or Gradle build:

build.mill (download, browse)
package build
import mill.*, groovylib.*

object foo extends GroovyMavenModule {

  def groovyVersion = "5.0.3"

  object test extends GroovyMavenTests with TestModule.Junit5 {}
  object integration extends GroovyMavenTests with TestModule.Junit5 {}
}

GroovyMavenModule is a variant of GroovyModule that uses the more verbose folder layout of Maven, sbt, and other tools:

  • foo/src/main/java

  • foo/src/main/groovy

  • foo/src/test/java

  • foo/src/test/groovy

  • foo/src/integration/java

  • foo/src/integration/groovy

Rather than Mill’s

  • foo/src

  • foo/test/src

This is especially useful if you are migrating from Maven to Mill (or vice versa), during which a particular module may be built using both Maven and Mill at the same time

For more details on migrating from other build tools, see Migrating to Mill

Single-File Scripts

Foo.groovy (download, browse)
class Foo {

    static void main(String[] args) {
        if(args == null || args.length != 1){
            println "Specify exactly one parameter for greeting"
            System.exit(1)
        }

        println "Hello ${args[0]}"
    }
}
> ./mill Foo.groovy bar
Compiling 1 Groovy sources to...
Hello bar
> ./mill Foo.groovy:run bar
Hello bar
> ./mill resolve Foo.groovy:_
Foo.groovy:run
Foo.groovy:runMain
Foo.groovy:compile
Foo.groovy:assembly
...

Programmable Module Config

build.mill (download, browse)
package build
import mill.*, groovylib.*

object foo extends GroovyModule {
  def groovyVersion = "5.0.3"

  // Groovy dependency version have been loaded via BOM from the Module already
  def mvnDeps = Seq(
    mvn"org.apache.groovy:groovy-cli-commons",
    mvn"org.apache.groovy:groovy-xml"
  )

  object test extends GroovyTests, TestModule.Junit5 {
    def mvnDeps = Seq(
      mvn"org.apache.groovy:groovy-test"
    )
  }
}

Defining Unit Test Suites

build.mill (download, browse)
package build
import mill.*, groovylib.*

object foo extends GroovyModule {

  def groovyVersion = "5.0.3"

  object test extends GroovyTests with TestModule.Junit5 {
    def jupiterVersion = "5.13.4"

    def mvnDeps = Seq(
      mvn"org.apache.groovy:groovy-test"
    )
  }
}

object bar extends GroovyModule {

  def mainClass = Some("bar.Bar")

  def groovyVersion = "5.0.3"

  object test extends GroovyTests, TestModule.Junit5 {
    def jupiterVersion = "5.13.4"

    def mvnDeps = Seq(
      mvn"org.apache.groovy:groovy-test"
    )
  }
}

Defining Integration Test Suites

build.mill (download, browse)
package build
import mill.*, groovylib.*
object qux extends GroovyModule {

  def groovyVersion = "5.0.3"

  object test extends GroovyTests with TestModule.Junit5 {
    def mvnDeps = super.mvnDeps() ++ Seq(
      mvn"org.apache.groovy:groovy-test"
    )
  }
  object integration extends GroovyTests with TestModule.Junit5 {
    def mvnDeps = super.mvnDeps() ++ Seq(
      mvn"org.apache.groovy:groovy-test"
    )
  }
}

The integration suite is just another regular test module within the parent GroovyModule

Using Spock

build.mill (download, browse)
package build
import mill.*, groovylib.*

object `package` extends GroovyModule {

  def groovyVersion = "5.0.3"

  object test extends GroovyTests with TestModule.Spock {
    def spockVersion = "2.4-groovy-5.0"
    def jupiterVersion = "5.13.4"
  }
}

Using Spock in Java projects with Maven layout

build.mill (download, browse)
package build
import mill.*, groovylib.*

object `package` extends JavaMavenModuleWithGroovyTests {

  object test extends GroovyMavenTests with TestModule.Spock {
    def spockVersion = "2.4-groovy-5.0"
    def groovyVersion = "5.0.3"
    def jupiterVersion = "5.13.4"
  }
}

JavaMavenModuleWithGroovyTests is a variant of JavaModule which also includes MavenModule and has a special test trait for Groovy tests:

  • foo/src/main/java

  • foo/src/test/groovy

This is usefull for Java projects which use Groovy only for testing. For non Maven-layout projects, this convenience trait is not necessary because you can just use GroovyModule for your test object instead.