Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
lihaoyi committed Dec 16, 2024
1 parent feefd0b commit 71c399c
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 93 deletions.
169 changes: 78 additions & 91 deletions main/src/mill/main/RunScript.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package mill.main

import mill.define._
import mill.eval.{Evaluator, EvaluatorPaths, Terminal}
import mill.eval.{Evaluator, EvaluatorPaths}
import mill.util.Watchable
import mill.api.{PathRef, Result, Val}
import mill.api.Strict.Agg
Expand All @@ -26,122 +26,109 @@ object RunScript {
selectMode,
selectiveExecution = false
)

def evaluateTasksNamed(
evaluator: Evaluator,
scriptArgs: Seq[String],
selectMode: SelectMode,
selectiveExecution: Boolean = false
selectiveExecution: Boolean = false,
selectiveExecutionSave: Boolean = false
): Either[
String,
(Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]])
] = mill.eval.Evaluator.currentEvaluator.withValue(evaluator) {
val enableSelective = selectiveExecution && os.exists(evaluator.outPath / OutFiles.millSelectiveExecution)
val selectedTargetsOrErr =
if (enableSelective) {
SelectiveExecution.resolve0(evaluator, scriptArgs).map(_.flatMap(Array("+", _)).drop(1))
} else {
Right(scriptArgs.toArray)
}

selectedTargetsOrErr.flatMap { resolvedTaskNames =>
if (enableSelective && resolvedTaskNames.isEmpty) Right((Nil, Right(Nil)))
else {
val resolved = Resolve.Tasks.resolve(
evaluator.rootModule,
resolvedTaskNames,
selectMode,
evaluator.allowPositionalCommandArgs
)


resolved.map { t =>
val evaluated = evaluateNamed0(evaluator, Agg.from(t))
if (selectiveExecution) {
for (res <- evaluated._2) {
val (results, terminals, _) = res
val allInputHashes = results
.iterator
.collect {
case (t: InputImpl[_], TaskResult(Result.Success(Val(value)), _)) =>
(terminals(t).render, value.##)
}
.toMap
SelectiveExecution.saveMetadata(
evaluator,
SelectiveExecution.Metadata(allInputHashes, evaluator.methodCodeHashSignatures)
)
}
}
val (ws, either) = evaluated
(ws, either.map { case (r, t, v) => v })
}
}
] = {
val resolved = mill.eval.Evaluator.currentEvaluator.withValue(evaluator) {
Resolve.Tasks.resolve(
evaluator.rootModule,
scriptArgs,
selectMode,
evaluator.allowPositionalCommandArgs
)
}
for (targets <- resolved)
yield evaluateNamed(evaluator, Agg.from(targets), selectiveExecution, selectiveExecutionSave)
}

def evaluateNamed(
evaluator: Evaluator,
targets: Agg[NamedTask[Any]]
): (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) =
evaluateNamed(evaluator, targets, selectiveExecution = false, selectiveExecutionSave = false)

/**
* @param evaluator
* @param targets
* @return (watched-paths, Either[err-msg, Seq[(task-result, Option[(task-name, task-return-as-json)])]])
*/
def evaluateNamed(
evaluator: Evaluator,
targets: Agg[NamedTask[Any]]
targets: Agg[NamedTask[Any]],
selectiveExecution: Boolean = false,
selectiveExecutionSave: Boolean = false
): (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) = {
val (ws, either) = evaluateNamed0(evaluator, targets)
(ws, either.map { case (r, t, v) => v })
}

def evaluateNamed0(
evaluator: Evaluator,
targets: Agg[NamedTask[Any]]
): (
Seq[Watchable],
Either[
String,
(
Map[Task[_], TaskResult[Val]],
Map[Task[_], Terminal],
Seq[(Any, Option[(TaskName, ujson.Value)])]
)
]
) = {
val evaluated: Results = evaluator.evaluate(targets, serialCommandExec = true)
val watched = evaluated.results
.iterator
.collect {
case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), _)) =>
ps.map(Watchable.Path(_))
case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), _)) =>
Seq(Watchable.Path(p))
case (t: InputImpl[_], TaskResult(result, recalc)) =>
val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum
Seq(Watchable.Value(() => recalc().hashCode(), result.hashCode(), pretty))
}
.flatten
.toSeq
val selectedTargetsOrErr =
if (selectiveExecution && os.exists(evaluator.outPath / OutFiles.millSelectiveExecution)) {
SelectiveExecution
.diffMetadata(evaluator, targets.map(_.ctx.segments.render).toSeq)
.map { selected =>
val selectedSet = selected.toSet
targets.filter {
case c: Command[_] if c.exclusive => true
case t => selectedSet(t.ctx.segments.render)
}
}
} else Right(targets)

val errorStr = Evaluator.formatFailing(evaluated)
evaluated.failing.keyCount match {
case 0 =>
val nameAndJson = for (t <- targets.toSeq) yield {
t match {
case t: mill.define.NamedTask[_] =>
val jsonFile = EvaluatorPaths.resolveDestPaths(evaluator.outPath, t).meta
val metadata = upickle.default.read[Evaluator.Cached](ujson.read(jsonFile.toIO))
Some((t.toString, metadata.value))
selectedTargetsOrErr match {
case Left(err) => (Nil, Left(err))
case Right(selectedTargets) =>
val evaluated: Results = evaluator.evaluate(selectedTargets, serialCommandExec = true)
val watched = evaluated.results
.iterator
.collect {
case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), _)) =>
ps.map(Watchable.Path(_))
case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), _)) =>
Seq(Watchable.Path(p))
case (t: InputImpl[_], TaskResult(result, recalc)) =>
val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum
Seq(Watchable.Value(() => recalc().hashCode(), result.hashCode(), pretty))
}
.flatten
.toSeq

case _ => None
val allInputHashes = evaluated.results
.iterator
.collect {
case (t: InputImpl[_], TaskResult(Result.Success(Val(value)), _)) =>
(t.ctx.segments.render, value.##)
}
.toMap

if (selectiveExecutionSave) {
SelectiveExecution.saveMetadata(
evaluator,
SelectiveExecution.Metadata(allInputHashes, evaluator.methodCodeHashSignatures)
)
}

val rhs: Seq[(Any, Option[(TaskName, ujson.Value)])] = evaluated.values.zip(nameAndJson)
val results: Map[Task[_], TaskResult[Val]] = evaluated.results.toMap
val errorStr = Evaluator.formatFailing(evaluated)
evaluated.failing.keyCount match {
case 0 =>
val nameAndJson = for (t <- selectedTargets.toSeq) yield {
t match {
case t: mill.define.NamedTask[_] =>
val jsonFile = EvaluatorPaths.resolveDestPaths(evaluator.outPath, t).meta
val metadata = upickle.default.read[Evaluator.Cached](ujson.read(jsonFile.toIO))
Some((t.toString, metadata.value))

watched -> Right((results, evaluated.terminals, rhs))
case _ => None
}
}

case n => watched -> Left(s"$n tasks failed\n$errorStr")
watched -> Right(evaluated.values.zip(nameAndJson))
case n => watched -> Left(s"$n tasks failed\n$errorStr")
}
}
}
}
4 changes: 2 additions & 2 deletions main/src/mill/main/SelectiveExecution.scala
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private[mill] object SelectiveExecution {
Resolve.Segments.resolve(
evaluator.rootModule,
tasks,
SelectMode.Multi,
SelectMode.Separated,
evaluator.allowPositionalCommandArgs
).map(_.map(_.render))
} else {
Expand All @@ -143,7 +143,7 @@ private[mill] object SelectiveExecution {

def resolve0(evaluator: Evaluator, tasks: Seq[String]): Either[String, Array[String]] = {
for {
resolved <- Resolve.Tasks.resolve(evaluator.rootModule, tasks, SelectMode.Multi)
resolved <- Resolve.Tasks.resolve(evaluator.rootModule, tasks, SelectMode.Separated)
diffed <- SelectiveExecution.diffMetadata(evaluator, tasks)
} yield {
resolved
Expand Down

0 comments on commit 71c399c

Please sign in to comment.