diff --git a/example/web/4-webapp-scalajs/build.sc b/example/web/4-webapp-scalajs/build.sc index 6f3d826a7fd..1d923ac2492 100644 --- a/example/web/4-webapp-scalajs/build.sc +++ b/example/web/4-webapp-scalajs/build.sc @@ -8,7 +8,7 @@ object app extends RootModule with ScalaModule{ ivy"com.lihaoyi::scalatags:0.12.0" ) - def resources = T.sources{ + def resources = T{ os.makeDir(T.dest / "webapp") val jsPath = client.fastLinkJS().dest.path // Move the main.js[.map] files into the proper filesystem position diff --git a/example/web/5-webapp-scalajs-shared/build.sc b/example/web/5-webapp-scalajs-shared/build.sc index a6ef830533f..581d4afb613 100644 --- a/example/web/5-webapp-scalajs-shared/build.sc +++ b/example/web/5-webapp-scalajs-shared/build.sc @@ -14,7 +14,7 @@ object app extends RootModule with AppScalaModule{ def ivyDeps = Agg(ivy"com.lihaoyi::cask:0.9.0") - def resources = T.sources{ + def resources = T{ os.makeDir(T.dest / "webapp") val jsPath = client.fastLinkJS().dest.path os.copy(jsPath / "main.js", T.dest / "webapp" / "main.js") diff --git a/main/core/src/mill/define/Discover.scala b/main/core/src/mill/define/Discover.scala index 734ab826a6d..c7cc67e4abd 100644 --- a/main/core/src/mill/define/Discover.scala +++ b/main/core/src/mill/define/Discover.scala @@ -64,17 +64,14 @@ object Discover { cases: (Type, Int, String)* ): Unit = { for (m <- methods.toList) { - for ((tt, n, label) <- cases) { - if ( - m.returnType <:< tt && - m.paramLists.length != n - ) { - c.abort( + cases + .find{case (tt, n, label) => m.returnType <:< tt} + .foreach{case (tt, n, label) => + if (m.paramLists.length != n) c.abort( m.pos, s"$label definitions must have $n parameter list" + (if (n == 1) "" else "s") ) } - } } } val mapping = for { @@ -84,11 +81,8 @@ object Discover { overridesRoutes = { assertParamListCounts( methods, - (weakTypeOf[mill.define.Sources], 0, "`T.sources`"), - (weakTypeOf[mill.define.Input[_]], 0, "`T.input`"), - (weakTypeOf[mill.define.Persistent[_]], 0, "`T.persistent`"), - (weakTypeOf[mill.define.Target[_]], 0, "`T{...}`"), - (weakTypeOf[mill.define.Command[_]], 1, "`T.command`") + (weakTypeOf[mill.define.Command[_]], 1, "`T.command`"), + (weakTypeOf[mill.define.Target[_]], 0, "Target"), ) for { diff --git a/main/core/src/mill/define/Task.scala b/main/core/src/mill/define/Task.scala index d2c4eaeec62..94e755a25db 100644 --- a/main/core/src/mill/define/Task.scala +++ b/main/core/src/mill/define/Task.scala @@ -40,8 +40,54 @@ abstract class Task[+T] extends Task.Ops[T] with Applyable[Task, T] { def self: Task[T] = this } +object Task { + + abstract class Ops[+T] { this: Task[T] => + def map[V](f: T => V) = new Task.Mapped(this, f) + + def filter(f: T => Boolean) = this + def withFilter(f: T => Boolean) = this + def zip[V](other: Task[V]) = new Task.Zipped(this, other) + + } + + class Sequence[+T](inputs0: Seq[Task[T]]) extends Task[Seq[T]] { + val inputs = inputs0 + def evaluate(args: mill.api.Ctx) = { + for (i <- 0 until args.length) + yield args(i).asInstanceOf[T] + } + } + class TraverseCtx[+T, V]( + inputs0: Seq[Task[T]], + f: (IndexedSeq[T], mill.api.Ctx) => Result[V] + ) extends Task[V] { + val inputs = inputs0 + def evaluate(args: mill.api.Ctx) = { + f( + for (i <- 0 until args.length) + yield args(i).asInstanceOf[T], + args + ) + } + } + class Mapped[+T, +V](source: Task[T], f: T => V) extends Task[V] { + def evaluate(args: mill.api.Ctx) = f(args(0)) + val inputs = List(source) + } + class Zipped[+T, +V](source1: Task[T], source2: Task[V]) extends Task[(T, V)] { + def evaluate(args: mill.api.Ctx) = (args(0), args(1)) + val inputs = List(source1, source2) + } +} + +/** + * Represents a task that can be referenced by its path segments. + */ trait NamedTask[+T] extends Task[T] { - def ctx: mill.define.Ctx + def t: Task[T] + def ctx0: mill.define.Ctx + def isPrivate: Option[Boolean] def label: String = ctx.segment match { case Segment.Label(v) => v case Segment.Cross(_) => throw new IllegalArgumentException( @@ -49,13 +95,19 @@ trait NamedTask[+T] extends Task[T] { ) } override def toString = ctx.segments.render - def isPrivate: Option[Boolean] = None -} -trait Target[+T] extends NamedTask[T] { - override def asTarget: Option[Target[T]] = Some(this) - def readWrite: RW[_] + + def evaluate(args: mill.api.Ctx) = args[T](0) + + val ctx = ctx0.withSegments(segments = ctx0.segments ++ Seq(ctx0.segment)) + val inputs = Seq(t) + + def readWriterOpt: Option[upickle.default.ReadWriter[_]] = None + + def writerOpt: Option[upickle.default.Writer[_]] = readWriterOpt.orElse(None) } +trait Target[+T] extends NamedTask[T] + object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { // convenience def dest(implicit ctx: mill.api.Ctx.Dest): os.Path = ctx.dest @@ -80,7 +132,7 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { val lhs = Applicative.impl0[Task, T, mill.api.Ctx](c)(reify(Result.Success(t.splice)).tree) - mill.moduledefs.Cacher.impl0[TargetImpl[T]](c)( + mill.moduledefs.Cacher.impl0[Target[T]](c)( reify( new TargetImpl[T]( lhs.splice, @@ -144,10 +196,10 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { ) } - def sources(values: Result[os.Path]*)(implicit ctx: mill.define.Ctx): Sources = macro sourcesImpl1 + def sources(values: Result[os.Path]*)(implicit ctx: mill.define.Ctx): Target[Seq[PathRef]] = macro sourcesImpl1 def sourcesImpl1(c: Context)(values: c.Expr[Result[os.Path]]*)(ctx: c.Expr[mill.define.Ctx]) - : c.Expr[Sources] = { + : c.Expr[Target[Seq[PathRef]]] = { import c.universe._ val wrapped = for (value <- values.toList) @@ -157,9 +209,9 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { val taskIsPrivate = isPrivateTargetOption(c) - mill.moduledefs.Cacher.impl0[Sources](c)( + mill.moduledefs.Cacher.impl0[SourcesImpl](c)( reify( - new Sources( + new SourcesImpl( Target.sequence(c.Expr[List[Task[PathRef]]](q"_root_.scala.List(..$wrapped)").splice), ctx.splice, taskIsPrivate.splice @@ -168,18 +220,18 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { ) } - def sources(values: Result[Seq[PathRef]])(implicit ctx: mill.define.Ctx): Sources = + def sources(values: Result[Seq[PathRef]])(implicit ctx: mill.define.Ctx): Target[Seq[PathRef]] = macro sourcesImpl2 def sourcesImpl2(c: Context)(values: c.Expr[Result[Seq[PathRef]]])(ctx: c.Expr[mill.define.Ctx]) - : c.Expr[Sources] = { + : c.Expr[Target[Seq[PathRef]]] = { import c.universe._ val taskIsPrivate = isPrivateTargetOption(c) - mill.moduledefs.Cacher.impl0[Sources](c)( + mill.moduledefs.Cacher.impl0[SourcesImpl](c)( reify( - new Sources( + new SourcesImpl( Applicative.impl0[Task, Seq[PathRef], mill.api.Ctx](c)(values.tree).splice, ctx.splice, taskIsPrivate.splice @@ -187,10 +239,10 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { ) ) } - def source(value: Result[os.Path])(implicit ctx: mill.define.Ctx): Source = macro sourceImpl1 + def source(value: Result[os.Path])(implicit ctx: mill.define.Ctx): Target[PathRef] = macro sourceImpl1 def sourceImpl1(c: Context)(value: c.Expr[Result[os.Path]])(ctx: c.Expr[mill.define.Ctx]) - : c.Expr[Source] = { + : c.Expr[Target[PathRef]] = { import c.universe._ val wrapped = @@ -200,9 +252,9 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { val taskIsPrivate = isPrivateTargetOption(c) - mill.moduledefs.Cacher.impl0[Source](c)( + mill.moduledefs.Cacher.impl0[Target[PathRef]](c)( reify( - new Source( + new SourceImpl( wrapped.splice, ctx.splice, taskIsPrivate.splice @@ -211,17 +263,17 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { ) } - def source(value: Result[PathRef])(implicit ctx: mill.define.Ctx): Source = macro sourceImpl2 + def source(value: Result[PathRef])(implicit ctx: mill.define.Ctx): Target[PathRef] = macro sourceImpl2 def sourceImpl2(c: Context)(value: c.Expr[Result[PathRef]])(ctx: c.Expr[mill.define.Ctx]) - : c.Expr[Source] = { + : c.Expr[Target[PathRef]] = { import c.universe._ val taskIsPrivate = isPrivateTargetOption(c) - mill.moduledefs.Cacher.impl0[Source](c)( + mill.moduledefs.Cacher.impl0[Target[PathRef]](c)( reify( - new Source( + new SourceImpl( Applicative.impl0[Task, PathRef, mill.api.Ctx](c)(value.tree).splice, ctx.splice, taskIsPrivate.splice @@ -232,20 +284,20 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { def input[T](value: Result[T])(implicit w: upickle.default.Writer[T], ctx: mill.define.Ctx - ): Input[T] = + ): Target[T] = macro inputImpl[T] def inputImpl[T: c.WeakTypeTag](c: Context)(value: c.Expr[T])( w: c.Expr[upickle.default.Writer[T]], ctx: c.Expr[mill.define.Ctx] - ): c.Expr[Input[T]] = { + ): c.Expr[Target[T]] = { import c.universe._ val taskIsPrivate = isPrivateTargetOption(c) - mill.moduledefs.Cacher.impl0[Input[T]](c)( + mill.moduledefs.Cacher.impl0[InputImpl[T]](c)( reify( - new Input[T]( + new InputImpl[T]( Applicative.impl[Task, T, mill.api.Ctx](c)(value).splice, ctx.splice, w.splice, @@ -342,20 +394,20 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { def task[T](t: Result[T]): Task[T] = macro Applicative.impl[Task, T, mill.api.Ctx] - def persistent[T](t: Result[T])(implicit rw: RW[T], ctx: mill.define.Ctx): Persistent[T] = + def persistent[T](t: Result[T])(implicit rw: RW[T], ctx: mill.define.Ctx): Target[T] = macro persistentImpl[T] def persistentImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T])( rw: c.Expr[RW[T]], ctx: c.Expr[mill.define.Ctx] - ): c.Expr[Persistent[T]] = { + ): c.Expr[PersistentImpl[T]] = { import c.universe._ val taskIsPrivate = isPrivateTargetOption(c) - mill.moduledefs.Cacher.impl0[Persistent[T]](c)( + mill.moduledefs.Cacher.impl0[PersistentImpl[T]](c)( reify( - new Persistent[T]( + new PersistentImpl[T]( Applicative.impl[Task, T, mill.api.Ctx](c)(t).splice, ctx.splice, rw.splice, @@ -375,123 +427,68 @@ object Target extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { } } -abstract class NamedTaskImpl[+T]( - ctx0: mill.define.Ctx, - t: Task[T], - override val isPrivate: Option[Boolean] -) extends NamedTask[T] { - def evaluate(args: mill.api.Ctx) = args[T](0) - val ctx = ctx0.withSegments(segments = ctx0.segments ++ Seq(ctx0.segment)) - val inputs = Seq(t) +class TargetImpl[+T]( + val t: Task[T], + val ctx0: mill.define.Ctx, + val readWriter: RW[_], + val isPrivate: Option[Boolean] +) extends Target[T]{ + override def asTarget: Option[Target[T]] = Some(this) + override def readWriterOpt = Some(readWriter) } -class TargetImpl[+T]( +class PersistentImpl[+T]( t: Task[T], ctx0: mill.define.Ctx, - override val readWrite: RW[_], + readWriter: RW[_], isPrivate: Option[Boolean] -) extends NamedTaskImpl[T](ctx0, t, isPrivate) - with Target[T] {} +) extends TargetImpl[T](t, ctx0, readWriter, isPrivate) { + override def flushDest = false +} class Command[+T]( - t: Task[T], - ctx0: mill.define.Ctx, + val t: Task[T], + val ctx0: mill.define.Ctx, val writer: W[_], val cls: Class[_], - isPrivate: Option[Boolean] -) extends NamedTaskImpl[T](ctx0, t, isPrivate) { + val isPrivate: Option[Boolean] +) extends NamedTask[T] { override def asCommand = Some(this) + override def writerOpt = Some(writer) } -class Worker[+T](t: Task[T], ctx0: mill.define.Ctx, isPrivate: Option[Boolean]) - extends NamedTaskImpl[T](ctx0, t, isPrivate) { +class Worker[+T](val t: Task[T], val ctx0: mill.define.Ctx, val isPrivate: Option[Boolean]) + extends NamedTask[T]{ override def flushDest = false override def asWorker = Some(this) } -class Persistent[+T]( - t: Task[T], - ctx0: mill.define.Ctx, - readWrite: RW[_], - isPrivate: Option[Boolean] -) extends TargetImpl[T](t, ctx0, readWrite, isPrivate) { - - override def flushDest = false -} - -class Input[T]( - t: Task[T], - ctx0: mill.define.Ctx, +class InputImpl[T]( + val t: Task[T], + val ctx0: mill.define.Ctx, val writer: upickle.default.Writer[_], - isPrivate: Option[Boolean] -) extends NamedTaskImpl[T](ctx0, t, isPrivate) { + val isPrivate: Option[Boolean] +) extends Target[T] { override def sideHash = util.Random.nextInt() + override def writerOpt = Some(writer) } -class Sources(t: Task[Seq[PathRef]], ctx0: mill.define.Ctx, isPrivate: Option[Boolean]) - extends Input[Seq[PathRef]]( +class SourcesImpl(t: Task[Seq[PathRef]], ctx0: mill.define.Ctx, isPrivate: Option[Boolean]) + extends InputImpl[Seq[PathRef]]( t, ctx0, - upickle.default.SeqLikeWriter[Seq, PathRef], + upickle.default.readwriter[Seq[PathRef]], isPrivate - ) + ){ + override def readWriterOpt = Some(upickle.default.readwriter[Seq[PathRef]]) +} -class Source(t: Task[PathRef], ctx0: mill.define.Ctx, isPrivate: Option[Boolean]) - extends Input[PathRef]( +class SourceImpl(t: Task[PathRef], ctx0: mill.define.Ctx, isPrivate: Option[Boolean]) + extends InputImpl[PathRef]( t, ctx0, - PathRef.jsonFormatter, + upickle.default.readwriter[PathRef], isPrivate - ) - -object Task { - - class Task0[T](t: T) extends Task[T] { - lazy val t0 = t - val inputs = Nil - def evaluate(args: mill.api.Ctx) = t0 - } - - abstract class Ops[+T] { this: Task[T] => - def map[V](f: T => V) = new Task.Mapped(this, f) - def mapDest[V](f: (T, mill.api.Ctx) => Result[V]) = new Task.MappedDest(this, f) - - def filter(f: T => Boolean) = this - def withFilter(f: T => Boolean) = this - def zip[V](other: Task[V]) = new Task.Zipped(this, other) - - } - - class Sequence[+T](inputs0: Seq[Task[T]]) extends Task[Seq[T]] { - val inputs = inputs0 - def evaluate(args: mill.api.Ctx) = { - for (i <- 0 until args.length) - yield args(i).asInstanceOf[T] - } - } - class TraverseCtx[+T, V]( - inputs0: Seq[Task[T]], - f: (IndexedSeq[T], mill.api.Ctx) => Result[V] - ) extends Task[V] { - val inputs = inputs0 - def evaluate(args: mill.api.Ctx) = { - f( - for (i <- 0 until args.length) - yield args(i).asInstanceOf[T], - args - ) - } - } - class Mapped[+T, +V](source: Task[T], f: T => V) extends Task[V] { - def evaluate(args: mill.api.Ctx) = f(args(0)) - val inputs = List(source) - } - class MappedDest[+T, +V](source: Task[T], f: (T, mill.api.Ctx) => Result[V]) extends Task[V] { - def evaluate(args: mill.api.Ctx) = f(args(0), args) - val inputs = List(source) - } - class Zipped[+T, +V](source1: Task[T], source2: Task[V]) extends Task[(T, V)] { - def evaluate(args: mill.api.Ctx) = (args(0), args(1)) - val inputs = List(source1, source2) - } + ){ + override def readWriterOpt = Some(upickle.default.readwriter[PathRef]) } diff --git a/main/core/src/mill/define/package.scala b/main/core/src/mill/define/package.scala new file mode 100644 index 00000000000..23f2fb6ad34 --- /dev/null +++ b/main/core/src/mill/define/package.scala @@ -0,0 +1,7 @@ +package mill +package object define{ + type Source = define.Target[mill.api.PathRef] + type Sources = define.Target[Seq[mill.api.PathRef]] + type Input[T] = define.Target[T] + type Persistent[T] = define.Target[T] +} diff --git a/main/core/src/mill/eval/Evaluator.scala b/main/core/src/mill/eval/Evaluator.scala index bfc0404321a..0e12f30c015 100644 --- a/main/core/src/mill/eval/Evaluator.scala +++ b/main/core/src/mill/eval/Evaluator.scala @@ -23,18 +23,7 @@ import scala.jdk.CollectionConverters._ import scala.reflect.ClassTag import scala.util.control.NonFatal -case class Labelled[T](task: NamedTask[T], segments: Segments) { - def format: Option[default.ReadWriter[T]] = task match { - case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) - case _ => None - } - def writer: Option[default.Writer[T]] = task match { - case t: mill.define.Command[T] => Some(t.writer.asInstanceOf[upickle.default.Writer[T]]) - case t: mill.define.Input[T] => Some(t.writer.asInstanceOf[upickle.default.Writer[T]]) - case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) - case _ => None - } -} +case class Labelled[T](task: NamedTask[T], segments: Segments) /** * Evaluate tasks. @@ -380,7 +369,7 @@ class Evaluator private (_home: os.Path, case NonFatal(_) => None } if cached.inputsHash == inputsHash - reader <- labelledNamedTask.format + reader <- labelledNamedTask.task.readWriterOpt parsed <- try Some(upickle.default.read(cached.value)(reader)) catch { @@ -493,7 +482,8 @@ class Evaluator private (_home: os.Path, } case None => val terminalResult = labelledNamedTask - .writer + .task + .writerOpt .asInstanceOf[Option[upickle.default.Writer[Any]]] .map(w => upickle.default.writeJs(v)(w) -> v) diff --git a/main/core/src/mill/eval/EvaluatorPaths.scala b/main/core/src/mill/eval/EvaluatorPaths.scala index b1516a10453..04f22cc6b8a 100644 --- a/main/core/src/mill/eval/EvaluatorPaths.scala +++ b/main/core/src/mill/eval/EvaluatorPaths.scala @@ -1,7 +1,7 @@ package mill.eval import mill.api.internal -import mill.define.{NamedTask, Segment, Segments} +import mill.define.{NamedTask, Segment, Segments, Target} case class EvaluatorPaths private (dest: os.Path, meta: os.Path, log: os.Path) { private def copy(dest: os.Path = dest, meta: os.Path = meta, log: os.Path = log): EvaluatorPaths = diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index cd1c0712797..d59caeb32b4 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -5,7 +5,7 @@ import mainargs.TokensReader import java.util.concurrent.LinkedBlockingQueue import mill.{BuildInfo, T} import mill.api.{Ctx, PathRef, Result, internal} -import mill.define.{Command, NamedTask, Segments, SelectMode, Target, Task} +import mill.define.{Command, Segments, SelectMode, NamedTask, TargetImpl, Task} import mill.eval.{Evaluator, EvaluatorPaths} import mill.util.{PrintLogger, Watched} import pprint.{Renderer, Tree, Truncated} @@ -180,7 +180,7 @@ trait MainModule extends mill.Module { def rec(t: Task[_]): Seq[Segments] = { if (seen(t)) Nil // do nothing else t match { - case t: Target[_] if evaluator.rootModule.millInternal.targets.contains(t) => + case t: TargetImpl[_] if evaluator.rootModule.millInternal.targets.contains(t) => Seq(t.ctx.segments) case _ => seen.add(t) diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 457591c1664..07a12dd8f4f 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -169,8 +169,8 @@ object RunScript { val watched = evaluated.results .iterator .collect { - case (t: Sources, Result.Success(ps: Seq[PathRef])) => ps - case (t: Source, Result.Success(p: PathRef)) => Seq(p) + case (t: SourcesImpl, Result.Success(ps: Seq[PathRef])) => ps + case (t: SourceImpl, Result.Success(p: PathRef)) => Seq(p) } .flatten .toSeq diff --git a/main/test/src/mill/define/GraphTests.scala b/main/test/src/mill/define/GraphTests.scala index 449fffefd55..36c5230d4c1 100644 --- a/main/test/src/mill/define/GraphTests.scala +++ b/main/test/src/mill/define/GraphTests.scala @@ -79,7 +79,7 @@ object GraphTests extends TestSuite { val important = important0.map(_(base)) val grouped = Graph.groupAroundImportantTargets(topoSorted) { - case t: Target[_] if important.contains(t) => t + case t: TargetImpl[_] if important.contains(t) => t: Target[_] } val flattened = Agg.from(grouped.values().flatMap(_.items)) diff --git a/main/test/src/mill/define/MacroErrorTests.scala b/main/test/src/mill/define/MacroErrorTests.scala index e620ef2823f..908e9853509 100644 --- a/main/test/src/mill/define/MacroErrorTests.scala +++ b/main/test/src/mill/define/MacroErrorTests.scala @@ -28,6 +28,7 @@ object MacroErrorTests extends TestSuite { e.pos.contains("def w = ") ) } + "target" - { val e = compileError(""" object foo extends mill.util.TestUtil.BaseModule{ @@ -36,7 +37,7 @@ object MacroErrorTests extends TestSuite { mill.define.Discover[foo.type] """) assert( - e.msg.contains("`T{...}` definitions must have 0 parameter lists"), + e.msg.contains("Target definitions must have 0 parameter lists"), e.pos.contains("def x() = ") ) } @@ -48,7 +49,7 @@ object MacroErrorTests extends TestSuite { mill.define.Discover[foo.type] """) assert( - e.msg.contains("`T.input` definitions must have 0 parameter lists"), + e.msg.contains("Target definitions must have 0 parameter lists"), e.pos.contains("def y() = ") ) } @@ -60,7 +61,7 @@ object MacroErrorTests extends TestSuite { mill.define.Discover[foo.type] """) assert( - e.msg.contains("`T.sources` definitions must have 0 parameter lists"), + e.msg.contains("Target definitions must have 0 parameter lists"), e.pos.contains("def z() = ") ) } @@ -72,7 +73,7 @@ object MacroErrorTests extends TestSuite { mill.define.Discover[foo.type] """) assert( - e.msg.contains("`T.persistent` definitions must have 0 parameter lists"), + e.msg.contains("Target definitions must have 0 parameter lists"), e.pos.contains("def a() = ") ) } diff --git a/main/test/src/mill/eval/EvaluationTests.scala b/main/test/src/mill/eval/EvaluationTests.scala index 36098a27fd8..de8c8d32f1c 100644 --- a/main/test/src/mill/eval/EvaluationTests.scala +++ b/main/test/src/mill/eval/EvaluationTests.scala @@ -1,7 +1,7 @@ package mill.eval import mill.util.TestUtil.{Test, test} -import mill.define.{Discover, Graph, Target, Task} +import mill.define.{Discover, Graph, TargetImpl, Task} import mill.{Module, T} import mill.util.{DummyLogger, TestEvaluator, TestGraphs, TestUtil} import mill.api.Strict.Agg @@ -181,14 +181,14 @@ class EvaluationTests(threadCount: Option[Int]) extends TestSuite { import separateGroups._ val checker = new Checker(separateGroups) val evaled1 = checker.evaluator.evaluate(Agg(right, left)) - val filtered1 = evaled1.evaluated.filter(_.isInstanceOf[Target[_]]) + val filtered1 = evaled1.evaluated.filter(_.isInstanceOf[TargetImpl[_]]) assert(filtered1.toSeq.sortBy(_.toString) == Seq(change, left, right).sortBy(_.toString)) val evaled2 = checker.evaluator.evaluate(Agg(right, left)) - val filtered2 = evaled2.evaluated.filter(_.isInstanceOf[Target[_]]) + val filtered2 = evaled2.evaluated.filter(_.isInstanceOf[TargetImpl[_]]) assert(filtered2 == Agg()) change.counter += 1 val evaled3 = checker.evaluator.evaluate(Agg(right, left)) - val filtered3 = evaled3.evaluated.filter(_.isInstanceOf[Target[_]]) + val filtered3 = evaled3.evaluated.filter(_.isInstanceOf[TargetImpl[_]]) assert(filtered3 == Agg(change, right)) } diff --git a/main/test/src/mill/eval/TaskTests.scala b/main/test/src/mill/eval/TaskTests.scala index 4b687d07cd6..77a295f8d99 100644 --- a/main/test/src/mill/eval/TaskTests.scala +++ b/main/test/src/mill/eval/TaskTests.scala @@ -7,7 +7,25 @@ import mill.util.{TestEvaluator, TestUtil} import utest.framework.TestPath trait TaskTests extends TestSuite { - trait Build extends TestUtil.BaseModule { + trait SuperBuild extends TestUtil.BaseModule { + + var superBuildInputCount = 0 + + def superBuildInputOverrideWithConstant = T.input { + superBuildInputCount += 1 + superBuildInputCount + } + + def superBuildInputOverrideUsingSuper = T.input { + superBuildInputCount += 1 + superBuildInputCount + } + + def superBuildTargetOverrideWithInput = T { + 1234 + } + } + trait Build extends SuperBuild{ var count = 0 var changeOnceCount = 0 var workerCloseCount = 0 @@ -89,6 +107,17 @@ trait TaskTests extends TestSuite { val w = changeOnceWorker() w.apply(1) } + + override def superBuildInputOverrideWithConstant = T{ 123 } + override def superBuildInputOverrideUsingSuper = T{ + 123 + super.superBuildInputOverrideUsingSuper() + } + + var superBuildTargetOverrideWithInputCount = 0 + override def superBuildTargetOverrideWithInput = T.input { + superBuildTargetOverrideWithInputCount += 1 + superBuildTargetOverrideWithInputCount + } } def withEnv(f: (Build, TestEvaluator) => Unit)(implicit tp: TestPath): Unit @@ -192,6 +221,27 @@ trait TaskTests extends TestSuite { assert(wc.head != secondCached) } } + + + "overrideDifferentKind" - { + "inputWithTarget" - { + "notUsingSuper" - withEnv { (build, check) => + check.apply(build.superBuildInputOverrideWithConstant) ==> Right((123, 1)) + check.apply(build.superBuildInputOverrideWithConstant) ==> Right((123, 0)) + check.apply(build.superBuildInputOverrideWithConstant) ==> Right((123, 0)) + } + "usingSuper" - withEnv { (build, check) => + check.apply(build.superBuildInputOverrideUsingSuper) ==> Right((124, 1)) + check.apply(build.superBuildInputOverrideUsingSuper) ==> Right((125, 1)) + check.apply(build.superBuildInputOverrideUsingSuper) ==> Right((126, 1)) + } + } + "targetWithInput" - withEnv { (build, check) => + check.apply(build.superBuildTargetOverrideWithInput) ==> Right((1, 0)) + check.apply(build.superBuildTargetOverrideWithInput) ==> Right((2, 0)) + check.apply(build.superBuildTargetOverrideWithInput) ==> Right((3, 0)) + } + } } } diff --git a/main/test/src/mill/util/TestEvaluator.scala b/main/test/src/mill/util/TestEvaluator.scala index ef3b315c8b1..5da300dc515 100644 --- a/main/test/src/mill/util/TestEvaluator.scala +++ b/main/test/src/mill/util/TestEvaluator.scala @@ -1,12 +1,7 @@ package mill.util -import mill.testkit.MillTestKit import java.io.{InputStream, PrintStream} -import mill.define.{Input, Target, Task} -import mill.api.Result.OuterStack import mill.eval.Evaluator -import mill.api.Strict.Agg -import utest.assert import utest.framework.TestPath import language.experimental.macros diff --git a/main/test/src/mill/util/TestUtil.scala b/main/test/src/mill/util/TestUtil.scala index 9c4f921e605..3dabf7aead8 100644 --- a/main/test/src/mill/util/TestUtil.scala +++ b/main/test/src/mill/util/TestUtil.scala @@ -43,13 +43,26 @@ object TestUtil extends MillTestKit { * controlled externally, so you can construct arbitrary dataflow graphs and * test how changes propagate. */ - class TestTarget(inputs: Seq[Task[Int]], val pure: Boolean)(implicit ctx0: mill.define.Ctx) - extends Test(inputs) - with Target[Int] { - val ctx = ctx0.withSegments(ctx0.segments ++ Seq(ctx0.segment)) - val readWrite = upickle.default.readwriter[Int] + class TestTarget(taskInputs: Seq[Task[Int]], val pure: Boolean)(implicit ctx0: mill.define.Ctx) + extends TargetImpl[Int]( + null, + ctx0, + upickle.default.readwriter[Int], + None + ){ + override def evaluate(args: mill.api.Ctx) = testTask.evaluate(args) + override val inputs = taskInputs + val testTask = new Test(taskInputs) + def counter_=(i: Int) = testTask.counter = i + def counter = testTask.counter + def failure_=(s: Option[String]) = testTask.failure = s + def failure = testTask.failure + def exception_=(s: Option[Throwable]) = testTask.exception = s + def exception = testTask.exception + override def sideHash = testTask.sideHash } + def checkTopological(targets: Agg[Task[_]]) = { val seen = mutable.Set.empty[Task[_]] for (t <- targets.indexed.reverseIterator) { diff --git a/main/testkit/src/mill/testkit/MillTestkit.scala b/main/testkit/src/mill/testkit/MillTestkit.scala index 2fbe434b2a4..7f4a8274f32 100644 --- a/main/testkit/src/mill/testkit/MillTestkit.scala +++ b/main/testkit/src/mill/testkit/MillTestkit.scala @@ -5,7 +5,7 @@ import mill.api.Result import mill.api.Result.OuterStack import mill.api.Strict.Agg import java.io.{InputStream, PrintStream} -import mill.define.{Input, Target, Task} +import mill.define.{Input, Task} import mill.eval.Evaluator import language.experimental.macros import mill.api.{DummyInputStream, Result} @@ -115,9 +115,8 @@ trait MillTestKit { Tuple2( evaluated.rawValues.head.asInstanceOf[Result.Success[T]].value, evaluated.evaluated.collect { - case t: Target[_] + case t: TargetImpl[_] if module.millInternal.targets.contains(t) - && !t.isInstanceOf[Input[_]] && !t.ctx.external => t case t: mill.define.Command[_] => t }.size @@ -148,11 +147,13 @@ trait MillTestKit { } def check(targets: Agg[Task[_]], expected: Agg[Task[_]]): Unit = { + + val evaluated = evaluator.evaluate(targets) .evaluated .flatMap(_.asTarget) .filter(module.millInternal.targets.contains) - .filter(!_.isInstanceOf[Input[_]]) + .filter(!_.isInstanceOf[InputImpl[_]]) assert( evaluated == expected, s"evaluated is not equal expected. evaluated=${evaluated}, expected=${expected}"