summaryrefslogtreecommitdiff
path: root/docs/pages/6 - Cross Builds.md
blob: 98540faee42179c1cb0685f43baa45a90813655b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
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.Resolver` 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.