Mill handles cross-building of all sorts via the `Cross[T]` module.
## Defining Cross Modules
You can use this as follows:
```scala
object foo extends mill.Cross[FooModule]("2.10", "2.11", "2.12")
class FooModule(crossVersion: String) extends Module{
def suffix = T{ crossVersion }
def bigSuffix = T{ suffix().toUpperCase() }
}
```
This defines three copies of `FooModule`: `"210"`, `"211"` and `"212"`, each of
which has their own `suffix` target. You can then run them via
```bash
mill --show foo[2.10].suffix
mill --show foo[2.10].bigSuffix
mill --show foo[2.11].suffix
mill --show foo[2.11].bigSuffix
mill --show foo[2.12].suffix
mill --show foo[2.12].bigSuffix
```
The modules each also have a `millSourcePath` of
```text
foo/2.10
foo/2.11
foo/2.12
```
And the `suffix` targets will have the corresponding output paths for their
metadata and files:
```text
foo/2.10/suffix
foo/2.10/bigSuffix
foo/2.11/suffix
foo/2.11/bigSuffix
foo/2.12/suffix
foo/2.12/bigSuffix
```
You can also have a cross-build with multiple inputs:
```scala
val crossMatrix = for{
crossVersion <- Seq("210", "211", "212")
platform <- Seq("jvm", "js", "native")
if !(platform == "native" && crossVersion != "212")
} yield (crossVersion, platform)
object foo extends mill.Cross[FooModule](crossMatrix:_*)
class FooModule(crossVersion: String, platform: String) extends Module{
def suffix = T{ crossVersion + "_" + platform }
}
```
Here, we define our cross-values programmatically using a `for`-loop that spits
out tuples instead of individual values. Our `FooModule` template class then
takes two parameters instead of one. This creates the following modules each
with their own `suffix` target:
```bash
mill --show foo[210,jvm].suffix
mill --show foo[211,jvm].suffix
mill --show foo[212,jvm].suffix
mill --show foo[210,js].suffix
mill --show foo[211,js].suffix
mill --show foo[212,js].suffix
mill --show foo[212,native].suffix
```
## Using Cross Modules from Outside
You can refer to targets defined in cross-modules as follows:
```scala
object foo extends mill.Cross[FooModule]("2.10", "2.11", "2.12")
class FooModule(crossVersion: String) extends Module{
def suffix = T{ crossVersion }
}
def bar = T{ "hello " + foo("2.10").suffix }
```
Here, `foo("2.10")` references the `"2.10"` instance of `FooModule`. You can
refer to whatever versions of the cross-module you want, even using multiple
versions of the cross-module in the same target:
```scala
object foo extends mill.Cross[FooModule]("2.10", "2.11", "2.12")
class FooModule(crossVersion: String) extends Module{
def suffix = T{ crossVersion }
}
def bar = T{ "hello " + foo("2.10").suffix + " world " + foo("2.12").suffix }
```
## Using Cross Modules from other Cross Modules
Targets in cross-modules can depend on one another the same way that external
targets:
```scala
object foo extends mill.Cross[FooModule]("2.10", "2.11", "2.12")
class FooModule(crossVersion: String) extends Module{
def suffix = T{ crossVersion }
}
object bar extends mill.Cross[BarModule]("2.10", "2.11", "2.12")
class BarModule(crossVersion: String) extends Module{
def bigSuffix = T{ foo(crossVersion).suffix().toUpperCase() }
}
```
Here, you can run:
```bash
mill --show foo[2.10].suffix
mill --show foo[2.11].suffix
mill --show foo[2.12].suffix
mill --show bar[2.10].bigSuffix
mill --show bar[2.11].bigSuffix
mill --show bar[2.12].bigSuffix
```
## Cross Resolvers
You can define an implicit `mill.define.Cross.Resolve` within your
cross-modules, which would let you use a shorthand `foo()` syntax when referring
to other cross-modules with an identical set of cross values:
```scala
trait MyModule extends Module{
def crossVersion: String
implicit object resolver extends mill.define.Cross.Resolver[MyModule]{
def resolve[V <: MyModule](c: Cross[V]): V = c.itemMap(List(crossVersion))
}
}
object foo extends mill.Cross[FooModule]("2.10", "2.11", "2.12")
class FooModule(val crossVersion: String) extends MyModule{
def suffix = T{ crossVersion }
}
object bar extends mill.Cross[BarModule]("2.10", "2.11", "2.12")
class BarModule(val crossVersion: String) extends MyModule{
def longSuffix = T{ "_" + foo().suffix() }
}
```
While the example `resolver` simply looks up the target `Cross` value for the
cross-module instance with the same `crossVersion`, you can make the resolver
arbitrarily complex. e.g. the `resolver` for `mill.scalalib.CrossSbtModule`
looks for a cross-module instance whose `scalaVersion` is binary compatible
(e.g. 2.10.5 is compatible with 2.10.3) with the current cross-module.