diff options
21 files changed, 193 insertions, 124 deletions
@@ -108,7 +108,7 @@ val bridges = for{ def sources = T.source { val path = basePath / 'src mkdir(path) - path + PathRef(path) } def allSources = T{ Seq(PathRef(shared.downloadBridgeSource(T.ctx().dest, crossVersion))) diff --git a/core/src/main/scala/mill/define/Task.scala b/core/src/main/scala/mill/define/Task.scala index b96e80c6..39ecb896 100644 --- a/core/src/main/scala/mill/define/Task.scala +++ b/core/src/main/scala/mill/define/Task.scala @@ -66,7 +66,7 @@ object Target extends TargetGenerated with Applicative.Applyer[Task, Task, Resul import c.universe._ val lhs = Applicative.impl0[Task, T, Ctx](c)(reify(Result.Success(t.splice)).tree) - mill.moduledefs.Cacher.impl0[TargetImpl, T](c)( + mill.moduledefs.Cacher.impl0[TargetImpl[T]](c)( reify( new TargetImpl[T]( lhs.splice, @@ -96,7 +96,7 @@ object Target extends TargetGenerated with Applicative.Applyer[Task, Task, Resul cl: c.Expr[Caller[mill.define.Task.Module]], o: c.Expr[Overrides]): c.Expr[Target[T]] = { import c.universe._ - mill.moduledefs.Cacher.impl0[Target, T](c)( + mill.moduledefs.Cacher.impl0[Target[T]](c)( reify( new TargetImpl[T]( Applicative.impl0[Task, T, Ctx](c)(t.tree).splice, @@ -127,7 +127,7 @@ object Target extends TargetGenerated with Applicative.Applyer[Task, Task, Resul cl: c.Expr[Caller[mill.define.Task.Module]], o: c.Expr[Overrides]): c.Expr[Target[T]] = { import c.universe._ - mill.moduledefs.Cacher.impl0[Target, T](c)( + mill.moduledefs.Cacher.impl0[Target[T]](c)( reify( new TargetImpl[T]( t.splice, @@ -148,7 +148,37 @@ object Target extends TargetGenerated with Applicative.Applyer[Task, Task, Resul cl: Caller[mill.define.Task.Module], o: Overrides): Command[T] = macro commandImpl[T] - def source(path: ammonite.ops.Path) = new Source(path) + def source[T](value: Result[T]) + (implicit r: R[T], + w: W[T], + e: sourcecode.Enclosing, + n: sourcecode.Name, + cl: Caller[mill.define.Task.Module], + o: Overrides): Source[T] = macro sourceImpl[T] + + def sourceImpl[T: c.WeakTypeTag](c: Context) + (value: c.Expr[T]) + (r: c.Expr[R[T]], + w: c.Expr[W[T]], + e: c.Expr[sourcecode.Enclosing], + n: c.Expr[sourcecode.Name], + cl: c.Expr[Caller[mill.define.Task.Module]], + o: c.Expr[Overrides]): c.Expr[Source[T]] = { + import c.universe._ + + mill.moduledefs.Cacher.impl0[Source[T]](c)( + reify( + new Source[T]( + Applicative.impl[Task, T, Ctx](c)(value).splice, + e.splice.value, + cl.splice.value, + n.splice.value, + upickle.default.ReadWriter(w.splice.write, r.splice.read), + o.splice.value + ) + ) + ) + } def command[T](t: Task[T]) (implicit c: Caller[Task.Module], @@ -197,7 +227,7 @@ object Target extends TargetGenerated with Applicative.Applyer[Task, Task, Resul import c.universe._ - mill.moduledefs.Cacher.impl0[Persistent, T](c)( + mill.moduledefs.Cacher.impl0[Persistent[T]](c)( reify( new Persistent[T]( Applicative.impl[Task, T, Ctx](c)(t).splice, @@ -264,14 +294,15 @@ class Persistent[+T](t: Task[T], override def flushDest = false override def asPersistent = Some(this) } -object Source{ - implicit def apply(p: ammonite.ops.Path) = new Source(p) -} -class Source(path: ammonite.ops.Path) extends Task[PathRef]{ - def handle = PathRef(path) - def evaluate(args: Ctx) = handle - override def sideHash = handle.hashCode() - val inputs = Nil +class Source[T](t: Task[T], + val enclosing: String, + val owner: Task.Module, + val name: String, + val readWrite: RW[_], + val overrides: Int) extends Target[T]{ + val inputs = Seq(t) + def evaluate(args: Ctx) = args[T](0) + override def sideHash = util.Random.nextInt() } object Task { @@ -280,9 +311,7 @@ object Task { trait TaskModule extends Module { def defaultCommandName(): String } - trait Module extends mill.moduledefs.Cacher[Target]{ - def wrapCached[T](t: Target[T], enclosing: String): Target[T] = t - } + trait Module extends mill.moduledefs.Cacher class Task0[T](t: T) extends Task[T]{ lazy val t0 = t diff --git a/core/src/main/scala/mill/eval/Evaluator.scala b/core/src/main/scala/mill/eval/Evaluator.scala index 6fc0661a..4d94d3d9 100644 --- a/core/src/main/scala/mill/eval/Evaluator.scala +++ b/core/src/main/scala/mill/eval/Evaluator.scala @@ -24,6 +24,7 @@ case class Labelled[T](target: NamedTask[T], } } class Evaluator[T](val workspacePath: Path, + val basePath: Path, val mapping: Discovered.Mapping[T], log: Logger, val classLoaderSig: Seq[(Path, Long)] = Evaluator.classLoaderSig){ @@ -69,7 +70,13 @@ class Evaluator[T](val workspacePath: Path, for((k, vs) <- sortedGroups.items()){ failing.addAll(k, vs.items.flatMap(results.get).collect{case f: Result.Failing => f}) } - Evaluator.Results(goals.indexed.map(results), evaluated, transitive, failing) + Evaluator.Results( + goals.indexed.map(results), + evaluated, + transitive, + failing, + results + ) } @@ -87,10 +94,11 @@ class Evaluator[T](val workspacePath: Path, terminal match{ case Left(task) => - evaluateGroup(group, results, paths = None, maybeTargetLabel = None) + evaluateGroup(group, results, groupBasePath = None, paths = None, maybeTargetLabel = None) case Right(labelledTarget) => - val paths = Evaluator.resolveDestPaths(workspacePath, labelledTarget) - mkdir(paths.base) + val paths = Evaluator.resolveDestPaths(workspacePath, labelledTarget.segments) + val groupBasePath = basePath / Evaluator.makeSegmentStrings(labelledTarget.segments) + mkdir(paths.out) val cached = for{ json <- scala.util.Try(upickle.json.read(read(paths.meta))).toOption (cachedHash, terminalResult) <- scala.util.Try(upickle.default.readJs[(Int, upickle.Js.Value)](json)).toOption @@ -117,8 +125,10 @@ class Evaluator[T](val workspacePath: Path, val (newResults, newEvaluated) = evaluateGroup( group, results, - Some(paths), - maybeTargetLabel = Some(msgParts.mkString)) + groupBasePath = Some(groupBasePath), + paths = Some(paths), + maybeTargetLabel = Some(msgParts.mkString) + ) newResults(labelledTarget.target) match{ case Result.Success(v) => @@ -148,6 +158,7 @@ class Evaluator[T](val workspacePath: Path, def evaluateGroup(group: OSet[Task[_]], results: collection.Map[Task[_], Result[Any]], + groupBasePath: Option[Path], paths: Option[Evaluator.Paths], maybeTargetLabel: Option[String]) = { @@ -183,6 +194,7 @@ class Evaluator[T](val workspacePath: Path, val args = new Ctx( targetInputValues.toArray[Any], paths.map(_.dest).orNull, + groupBasePath.orNull, multiLogger, new Ctx.LoaderCtx{ def load[T](x: Ctx.Loader[T]): T = { @@ -225,15 +237,16 @@ class Evaluator[T](val workspacePath: Path, object Evaluator{ - case class Paths(base: Path, dest: Path, meta: Path, log: Path) - def resolveDestPaths(workspacePath: Path, t: Labelled[_]): Paths = { - resolveDestPaths(workspacePath, t.segments) + case class Paths(out: Path, + dest: Path, + meta: Path, + log: Path) + def makeSegmentStrings(segments: Seq[Segment]) = segments.flatMap{ + case Mirror.Segment.Label(s) => Seq(s) + case Mirror.Segment.Cross(values) => values.map(_.toString) } def resolveDestPaths(workspacePath: Path, segments: Seq[Segment]): Paths = { - val segmentStrings = segments.flatMap{ - case Mirror.Segment.Label(s) => Seq(s) - case Mirror.Segment.Cross(values) => values.map(_.toString) - } + val segmentStrings = makeSegmentStrings(segments) val targetPath = workspacePath / segmentStrings Paths(targetPath, targetPath / 'dest, targetPath / "meta.json", targetPath / 'log) } @@ -248,7 +261,8 @@ object Evaluator{ case class Results(rawValues: Seq[Result[Any]], evaluated: OSet[Task[_]], transitive: OSet[Task[_]], - failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing]){ + failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing], + results: collection.Map[Task[_], Result[Any]]){ def values = rawValues.collect{case Result.Success(v) => v} } } diff --git a/core/src/main/scala/mill/main/MainRunner.scala b/core/src/main/scala/mill/main/MainRunner.scala index f354e838..8f58f114 100644 --- a/core/src/main/scala/mill/main/MainRunner.scala +++ b/core/src/main/scala/mill/main/MainRunner.scala @@ -1,9 +1,9 @@ package mill.main -import ammonite.interp.Preprocessor +import ammonite.interp.{Interpreter, Preprocessor} import ammonite.ops.Path import ammonite.util.{Imports, Name, Res, Util} import mill.discover.Discovered -import mill.eval.Evaluator +import mill.eval.{Evaluator, PathRef} import upickle.Js /** @@ -33,9 +33,20 @@ class MainRunner(config: ammonite.main.Cli.Config, ) result match{ case Res.Success(data) => - val (eval, evaluationWatches, res) = data + val (eval, evaluationWatches0, res) = data + val alreadyStale = evaluationWatches0.exists(p => p.sig != PathRef(p.path, p.quick).sig) + // If the file changed between the creation of the original + // `PathRef` and the current moment, use random junk .sig values + // to force an immediate re-run. Otherwise calculate the + // pathSignatures the same way Ammonite would and hand over the + // values, so Ammonite can watch them and only re-run if they + // subsequently change + val evaluationWatches = + if (alreadyStale) evaluationWatches0.map(_.path -> util.Random.nextLong()) + else evaluationWatches0.map(p => p.path -> Interpreter.pathSignature(p.path)) + lastEvaluator = Some((interpWatched, eval)) - (Res.Success(res.flatMap(_._2)), interpWatched ++ evaluationWatches) + (Res(res.map(_.flatMap(_._2))), interpWatched ++ evaluationWatches) case _ => (result, interpWatched) } diff --git a/core/src/main/scala/mill/main/ReplApplyHandler.scala b/core/src/main/scala/mill/main/ReplApplyHandler.scala index d0c8365a..3832da68 100644 --- a/core/src/main/scala/mill/main/ReplApplyHandler.scala +++ b/core/src/main/scala/mill/main/ReplApplyHandler.scala @@ -11,6 +11,7 @@ object ReplApplyHandler{ new ReplApplyHandler( new mill.eval.Evaluator( ammonite.ops.pwd / 'out, + ammonite.ops.pwd, mapping, new mill.util.PrintLogger(true) ) diff --git a/core/src/main/scala/mill/main/RunScript.scala b/core/src/main/scala/mill/main/RunScript.scala index ad813026..dc7b74d6 100644 --- a/core/src/main/scala/mill/main/RunScript.scala +++ b/core/src/main/scala/mill/main/RunScript.scala @@ -6,7 +6,7 @@ import ammonite.interp.Interpreter import ammonite.ops.{Path, pwd, read} import ammonite.util.Util.CodeSource import ammonite.util.{Name, Res, Util} -import mill.define +import mill.{PathRef, define} import mill.define.Task import mill.discover.Mirror.Segment import mill.discover.{Discovered, Mirror} @@ -27,8 +27,7 @@ object RunScript{ path: Path, interp: ammonite.interp.Interpreter, scriptArgs: Seq[String], - lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_])]) - : Res[(Evaluator[_], Seq[(Path, Long)], Seq[(Any, Option[Js.Value])])] = { + lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_])]) = { val log = new PrintLogger(true) for{ @@ -40,15 +39,10 @@ object RunScript{ case _ => interp.watch(path) for(mapping <- evaluateMapping(wd, path, interp)) - yield new Evaluator(pwd / 'out, mapping, log) + yield new Evaluator(pwd / 'out, pwd, mapping, log) } - evaluationWatches = mutable.Buffer.empty[(Path, Long)] - res <- Res(evaluateTarget( - evaluator, - scriptArgs, - p => evaluationWatches.append((p, Interpreter.pathSignature(p))) - )) - } yield (evaluator, evaluationWatches, res) + (watches, res) <- Res(evaluateTarget(evaluator, scriptArgs)) + } yield (evaluator, watches, res) } def watchedSigUnchanged(sig: Seq[(Path, Long)]) = { @@ -98,8 +92,7 @@ object RunScript{ } yield mapping } def evaluateTarget[T](evaluator: Evaluator[_], - scriptArgs: Seq[String], - watch: Path => Unit) = { + scriptArgs: Seq[String]) = { val Seq(selectorString, rest @_*) = scriptArgs for { @@ -112,17 +105,18 @@ object RunScript{ sel, evaluator.mapping.mirror, evaluator.mapping.base, rest, crossSelectors, Nil ) - res <- evaluate(evaluator, target, watch) - } yield res + (watched, res) = evaluate(evaluator, target) + } yield (watched, res) } def evaluate(evaluator: Evaluator[_], - target: Task[Any], - watch: Path => Unit): Either[String, Seq[(Any, Option[upickle.Js.Value])]] = { + target: Task[Any]): (Seq[PathRef], Either[String, Seq[(Any, Option[upickle.Js.Value])]]) = { val evaluated = evaluator.evaluate(OSet(target)) - evaluated.transitive.foreach { - case t: define.Source => watch(t.handle.path) - case _ => // do nothing - } + val watched = evaluated.results + .iterator + .collect { + case (t: define.Source[_], Result.Success(p: PathRef)) => p + } + .toSeq val errorStr = (for((k, fs) <- evaluated.failing.items()) yield { @@ -151,8 +145,8 @@ object RunScript{ } } - Right(evaluated.values.zip(json)) - case n => Left(s"$n targets failed\n$errorStr") + watched -> Right(evaluated.values.zip(json)) + case n => watched -> Left(s"$n targets failed\n$errorStr") } } diff --git a/core/src/main/scala/mill/util/Ctx.scala b/core/src/main/scala/mill/util/Ctx.scala index 9604021a..60e6bdbf 100644 --- a/core/src/main/scala/mill/util/Ctx.scala +++ b/core/src/main/scala/mill/util/Ctx.scala @@ -2,7 +2,7 @@ package mill.util import ammonite.ops.Path import mill.define.Applicative.ImplicitStub -import mill.util.Ctx.{ArgCtx, DestCtx, LoaderCtx, LogCtx} +import mill.util.Ctx.{ArgCtx, BaseCtx, DestCtx, LoaderCtx, LogCtx} import scala.annotation.compileTimeOnly import scala.language.implicitConversions @@ -13,15 +13,23 @@ object Ctx{ implicit def taskCtx: Ctx = ??? object DestCtx { - implicit def pathToCtx(path: Path): DestCtx = - new DestCtx { def dest: Path = path } + implicit def pathToCtx(path: Path): DestCtx = new DestCtx { def dest = path } } trait DestCtx{ def dest: Path } + trait BaseCtx{ + def base: Path + } + object BaseCtx { + implicit def pathToCtx(path: Path): BaseCtx = new BaseCtx { def base = path } + } trait LogCtx{ def log: Logger } + object LogCtx{ + implicit def logToCtx(l: Logger): LogCtx = new LogCtx { def log = l } + } trait ArgCtx{ def args: IndexedSeq[_] } @@ -34,12 +42,14 @@ object Ctx{ } class Ctx(val args: IndexedSeq[_], val dest: Path, + val base: Path, val log: Logger, workerCtx0: Ctx.LoaderCtx) extends DestCtx with LogCtx with ArgCtx - with LoaderCtx{ + with LoaderCtx + with BaseCtx{ def load[T](x: Ctx.Loader[T]): T = workerCtx0.load(x) def length = args.length diff --git a/core/src/test/scala/mill/define/CacherTests.scala b/core/src/test/scala/mill/define/CacherTests.scala index d85e9e6b..e46c0390 100644 --- a/core/src/test/scala/mill/define/CacherTests.scala +++ b/core/src/test/scala/mill/define/CacherTests.scala @@ -29,7 +29,7 @@ object CacherTests extends TestSuite{ def eval[V](mapping: Discovered.Mapping[_], v: Task[V])(implicit tp: TestPath) = { val workspace = ammonite.ops.pwd / 'target / 'workspace / tp.value - val evaluator = new Evaluator(workspace, mapping, DummyLogger) + val evaluator = new Evaluator(workspace, ammonite.ops.pwd, mapping, DummyLogger) evaluator.evaluate(OSet(v)).values(0) } diff --git a/core/src/test/scala/mill/eval/EvaluationTests.scala b/core/src/test/scala/mill/eval/EvaluationTests.scala index 75a6cc52..fb3f6b97 100644 --- a/core/src/test/scala/mill/eval/EvaluationTests.scala +++ b/core/src/test/scala/mill/eval/EvaluationTests.scala @@ -15,7 +15,7 @@ object EvaluationTests extends TestSuite{ val workspace = ammonite.ops.pwd / 'target / 'workspace / tp.value ammonite.ops.rm(ammonite.ops.Path(workspace, ammonite.ops.pwd)) // Make sure data is persisted even if we re-create the evaluator each time - def evaluator = new Evaluator(workspace, mapping, DummyLogger) + def evaluator = new Evaluator(workspace, ammonite.ops.pwd, mapping, DummyLogger) def apply(target: Task[_], expValue: Any, expEvaled: OSet[Task[_]], diff --git a/core/src/test/scala/mill/eval/FailureTests.scala b/core/src/test/scala/mill/eval/FailureTests.scala index 42331a74..c77ad74b 100644 --- a/core/src/test/scala/mill/eval/FailureTests.scala +++ b/core/src/test/scala/mill/eval/FailureTests.scala @@ -14,7 +14,7 @@ object FailureTests extends TestSuite{ } class Checker(mapping: Discovered.Mapping[_])(implicit tp: TestPath){ - val evaluator = new Evaluator(workspace, mapping, DummyLogger) + val evaluator = new Evaluator(workspace, ammonite.ops.pwd, mapping, DummyLogger) def apply(target: Target[_], expectedFailCount: Int, expectedRawValues: Seq[Result[_]]) = { diff --git a/core/src/test/scala/mill/eval/JavaCompileJarTests.scala b/core/src/test/scala/mill/eval/JavaCompileJarTests.scala index 0efd0bf1..667aaa22 100644 --- a/core/src/test/scala/mill/eval/JavaCompileJarTests.scala +++ b/core/src/test/scala/mill/eval/JavaCompileJarTests.scala @@ -2,7 +2,7 @@ package mill.eval import ammonite.ops.ImplicitWd._ import ammonite.ops._ -import mill.define.{Target, Task} +import mill.define.{Source, Target, Task} import mill.discover.Discovered import mill.modules.Jvm import mill.util.Ctx.DestCtx @@ -36,8 +36,8 @@ object JavaCompileJarTests extends TestSuite{ // | // v // resourceRoot ----> jar - def sourceRoot = T.source{ sourceRootPath } - def resourceRoot = T.source{ resourceRootPath } + def sourceRoot = T.source{ PathRef(sourceRootPath) } + def resourceRoot = T.source{ PathRef(resourceRootPath) } def allSources = T{ ls.rec(sourceRoot().path).map(PathRef(_)) } def classFiles = T{ compileAll(allSources()) } def jar = T{ Jvm.createJar(Seq(resourceRoot().path, classFiles().path)) } @@ -51,7 +51,7 @@ object JavaCompileJarTests extends TestSuite{ val mapping = Discovered.mapping(Build) def eval[T](t: Task[T]) = { - val evaluator = new Evaluator(workspacePath, mapping, DummyLogger) + val evaluator = new Evaluator(workspacePath, pwd, mapping, DummyLogger) val evaluated = evaluator.evaluate(OSet(t)) if (evaluated.failing.keyCount == 0){ @@ -68,12 +68,13 @@ object JavaCompileJarTests extends TestSuite{ } def check(targets: OSet[Task[_]], expected: OSet[Task[_]]) = { - val evaluator = new Evaluator(workspacePath, mapping, DummyLogger) + val evaluator = new Evaluator(workspacePath, pwd, mapping, DummyLogger) val evaluated = evaluator.evaluate(targets) .evaluated .flatMap(_.asTarget) .filter(mapping.targets.contains) + .filter(!_.isInstanceOf[Source[_]]) assert(evaluated == expected) } @@ -144,7 +145,7 @@ object JavaCompileJarTests extends TestSuite{ val Right((runOutput, evalCount)) = eval(Build.run("test.Foo")) assert( runOutput.out.string == (31337 + 271828) + "\n", - evalCount == 1 + evalCount == 2 ) } @@ -165,12 +166,12 @@ object JavaCompileJarTests extends TestSuite{ val Right((runOutput2, evalCount2)) = eval(Build.run("test.BarFour")) assert( runOutput2.out.string == "New Cls!\n", - evalCount2 == 3 + evalCount2 == 4 ) val Right((runOutput3, evalCount3)) = eval(Build.run("test.BarFour")) assert( runOutput3.out.string == "New Cls!\n", - evalCount3 == 1 + evalCount3 == 2 ) } } diff --git a/core/src/test/scala/mill/util/TestEvaluator.scala b/core/src/test/scala/mill/util/TestEvaluator.scala index 5d16de43..6ae90167 100644 --- a/core/src/test/scala/mill/util/TestEvaluator.scala +++ b/core/src/test/scala/mill/util/TestEvaluator.scala @@ -1,12 +1,14 @@ package mill.util import ammonite.ops.Path -import mill.define.{Target, Task} +import mill.define.{Source, Target, Task} import mill.discover.{Discovered, Mirror} import mill.eval.{Evaluator, Result} -class TestEvaluator(mapping: Discovered.Mapping[_], workspacePath: Path){ - val evaluator = new Evaluator(workspacePath, mapping, DummyLogger) +class TestEvaluator(mapping: Discovered.Mapping[_], + workspacePath: Path, + basePath: Path){ + val evaluator = new Evaluator(workspacePath, basePath, mapping, DummyLogger) def apply[T](t: Task[T]): Either[Result.Failing, (T, Int)] = { val evaluated = evaluator.evaluate(OSet(t)) @@ -16,7 +18,7 @@ class TestEvaluator(mapping: Discovered.Mapping[_], workspacePath: Path){ Tuple2( evaluated.rawValues.head.asInstanceOf[Result.Success[T]].value, evaluated.evaluated.collect { - case t: Target[_] if mapping.targets.contains(t) => t + case t: Target[_] if mapping.targets.contains(t) && !t.isInstanceOf[Source[_]] => t case t: mill.define.Command[_] => t }.size )) diff --git a/moduledefs/src/main/scala/mill/moduledefs/Cacher.scala b/moduledefs/src/main/scala/mill/moduledefs/Cacher.scala index cea2ca41..023f03be 100644 --- a/moduledefs/src/main/scala/mill/moduledefs/Cacher.scala +++ b/moduledefs/src/main/scala/mill/moduledefs/Cacher.scala @@ -4,19 +4,21 @@ import scala.collection.mutable import scala.reflect.macros.blackbox.Context -trait Cacher[C[_]]{ - private[this] lazy val cacherLazyMap = mutable.Map.empty[sourcecode.Enclosing, C[_]] - def wrapCached[T](in: C[T], enclosing: String): C[T] - protected[this] def cachedTarget[T](t: => C[T]) - (implicit c: sourcecode.Enclosing): C[T] = synchronized{ - cacherLazyMap.getOrElseUpdate(c, wrapCached(t, c.value)).asInstanceOf[C[T]] +trait Cacher{ + private[this] lazy val cacherLazyMap = mutable.Map.empty[sourcecode.Enclosing, Any] + + protected[this] def cachedTarget[T](t: => T) + (implicit c: sourcecode.Enclosing): T = synchronized{ + cacherLazyMap.getOrElseUpdate(c, t).asInstanceOf[T] } } + object Cacher{ - def impl0[M[_], T: c.WeakTypeTag](c: Context)(t: c.Expr[M[T]]): c.Expr[M[T]] = { - c.Expr[M[T]](wrapCached(c)(t.tree)) + def impl0[T: c.WeakTypeTag](c: Context) + (t: c.Expr[T]): c.Expr[T] = { + c.Expr[T](wrapCached[T](c)(t.tree)) } - def wrapCached(c: Context)(t: c.Tree) = { + def wrapCached[R: c.WeakTypeTag](c: Context)(t: c.Tree) = { import c.universe._ val owner = c.internal.enclosingOwner @@ -24,7 +26,7 @@ object Cacher{ owner.owner.isClass && owner.owner.asClass.baseClasses.exists(_.fullName == "mill.moduledefs.Cacher") - if (ownerIsCacherClass && owner.isMethod) q"this.cachedTarget($t)" + if (ownerIsCacherClass && owner.isMethod) q"this.cachedTarget[${weakTypeTag[R]}]($t)" else c.abort( c.enclosingPosition, "T{} members must be defs defined in a Cacher class/trait/object body" diff --git a/scalajslib/src/test/scala/mill/scalajslib/HelloJSWorldTests.scala b/scalajslib/src/test/scala/mill/scalajslib/HelloJSWorldTests.scala index ad392c42..85052a04 100644 --- a/scalajslib/src/test/scala/mill/scalajslib/HelloJSWorldTests.scala +++ b/scalajslib/src/test/scala/mill/scalajslib/HelloJSWorldTests.scala @@ -54,7 +54,8 @@ object HelloJSWorldTests extends TestSuite { val helloWorldEvaluator = new TestEvaluator( Discovered.mapping(HelloJSWorld), - workspacePath + workspacePath, + srcPath ) class Console { diff --git a/scalalib/src/main/scala/mill/scalalib/GenIdea.scala b/scalalib/src/main/scala/mill/scalalib/GenIdea.scala index 6fd5031a..8b89fa9a 100644 --- a/scalalib/src/main/scala/mill/scalalib/GenIdea.scala +++ b/scalalib/src/main/scala/mill/scalalib/GenIdea.scala @@ -16,9 +16,8 @@ object GenIdea { rm! pwd/".idea" rm! pwd/".idea_modules" - val workspacePath = pwd / 'out - val evaluator = new Evaluator(workspacePath, mapping, new PrintLogger(true)) + val evaluator = new Evaluator(pwd / 'out, pwd, mapping, new PrintLogger(true)) for((relPath, xml) <- xmlFileLayout(evaluator)){ write.over(pwd/relPath, pp.format(xml)) @@ -85,7 +84,7 @@ object GenIdea { val elem = moduleXmlTemplate( sourcePath.path, - Seq(paths.base), + Seq(paths.out), resolvedDeps.map(pathToLibName), for(m <- mod.projectDeps) yield moduleName(moduleLabels(m)) diff --git a/scalalib/src/main/scala/mill/scalalib/Module.scala b/scalalib/src/main/scala/mill/scalalib/Module.scala index 1ff4c240..76c50aed 100644 --- a/scalalib/src/main/scala/mill/scalalib/Module.scala +++ b/scalalib/src/main/scala/mill/scalalib/Module.scala @@ -191,8 +191,8 @@ trait Module extends mill.Module with TaskModule { outer => def prependShellScript: T[String] = T{ "" } - def sources = T.source{ basePath / 'src } - def resources = T.source{ basePath / 'resources } + def sources = T.source{ PathRef(basePath / 'src) } + def resources = T.source{ PathRef(basePath / 'resources) } def allSources = T{ Seq(sources()) } def compile: T[CompilationResult] = T.persistent{ compileScala( @@ -346,11 +346,11 @@ trait PublishModule extends Module { outer => trait SbtModule extends Module { outer => def basePath: Path - override def sources = T.source{ basePath / 'src / 'main / 'scala } - override def resources = T.source{ basePath / 'src / 'main / 'resources } + override def sources = T.source{ PathRef(basePath / 'src / 'main / 'scala) } + override def resources = T.source{ PathRef(basePath / 'src / 'main / 'resources) } trait Tests extends super.Tests{ def basePath = outer.basePath - override def sources = T.source{ basePath / 'src / 'test / 'scala } - override def resources = T.source{ basePath / 'src / 'test / 'resources } + override def sources = T.source{ PathRef(basePath / 'src / 'test / 'scala) } + override def resources = T.source{ PathRef(basePath / 'src / 'test / 'resources) } } } diff --git a/scalalib/src/main/scala/mill/scalalib/TestRunner.scala b/scalalib/src/main/scala/mill/scalalib/TestRunner.scala index 7d42bdea..d92a9deb 100644 --- a/scalalib/src/main/scala/mill/scalalib/TestRunner.scala +++ b/scalalib/src/main/scala/mill/scalalib/TestRunner.scala @@ -46,9 +46,7 @@ object TestRunner { entireClasspath = args(1).split(" ").map(Path(_)), testClassfilePath = args(2).split(" ").map(Path(_)), args = args(3) match{ case "" => Nil case x => x.split(" ").toList } - )(new LogCtx { - def log = new PrintLogger(true) - }) + )(new PrintLogger(true)) val outputPath = args(4) ammonite.ops.write(Path(outputPath), upickle.default.write(result)) diff --git a/scalalib/src/test/scala/mill/scalalib/AcyclicTests.scala b/scalalib/src/test/scala/mill/scalalib/AcyclicTests.scala index 9b68ea70..44a43702 100644 --- a/scalalib/src/test/scala/mill/scalalib/AcyclicTests.scala +++ b/scalalib/src/test/scala/mill/scalalib/AcyclicTests.scala @@ -53,7 +53,7 @@ object AcyclicTests extends TestSuite{ mkdir(workspacePath/up) cp(srcPath, workspacePath) val mapping = Discovered.mapping(AcyclicBuild) - val eval = new TestEvaluator(mapping, workspacePath) + val eval = new TestEvaluator(mapping, workspacePath, srcPath) def check(scalaVersion: String) = { // We can compile diff --git a/scalalib/src/test/scala/mill/scalalib/BetterFilesTests.scala b/scalalib/src/test/scala/mill/scalalib/BetterFilesTests.scala index c3004f7b..1f0a3d70 100644 --- a/scalalib/src/test/scala/mill/scalalib/BetterFilesTests.scala +++ b/scalalib/src/test/scala/mill/scalalib/BetterFilesTests.scala @@ -96,7 +96,7 @@ object BetterFilesTests extends TestSuite{ mkdir(workspacePath/up) cp(srcPath, workspacePath) val mapping = Discovered.mapping(BetterFilesBuild) - val eval = new TestEvaluator(mapping, workspacePath) + val eval = new TestEvaluator(mapping, workspacePath, srcPath) 'test - { diff --git a/scalalib/src/test/scala/mill/scalalib/HelloWorldTests.scala b/scalalib/src/test/scala/mill/scalalib/HelloWorldTests.scala index aafd980a..0b6ac62b 100644 --- a/scalalib/src/test/scala/mill/scalalib/HelloWorldTests.scala +++ b/scalalib/src/test/scala/mill/scalalib/HelloWorldTests.scala @@ -17,7 +17,7 @@ import scala.collection.JavaConverters._ trait HelloWorldModule extends scalalib.Module { def scalaVersion = "2.12.4" - def basePath = HelloWorldTests.workspacePath + def basePath = HelloWorldTests.workingSrcPath } object HelloWorld extends HelloWorldModule @@ -66,31 +66,38 @@ object HelloWorldScalaOverride extends HelloWorldModule { object HelloWorldTests extends TestSuite { val srcPath = pwd / 'scalalib / 'src / 'test / 'resource / "hello-world" - val workspacePath = pwd / 'target / 'workspace / "hello-world" - val mainObject = workspacePath / 'src / 'main / 'scala / "Main.scala" + val basePath = pwd / 'target / 'workspace / "hello-world" + val workingSrcPath = basePath / 'src + val outPath = basePath / 'out + val mainObject = workingSrcPath / 'src / 'main / 'scala / "Main.scala" val helloWorldEvaluator = new TestEvaluator( Discovered.mapping(HelloWorld), - workspacePath + outPath, + workingSrcPath ) val helloWorldWithMainEvaluator = new TestEvaluator( Discovered.mapping(HelloWorldWithMain), - workspacePath + outPath, + workingSrcPath ) val helloWorldFatalEvaluator = new TestEvaluator( Discovered.mapping(HelloWorldFatalWarnings), - workspacePath + outPath, + workingSrcPath ) val helloWorldOverrideEvaluator = new TestEvaluator( Discovered.mapping(HelloWorldScalaOverride), - workspacePath + outPath, + workingSrcPath ) val helloWorldCrossEvaluator = new TestEvaluator( Discovered.mapping(CrossHelloWorld), - workspacePath + outPath, + workingSrcPath ) @@ -136,12 +143,11 @@ object HelloWorldTests extends TestSuite { 'fromScratch - { val Right((result, evalCount)) = helloWorldEvaluator(HelloWorld.compile) - val outPath = result.classes.path val analysisFile = result.analysisFile - val outputFiles = ls.rec(outPath) - val expectedClassfiles = compileClassfiles.map(workspacePath / 'compile / 'dest / 'classes / _) + val outputFiles = ls.rec(result.classes.path) + val expectedClassfiles = compileClassfiles.map(outPath / 'compile / 'dest / 'classes / _) assert( - outPath == workspacePath / 'compile / 'dest / 'classes, + result.classes.path == outPath / 'compile / 'dest / 'classes, exists(analysisFile), outputFiles.nonEmpty, outputFiles.forall(expectedClassfiles.contains), @@ -169,7 +175,7 @@ object HelloWorldTests extends TestSuite { assert(err.isInstanceOf[CompileFailed]) val paths = Evaluator.resolveDestPaths( - workspacePath, + outPath, helloWorldEvaluator.evaluator.mapping.targets(HelloWorld.compile) ) @@ -195,7 +201,7 @@ object HelloWorldTests extends TestSuite { assert(evalCount > 0) - val runResult = workspacePath / "hello-mill" + val runResult = basePath / "hello-mill" assert( exists(runResult), read(runResult) == "hello rockjam, your age is: 25" @@ -210,7 +216,7 @@ object HelloWorldTests extends TestSuite { assert(evalCount > 0) - val runResult = workspacePath / "hello-mill" + val runResult = basePath / "hello-mill" assert( exists(runResult), read(runResult) == "hello rockjam, your age is: 25" @@ -246,7 +252,7 @@ object HelloWorldTests extends TestSuite { assert(evalCount > 0) - val runResult = workspacePath / "hello-mill" + val runResult = basePath / "hello-mill" assert( exists(runResult), read(runResult) == "hello rockjam, your age is: 25" @@ -291,7 +297,7 @@ object HelloWorldTests extends TestSuite { %("scala", result.path) - val runResult = workspacePath / "hello-mill" + val runResult = basePath / "hello-mill" assert( exists(runResult), read(runResult) == "hello rockjam, your age is: 25" @@ -300,7 +306,7 @@ object HelloWorldTests extends TestSuite { 'logOutputToFile { helloWorldEvaluator(HelloWorld.compile) - val logFile = workspacePath / 'compile / 'log + val logFile = outPath / 'compile / 'log assert(exists(logFile)) } } @@ -315,9 +321,10 @@ object HelloWorldTests extends TestSuite { ) def prepareWorkspace(): Unit = { - rm(workspacePath) - mkdir(workspacePath / up) - cp(srcPath, workspacePath) + rm(outPath) + rm(workingSrcPath) + mkdir(outPath) + cp(srcPath, workingSrcPath) } } diff --git a/scalalib/src/test/scala/mill/scalalib/JawnTests.scala b/scalalib/src/test/scala/mill/scalalib/JawnTests.scala index 71e4d506..8449cc02 100644 --- a/scalalib/src/test/scala/mill/scalalib/JawnTests.scala +++ b/scalalib/src/test/scala/mill/scalalib/JawnTests.scala @@ -75,7 +75,7 @@ object JawnTests extends TestSuite{ mkdir(workspacePath/up) cp(srcPath, workspacePath) val mapping = Discovered.mapping(JawnBuild) - val eval = new TestEvaluator(mapping, workspacePath) + val eval = new TestEvaluator(mapping, workspacePath, srcPath) 'test - { def compileOutput = workspacePath / 'jawn / "2.12.3" / 'Parser / 'compile |