Scala Native Examples
This page contains examples of using Mill as a build tool for scala-native applications. It covers setting up a basic scala-native application that calls C function within it, as well as example of two modules with a scala-native application.
Simple
package build
import mill._, scalalib._, scalanativelib._
object `package` extends RootModule with ScalaNativeModule {
def scalaVersion = "3.3.4"
def scalaNativeVersion = "0.5.5"
// You can have arbitrary numbers of third-party dependencies
def ivyDeps = Agg(
ivy"com.lihaoyi::mainargs::0.7.6"
)
object test extends ScalaNativeTests with TestModule.Utest {
def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4")
def testFramework = "utest.runner.Framework"
}
}
This example demonstrates a simple Scala program that generates HTML content
from user-provided text and prints it to the standard output, utilizing Scala
Native for native integration and mainargs
for command-line argument parsing.
> ./mill run --text hello
<h1>hello</h1>
> ./mill show nativeLink # Build and link native binary
".../out/nativeLink.dest/out"
> ./out/nativeLink.dest/out --text hello # Run the executable
<h1>hello</h1>
Interop
package build
import mill._, scalalib._, scalanativelib._
object `package` extends RootModule with ScalaNativeModule {
def scalaVersion = "3.3.4"
def scalaNativeVersion = "0.5.5"
object test extends ScalaNativeTests {
def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4")
def testFramework = "utest.runner.Framework"
}
}
This is an example of how to use Mill to compile C code together with your Scala Native code.
The above build expect the following project layout:
build.mill src/ foo/ HelloWorld.scala resources/ scala-native/ HelloWorld.c test/ src/ foo/ HelloWorldTests.scala
Note: C/C++ source files need to be in resources/scala-native
directory so
It can be linked and compiled successfully. More info from Scala Native doc
here
and also Scala user forum here
This example is pretty minimal, but it demonstrates the core principles, and can be extended if necessary to more elaborate use cases.
> ./mill run
Running HelloWorld function
Done...
Reversed: !dlroW ,olleH
> ./mill test
Tests: 1, Passed: 1, Failed: 0
Multi-Module
package build
import mill._, scalalib._, scalanativelib._
trait MyModule extends ScalaNativeModule {
def scalaVersion = "3.3.4"
def scalaNativeVersion = "0.5.5"
object test extends ScalaNativeTests {
def ivyDeps = Agg(ivy"com.lihaoyi::utest::0.8.4")
def testFramework = "utest.runner.Framework"
}
}
object foo extends MyModule {
def moduleDeps = Seq(bar)
def ivyDeps = Agg(ivy"com.lihaoyi::mainargs::0.7.6")
}
object bar extends MyModule
This example contains a simple Mill build with two modules, foo
and bar
.
We don’t mark either module as top-level using extends RootModule
, so
running tasks needs to use the module name as the prefix e.g. foo.run
or
bar.run
. You can define multiple modules the same way you define a single
module, using def moduleDeps
to define the relationship between them.
Note that we split out the test
submodule configuration common to both
modules into a separate trait MyModule
. This lets us avoid the need to
copy-paste common settings, while still letting us define any per-module
configuration such as ivyDeps
specific to a particular module.
The above builds expect the following project layout:
build.mill bar/ resources/ scala-native/ bar.h HelloWorldBar.c src/ Bar.scala test/ src/ BarTests.scala foo/ resources/ scala-native/ bar.h HelloWorldFoo.c src/ Foo.scala
Note: C/C++ source files need to be in resources/scala-native
directory so
It can be linked and compiled successfully. More info from Scala Native doc
here
and also Scala user forum here
> ./mill bar.run hello
Running HelloWorld function
Done...
Bar value: Argument length is 5
> ./mill bar.test
Tests: 1, Passed: 1, Failed: 0
> ./mill foo.run --bar-text hello --foo-text world
Foo.value: The vowel density of 'world' is 20
Bar.value: The string length of 'hello' is 5
Common Config
package build
import mill._, scalalib._, scalanativelib._, scalanativelib.api._
object `package` extends RootModule with ScalaNativeModule {
def scalaVersion = "3.3.4"
def scalaNativeVersion = "0.5.5"
// You can have arbitrary numbers of third-party dependencies
// Scala Native uses double colon `::` between organization and the dependency names
def ivyDeps = Agg(
ivy"com.lihaoyi::fansi::0.5.0"
)
// Set the releaseMode to ReleaseFast.
def releaseMode: T[ReleaseMode] = ReleaseMode.ReleaseFast
// Set incremental compilation to true
def nativeIncrementalCompilation: T[Boolean] = true
// Set nativeLinkingOptions path to a directory named `target`.
def nativeLinkingOptions = Seq("-L" + millSourcePath.toString + "/target")
// Set nativeWorkdir directory to `newDir`
def nativeWorkdir = T.dest / "newDir"
}
This example shows some of the common tasks you may want to override on a
ScalaNativeModule
: specifying the releaseMode
,
nativeIncrementalCompilation, `nativeLinkingOptions
and nativeWorkdir
.
> ./mill run
...
Value: <h1>hello</h1>
> ./mill show releaseMode
"mill.scalanativelib.api.ReleaseMode.ReleaseFast"
> ./mill show nativeIncrementalCompilation
true
> ./mill show nativeLinkingOptions
...
> ./mill show nativeWorkdir
...