Multi-Language Builds

This example demonstrates a simple multi-langauge project, running a spring boot webserver serving a react client and interacting with a python binary through the web-server api.

build.mill (download, browse)
package build
import mill._, javascriptlib._, pythonlib._, javalib._

object client extends ReactScriptsModule

object `sentiment-analysis` extends PythonModule {
  def mainScript = Task.Source { moduleDir / "src" / "foo.py" }

  def pythonDeps = Seq("textblob==0.19.0")

  object test extends PythonTests with pythonlib.TestModule.Unittest
}

object server extends JavaModule {
  def mvnDeps = Seq(
    mvn"org.springframework.boot:spring-boot-starter-web:2.5.6",
    mvn"org.springframework.boot:spring-boot-starter-actuator:2.5.6"
  )

  /** Bundle client & sentiment-analysis as resource */
  def resources = Task {
    os.copy(client.bundle().path, Task.dest / "static")
    os.makeDir.all(Task.dest / "analysis")
    os.copy(`sentiment-analysis`.bundle().path, Task.dest / "analysis" / "analysis.pex")
    super.resources() ++ Seq(PathRef(Task.dest))
  }

  object test extends JavaTests with javalib.TestModule.Junit5 {
    def mvnDeps = super.mvnDeps() ++ Seq(
      mvn"org.springframework.boot:spring-boot-starter-test:2.5.6"
    )
  }
}

Mill as a build tool is not inherently limited to working with JVM languages like Java/Scala/Kotlin, and you can set up toolchains for other languages as well. This example makes use of Mill’s experimental Python Toolchain and Typescript Toolchain together with it’s builtin Java Toolchain to build a multi-language project containing all three languages. This provides several benefits over having separate build tools for each language:

  • Mill can provide caching and automatic invalidation across languages, e.g. changing the Typescript client code automatically makes the Java server re-build

  • Mill can parallelize different language toolchains, e.g. building the Typescript client, Python sentiment-analysis module, and Java server code in parallel on multiple cores

If you wish to learn how additional language toolchains can be added to Mill, you can see the pages on Example: Python Support and Example: Typescript Support for walk-throughs on how language traits such as PythonModule can be defined by users.

> ./mill client.test
PASS src/test/App.test.tsx
...Text Analysis Tool
...renders the app with initial UI...
...displays sentiment result...
...
Test Suites:...1 passed, 1 total
Tests:...2 passed, 2 total
...

> ./mill sentiment-analysis.test
...
test_negative_sentiment... ok
test_neutral_sentiment... ok
test_positive_sentiment... ok
...
Ran 3 tests...
...
OK
...

> ./mill server.test
...com.example.ServerTest#shouldReturnStaticPage() finished...
...com.example.ServerTest#shouldReturnPositiveAnalysis() finished...
...com.example.ServerTest#shouldReturnNegativeAnalysis() finished...

> ./mill server.runBackground

> curl http://localhost:8086
...<title>Sentiment Analysis Tool</title>...

> curl -X POST http://localhost:8086/api/analysis -H "Content-Type: text/plain" --data "This is awesome!" # Make request to the analysis api
Positive sentiment (polarity: 1.0)

> ./mill clean server.runBackground