Skip to content

Commit

Permalink
feat: RootModule and Cross can be applied to the same module (#4593)
Browse files Browse the repository at this point in the history
Implements #3693 

These seem to be the minimal changes to allow RootModule and Cross to be
applied to the same module.
it was only necessary to make `Cross` a trait. I did try making
`RootModule` also a trait but it proved a little trickier due to it
usign the class constructor for `BaseModule` that it extends.

The main issue I ran into that caused me to have to remove the implicit
parameter on `Cross` was the Ctx implicit was not being filled in
properly and i was seeing `Modules and Tasks can only be defined within
a mill Module` from the dummy being used. There may have been a way
around this in the `CrossMacros`, but I couldn't see it, and this
solution appears to work, at least for the examples shown in the
integration tests added.
  • Loading branch information
jbwheatley authored Feb 20, 2025
1 parent 1be196b commit fb7ad6d
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 34 deletions.
7 changes: 3 additions & 4 deletions core/define/src/mill/define/Cross.scala
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,9 @@ object Cross {
* }
* }}}
*/
class Cross[M <: Cross.Module[?]](factories: Cross.Factory[M]*)(implicit
ctx: mill.define.Ctx
) extends mill.define.Module {
trait Cross[M <: Cross.Module[?]](factories: Cross.Factory[M]*) extends mill.define.Module {

val ctx: Ctx = moduleCtx

trait Item {
def crossValues: List[Any]
Expand All @@ -184,7 +184,6 @@ class Cross[M <: Cross.Module[?]](factories: Cross.Factory[M]*)(implicit
Option(ctx.fileName).filter(_.nonEmpty)
)
}
ctx.segments.last.pathSegments
val module0 = new Lazy(() =>
make(
ctx
Expand Down
47 changes: 18 additions & 29 deletions core/define/src/mill/define/Segments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import scala.math.Ordering.Implicits.seqOrdering

/**
* Models a path with the Mill build hierarchy, e.g. `amm.util[2.11].test.compile`.
* Segments must start with a [[Segment.Label]].
*
* `.`-separated segments are [[Segment.Label]]s,
* while `[]`-delimited segments are [[Segment.Cross]]s
Expand All @@ -25,34 +24,24 @@ case class Segments private (value: Seq[Segment]) {
throw new IllegalArgumentException("Segments must end with a Label, but found a Cross.")
}

def parts: List[String] = value.toList match {
case Nil => Nil
case Segment.Label(head) :: rest =>
val stringSegments = rest.flatMap {
case Segment.Label(s) => Seq(s)
case Segment.Cross(vs) => vs
}
head +: stringSegments
case Segment.Cross(_) :: _ =>
throw new IllegalArgumentException("Segments must start with a Label, but found a Cross.")
}

def head: Segment.Label = value.head match {
case l: Segment.Label => l
case _ =>
throw new IllegalArgumentException("Segments must start with a Label, but found a Cross.")
}

def render: String = value.toList match {
case Nil => ""
case Segment.Label(head) :: rest =>
val stringSegments = rest.map {
case Segment.Label(s) => "." + s
case Segment.Cross(vs) => "[" + vs.mkString(",") + "]"
}
head + stringSegments.mkString
case Segment.Cross(_) :: _ =>
throw new IllegalArgumentException("Segments must start with a Label, but found a Cross.")
def parts: List[String] = value.flatMap(_.pathSegments).toList

def head: Segment = value.head

def render: String = {
def renderCross(cross: Segment.Cross): String = "[" + cross.value.mkString(",") + "]"
value.toList match {
case Nil => ""
case head :: rest =>
val headSegment = head match
case Segment.Label(s) => s
case c: Segment.Cross => renderCross(c)
val stringSegments = rest.map {
case Segment.Label(s) => "." + s
case c: Segment.Cross => renderCross(c)
}
headSegment + stringSegments.mkString
}
}
override lazy val hashCode: Int = value.hashCode()
}
Expand Down
4 changes: 3 additions & 1 deletion core/resolve/src/mill/resolve/ParseArgs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,9 @@ private[mill] object ParseArgs {
def crossSegment = P("[" ~ identCross.rep(1, sep = ",") ~ "]").map(Segment.Cross(_))
def defaultCrossSegment = P("[]").map(_ => Segment.Cross(Seq()))

def simpleQuery = P(segment ~ ("." ~ segment | crossSegment | defaultCrossSegment).rep).map {
def simpleQuery = P(
(segment | crossSegment | defaultCrossSegment) ~ ("." ~ segment | crossSegment | defaultCrossSegment).rep
).map {
case (h, rest) => Segments(h +: rest)
}

Expand Down
10 changes: 10 additions & 0 deletions integration/feature/root-cross-module/resources/bar/package.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package build.bar

import mill._
import scalalib._

object `package` extends RootModule with Cross[FooModule]("3.6.2", "2.13.16") {}

trait FooModule extends CrossScalaModule {
def foo = Task { true }
}
12 changes: 12 additions & 0 deletions integration/feature/root-cross-module/resources/build.mill
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package build
import $packages._

import mill._, scalalib._

object `package` extends RootModule with Cross[FooModule]("3.6.2", "2.13.16") {
object baz extends Cross[FooModule]("3.6.2", "2.13.16") {}
}

trait FooModule extends CrossScalaModule {
def foo = Task { true }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package mill.integration

import mill.testkit.UtestIntegrationTestSuite

import utest._

object RootCrossModuleTests extends UtestIntegrationTestSuite {
val tests: Tests = Tests {
test("root") - integrationTest {
tester =>
import tester._
assert(eval("[2.13.16].foo").isSuccess)
}

test("module") - integrationTest {
tester =>
import tester._
assert(eval("baz[2.13.16].foo").isSuccess)
}

test("subpackage") - integrationTest {
tester =>
import tester._
assert(eval("bar[2.13.16].foo").isSuccess)
}
}
}

0 comments on commit fb7ad6d

Please sign in to comment.