Skip to content

Commit

Permalink
Show workers in resolve / show / inspect too (#3580)
Browse files Browse the repository at this point in the history
This PR generalizes some `Target`-related stuff in `Resolve` to all
`NamedTask`s with no arguments (targets and workers), and makes sure
`resolve` / `show` / `inspect` work fine with workers. In particular,
running `show` on a worker now prints something like
```json
{
  "worker": "foo.theWorker",
  "toString": "build_.package_$foo$$anon$1@4acbdb44",
  "inputsHash": -2075822978
}
```
  • Loading branch information
alexarchambault authored Sep 26, 2024
1 parent c38460b commit 69243dc
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 33 deletions.
16 changes: 15 additions & 1 deletion integration/feature/docannotations/resources/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,27 @@ trait JUnitTests extends TestModule.Junit4 {
def task = Task {
"???"
}

/**
* *The worker*
*/
def theWorker = Task.Worker {
()
}
}

/**
* The Core Module Docz!
*/
object core extends JavaModule {
object test extends JavaTests with JUnitTests
object test extends JavaTests with JUnitTests {
/**
* -> The worker <-
*/
def theWorker = Task.Worker {
()
}
}

/**
* Core Target Docz!
Expand Down
16 changes: 16 additions & 0 deletions integration/feature/docannotations/src/DocAnnotationsTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ object DocAnnotationsTests extends UtestIntegrationTestSuite {
)
)

assert(eval(("inspect", "core.test.theWorker")).isSuccess)
val theWorkerInspect = out("inspect").json.str

assert(
globMatches(
"""core.test.theWorker(build.mill:...)
| -> The worker <-
|
| *The worker*
|
|Inputs:
|""".stripMargin,
theWorkerInspect
)
)

// Make sure both kebab-case and camelCase flags work, even though the
// docs from `inspect` only show the kebab-case version
assert(eval(("core.ivyDepsTree", "--withCompile", "--withRuntime")).isSuccess)
Expand Down
48 changes: 28 additions & 20 deletions main/eval/src/mill/eval/GroupEvaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -364,28 +364,36 @@ private[mill] trait GroupEvaluator {
inputsHash: Int,
labelled: Terminal.Labelled[_]
): Unit = {
labelled.task.asWorker match {
case Some(w) =>
workerCache.synchronized {
workerCache.update(w.ctx.segments, (workerCacheHash(inputsHash), v))
}
case None =>
val terminalResult = labelled
.task
.writerOpt
.asInstanceOf[Option[upickle.default.Writer[Any]]]
.map { w => upickle.default.writeJs(v.value)(w) }

for (json <- terminalResult) {
os.write.over(
metaPath,
upickle.default.stream(
Evaluator.Cached(json, hashCode, inputsHash),
indent = 4
),
createFolders = true
for (w <- labelled.task.asWorker)
workerCache.synchronized {
workerCache.update(w.ctx.segments, (workerCacheHash(inputsHash), v))
}

val terminalResult = labelled
.task
.writerOpt
.map { w =>
upickle.default.writeJs(v.value)(w.asInstanceOf[upickle.default.Writer[Any]])
}
.orElse {
labelled.task.asWorker.map { w =>
ujson.Obj(
"worker" -> ujson.Str(labelled.segments.render),
"toString" -> ujson.Str(v.value.toString),
"inputsHash" -> ujson.Num(inputsHash)
)
}
}

for (json <- terminalResult) {
os.write.over(
metaPath,
upickle.default.stream(
Evaluator.Cached(json, hashCode, inputsHash),
indent = 4
),
createFolders = true
)
}
}

Expand Down
15 changes: 9 additions & 6 deletions main/resolve/src/mill/resolve/Resolve.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ object Resolve {
allowPositionalCommandArgs: Boolean
) = {
val taskList = resolved.map {
case r: Resolved.Target =>
case r: Resolved.NamedTask =>
val instantiated = ResolveCore
.instantiateModule(rootModule, r.segments.init)
.flatMap(instantiateTarget(r, _))
.flatMap(instantiateNamedTask(r, _))

instantiated.map(Some(_))

Expand Down Expand Up @@ -76,7 +76,7 @@ object Resolve {

directChildrenOrErr.flatMap(directChildren =>
directChildren.head match {
case r: Resolved.Target => instantiateTarget(r, value).map(Some(_))
case r: Resolved.NamedTask => instantiateNamedTask(r, value).map(Some(_))
case r: Resolved.Command =>
instantiateCommand(
rootModule,
Expand Down Expand Up @@ -104,13 +104,16 @@ object Resolve {
items.distinctBy(_.ctx.segments)
}

private def instantiateTarget(r: Resolved.Target, p: Module): Either[String, Target[_]] = {
private def instantiateNamedTask(
r: Resolved.NamedTask,
p: Module
): Either[String, NamedTask[_]] = {
val definition = Reflect
.reflect(p.getClass, classOf[Target[_]], _ == r.segments.parts.last, true)
.reflect(p.getClass, classOf[NamedTask[_]], _ == r.segments.parts.last, true)
.head

ResolveCore.catchWrapException(
definition.invoke(p).asInstanceOf[Target[_]]
definition.invoke(p).asInstanceOf[NamedTask[_]]
)
}

Expand Down
12 changes: 6 additions & 6 deletions main/resolve/src/mill/resolve/ResolveCore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ private object ResolveCore {

object Resolved {
case class Module(segments: Segments, cls: Class[_]) extends Resolved
case class Target(segments: Segments) extends Resolved
case class NamedTask(segments: Segments) extends Resolved
case class Command(segments: Segments) extends Resolved
}

Expand Down Expand Up @@ -327,7 +327,7 @@ private object ResolveCore {
.map(
_.map {
case (Resolved.Module(s, cls), _) => Resolved.Module(segments ++ s, cls)
case (Resolved.Target(s), _) => Resolved.Target(segments ++ s)
case (Resolved.NamedTask(s), _) => Resolved.NamedTask(segments ++ s)
case (Resolved.Command(s), _) => Resolved.Command(segments ++ s)
}
.toSet
Expand Down Expand Up @@ -376,10 +376,10 @@ private object ResolveCore {
}
}

val targets = Reflect
.reflect(cls, classOf[Target[_]], namePred, noParams = true)
val namedTasks = Reflect
.reflect(cls, classOf[NamedTask[_]], namePred, noParams = true)
.map { m =>
Resolved.Target(Segments.labels(decode(m.getName))) ->
Resolved.NamedTask(Segments.labels(decode(m.getName))) ->
None
}

Expand All @@ -388,7 +388,7 @@ private object ResolveCore {
.map(m => decode(m.getName))
.map { name => Resolved.Command(Segments.labels(name)) -> None }

modulesOrErr.map(_ ++ targets ++ commands)
modulesOrErr.map(_ ++ namedTasks ++ commands)
}

def notFoundResult(
Expand Down
43 changes: 43 additions & 0 deletions main/test/src/mill/main/MainModuleTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ object MainModuleTests extends TestSuite {
Map("1" -> "hello", "2" -> "world")
}
def helloCommand(x: Int, y: Task[String]) = Task.Command { (x, y(), hello()) }

/**
* The hello worker
*/
def helloWorker = Task.Worker {
// non-JSON-serializable, but everything should work fine nonetheless
new AutoCloseable {
def close() = ()
override def toString =
"theHelloWorker"
}
}
override lazy val millDiscover: Discover = Discover[this.type]
}

Expand Down Expand Up @@ -93,6 +105,17 @@ object MainModuleTests extends TestSuite {
res.contains("hello")
)
}
test("worker") - UnitTester(mainModule, null).scoped { eval =>
val Right(result) = eval.apply("inspect", "helloWorker")

val Seq(res: String) = result.value
assert(
res.startsWith("helloWorker("),
res.contains("MainModuleTests.scala:"),
res.contains("The hello worker"),
res.contains("hello")
)
}
}

test("show") {
Expand Down Expand Up @@ -173,6 +196,14 @@ object MainModuleTests extends TestSuite {
val Seq(res) = result.value
assert(res == ujson.Arr(1337, "lol", ujson.Arr("hello", "world")))
}

test("worker") {
val Right(result) = evaluator.apply("show", "helloWorker")
val Seq(res: ujson.Obj) = result.value
assert(res("toString").str == "theHelloWorker")
assert(res("worker").str == "helloWorker")
assert(res("inputsHash").numOpt.isDefined)
}
}

test("showNamed") {
Expand Down Expand Up @@ -209,6 +240,18 @@ object MainModuleTests extends TestSuite {
}
}

test("resolve") {
UnitTester(mainModule, null).scoped { eval =>
val Right(result) = eval.apply("resolve", "_")

val Seq(res: Seq[String]) = result.value
assert(res.contains("hello"))
assert(res.contains("hello2"))
assert(res.contains("helloCommand"))
assert(res.contains("helloWorker"))
}
}

test("clean") {
val ev = UnitTester(cleanModule, null)
val out = ev.evaluator.outPath
Expand Down

0 comments on commit 69243dc

Please sign in to comment.