diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-01-20 03:49:17 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-01-20 03:49:17 -0800 |
commit | d14f56a3fd881f809e58783c49866d1491a5f3fe (patch) | |
tree | 4a9f93d3d7f69211aa444ce15837fe6e79b9db7f /core/src/main/scala | |
parent | aebd7a144fab5bdb95f6ee4f4bc170be65cd0549 (diff) | |
download | mill-d14f56a3fd881f809e58783c49866d1491a5f3fe.tar.gz mill-d14f56a3fd881f809e58783c49866d1491a5f3fe.tar.bz2 mill-d14f56a3fd881f809e58783c49866d1491a5f3fe.zip |
Swap over to simplified Mill module/source layout from SBT's
Removes a lot of useless folders and gives us a chance to exercise this simplified layout. Support for the SBT layout is still verified by our integration tests
Diffstat (limited to 'core/src/main/scala')
27 files changed, 0 insertions, 2731 deletions
diff --git a/core/src/main/scala/mill/Main.scala b/core/src/main/scala/mill/Main.scala deleted file mode 100644 index 3025994c..00000000 --- a/core/src/main/scala/mill/Main.scala +++ /dev/null @@ -1,83 +0,0 @@ -package mill - -import ammonite.main.Cli.{formatBlock, genericSignature, replSignature} -import ammonite.ops._ -import ammonite.util.Util - -object Main { - case class Config(home: ammonite.ops.Path = pwd/'out/'ammonite, - colored: Option[Boolean] = None, - help: Boolean = false, - repl: Boolean = false, - watch: Boolean = false) - - def main(args: Array[String]): Unit = { - - import ammonite.main.Cli - - var show = false - val showCliArg = Cli.Arg[Cli.Config, Unit]( - "show", - None, - "Display the json-formatted value of the given target, if any", - (x, _) => { - show = true - x - } - ) - val removed = Set("predef-code", "home", "no-home-predef") - val millArgSignature = (Cli.genericSignature :+ showCliArg).filter(a => !removed(a.name)) - Cli.groupArgs( - args.toList, - millArgSignature, - Cli.Config(remoteLogging = false) - ) match{ - case Left(msg) => - System.err.println(msg) - System.exit(1) - case Right((cliConfig, _)) if cliConfig.help => - val leftMargin = millArgSignature.map(ammonite.main.Cli.showArg(_).length).max + 2 - System.out.println( - s"""Mill Build Tool - |usage: mill [mill-options] [target [target-options]] - | - |${formatBlock(millArgSignature, leftMargin).mkString(Util.newLine)}""".stripMargin - ) - System.exit(0) - case Right((cliConfig, leftoverArgs)) => - - val repl = leftoverArgs.isEmpty - val config = - if(!repl) cliConfig - else cliConfig.copy( - predefCode = - """import $file.build, build._ - |implicit val replApplyHandler = mill.main.ReplApplyHandler( - | interp.colors(), - | repl.pprinter(), - | build.millSelf.get, - | build.millDiscover - |) - |repl.pprinter() = replApplyHandler.pprinter - |import replApplyHandler.generatedEval._ - | - """.stripMargin, - welcomeBanner = None - ) - - val runner = new mill.main.MainRunner( - config, show, - System.out, System.err, System.in - ) - if (repl){ - runner.printInfo("Loading...") - runner.runRepl() - } else { - val result = runner.runScript(pwd / "build.sc", leftoverArgs) - System.exit(if(result) 0 else 1) - } - } - } -} - - diff --git a/core/src/main/scala/mill/define/Applicative.scala b/core/src/main/scala/mill/define/Applicative.scala deleted file mode 100644 index 69c506f7..00000000 --- a/core/src/main/scala/mill/define/Applicative.scala +++ /dev/null @@ -1,108 +0,0 @@ -package mill.define - -import scala.annotation.{StaticAnnotation, compileTimeOnly} -import scala.language.higherKinds -import scala.reflect.macros.blackbox.Context - -/** - * A generic Applicative-functor macro: translates calls to - * - * Applier.apply{ ... applyable1.apply() ... applyable2.apply() ... } - * - * into - * - * Applier.zipMap(applyable1, applyable2){ (a1, a2, ctx) => ... a1 ... a2 ... } - */ -object Applicative { - trait ApplyHandler[M[+_]]{ - def apply[T](t: M[T]): T - } - object ApplyHandler{ - @compileTimeOnly("Target#apply() can only be used with a T{...} block") - implicit def defaultApplyHandler[M[+_]]: ApplyHandler[M] = ??? - } - trait Applyable[M[+_], +T]{ - def self: M[T] - def apply()(implicit handler: ApplyHandler[M]): T = handler(self) - } - class ImplicitStub extends StaticAnnotation - type Id[+T] = T - - trait Applyer[W[_], T[_], Z[_], Ctx] extends ApplyerGenerated[T, Z, Ctx] { - def ctx()(implicit c: Ctx) = c - def underlying[A](v: W[A]): T[_] - - def zipMap[R]()(cb: Ctx => Z[R]) = mapCtx(zip()){ (_, ctx) => cb(ctx)} - def zipMap[A, R](a: T[A])(f: (A, Ctx) => Z[R]) = mapCtx(a)(f) - def zip(): T[Unit] - def zip[A](a: T[A]): T[Tuple1[A]] - } - - def impl[M[_], T: c.WeakTypeTag, Ctx: c.WeakTypeTag](c: Context) - (t: c.Expr[T]): c.Expr[M[T]] = { - impl0(c)(t.tree)(implicitly[c.WeakTypeTag[T]], implicitly[c.WeakTypeTag[Ctx]]) - } - def impl0[M[_], T: c.WeakTypeTag, Ctx: c.WeakTypeTag](c: Context) - (t: c.Tree): c.Expr[M[T]] = { - import c.universe._ - def rec(t: Tree): Iterator[c.Tree] = Iterator(t) ++ t.children.flatMap(rec(_)) - - val bound = collection.mutable.Buffer.empty[(c.Tree, ValDef)] - val targetApplySym = typeOf[Applyable[Nothing, _]].member(TermName("apply")) - - // Derived from @olafurpg's - // https://gist.github.com/olafurpg/596d62f87bf3360a29488b725fbc7608 - val defs = rec(t).filter(_.isDef).map(_.symbol).toSet - - val ctxName = TermName(c.freshName("ctx")) - val ctxSym = c.internal.newTermSymbol(c.internal.enclosingOwner, ctxName) - c.internal.setInfo(ctxSym, weakTypeOf[Ctx]) - - val transformed = c.internal.typingTransform(t) { - case (t @ q"$fun.apply()($handler)", api) if t.symbol == targetApplySym => - - val localDefs = rec(fun).filter(_.isDef).map(_.symbol).toSet - val banned = rec(t).filter(x => defs(x.symbol) && !localDefs(x.symbol)) - - if (banned.hasNext){ - val banned0 = banned.next() - c.abort( - banned0.pos, - "Target#apply() call cannot use `" + banned0.symbol + "` defined within the T{...} block" - ) - } - val tempName = c.freshName(TermName("tmp")) - val tempSym = c.internal.newTermSymbol(c.internal.enclosingOwner, tempName) - c.internal.setInfo(tempSym, t.tpe) - val tempIdent = Ident(tempSym) - c.internal.setType(tempIdent, t.tpe) - c.internal.setFlag(tempSym, (1L << 44).asInstanceOf[c.universe.FlagSet]) - bound.append((q"${c.prefix}.underlying($fun)", c.internal.valDef(tempSym))) - tempIdent - case (t, api) - if t.symbol != null - && t.symbol.annotations.exists(_.tree.tpe =:= typeOf[ImplicitStub]) => - - val tempIdent = Ident(ctxSym) - c.internal.setType(tempIdent, t.tpe) - c.internal.setFlag(ctxSym, (1L << 44).asInstanceOf[c.universe.FlagSet]) - tempIdent - - case (t, api) => api.default(t) - } - - val (exprs, bindings) = bound.unzip - - - val ctxBinding = c.internal.valDef(ctxSym) - - val callback = c.typecheck(q"(..$bindings, $ctxBinding) => $transformed ") - - val res = q"${c.prefix}.zipMap(..$exprs){ $callback }" - - c.internal.changeOwner(transformed, c.internal.enclosingOwner, callback.symbol) - - c.Expr[M[T]](res) - } - -} diff --git a/core/src/main/scala/mill/define/Cross.scala b/core/src/main/scala/mill/define/Cross.scala deleted file mode 100644 index b51064be..00000000 --- a/core/src/main/scala/mill/define/Cross.scala +++ /dev/null @@ -1,90 +0,0 @@ -package mill.define -import language.experimental.macros -import scala.reflect.macros.blackbox - - -object Cross{ - case class Factory[T](make: (Product, mill.define.Ctx) => T) - - object Factory{ - implicit def make[T]: Factory[T] = macro makeImpl[T] - def makeImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Factory[T]] = { - import c.universe._ - val tpe = weakTypeOf[T] - - val primaryConstructorArgs = - tpe.typeSymbol.asClass.primaryConstructor.typeSignature.paramLists.head - - val argTupleValues = - for((a, n) <- primaryConstructorArgs.zipWithIndex) - yield q"v.productElement($n).asInstanceOf[${a.info}]" - - val instance = c.Expr[(Product, mill.define.Ctx) => T]( - q"{ (v, ctx0) => new $tpe(..$argTupleValues){ override def millOuterCtx = ctx0 } }" - ) - - reify { mill.define.Cross.Factory[T](instance.splice) } - } - } - - trait Resolver[-T]{ - def resolve[V <: T](c: Cross[V]): V - } -} - -/** - * Models "cross-builds": sets of duplicate builds which differ only in the - * value of one or more "case" variables whose values are determined at runtime. - * Used via: - * - * object foo extends Cross[FooModule]("bar", "baz", "qux") - * class FooModule(v: String) extends Module{ - * ... - * } - */ -class Cross[T](cases: Any*) - (implicit ci: Cross.Factory[T], - ctx: mill.define.Ctx) extends mill.define.Module()(ctx) { - - override lazy val millModuleDirectChildren = - this.millInternal.reflectNestedObjects[Module] ++ - items.collect{case (k, v: mill.define.Module) => v} - - val items = for(c0 <- cases.toList) yield{ - val c = c0 match{ - case p: Product => p - case v => Tuple1(v) - } - val crossValues = c.productIterator.toList - val relPath = ctx.segment.pathSegments - val sub = ci.make( - c, - ctx.copy( - segments = ctx.segments ++ Seq(ctx.segment), - basePath = ctx.basePath / relPath, - segment = Segment.Cross(crossValues) - ) - ) - (crossValues, sub) - } - val itemMap = items.toMap - - /** - * Fetch the cross module corresponding to the given cross values - */ - def get(args: Seq[Any]) = itemMap(args.toList) - - /** - * Fetch the cross module corresponding to the given cross values - */ - def apply(arg0: Any, args: Any*) = itemMap(arg0 :: args.toList) - - /** - * Fetch the relevant cross module given the implicit resolver you have in - * scope. This is often the first cross module whose cross-version is - * compatible with the current module. - */ - def apply[V >: T]()(implicit resolver: Cross.Resolver[V]): T = { - resolver.resolve(this.asInstanceOf[Cross[V]]).asInstanceOf[T] - } -}
\ No newline at end of file diff --git a/core/src/main/scala/mill/define/Ctx.scala b/core/src/main/scala/mill/define/Ctx.scala deleted file mode 100644 index 6d685521..00000000 --- a/core/src/main/scala/mill/define/Ctx.scala +++ /dev/null @@ -1,69 +0,0 @@ -package mill.define - -import ammonite.main.Router.Overrides -import ammonite.ops.{Path, RelPath} - -import scala.annotation.implicitNotFound - -sealed trait Segment{ - def pathSegments: Seq[String] = this match{ - case Segment.Label(s) => List(s) - case Segment.Cross(vs) => vs.map(_.toString) - } -} -object Segment{ - case class Label(value: String) extends Segment - case class Cross(value: Seq[Any]) extends Segment -} - -case class BasePath(value: Path) - - -/** - * Models a path with the Mill build hierarchy, e.g. - * - * amm.util[2.11].test.compile - * - * .-separated segments are [[Segment.Label]]s, while []-delimited - * segments are [[Segment.Cross]]s - */ -case class Segments(value: Segment*){ - def ++(other: Seq[Segment]): Segments = Segments(value ++ other:_*) - def ++(other: Segments): Segments = Segments(value ++ other.value:_*) - def render = value 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 - } -} - -@implicitNotFound("Modules, Targets and Commands can only be defined within a mill Module") -case class Ctx(enclosing: String, - lineNum: Int, - segment: Segment, - basePath: Path, - segments: Segments, - overrides: Int){ -} - -object Ctx{ - implicit def make(implicit millModuleEnclosing0: sourcecode.Enclosing, - millModuleLine0: sourcecode.Line, - millName0: sourcecode.Name, - millModuleBasePath0: BasePath, - segments0: Segments, - overrides0: Overrides): Ctx = { - Ctx( - millModuleEnclosing0.value, - millModuleLine0.value, - Segment.Label(millName0.value), - millModuleBasePath0.value, - segments0, - overrides0.value - ) - } -}
\ No newline at end of file diff --git a/core/src/main/scala/mill/define/Discover.scala b/core/src/main/scala/mill/define/Discover.scala deleted file mode 100644 index 52f4ab77..00000000 --- a/core/src/main/scala/mill/define/Discover.scala +++ /dev/null @@ -1,57 +0,0 @@ -package mill.define -import language.experimental.macros -import ammonite.main.Router.EntryPoint - -import scala.collection.mutable -import scala.reflect.macros.blackbox - -case class Discover(value: Map[Class[_], Seq[EntryPoint[_]]]) -object Discover { - def apply[T]: Discover = macro applyImpl[T] - - def applyImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Discover] = { - import c.universe._ - import compat._ - val seen = mutable.Set.empty[Type] - def rec(tpe: Type): Unit = { - if (!seen(tpe)){ - seen.add(tpe) - for{ - m <- tpe.members - memberTpe = m.typeSignature - if memberTpe.resultType <:< typeOf[mill.define.Module] && memberTpe.paramLists.isEmpty - } rec(memberTpe.resultType) - - if (tpe <:< typeOf[mill.define.Cross[_]]){ - val inner = typeOf[Cross[_]] - .typeSymbol - .asClass - .typeParams - .head - .asType - .toType - .asSeenFrom(tpe, typeOf[Cross[_]].typeSymbol) - - rec(inner) - } - } - } - rec(weakTypeOf[T]) - - val router = new ammonite.main.Router(c) - val mapping = for{ - discoveredModuleType <- seen - val routes = router.getAllRoutesForClass( - discoveredModuleType.asInstanceOf[router.c.Type], - _.returnType <:< weakTypeOf[mill.define.Command[_]].asInstanceOf[router.c.Type] - ).map(_.asInstanceOf[c.Tree]) - if routes.nonEmpty - } yield { - val lhs = q"classOf[${discoveredModuleType.typeSymbol.asClass}]" - val rhs = q"scala.Seq[ammonite.main.Router.EntryPoint[${discoveredModuleType.typeSymbol.asClass}]](..$routes)" - q"$lhs -> $rhs" - } - - c.Expr[Discover](q"mill.define.Discover(scala.collection.immutable.Map(..$mapping))") - } -} diff --git a/core/src/main/scala/mill/define/Graph.scala b/core/src/main/scala/mill/define/Graph.scala deleted file mode 100644 index f06dca11..00000000 --- a/core/src/main/scala/mill/define/Graph.scala +++ /dev/null @@ -1,61 +0,0 @@ -package mill.define - -import mill.eval.Tarjans -import mill.util.MultiBiMap -import mill.util.Strict.Agg - -object Graph { - class TopoSorted private[Graph](val values: Agg[Task[_]]) - def groupAroundImportantTargets[T](topoSortedTargets: TopoSorted) - (important: PartialFunction[Task[_], T]): MultiBiMap[T, Task[_]] = { - - val output = new MultiBiMap.Mutable[T, Task[_]]() - for ((target, t) <- topoSortedTargets.values.flatMap(t => important.lift(t).map((t, _)))) { - - val transitiveTargets = new Agg.Mutable[Task[_]] - def rec(t: Task[_]): Unit = { - if (transitiveTargets.contains(t)) () // do nothing - else if (important.isDefinedAt(t) && t != target) () // do nothing - else { - transitiveTargets.append(t) - t.inputs.foreach(rec) - } - } - rec(target) - output.addAll(t, topoSorted(transitiveTargets).values) - } - output - } - - def transitiveTargets(sourceTargets: Agg[Task[_]]): Agg[Task[_]] = { - val transitiveTargets = new Agg.Mutable[Task[_]] - def rec(t: Task[_]): Unit = { - if (transitiveTargets.contains(t)) () // do nothing - else { - transitiveTargets.append(t) - t.inputs.foreach(rec) - } - } - - sourceTargets.items.foreach(rec) - transitiveTargets - } - /** - * Takes the given targets, finds all the targets they transitively depend - * on, and sort them topologically. Fails if there are dependency cycles - */ - def topoSorted(transitiveTargets: Agg[Task[_]]): TopoSorted = { - - val indexed = transitiveTargets.indexed - val targetIndices = indexed.zipWithIndex.toMap - - val numberedEdges = - for(t <- transitiveTargets.items) - yield t.inputs.collect(targetIndices) - - val sortedClusters = Tarjans(numberedEdges) - val nonTrivialClusters = sortedClusters.filter(_.length > 1) - assert(nonTrivialClusters.isEmpty, nonTrivialClusters) - new TopoSorted(Agg.from(sortedClusters.flatten.map(indexed))) - } -} diff --git a/core/src/main/scala/mill/define/Module.scala b/core/src/main/scala/mill/define/Module.scala deleted file mode 100644 index e42ce798..00000000 --- a/core/src/main/scala/mill/define/Module.scala +++ /dev/null @@ -1,104 +0,0 @@ -package mill.define - -import java.lang.reflect.Modifier - -import ammonite.main.Router.{EntryPoint, Overrides} -import ammonite.ops.Path - -import scala.language.experimental.macros -import scala.reflect.ClassTag -import scala.reflect.macros.blackbox -/** - * `Module` is a class meant to be extended by `trait`s *only*, in order to - * propagate the implicit parameters forward to the final concrete - * instantiation site so they can capture the enclosing/line information of - * the concrete instance. - */ -class Module(implicit outerCtx0: mill.define.Ctx) extends mill.moduledefs.Cacher{ outer => - - /** - * Miscellaneous machinery around traversing & querying the build hierarchy, - * that should not be needed by normal users of Mill - */ - object millInternal extends Module.Internal(this) - - lazy val millModuleDirectChildren = millInternal.reflectNestedObjects[Module] - def millOuterCtx = outerCtx0 - def basePath: Path = millOuterCtx.basePath / millOuterCtx.segment.pathSegments - implicit def millModuleBasePath: BasePath = BasePath(basePath) - implicit def millModuleSegments: Segments = { - millOuterCtx.segments ++ Seq(millOuterCtx.segment) - } -} - -object Module{ - class Internal(outer: Module){ - def traverse[T](f: Module => Seq[T]): Seq[T] = { - def rec(m: Module): Seq[T] = f(m) ++ m.millModuleDirectChildren.flatMap(rec) - rec(outer) - } - lazy val segmentsToModules = traverse{m => Seq(m.millModuleSegments -> m)} - .toMap - - lazy val targets = traverse{_.millInternal.reflect[Target[_]]}.toSet - - lazy val segmentsToTargets = targets - .map(t => (t.ctx.segments, t)) - .toMap - - // Ensure we do not propagate the implicit parameters as implicits within - // the body of any inheriting class/trait/objects, as it would screw up any - // one else trying to use sourcecode.{Enclosing,Line} to capture debug info - lazy val millModuleEnclosing = outer.millOuterCtx.enclosing - lazy val millModuleLine = outer.millOuterCtx.lineNum - - def reflect[T: ClassTag] = { - outer - .getClass - .getMethods - .filter(!_.getName.contains('$')) - .filter(_.getParameterCount == 0) - .filter(x => (x.getModifiers & Modifier.STATIC) == 0) - .filter(implicitly[ClassTag[T]].runtimeClass isAssignableFrom _.getReturnType) - .map(_.invoke(outer).asInstanceOf[T]) - } - def reflectNames[T: ClassTag] = { - outer - .getClass - .getMethods - .filter(x => (x.getModifiers & Modifier.STATIC) == 0) - .filter(implicitly[ClassTag[T]].runtimeClass isAssignableFrom _.getReturnType) - .map(_.getName) - } - // For some reason, this fails to pick up concrete `object`s nested directly within - // another top-level concrete `object`. This is fine for now, since Mill's Ammonite - // script/REPL runner always wraps user code in a wrapper object/trait - def reflectNestedObjects[T: ClassTag] = { - reflect[T] ++ - outer - .getClass - .getClasses - .filter(implicitly[ClassTag[T]].runtimeClass isAssignableFrom _) - .flatMap(c => c.getFields.find(_.getName == "MODULE$").map(_.get(c).asInstanceOf[T])) - } - } -} -trait TaskModule extends Module { - def defaultCommandName(): String -} - -class BaseModule(basePath0: Path) - (implicit millModuleEnclosing0: sourcecode.Enclosing, - millModuleLine0: sourcecode.Line, - millName0: sourcecode.Name, - overrides0: Overrides) - extends Module()( - mill.define.Ctx.make(implicitly, implicitly, implicitly, BasePath(basePath0), Segments(), implicitly) - ){ - // A BaseModule should provide an empty Segments list to it's children, since - // it is the root of the module tree, and thus must not include it's own - // sourcecode.Name as part of the list, - override implicit def millModuleSegments: Segments = Segments() - override implicit def millModuleBasePath: BasePath = BasePath(millOuterCtx.basePath) - override def basePath = millOuterCtx.basePath -}
\ No newline at end of file diff --git a/core/src/main/scala/mill/define/Task.scala b/core/src/main/scala/mill/define/Task.scala deleted file mode 100644 index 90908e4e..00000000 --- a/core/src/main/scala/mill/define/Task.scala +++ /dev/null @@ -1,308 +0,0 @@ -package mill.define - -import mill.define.Applicative.Applyable -import mill.eval.{PathRef, Result} - -import upickle.default.{ReadWriter => RW, Reader => R, Writer => W} - -import scala.language.experimental.macros -import scala.reflect.macros.blackbox.Context - -/** - * Models a single node in the Mill build graph, with a list of inputs and a - * single output of type [[T]]. - * - * Generally not instantiated manually, but instead constructed via the - * [[Target.apply]] & similar macros. - */ -abstract class Task[+T] extends Task.Ops[T] with Applyable[Task, T]{ - /** - * What other Targets does this Target depend on? - */ - val inputs: Seq[Task[_]] - - /** - * Evaluate this target - */ - def evaluate(args: mill.util.Ctx): Result[T] - - /** - * Even if this target's inputs did not change, does it need to re-evaluate - * anyway? - */ - def sideHash: Int = 0 - - def flushDest: Boolean = true - - def asTarget: Option[Target[T]] = None - def asCommand: Option[Command[T]] = None - def asPersistent: Option[Persistent[T]] = None - def self = this -} - -trait NamedTask[+T] extends Task[T]{ - def ctx: mill.define.Ctx - def label = ctx.segment match{case Segment.Label(v) => v} -} -trait Target[+T] extends NamedTask[T]{ - override def asTarget = Some(this) - def readWrite: RW[_] -} - -object Target extends TargetGenerated with Applicative.Applyer[Task, Task, Result, mill.util.Ctx] { - - implicit def apply[T](t: T) - (implicit r: R[T], - w: W[T], - ctx: mill.define.Ctx): Target[T] = macro targetImpl[T] - - def targetImpl[T: c.WeakTypeTag](c: Context) - (t: c.Expr[T]) - (r: c.Expr[R[T]], - w: c.Expr[W[T]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Target[T]] = { - import c.universe._ - val lhs = Applicative.impl0[Task, T, mill.util.Ctx](c)(reify(Result.Success(t.splice)).tree) - - mill.moduledefs.Cacher.impl0[TargetImpl[T]](c)( - reify( - new TargetImpl[T](lhs.splice, ctx.splice, RW(w.splice.write, r.splice.read)) - ) - ) - } - - implicit def apply[T](t: Result[T]) - (implicit r: R[T], - w: W[T], - ctx: mill.define.Ctx): Target[T] = macro targetResultImpl[T] - - def targetResultImpl[T: c.WeakTypeTag](c: Context) - (t: c.Expr[Result[T]]) - (r: c.Expr[R[T]], - w: c.Expr[W[T]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Target[T]] = { - import c.universe._ - mill.moduledefs.Cacher.impl0[Target[T]](c)( - reify( - new TargetImpl[T]( - Applicative.impl0[Task, T, mill.util.Ctx](c)(t.tree).splice, - ctx.splice, - RW(w.splice.write, r.splice.read) - ) - ) - ) - } - - def apply[T](t: Task[T]) - (implicit r: R[T], - w: W[T], - ctx: mill.define.Ctx): Target[T] = macro targetTaskImpl[T] - - def targetTaskImpl[T: c.WeakTypeTag](c: Context) - (t: c.Expr[Task[T]]) - (r: c.Expr[R[T]], - w: c.Expr[W[T]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Target[T]] = { - import c.universe._ - mill.moduledefs.Cacher.impl0[Target[T]](c)( - reify( - new TargetImpl[T](t.splice, ctx.splice, RW(w.splice.write, r.splice.read)) - ) - ) - } - - def source(value: Result[ammonite.ops.Path]) - (implicit r: R[PathRef], - w: W[PathRef], - ctx: mill.define.Ctx): Input[PathRef] = macro sourceImpl - - def sourceImpl(c: Context) - (value: c.Expr[Result[ammonite.ops.Path]]) - (r: c.Expr[R[PathRef]], - w: c.Expr[W[PathRef]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Input[PathRef]] = { - import c.universe._ - val wrapped: c.Expr[Result[PathRef]] = reify(value.splice match{ - case Result.Success(p) => Result.Success(PathRef(p)) - case x: Result.Failing => x - }) - mill.moduledefs.Cacher.impl0[Input[PathRef]](c)( - reify( - new Input[PathRef]( - Applicative.impl0[Task, PathRef, mill.util.Ctx](c)(wrapped.tree).splice, - ctx.splice, - RW(w.splice.write, r.splice.read), - ) - ) - ) - } - - def input[T](value: Result[T]) - (implicit r: R[T], - w: W[T], - ctx: mill.define.Ctx): Input[T] = macro inputImpl[T] - - def inputImpl[T: c.WeakTypeTag](c: Context) - (value: c.Expr[T]) - (r: c.Expr[R[T]], - w: c.Expr[W[T]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Input[T]] = { - import c.universe._ - - mill.moduledefs.Cacher.impl0[Input[T]](c)( - reify( - new Input[T]( - Applicative.impl[Task, T, mill.util.Ctx](c)(value).splice, - ctx.splice, - RW(w.splice.write, r.splice.read) - ) - ) - ) - } - - def command[T](t: Task[T]) - (implicit ctx: mill.define.Ctx, - w: W[T]): Command[T] = new Command(t, ctx, w) - - def command[T](t: Result[T]) - (implicit w: W[T], - ctx: mill.define.Ctx): Command[T] = macro commandImpl[T] - - def commandImpl[T: c.WeakTypeTag](c: Context) - (t: c.Expr[T]) - (w: c.Expr[W[T]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Command[T]] = { - import c.universe._ - reify( - new Command[T](Applicative.impl[Task, T, mill.util.Ctx](c)(t).splice, ctx.splice, w.splice) - ) - } - - def task[T](t: Result[T]): Task[T] = macro Applicative.impl[Task, T, mill.util.Ctx] - - def persistent[T](t: Result[T])(implicit r: R[T], - w: W[T], - ctx: mill.define.Ctx): Target[T] = macro persistentImpl[T] - - def persistentImpl[T: c.WeakTypeTag](c: Context) - (t: c.Expr[T]) - (r: c.Expr[R[T]], - w: c.Expr[W[T]], - ctx: c.Expr[mill.define.Ctx]): c.Expr[Persistent[T]] = { - import c.universe._ - - - mill.moduledefs.Cacher.impl0[Persistent[T]](c)( - reify( - new Persistent[T]( - Applicative.impl[Task, T, mill.util.Ctx](c)(t).splice, - ctx.splice, - RW(w.splice.write, r.splice.read) - ) - ) - ) - } - - type TT[+X] = Task[X] - def makeT[X](inputs0: Seq[TT[_]], evaluate0: mill.util.Ctx => Result[X]) = new Task[X] { - val inputs = inputs0 - def evaluate(x: mill.util.Ctx) = evaluate0(x) - } - - def underlying[A](v: Task[A]) = v - def mapCtx[A, B](t: Task[A])(f: (A, mill.util.Ctx) => Result[B]) = t.mapDest(f) - def zip() = new Task.Task0(()) - def zip[A](a: Task[A]) = a.map(Tuple1(_)) - def zip[A, B](a: Task[A], b: Task[B]) = a.zip(b) -} - -case class Caller[A](value: A) -object Caller { - def apply[T]()(implicit c: Caller[T]) = c.value - implicit def generate[T]: Caller[T] = macro impl[T] - def impl[T: c.WeakTypeTag](c: Context): c.Tree = { - import c.universe._ - q"new _root_.mill.define.Caller[${weakTypeOf[T]}](this)" - } -} - -class TargetImpl[+T](t: Task[T], - ctx0: mill.define.Ctx, - val readWrite: RW[_]) extends Target[T] { - val ctx = ctx0.copy(segments = ctx0.segments ++ Seq(ctx0.segment)) - val inputs = Seq(t) - def evaluate(args: mill.util.Ctx) = args[T](0) - override def toString = ctx.enclosing + "@" + Integer.toHexString(System.identityHashCode(this)) -} -class Command[+T](t: Task[T], - ctx0: mill.define.Ctx, - val writer: W[_]) extends NamedTask[T] { - val ctx = ctx0.copy(segments = ctx0.segments ++ Seq(ctx0.segment)) - val inputs = Seq(t) - def evaluate(args: mill.util.Ctx) = args[T](0) - override def asCommand = Some(this) -} -class Persistent[+T](t: Task[T], - ctx0: mill.define.Ctx, - readWrite: RW[_]) - extends TargetImpl[T](t, ctx0, readWrite) { - - override def flushDest = false - override def asPersistent = Some(this) -} -class Input[T](t: Task[T], - ctx0: mill.define.Ctx, - val readWrite: RW[_]) extends Target[T]{ - val ctx = ctx0.copy(segments = ctx0.segments ++ Seq(ctx0.segment)) - val inputs = Seq(t) - def evaluate(args: mill.util.Ctx) = args[T](0) - override def sideHash = util.Random.nextInt() -} - -object Task { - - - - - class Task0[T](t: T) extends Task[T]{ - lazy val t0 = t - val inputs = Nil - def evaluate(args: mill.util.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.util.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) - - } - - def traverse[T, V](source: Seq[T])(f: T => Task[V]) = { - new Sequence[V](source.map(f)) - } - def sequence[T](source: Seq[Task[T]]) = new Sequence[T](source) - - class Sequence[+T](inputs0: Seq[Task[T]]) extends Task[Seq[T]]{ - val inputs = inputs0 - def evaluate(args: mill.util.Ctx) = { - for (i <- 0 until args.length) - yield args(i).asInstanceOf[T] - } - - } - class Mapped[+T, +V](source: Task[T], f: T => V) extends Task[V]{ - def evaluate(args: mill.util.Ctx) = f(args(0)) - val inputs = List(source) - } - class MappedDest[+T, +V](source: Task[T], f: (T, mill.util.Ctx) => Result[V]) extends Task[V]{ - def evaluate(args: mill.util.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.util.Ctx) = (args(0), args(1)) - val inputs = List(source1, source2) - } -} diff --git a/core/src/main/scala/mill/define/Worker.scala b/core/src/main/scala/mill/define/Worker.scala deleted file mode 100644 index 3d35d2e0..00000000 --- a/core/src/main/scala/mill/define/Worker.scala +++ /dev/null @@ -1,32 +0,0 @@ -package mill.define - - -/** - * Worker serves three purposes: - * - * - Cache in-memory state between tasks (e.g. object initialization) - * - Including warm classloaders with isolated bytecode - * - Mutex to limit concurrency - * - Manage out-of-process subprocesses <-- skip this for now - * - * Key usage: - * - * - T{ - * ZincWorker().compile(a() + b()) - * } - * - * Desugars into: - * - * - T.zipMap(ZincWorker, a, b){ (z, a1, b1) => z.compile(a1, b1) } - * - * Workers are shoehorned into the `Task` type. This lets them fit nicely in - * the `T{...}` syntax, as well as being statically-inspectable before - * evaluating the task graph. The Worker defines how it is evaluated, but it's - * evaluation/caching/lifecycle are controlled by the `Evaluator` - */ -trait Worker[V] extends Task[V] with mill.util.Ctx.Loader[V]{ - val inputs = Nil - def make(): V - def evaluate(args: mill.util.Ctx) = args.load(this) - def path = this.getClass.getCanonicalName.filter(_ != '$').split('.') -} diff --git a/core/src/main/scala/mill/eval/Evaluator.scala b/core/src/main/scala/mill/eval/Evaluator.scala deleted file mode 100644 index 44f24275..00000000 --- a/core/src/main/scala/mill/eval/Evaluator.scala +++ /dev/null @@ -1,285 +0,0 @@ -package mill.eval - -import java.net.URLClassLoader - -import ammonite.ops._ -import ammonite.runtime.SpecialClassLoader -import mill.define.{Graph, NamedTask, Segment, Segments, Target, Task} -import mill.util -import mill.util.Ctx.Loader -import mill.util._ -import mill.util.Strict.Agg - -import scala.collection.mutable -import scala.util.control.NonFatal -case class Labelled[T](target: NamedTask[T], - segments: Segments){ - def format = target match{ - case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) - case _ => None - } - def writer = target match{ - case t: mill.define.Command[T] => Some(t.writer.asInstanceOf[upickle.default.Writer[T]]) - case t: Target[T] => Some(t.readWrite.asInstanceOf[upickle.default.ReadWriter[T]]) - case _ => None - } -} -object RootModuleLoader extends Loader[mill.Module] { - def make() = ??? -} -class Evaluator[T](val workspacePath: Path, - val basePath: Path, - val rootModule: mill.Module, - log: Logger, - val classLoaderSig: Seq[(Path, Long)] = Evaluator.classLoaderSig){ - - - val workerCache = mutable.Map.empty[Ctx.Loader[_], Any] - workerCache(RootModuleLoader) = rootModule - def evaluate(goals: Agg[Task[_]]): Evaluator.Results = { - mkdir(workspacePath) - - val transitive = Graph.transitiveTargets(goals) - val topoSorted = Graph.topoSorted(transitive) - val sortedGroups = Graph.groupAroundImportantTargets(topoSorted){ - case t: NamedTask[Any] => - val segments = t.ctx.segments - val (finalTaskOverrides, enclosing) = t match{ - case t: Target[_] => rootModule.millInternal.segmentsToTargets(segments).ctx.overrides -> t.ctx.enclosing - case c: mill.define.Command[_] => 0 -> c.ctx.enclosing - } - val additional = - if (finalTaskOverrides == t.ctx.overrides) Nil - else Seq(Segment.Label("overriden"), Segment.Label(enclosing)) - - Right(Labelled(t, segments ++ additional)) - case t if goals.contains(t) => Left(t) - } - - val evaluated = new Agg.Mutable[Task[_]] - val results = mutable.LinkedHashMap.empty[Task[_], Result[Any]] - - for (((terminal, group), i) <- sortedGroups.items().zipWithIndex){ - // Increment the counter message by 1 to go from 1/10 to 10/10 instead of 0/10 to 9/10 - val counterMsg = (i+1) + "/" + sortedGroups.keyCount - val (newResults, newEvaluated) = evaluateGroupCached(terminal, group, results, counterMsg) - for(ev <- newEvaluated){ - evaluated.append(ev) - } - for((k, v) <- newResults) results.put(k, v) - - } - - val failing = new util.MultiBiMap.Mutable[Either[Task[_], Labelled[_]], Result.Failing] - 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, - results - ) - } - - - def evaluateGroupCached(terminal: Either[Task[_], Labelled[_]], - group: Agg[Task[_]], - results: collection.Map[Task[_], Result[Any]], - counterMsg: String): (collection.Map[Task[_], Result[Any]], Seq[Task[_]]) = { - - - val externalInputs = group.items.flatMap(_.inputs).filter(!group.contains(_)) - - val inputsHash = - externalInputs.map(results).toVector.hashCode + - group.toIterator.map(_.sideHash).toVector.hashCode() + - classLoaderSig.hashCode() - - terminal match{ - case Left(task) => - evaluateGroup( - group, - results, - groupBasePath = None, - paths = None, - maybeTargetLabel = None, - counterMsg = counterMsg - ) - case Right(labelledTarget) => - 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 - if cachedHash == inputsHash - reader <- labelledTarget.format - parsed <- reader.read.lift(terminalResult) - } yield parsed - - cached match{ - case Some(parsed) => - val newResults = mutable.LinkedHashMap.empty[Task[_], Result[Any]] - newResults(labelledTarget.target) = parsed - (newResults, Nil) - - case _ => - - val Seq(first, rest @_*) = labelledTarget.segments.value - val msgParts = Seq(first.asInstanceOf[Segment.Label].value) ++ rest.map{ - case Segment.Label(s) => "." + s - case Segment.Cross(s) => "[" + s.mkString(",") + "]" - } - - if (labelledTarget.target.flushDest) rm(paths.dest) - val (newResults, newEvaluated) = evaluateGroup( - group, - results, - groupBasePath = Some(groupBasePath), - paths = Some(paths), - maybeTargetLabel = Some(msgParts.mkString), - counterMsg = counterMsg - ) - - newResults(labelledTarget.target) match{ - case Result.Success(v) => - val terminalResult = labelledTarget - .writer - .asInstanceOf[Option[upickle.default.Writer[Any]]] - .map(_.write(v)) - - for(t <- terminalResult){ - write.over(paths.meta, upickle.default.write(inputsHash -> t, indent = 4)) - } - case _ => - // Wipe out any cached meta.json file that exists, so - // a following run won't look at the cached metadata file and - // assume it's associated with the possibly-borked state of the - // destPath after an evaluation failure. - rm(paths.meta) - } - - - - (newResults, newEvaluated) - } - } - } - - - def evaluateGroup(group: Agg[Task[_]], - results: collection.Map[Task[_], Result[Any]], - groupBasePath: Option[Path], - paths: Option[Evaluator.Paths], - maybeTargetLabel: Option[String], - counterMsg: String) = { - - - val newEvaluated = mutable.Buffer.empty[Task[_]] - val newResults = mutable.LinkedHashMap.empty[Task[_], Result[Any]] - - val nonEvaluatedTargets = group.indexed.filterNot(results.contains) - - maybeTargetLabel.foreach { targetLabel => - val inputResults = for { - target <- nonEvaluatedTargets - item <- target.inputs.filterNot(group.contains) - } yield results(item) - - val logRun = inputResults.forall(_.isInstanceOf[Result.Success[_]]) - - if(logRun) { log.ticker(s"[$counterMsg] $targetLabel ") } - } - - val multiLogger = resolveLogger(paths.map(_.log)) - - for (target <- nonEvaluatedTargets) { - - newEvaluated.append(target) - val targetInputValues = target.inputs - .map(x => newResults.getOrElse(x, results(x))) - .collect{ case Result.Success(v) => v } - - val res = - if (targetInputValues.length != target.inputs.length) Result.Skipped - else { - 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 = { - workerCache.getOrElseUpdate(x, x.make()).asInstanceOf[T] - } - } - ) - - val out = System.out - val err = System.err - try{ - System.setErr(multiLogger.errorStream) - System.setOut(multiLogger.outputStream) - Console.withOut(multiLogger.outputStream){ - Console.withErr(multiLogger.errorStream){ - target.evaluate(args) - } - } - }catch{ case NonFatal(e) => - val currentStack = new Exception().getStackTrace - Result.Exception(e, currentStack) - }finally{ - System.setErr(err) - System.setOut(out) - } - } - - newResults(target) = res - } - - multiLogger.close() - - (newResults, newEvaluated) - } - - def resolveLogger(logPath: Option[Path]): Logger = logPath match{ - case None => log - case Some(path) => - rm(path) - MultiLogger(log.colored, log, FileLogger(log.colored, path)) - } -} - - -object Evaluator{ - case class Paths(out: Path, - dest: Path, - meta: Path, - log: Path) - def makeSegmentStrings(segments: Segments) = segments.value.flatMap{ - case Segment.Label(s) => Seq(s) - case Segment.Cross(values) => values.map(_.toString) - } - def resolveDestPaths(workspacePath: Path, segments: Segments): Paths = { - val segmentStrings = makeSegmentStrings(segments) - val targetPath = workspacePath / segmentStrings - Paths(targetPath, targetPath / 'dest, targetPath / "meta.json", targetPath / 'log) - } - - // check if the build itself has changed - def classLoaderSig = Thread.currentThread().getContextClassLoader match { - case scl: SpecialClassLoader => scl.classpathSignature - case ucl: URLClassLoader => SpecialClassLoader.initialClasspathSignature(ucl) - case _ => Nil - - } - case class Results(rawValues: Seq[Result[Any]], - evaluated: Agg[Task[_]], - transitive: Agg[Task[_]], - 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/eval/PathRef.scala b/core/src/main/scala/mill/eval/PathRef.scala deleted file mode 100644 index 0a629a14..00000000 --- a/core/src/main/scala/mill/eval/PathRef.scala +++ /dev/null @@ -1,70 +0,0 @@ -package mill.eval - -import java.io.IOException -import java.nio.file.attribute.BasicFileAttributes -import java.nio.file.{FileVisitResult, FileVisitor} -import java.nio.{file => jnio} -import java.security.MessageDigest -import upickle.default.{ReadWriter => RW} -import ammonite.ops.Path -import mill.util.JsonFormatters - - -/** - * A wrapper around `ammonite.ops.Path` that calculates it's hashcode based - * on the contents of the filesystem underneath it. Used to ensure filesystem - * changes can bust caches which are keyed off hashcodes. - */ -case class PathRef(path: ammonite.ops.Path, quick: Boolean = false){ - val sig = { - val digest = MessageDigest.getInstance("MD5") - - val buffer = new Array[Byte](16 * 1024) - jnio.Files.walkFileTree( - path.toNIO, - new FileVisitor[jnio.Path] { - def preVisitDirectory(dir: jnio.Path, attrs: BasicFileAttributes) = { - digest.update(dir.toAbsolutePath.toString.getBytes) - FileVisitResult.CONTINUE - } - - def visitFile(file: jnio.Path, attrs: BasicFileAttributes) = { - digest.update(file.toAbsolutePath.toString.getBytes) - if (quick){ - val value = (path.mtime.toMillis, path.size).hashCode() - digest.update((value >>> 24).toByte) - digest.update((value >>> 16).toByte) - digest.update((value >>> 8).toByte) - digest.update(value.toByte) - }else { - val is = jnio.Files.newInputStream(file) - - def rec(): Unit = { - val length = is.read(buffer) - if (length != -1) { - digest.update(buffer, 0, length) - rec() - } - } - rec() - - is.close() - } - FileVisitResult.CONTINUE - } - - def visitFileFailed(file: jnio.Path, exc: IOException) = FileVisitResult.CONTINUE - def postVisitDirectory(dir: jnio.Path, exc: IOException) = FileVisitResult.CONTINUE - } - ) - - java.util.Arrays.hashCode(digest.digest()) - - } - override def hashCode() = sig -} - -object PathRef{ - private implicit val pathFormat: RW[Path] = JsonFormatters.pathReadWrite - implicit def jsonFormatter: RW[PathRef] = upickle.default.macroRW -} diff --git a/core/src/main/scala/mill/eval/Result.scala b/core/src/main/scala/mill/eval/Result.scala deleted file mode 100644 index a9b2c70b..00000000 --- a/core/src/main/scala/mill/eval/Result.scala +++ /dev/null @@ -1,14 +0,0 @@ -package mill.eval - -sealed trait Result[+T] -object Result{ - implicit def create[T](t: => T): Result[T] = { - try Success(t) - catch { case e: Throwable => Exception(e, new java.lang.Exception().getStackTrace) } - } - case class Success[T](value: T) extends Result[T] - case object Skipped extends Result[Nothing] - sealed trait Failing extends Result[Nothing] - case class Failure(msg: String) extends Failing - case class Exception(throwable: Throwable, outerStack: Seq[StackTraceElement]) extends Failing -}
\ No newline at end of file diff --git a/core/src/main/scala/mill/eval/Tarjans.scala b/core/src/main/scala/mill/eval/Tarjans.scala deleted file mode 100644 index ade335a9..00000000 --- a/core/src/main/scala/mill/eval/Tarjans.scala +++ /dev/null @@ -1,51 +0,0 @@ -package mill.eval - -import scala.collection.mutable - -// Adapted from -// https://github.com/indy256/codelibrary/blob/c52247216258e84aac442a23273b7d8306ef757b/java/src/SCCTarjan.java -object Tarjans { - def apply(graph0: TraversableOnce[TraversableOnce[Int]]): Seq[Seq[Int]] = { - val graph = graph0.map(_.toArray).toArray - val n = graph.length - val visited = new Array[Boolean](n) - val stack = mutable.ArrayBuffer.empty[Integer] - var time = 0 - val lowlink = new Array[Int](n) - val components = mutable.ArrayBuffer.empty[Seq[Int]] - - - for (u <- 0 until n) { - if (!visited(u)) dfs(u) - } - - def dfs(u: Int): Unit = { - lowlink(u) = time - time += 1 - visited(u) = true - stack.append(u) - var isComponentRoot = true - for (v <- graph(u)) { - if (!visited(v)) dfs(v) - if (lowlink(u) > lowlink(v)) { - lowlink(u) = lowlink(v) - isComponentRoot = false - } - } - if (isComponentRoot) { - val component = mutable.Buffer.empty[Int] - - var done = false - while (!done) { - val x = stack.last - stack.remove(stack.length - 1) - component.append(x) - lowlink(x) = Integer.MAX_VALUE - if (x == u) done = true - } - components.append(component) - } - } - components - } -} diff --git a/core/src/main/scala/mill/main/MainRunner.scala b/core/src/main/scala/mill/main/MainRunner.scala deleted file mode 100644 index 5281b886..00000000 --- a/core/src/main/scala/mill/main/MainRunner.scala +++ /dev/null @@ -1,116 +0,0 @@ -package mill.main -import java.io.{InputStream, OutputStream, PrintStream} - -import ammonite.Main -import ammonite.interp.{Interpreter, Preprocessor} -import ammonite.ops.Path -import ammonite.util._ -import mill.define.Discover -import mill.eval.{Evaluator, PathRef} -import mill.util.PrintLogger -import upickle.Js - -/** - * Customized version of [[ammonite.MainRunner]], allowing us to run Mill - * `build.sc` scripts with mill-specific tweaks such as a custom - * `scriptCodeWrapper` or with a persistent evaluator between runs. - */ -class MainRunner(config: ammonite.main.Cli.Config, - show: Boolean, - outprintStream: PrintStream, - errPrintStream: PrintStream, - stdIn: InputStream) - extends ammonite.MainRunner( - config, outprintStream, errPrintStream, - stdIn, outprintStream, errPrintStream - ){ - var lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_], Discover)] = None - - override def runScript(scriptPath: Path, scriptArgs: List[String]) = - watchLoop( - isRepl = false, - printing = true, - mainCfg => { - val (result, interpWatched) = RunScript.runScript( - mainCfg.wd, - scriptPath, - mainCfg.instantiateInterpreter(), - scriptArgs, - lastEvaluator, - new PrintLogger( - colors != ammonite.util.Colors.BlackWhite, - colors, - if (show) errPrintStream else outprintStream, - errPrintStream, - errPrintStream - ) - ) - - result match{ - case Res.Success(data) => - val (eval, discover, evaluationWatches, res) = data - - lastEvaluator = Some((interpWatched, eval, discover)) - - (Res(res), interpWatched ++ evaluationWatches) - case _ => (result, interpWatched) - } - } - ) - - override def handleWatchRes[T](res: Res[T], printing: Boolean) = { - res match{ - case Res.Success(value) => - if (show){ - for(json <- value.asInstanceOf[Seq[Js.Value]]){ - outprintStream.println(json) - } - } - - true - - case _ => super.handleWatchRes(res, printing) - } - - } - override def initMain(isRepl: Boolean) = { - super.initMain(isRepl).copy( - scriptCodeWrapper = CustomCodeWrapper, - // Ammonite does not properly forward the wd from CliConfig to Main, so - // force forward it outselves - wd = config.wd - ) - } - object CustomCodeWrapper extends Preprocessor.CodeWrapper { - def top(pkgName: Seq[Name], imports: Imports, indexedWrapperName: Name) = { - val wrapName = indexedWrapperName.backticked - s""" - |package ${pkgName.head.encoded} - |package ${Util.encodeScalaSourcePath(pkgName.tail)} - |$imports - |import mill._ - | - |object $wrapName extends mill.define.BaseModule(ammonite.ops.Path(${pprint.Util.literalize(config.wd.toString)})) with $wrapName{ - | // Stub to make sure Ammonite has something to call after it evaluates a script, - | // even if it does nothing... - | def $$main() = Iterator[String]() - | - | val millDiscover: mill.define.Discover = mill.define.Discover[this.type] - | // Need to wrap the returned Module in Some(...) to make sure it - | // doesn't get picked up during reflective child-module discovery - | val millSelf = Some(this) - |} - | - |sealed trait $wrapName extends mill.Module{ - |""".stripMargin - } - - - def bottom(printCode: String, indexedWrapperName: Name, extraCode: String) = { - // We need to disable the `$main` method definition inside the wrapper class, - // because otherwise it might get picked up by Ammonite and run as a static - // class method, which blows up since it's defined as an instance method - "\n}" - } - } -} diff --git a/core/src/main/scala/mill/main/ParseArgs.scala b/core/src/main/scala/mill/main/ParseArgs.scala deleted file mode 100644 index dc848418..00000000 --- a/core/src/main/scala/mill/main/ParseArgs.scala +++ /dev/null @@ -1,142 +0,0 @@ -package mill.main - -import mill.util.EitherOps -import fastparse.all._ -import mill.define.Segment - -object ParseArgs { - - def apply(scriptArgs: Seq[String]) - : Either[String, (List[List[Segment]], Seq[String])] = { - val (selectors, args, isMultiSelectors) = extractSelsAndArgs(scriptArgs) - for { - _ <- validateSelectors(selectors) - expandedSelectors <- EitherOps - .sequence(selectors.map(expandBraces)) - .map(_.flatten) - _ <- validateExpanded(expandedSelectors, isMultiSelectors) - selectors <- EitherOps.sequence(expandedSelectors.map(extractSegments)) - } yield (selectors.toList, args) - } - - def extractSelsAndArgs( - scriptArgs: Seq[String]): (Seq[String], Seq[String], Boolean) = { - val multiFlags = Seq("--all", "--seq") - val isMultiSelectors = scriptArgs.headOption.exists(multiFlags.contains) - - if (isMultiSelectors) { - val dd = scriptArgs.indexOf("--") - val selectors = (if (dd == -1) scriptArgs - else scriptArgs.take(dd)).filterNot(multiFlags.contains) - val args = if (dd == -1) Seq.empty else scriptArgs.drop(dd + 1) - - (selectors, args, isMultiSelectors) - } else { - (scriptArgs.take(1), scriptArgs.drop(1), isMultiSelectors) - } - } - - private def validateSelectors( - selectors: Seq[String]): Either[String, Unit] = { - if (selectors.isEmpty || selectors.exists(_.isEmpty)) - Left("Selector cannot be empty") - else Right(()) - } - - private def validateExpanded(expanded: Seq[String], - isMulti: Boolean): Either[String, Unit] = { - if (!isMulti && expanded.length > 1) - Left("Please use --all flag to run multiple tasks") - else Right(()) - } - - def expandBraces(selectorString: String): Either[String, List[String]] = { - parseBraceExpansion(selectorString) match { - case f: Parsed.Failure => Left(s"Parsing exception ${f.msg}") - case Parsed.Success(expanded, _) => Right(expanded.toList) - } - } - - private sealed trait Fragment - private object Fragment { - case class Keep(value: String) extends Fragment - case class Expand(values: List[List[Fragment]]) extends Fragment - - def unfold(fragments: List[Fragment]): Seq[String] = { - fragments match { - case head :: rest => - val prefixes = head match { - case Keep(v) => Seq(v) - case Expand(Nil) => Seq("{}") - case Expand(List(vs)) => unfold(vs).map("{" + _ + "}") - case Expand(vss) => vss.flatMap(unfold) - } - for { - prefix <- prefixes - suffix <- unfold(rest) - } yield prefix + suffix - - case Nil => Seq("") - } - } - } - - private object BraceExpansionParser { - val plainChars = - P(CharsWhile(c => c != ',' && c != '{' && c != '}')).!.map(Fragment.Keep) - - val toExpand: P[Fragment] = - P("{" ~ braceParser.rep(1).rep(sep = ",") ~ "}").map( - x => Fragment.Expand(x.toList.map(_.toList)) - ) - - val braceParser = P(toExpand | plainChars) - - val parser = P(braceParser.rep(1).rep(sep = ",") ~ End) - } - - private def parseBraceExpansion(input: String) = { - def unfold(vss: List[Seq[String]]): Seq[String] = { - vss match { - case Nil => Seq("") - case head :: rest => - for { - str <- head - r <- unfold(rest) - } yield - r match { - case "" => str - case _ => str + "," + r - } - } - } - - BraceExpansionParser.parser - .map { vss => - val stringss = vss.map(x => Fragment.unfold(x.toList)).toList - unfold(stringss) - } - .parse(input) - } - - def extractSegments(selectorString: String): Either[String, List[Segment]] = - parseSelector(selectorString) match { - case f: Parsed.Failure => Left(s"Parsing exception ${f.msg}") - case Parsed.Success(selector, _) => Right(selector) - } - - private def parseSelector(input: String) = { - val segment = - P(CharsWhileIn(('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')).!).map( - Segment.Label - ) - val crossSegment = - P("[" ~ CharsWhile(c => c != ',' && c != ']').!.rep(1, sep = ",") ~ "]") - .map(Segment.Cross) - val query = P(segment ~ ("." ~ segment | crossSegment).rep ~ End).map { - case (h, rest) => h :: rest.toList - } - query.parse(input) - } - -} diff --git a/core/src/main/scala/mill/main/ReplApplyHandler.scala b/core/src/main/scala/mill/main/ReplApplyHandler.scala deleted file mode 100644 index 97efb6e5..00000000 --- a/core/src/main/scala/mill/main/ReplApplyHandler.scala +++ /dev/null @@ -1,124 +0,0 @@ -package mill.main - - -import mill.define.Applicative.ApplyHandler -import mill.define.Segment.Label -import mill.define._ -import mill.eval.{Evaluator, Result} -import mill.util.Strict.Agg - -import scala.collection.mutable -object ReplApplyHandler{ - def apply[T](colors: ammonite.util.Colors, - pprinter0: pprint.PPrinter, - rootModule: mill.Module, - discover: Discover) = { - new ReplApplyHandler( - pprinter0, - new mill.eval.Evaluator( - ammonite.ops.pwd / 'out, - ammonite.ops.pwd, - rootModule, - new mill.util.PrintLogger( - colors != ammonite.util.Colors.BlackWhite, - colors, - System.out, - System.err, - System.err - ) - ), - discover - ) - } -} -class ReplApplyHandler(pprinter0: pprint.PPrinter, - evaluator: Evaluator[_], - discover: Discover) extends ApplyHandler[Task] { - // Evaluate classLoaderSig only once in the REPL to avoid busting caches - // as the user enters more REPL commands and changes the classpath - val classLoaderSig = Evaluator.classLoaderSig - override def apply[V](t: Task[V]) = { - val res = evaluator.evaluate(Agg(t)) - res.values match{ - case Seq(head: V) => head - case Nil => - val msg = new mutable.StringBuilder() - msg.append(res.failing.keyCount + " targets failed\n") - for((k, vs) <- res.failing.items){ - msg.append(k match{ - case Left(t) => "Anonymous Task\n" - case Right(k) => k.segments.render + "\n" - }) - - for(v <- vs){ - v match{ - case Result.Failure(m) => msg.append(m + "\n") - case Result.Exception(t, outerStack) => - msg.append( - t.toString + t.getStackTrace.dropRight(outerStack.length).map("\n " + _).mkString + "\n" - ) - - } - } - } - throw new Exception(msg.toString) - } - } - - val generatedEval = new EvalGenerated(evaluator) - - val millHandlers: PartialFunction[Any, pprint.Tree] = { - case c: Cross[_] => - pprint.Tree.Lazy( ctx => - Iterator(c.millOuterCtx.enclosing , ":", c.millOuterCtx.lineNum.toString, ctx.applyPrefixColor("\nChildren:").toString) ++ - c.items.iterator.map(x => - "\n (" + x._1.map(pprint.PPrinter.BlackWhite.apply(_)).mkString(", ") + ")" - ) - ) - case m: mill.Module if evaluator.rootModule.millModuleDirectChildren.contains(m) => - pprint.Tree.Lazy( ctx => - Iterator(m.millInternal.millModuleEnclosing, ":", m.millInternal.millModuleLine.toString) ++ - (if (m.millInternal.reflect[mill.Module].isEmpty) Nil - else - ctx.applyPrefixColor("\nChildren:").toString +: - m.millInternal.reflect[mill.Module].map("\n ." + _.millOuterCtx.segments.render)) ++ - (discover.value.get(m.getClass) match{ - case None => Nil - case Some(commands) => - ctx.applyPrefixColor("\nCommands:").toString +: commands.map{c => - "\n ." + c.name + "(" + - c.argSignatures.map(s => s.name + ": " + s.typeString).mkString(", ") + - ")()" - } - }) ++ - (if (m.millInternal.reflect[Target[_]].isEmpty) Nil - else { - Seq(ctx.applyPrefixColor("\nTargets:").toString) ++ - m.millInternal.reflect[Target[_]].sortBy(_.label).map(t => - "\n ." + t.label + "()" - ) - }) - - ) - case t: mill.define.Target[_] if evaluator.rootModule.millInternal.targets.contains(t) => - val seen = mutable.Set.empty[Task[_]] - 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) => - Seq(t.ctx.segments) - case _ => - seen.add(t) - t.inputs.flatMap(rec) - } - } - pprint.Tree.Lazy(ctx => - Iterator(t.ctx.enclosing, ":", t.ctx.lineNum.toString, "\n", ctx.applyPrefixColor("Inputs:").toString) ++ - t.inputs.iterator.flatMap(rec).map("\n " + _.render) - ) - - } - val pprinter = pprinter0.copy( - additionalHandlers = millHandlers orElse pprinter0.additionalHandlers - ) -} diff --git a/core/src/main/scala/mill/main/Resolve.scala b/core/src/main/scala/mill/main/Resolve.scala deleted file mode 100644 index ed4c4f80..00000000 --- a/core/src/main/scala/mill/main/Resolve.scala +++ /dev/null @@ -1,81 +0,0 @@ -package mill.main - -import mill.define._ -import mill.define.TaskModule -import ammonite.main.Router -import ammonite.main.Router.EntryPoint - -object Resolve { - def resolve[T, V](remainingSelector: List[Segment], - obj: mill.Module, - discover: Discover, - rest: Seq[String], - remainingCrossSelectors: List[List[String]], - revSelectorsSoFar: List[Segment]): Either[String, Task[Any]] = { - - remainingSelector match{ - case Segment.Cross(_) :: Nil => Left("Selector cannot start with a [cross] segment") - case Segment.Label(last) :: Nil => - val target = - obj - .millInternal - .reflect[Target[_]] - .find(_.label == last) - .map(Right(_)) - - def invokeCommand[V](target: mill.Module, name: String) = { - for(cmd <- discover.value.get(target.getClass).toSeq.flatten.find(_.name == name)) - yield cmd.asInstanceOf[EntryPoint[mill.Module]].invoke(target, ammonite.main.Scripts.groupArgs(rest.toList)) match { - case Router.Result.Success(v) => Right(v) - case _ => Left(s"Command failed $last") - } - } - - val runDefault = for{ - child <- obj.millInternal.reflectNestedObjects[mill.Module] - if child.millOuterCtx.segment == Segment.Label(last) - res <- child match{ - case taskMod: TaskModule => Some(invokeCommand(child, taskMod.defaultCommandName())) - case _ => None - } - } yield res - - val command = invokeCommand(obj, last) - - command orElse target orElse runDefault.headOption.flatten match{ - case None => Left("Cannot resolve task " + - Segments((Segment.Label(last) :: revSelectorsSoFar).reverse:_*).render - ) - // Contents of `either` *must* be a `Task`, because we only select - // methods returning `Task` in the discovery process - case Some(either) => either.right.map{ case x: Task[Any] => x } - } - - - case head :: tail => - val newRevSelectorsSoFar = head :: revSelectorsSoFar - head match{ - case Segment.Label(singleLabel) => - obj.millInternal.reflectNestedObjects[mill.Module].find{ - _.millOuterCtx.segment == Segment.Label(singleLabel) - } match{ - case Some(child: mill.Module) => resolve(tail, child, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar) - case None => Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render) - } - - case Segment.Cross(cross) => - obj match{ - case c: Cross[_] => - c.itemMap.get(cross.toList) match{ - case Some(m: mill.Module) => resolve(tail, m, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar) - case None => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render) - - } - case _ => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render) - } - } - - case Nil => Left("Selector cannot be empty") - } - } -} diff --git a/core/src/main/scala/mill/main/RunScript.scala b/core/src/main/scala/mill/main/RunScript.scala deleted file mode 100644 index aa254048..00000000 --- a/core/src/main/scala/mill/main/RunScript.scala +++ /dev/null @@ -1,210 +0,0 @@ -package mill.main - -import java.nio.file.NoSuchFileException - -import ammonite.interp.Interpreter -import ammonite.ops.{Path, read} -import ammonite.runtime.SpecialClassLoader -import ammonite.util.Util.CodeSource -import ammonite.util.{Name, Res, Util} -import mill.{PathRef, define} -import mill.define.{Discover, Segment, Task} -import mill.eval.{Evaluator, Result} -import mill.util.{EitherOps, Logger} -import mill.util.Strict.Agg -import upickle.Js - -/** - * Custom version of ammonite.main.Scripts, letting us run the build.sc script - * directly without going through Ammonite's main-method/argument-parsing - * subsystem - */ -object RunScript{ - def runScript(wd: Path, - path: Path, - instantiateInterpreter: => Either[(Res.Failing, Seq[(Path, Long)]), ammonite.interp.Interpreter], - scriptArgs: Seq[String], - lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[_], Discover)], - log: Logger) - : (Res[(Evaluator[_], Discover, Seq[(Path, Long)], Either[String, Seq[Js.Value]])], Seq[(Path, Long)]) = { - - val (evalRes, interpWatched) = lastEvaluator match{ - case Some((prevInterpWatchedSig, prevEvaluator, prevDiscover)) - if watchedSigUnchanged(prevInterpWatchedSig) => - - (Res.Success(prevEvaluator -> prevDiscover), prevInterpWatchedSig) - - case _ => - instantiateInterpreter match{ - case Left((res, watched)) => (res, watched) - case Right(interp) => - interp.watch(path) - val eval = - for((mapping, discover) <- evaluateMapping(wd, path, interp)) - yield ( - new Evaluator( - wd / 'out, wd, mapping, log, - mapping.getClass.getClassLoader.asInstanceOf[SpecialClassLoader].classpathSignature - ), - discover - ) - (eval, interp.watchedFiles) - } - } - - val evaluated = for{ - (evaluator, discover) <- evalRes - (evalWatches, res) <- Res(evaluateTarget(evaluator, discover, scriptArgs)) - } yield { - val alreadyStale = evalWatches.exists(p => p.sig != new 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) evalWatches.map(_.path -> util.Random.nextLong()) - else evalWatches.map(p => p.path -> Interpreter.pathSignature(p.path)) - - (evaluator, discover, evaluationWatches, res.map(_.flatMap(_._2))) - } - (evaluated, interpWatched) - } - - def watchedSigUnchanged(sig: Seq[(Path, Long)]) = { - sig.forall{case (p, l) => Interpreter.pathSignature(p) == l} - } - - def evaluateMapping(wd: Path, - path: Path, - interp: ammonite.interp.Interpreter): Res[(mill.Module, Discover)] = { - - val (pkg, wrapper) = Util.pathToPackageWrapper(Seq(), path relativeTo wd) - - for { - scriptTxt <- - try Res.Success(Util.normalizeNewlines(read(path))) - catch { case e: NoSuchFileException => Res.Failure("Script file not found: " + path) } - - processed <- interp.processModule( - scriptTxt, - CodeSource(wrapper, pkg, Seq(Name("ammonite"), Name("$file")), Some(path)), - autoImport = true, - extraCode = "", - hardcoded = true - ) - - buildClsName <- processed.blockInfo.lastOption match { - case Some(meta) => Res.Success(meta.id.wrapperPath) - case None => Res.Skip - } - - buildCls = interp - .evalClassloader - .loadClass(buildClsName) - - module <- try { - Util.withContextClassloader(interp.evalClassloader) { - Res.Success( - buildCls.getMethod("millSelf") - .invoke(null) - .asInstanceOf[Some[mill.Module]] - .get - ) - } - } catch { - case e: Throwable => Res.Exception(e, "") - } - discover <- try { - Util.withContextClassloader(interp.evalClassloader) { - Res.Success( - buildCls.getMethod("millDiscover") - .invoke(null) - .asInstanceOf[Discover] - ) - } - } catch { - case e: Throwable => Res.Exception(e, "") - } -// _ <- Res(consistencyCheck(mapping)) - } yield (module, discover) - } - - def evaluateTarget[T](evaluator: Evaluator[_], - discover: Discover, - scriptArgs: Seq[String]) = { - for { - parsed <- ParseArgs(scriptArgs) - (selectors, args) = parsed - targets <- { - val selected = selectors.map { sel => - val crossSelectors = sel.map { - case Segment.Cross(x) => x.toList.map(_.toString) - case _ => Nil - } - mill.main.Resolve.resolve( - sel, evaluator.rootModule, - discover, - args, crossSelectors, Nil - ) - } - EitherOps.sequence(selected) - } - (watched, res) = evaluate(evaluator, targets) - } yield (watched, res) - } - - def evaluate(evaluator: Evaluator[_], - targets: Seq[Task[Any]]): (Seq[PathRef], Either[String, Seq[(Any, Option[upickle.Js.Value])]]) = { - val evaluated = evaluator.evaluate(Agg.from(targets)) - val watched = evaluated.results - .iterator - .collect { - case (t: define.Input[_], Result.Success(p: PathRef)) => p - } - .toSeq - - val errorStr = - (for((k, fs) <- evaluated.failing.items()) yield { - val ks = k match{ - case Left(t) => t.toString - case Right(t) => t.segments.render - } - val fss = fs.map{ - case Result.Exception(t, outerStack) => - t.toString + t.getStackTrace.dropRight(outerStack.length).map("\n " + _).mkString - case Result.Failure(t) => t - } - s"$ks ${fss.mkString(", ")}" - }).mkString("\n") - - evaluated.failing.keyCount match { - case 0 => - val json = for(t <- targets) yield { - t match { - case t: mill.define.NamedTask[_] => - val jsonFile = Evaluator - .resolveDestPaths(evaluator.workspacePath, t.ctx.segments) - .meta - val metadata = upickle.json.read(jsonFile.toIO) - Some(metadata(1)) - - case _ => None - } - } - - watched -> Right(evaluated.values.zip(json)) - case n => watched -> Left(s"$n targets failed\n$errorStr") - } - } - -// def consistencyCheck[T](mapping: Discovered.Mapping[T]): Either[String, Unit] = { -// val consistencyErrors = Discovered.consistencyCheck(mapping) -// if (consistencyErrors.nonEmpty) { -// Left(s"Failed Discovered.consistencyCheck: ${consistencyErrors.map(_.render)}") -// } else { -// Right(()) -// } -// } -} diff --git a/core/src/main/scala/mill/modules/Jvm.scala b/core/src/main/scala/mill/modules/Jvm.scala deleted file mode 100644 index 888a687b..00000000 --- a/core/src/main/scala/mill/modules/Jvm.scala +++ /dev/null @@ -1,259 +0,0 @@ -package mill.modules - -import java.io.FileOutputStream -import java.lang.reflect.Modifier -import java.net.URLClassLoader -import java.nio.file.attribute.PosixFilePermission -import java.util.jar.{JarEntry, JarFile, JarOutputStream} - -import ammonite.ops._ -import mill.define.Task -import mill.eval.PathRef -import mill.util.Ctx -import mill.util.Ctx.LogCtx -import mill.util.Loose.Agg - -import scala.annotation.tailrec -import scala.collection.mutable - - -object Jvm { - def gatherClassloaderJars(): Agg[Path] = { - val allJars = new Agg.Mutable[Path]() - var currentClassloader = Thread.currentThread().getContextClassLoader - while(currentClassloader != null){ - currentClassloader match{ - case u: URLClassLoader => allJars.appendAll(u.getURLs.map(x => Path(x.getFile))) - case _ => - } - currentClassloader = currentClassloader.getParent - } - allJars - } - - def interactiveSubprocess(mainClass: String, - classPath: Agg[Path], - options: Seq[String] = Seq.empty): Unit = { - import ammonite.ops.ImplicitWd._ - %("java", "-cp", classPath.mkString(":"), mainClass, options) - } - - def inprocess(mainClass: String, - classPath: Agg[Path], - options: Seq[String] = Seq.empty) - (implicit ctx: Ctx): Unit = { - inprocess(classPath, classLoaderOverrideSbtTesting = false, cl => { - getMainMethod(mainClass, cl).invoke(null, options.toArray) - }) - } - - private def getMainMethod(mainClassName: String, cl: ClassLoader) = { - val mainClass = cl.loadClass(mainClassName) - val method = mainClass.getMethod("main", classOf[Array[String]]) - // jvm allows the actual main class to be non-public and to run a method in the non-public class, - // we need to make it accessible - method.setAccessible(true) - val modifiers = method.getModifiers - if (!Modifier.isPublic(modifiers)) - throw new NoSuchMethodException(mainClassName + ".main is not public") - if (!Modifier.isStatic(modifiers)) - throw new NoSuchMethodException(mainClassName + ".main is not static") - method - } - - - def inprocess[T](classPath: Agg[Path], - classLoaderOverrideSbtTesting: Boolean, - body: ClassLoader => T): T = { - val cl = if (classLoaderOverrideSbtTesting) { - val outerClassLoader = getClass.getClassLoader - new URLClassLoader( - classPath.map(_.toIO.toURI.toURL).toArray, - ClassLoader.getSystemClassLoader().getParent()){ - override def findClass(name: String) = { - if (name.startsWith("sbt.testing.")){ - outerClassLoader.loadClass(name) - }else{ - super.findClass(name) - } - } - } - } else { - new URLClassLoader( - classPath.map(_.toIO.toURI.toURL).toArray, - ClassLoader.getSystemClassLoader().getParent()) - } - val oldCl = Thread.currentThread().getContextClassLoader - Thread.currentThread().setContextClassLoader(cl) - try { - body(cl) - }finally{ - Thread.currentThread().setContextClassLoader(oldCl) - cl.close() - } - } - - def subprocess(mainClass: String, - classPath: Agg[Path], - jvmOptions: Seq[String] = Seq.empty, - options: Seq[String] = Seq.empty, - workingDir: Path = null) - (implicit ctx: Ctx) = { - - val commandArgs = - Vector("java") ++ - jvmOptions ++ - Vector("-cp", classPath.mkString(":"), mainClass) ++ - options - - val workingDir1 = Option(workingDir).getOrElse(ctx.dest) - mkdir(workingDir1) - val proc = - new java.lang.ProcessBuilder() - .directory(workingDir1.toIO) - .command(commandArgs:_*) - .redirectOutput(ProcessBuilder.Redirect.PIPE) - .redirectError(ProcessBuilder.Redirect.PIPE) - .start() - - val stdout = proc.getInputStream - val stderr = proc.getErrorStream - val sources = Seq( - (stdout, Left(_: Bytes), ctx.log.outputStream), - (stderr, Right(_: Bytes),ctx.log.errorStream ) - ) - val chunks = mutable.Buffer.empty[Either[Bytes, Bytes]] - while( - // Process.isAlive doesn't exist on JDK 7 =/ - util.Try(proc.exitValue).isFailure || - stdout.available() > 0 || - stderr.available() > 0 - ){ - var readSomething = false - for ((subStream, wrapper, parentStream) <- sources){ - while (subStream.available() > 0){ - readSomething = true - val array = new Array[Byte](subStream.available()) - val actuallyRead = subStream.read(array) - chunks.append(wrapper(new ammonite.ops.Bytes(array))) - parentStream.write(array, 0, actuallyRead) - } - } - // if we did not read anything sleep briefly to avoid spinning - if(!readSomething) - Thread.sleep(2) - } - - if (proc.exitValue() != 0) throw new InteractiveShelloutException() - else ammonite.ops.CommandResult(proc.exitValue(), chunks) - } - - private def createManifest(mainClass: Option[String]) = { - val m = new java.util.jar.Manifest() - m.getMainAttributes.put(java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.0") - m.getMainAttributes.putValue( "Created-By", "Scala mill" ) - mainClass.foreach( - m.getMainAttributes.put(java.util.jar.Attributes.Name.MAIN_CLASS, _) - ) - m - } - - def createJar(inputPaths: Agg[Path], mainClass: Option[String] = None) - (implicit ctx: Ctx.DestCtx): PathRef = { - val outputPath = ctx.dest - rm(outputPath) - if(inputPaths.nonEmpty) { - mkdir(outputPath/up) - - val jar = new JarOutputStream( - new FileOutputStream(outputPath.toIO), - createManifest(mainClass) - ) - - try{ - assert(inputPaths.forall(exists(_))) - for{ - p <- inputPaths - (file, mapping) <- - if (p.isFile) Iterator(p -> empty/p.last) - else ls.rec(p).filter(_.isFile).map(sub => sub -> sub.relativeTo(p)) - } { - val entry = new JarEntry(mapping.toString) - entry.setTime(file.mtime.toMillis) - jar.putNextEntry(entry) - jar.write(read.bytes(file)) - jar.closeEntry() - } - } finally { - jar.close() - } - - } - PathRef(outputPath) - } - - def createAssembly(inputPaths: Agg[Path], - mainClass: Option[String] = None, - prependShellScript: String = "") - (implicit ctx: Ctx.DestCtx): PathRef = { - val outputPath = ctx.dest - rm(outputPath) - - if(inputPaths.nonEmpty) { - mkdir(outputPath/up) - - val output = new FileOutputStream(outputPath.toIO) - - // Prepend shell script and make it executable - if (prependShellScript.nonEmpty) { - output.write((prependShellScript + "\n").getBytes) - val perms = java.nio.file.Files.getPosixFilePermissions(outputPath.toNIO) - perms.add(PosixFilePermission.GROUP_EXECUTE) - perms.add(PosixFilePermission.OWNER_EXECUTE) - perms.add(PosixFilePermission.OTHERS_EXECUTE) - java.nio.file.Files.setPosixFilePermissions(outputPath.toNIO, perms) - } - - val jar = new JarOutputStream( - output, - createManifest(mainClass) - ) - - val seen = mutable.Set("META-INF/MANIFEST.MF") - try{ - - - for{ - p <- inputPaths - if exists(p) - (file, mapping) <- - if (p.isFile) { - val jf = new JarFile(p.toIO) - import collection.JavaConverters._ - for(entry <- jf.entries().asScala if !entry.isDirectory) yield { - read.bytes(jf.getInputStream(entry)) -> entry.getName - } - } - else { - ls.rec(p).iterator - .filter(_.isFile) - .map(sub => read.bytes(sub) -> sub.relativeTo(p).toString) - } - if !seen(mapping) - } { - seen.add(mapping) - val entry = new JarEntry(mapping.toString) - jar.putNextEntry(entry) - jar.write(file) - jar.closeEntry() - } - } finally { - jar.close() - output.close() - } - - } - PathRef(outputPath) - } - -} diff --git a/core/src/main/scala/mill/modules/Util.scala b/core/src/main/scala/mill/modules/Util.scala deleted file mode 100644 index cd674bad..00000000 --- a/core/src/main/scala/mill/modules/Util.scala +++ /dev/null @@ -1,26 +0,0 @@ -package mill.modules - -import ammonite.ops.RelPath -import mill.eval.PathRef -import mill.util.Ctx - -object Util { - def download(url: String, dest: RelPath)(implicit ctx: Ctx.DestCtx) = { - ammonite.ops.mkdir(ctx.dest) - val out = ctx.dest / dest - - val website = new java.net.URI(url).toURL - val rbc = java.nio.channels.Channels.newChannel(website.openStream) - try{ - val fos = new java.io.FileOutputStream(out.toIO) - try{ - fos.getChannel.transferFrom(rbc, 0, java.lang.Long.MAX_VALUE) - PathRef(out) - } finally{ - fos.close() - } - } finally{ - rbc.close() - } - } -} diff --git a/core/src/main/scala/mill/package.scala b/core/src/main/scala/mill/package.scala deleted file mode 100644 index 93916c8b..00000000 --- a/core/src/main/scala/mill/package.scala +++ /dev/null @@ -1,12 +0,0 @@ -import mill.util.JsonFormatters - -package object mill extends JsonFormatters{ - val T = define.Target - type T[T] = define.Target[T] - val PathRef = mill.eval.PathRef - type PathRef = mill.eval.PathRef - type Module = define.Module - type Cross[T] = define.Cross[T] - type Agg[T] = util.Loose.Agg[T] - val Agg = util.Loose.Agg -} diff --git a/core/src/main/scala/mill/util/AggWrapper.scala b/core/src/main/scala/mill/util/AggWrapper.scala deleted file mode 100644 index c2994a9a..00000000 --- a/core/src/main/scala/mill/util/AggWrapper.scala +++ /dev/null @@ -1,116 +0,0 @@ -package mill.util - - - -import scala.collection.mutable -object Strict extends AggWrapper(true) -object Loose extends AggWrapper(false) -sealed class AggWrapper(strictUniqueness: Boolean){ - /** - * A collection with enforced uniqueness, fast contains and deterministic - * ordering. Raises an exception if a duplicate is found; call - * `toSeq.distinct` if you explicitly want to make it swallow duplicates - */ - trait Agg[V] extends TraversableOnce[V]{ - def contains(v: V): Boolean - def items: Iterator[V] - def indexed: IndexedSeq[V] - def flatMap[T](f: V => TraversableOnce[T]): Agg[T] - def map[T](f: V => T): Agg[T] - def filter(f: V => Boolean): Agg[V] - def withFilter(f: V => Boolean): Agg[V] - def collect[T](f: PartialFunction[V, T]): Agg[T] - def zipWithIndex: Agg[(V, Int)] - def reverse: Agg[V] - def zip[T](other: Agg[T]): Agg[(V, T)] - def ++[T >: V](other: TraversableOnce[T]): Agg[T] - } - - object Agg{ - def empty[V]: Agg[V] = new Agg.Mutable[V] - implicit def jsonFormat[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[Agg[T]] = - upickle.default.ReadWriter[Agg[T]] ( - oset => upickle.default.writeJs(oset.toList), - {case json => Agg.from(upickle.default.readJs[Seq[T]](json))} - ) - def apply[V](items: V*) = from(items) - - implicit def from[V](items: TraversableOnce[V]): Agg[V] = { - val set = new Agg.Mutable[V]() - items.foreach(set.append) - set - } - - - class Mutable[V]() extends Agg[V]{ - - private[this] val set0 = mutable.LinkedHashSet.empty[V] - def contains(v: V) = set0.contains(v) - def append(v: V) = if (!contains(v)){ - set0.add(v) - - }else if (strictUniqueness){ - throw new Exception("Duplicated item inserted into OrderedSet: " + v) - } - def appendAll(vs: Seq[V]) = vs.foreach(append) - def items = set0.iterator - def indexed: IndexedSeq[V] = items.toIndexedSeq - def set: collection.Set[V] = set0 - - def map[T](f: V => T): Agg[T] = { - val output = new Agg.Mutable[T] - for(i <- items) output.append(f(i)) - output - } - def flatMap[T](f: V => TraversableOnce[T]): Agg[T] = { - val output = new Agg.Mutable[T] - for(i <- items) for(i0 <- f(i)) output.append(i0) - output - } - def filter(f: V => Boolean): Agg[V] = { - val output = new Agg.Mutable[V] - for(i <- items) if (f(i)) output.append(i) - output - } - def withFilter(f: V => Boolean): Agg[V] = filter(f) - - def collect[T](f: PartialFunction[V, T]) = this.filter(f.isDefinedAt).map(x => f(x)) - - def zipWithIndex = { - var i = 0 - this.map{ x => - i += 1 - (x, i-1) - } - } - - def reverse = Agg.from(indexed.reverseIterator) - - def zip[T](other: Agg[T]) = Agg.from(items.zip(other.items)) - def ++[T >: V](other: TraversableOnce[T]) = Agg.from(items ++ other) - - // Members declared in scala.collection.GenTraversableOnce - def isTraversableAgain: Boolean = items.isTraversableAgain - def toIterator: Iterator[V] = items.toIterator - def toStream: Stream[V] = items.toStream - - // Members declared in scala.collection.TraversableOnce - def copyToArray[B >: V](xs: Array[B], start: Int,len: Int): Unit = items.copyToArray(xs, start, len) - def exists(p: V => Boolean): Boolean = items.exists(p) - def find(p: V => Boolean): Option[V] = items.find(p) - def forall(p: V => Boolean): Boolean = items.forall(p) - def foreach[U](f: V => U): Unit = items.foreach(f) - def hasDefiniteSize: Boolean = items.hasDefiniteSize - def isEmpty: Boolean = items.isEmpty - def seq: scala.collection.TraversableOnce[V] = items - def toTraversable: Traversable[V] = items.toTraversable - - override def hashCode() = items.map(_.hashCode()).sum - override def equals(other: Any) = other match{ - case s: Agg[_] => items.sameElements(s.items) - case _ => super.equals(other) - } - override def toString = items.mkString("Agg(", ", ", ")") - } - } -} diff --git a/core/src/main/scala/mill/util/Ctx.scala b/core/src/main/scala/mill/util/Ctx.scala deleted file mode 100644 index 60e6bdbf..00000000 --- a/core/src/main/scala/mill/util/Ctx.scala +++ /dev/null @@ -1,60 +0,0 @@ -package mill.util - -import ammonite.ops.Path -import mill.define.Applicative.ImplicitStub -import mill.util.Ctx.{ArgCtx, BaseCtx, DestCtx, LoaderCtx, LogCtx} - -import scala.annotation.compileTimeOnly -import scala.language.implicitConversions - -object Ctx{ - @compileTimeOnly("Target.ctx() can only be used with a T{...} block") - @ImplicitStub - implicit def taskCtx: Ctx = ??? - - object DestCtx { - 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[_] - } - trait LoaderCtx{ - def load[T](x: Loader[T]): T - } - trait Loader[T]{ - def make(): T - } -} -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 BaseCtx{ - - def load[T](x: Ctx.Loader[T]): T = workerCtx0.load(x) - def length = args.length - def apply[T](index: Int): T = { - if (index >= 0 && index < args.length) args(index).asInstanceOf[T] - else throw new IndexOutOfBoundsException(s"Index $index outside of range 0 - ${args.length}") - } -} diff --git a/core/src/main/scala/mill/util/EitherOps.scala b/core/src/main/scala/mill/util/EitherOps.scala deleted file mode 100644 index da2552c8..00000000 --- a/core/src/main/scala/mill/util/EitherOps.scala +++ /dev/null @@ -1,18 +0,0 @@ -package mill.util - -import scala.collection.generic.CanBuildFrom -import scala.collection.mutable -import scala.language.higherKinds - -object EitherOps { - - // implementation similar to scala.concurrent.Future#sequence - def sequence[A, B, M[X] <: TraversableOnce[X]](in: M[Either[A, B]])( - implicit cbf: CanBuildFrom[M[Either[A, B]], B, M[B]]): Either[A, M[B]] = { - in.foldLeft[Either[A, mutable.Builder[B, M[B]]]](Right(cbf(in))) { - case (acc, el) => - for (a <- acc; e <- el) yield a += e - } - .map(_.result()) - } -} diff --git a/core/src/main/scala/mill/util/JsonFormatters.scala b/core/src/main/scala/mill/util/JsonFormatters.scala deleted file mode 100644 index 00a40e7d..00000000 --- a/core/src/main/scala/mill/util/JsonFormatters.scala +++ /dev/null @@ -1,42 +0,0 @@ -package mill.util - -import ammonite.ops.{Bytes, Path} -import upickle.Js -import upickle.default.{ReadWriter => RW} -object JsonFormatters extends JsonFormatters -trait JsonFormatters { - implicit val pathReadWrite: RW[ammonite.ops.Path] = RW[ammonite.ops.Path]( - o => Js.Str(o.toString()), - {case Js.Str(json) => Path(json.toString)}, - ) - - implicit val bytesReadWrite: RW[Bytes] = RW[Bytes]( - o => Js.Str(javax.xml.bind.DatatypeConverter.printBase64Binary(o.array)), - {case Js.Str(json) => new Bytes(javax.xml.bind.DatatypeConverter.parseBase64Binary(json.toString))} - ) - - - implicit lazy val crFormat: RW[ammonite.ops.CommandResult] = upickle.default.macroRW - - implicit lazy val modFormat: RW[coursier.Module] = upickle.default.macroRW - implicit lazy val depFormat: RW[coursier.Dependency]= upickle.default.macroRW - implicit lazy val attrFormat: RW[coursier.Attributes] = upickle.default.macroRW - implicit val stackTraceRW = upickle.default.ReadWriter[StackTraceElement]( - ste => Js.Obj( - "declaringClass" -> Js.Str(ste.getClassName), - "methodName" -> Js.Str(ste.getMethodName), - "fileName" -> Js.Str(ste.getFileName), - "lineNumber" -> Js.Num(ste.getLineNumber) - ), - {case json: Js.Obj => - new StackTraceElement( - json("declaringClass").str.toString, - json("methodName").str.toString, - json("fileName").str.toString, - json("lineNumber").num.toInt - ) - } - ) - - -} diff --git a/core/src/main/scala/mill/util/Logger.scala b/core/src/main/scala/mill/util/Logger.scala deleted file mode 100644 index 17c66d27..00000000 --- a/core/src/main/scala/mill/util/Logger.scala +++ /dev/null @@ -1,138 +0,0 @@ -package mill.util - -import java.io._ - -import ammonite.ops.Path -import ammonite.util.Colors - - -/** - * The standard logging interface of the Mill build tool. - * - * Contains four primary logging methods, in order of increasing importance: - * - * - `ticker`: short-lived logging output where consecutive lines over-write - * each other; useful for information which is transient and disposable - * - * - `info`: miscellaneous logging output which isn't part of the main output - * a user is looking for, but useful to provide context on what Mill is doing - * - * - `error`: logging output which represents problems the user should care - * about - * - * Also contains the two forwarded stdout and stderr streams, for code executed - * by Mill to use directly. Typically these correspond to the stdout and stderr, - * but when `--show` is used both are forwarded to stderr and stdout is only - * used to display the final `--show` output for easy piping. - */ -trait Logger { - def colored: Boolean - val errorStream: PrintStream - val outputStream: PrintStream - def info(s: String): Unit - def error(s: String): Unit - def ticker(s: String): Unit - def close(): Unit = () -} - -object DummyLogger extends Logger { - def colored = false - object errorStream extends PrintStream(_ => ()) - object outputStream extends PrintStream(_ => ()) - def info(s: String) = () - def error(s: String) = () - def ticker(s: String) = () -} - -class CallbackStream(wrapped: OutputStream, f: () => Unit) extends OutputStream{ - override def write(b: Array[Byte]): Unit = { f(); wrapped.write(b) } - - override def write(b: Array[Byte], off: Int, len: Int): Unit = { - f() - wrapped.write(b, off, len) - } - - def write(b: Int) = {f(); wrapped.write(b)} -} -case class PrintLogger(colored: Boolean, - colors: ammonite.util.Colors, - outStream: PrintStream, - infoStream: PrintStream, - errStream: PrintStream) extends Logger { - - var lastLineTicker = false - def falseTicker[T](t: T) = { - lastLineTicker = false - t - } - override val errorStream = new PrintStream( - new CallbackStream(errStream, () => lastLineTicker = false) - ) - override val outputStream = new PrintStream( - new CallbackStream(outStream, () => lastLineTicker = false) - ) - - - def info(s: String) = { - lastLineTicker = false - infoStream.println(colors.info()(s)) - } - def error(s: String) = { - lastLineTicker = false - errStream.println(colors.error()(s)) - } - def ticker(s: String) = { - if (lastLineTicker){ - val p = new PrintWriter(infoStream) - val nav = new ammonite.terminal.AnsiNav(p) - nav.up(1) - nav.clearLine(2) - nav.left(9999) - p.flush() - }else{ - infoStream.println() - } - lastLineTicker = true - infoStream.println(colors.info()(s)) - } -} - -case class FileLogger(colored: Boolean, file: Path) extends Logger { - private[this] var outputStreamUsed: Boolean = false - - lazy val outputStream = { - outputStreamUsed = true - new PrintStream(new FileOutputStream(file.toIO.getAbsolutePath)) - } - - lazy val errorStream = { - outputStreamUsed = true - new PrintStream(new FileOutputStream(file.toIO.getAbsolutePath)) - } - - def info(s: String) = outputStream.println(s) - def error(s: String) = outputStream.println(s) - def ticker(s: String) = outputStream.println(s) - override def close() = { - if (outputStreamUsed) - outputStream.close() - } -} - -case class MultiLogger(colored: Boolean, streams: Logger*) extends Logger { - lazy val outputStream: PrintStream = - new PrintStream(b => streams.foreach(_.outputStream.write(b))) { - override def flush() = streams.foreach(_.outputStream.flush()) - override def close() = streams.foreach(_.outputStream.close()) - } - lazy val errorStream: PrintStream = - new PrintStream(b => streams.foreach(_.outputStream.write(b))) { - override def flush() = streams.foreach(_.outputStream.flush()) - override def close() = streams.foreach(_.outputStream.close()) - } - - def info(s: String) = streams.foreach(_.info(s)) - def error(s: String) = streams.foreach(_.error(s)) - def ticker(s: String) = streams.foreach(_.ticker(s)) - override def close() = streams.foreach(_.close()) -}
\ No newline at end of file diff --git a/core/src/main/scala/mill/util/MultiBiMap.scala b/core/src/main/scala/mill/util/MultiBiMap.scala deleted file mode 100644 index 2cb81944..00000000 --- a/core/src/main/scala/mill/util/MultiBiMap.scala +++ /dev/null @@ -1,55 +0,0 @@ -package mill.util - -import scala.collection.mutable -import Strict.Agg -/** - * A map from keys to collections of values: you can assign multiple values - * to any particular key. Also allows lookups in both directions: what values - * are assigned to a key or what key a value is assigned ti. - */ -trait MultiBiMap[K, V]{ - def containsValue(v: V): Boolean - def lookupKey(k: K): Agg[V] - def lookupValue(v: V): K - def lookupValueOpt(v: V): Option[K] - def add(k: K, v: V): Unit - def removeAll(k: K): Agg[V] - def addAll(k: K, vs: TraversableOnce[V]): Unit - def keys(): Iterator[K] - def items(): Iterator[(K, Agg[V])] - def values(): Iterator[Agg[V]] - def keyCount: Int -} - -object MultiBiMap{ - class Mutable[K, V]() extends MultiBiMap[K, V]{ - private[this] val valueToKey = mutable.LinkedHashMap.empty[V, K] - private[this] val keyToValues = mutable.LinkedHashMap.empty[K, Agg.Mutable[V]] - def containsValue(v: V) = valueToKey.contains(v) - def lookupKey(k: K) = keyToValues(k) - def lookupKeyOpt(k: K) = keyToValues.get(k) - def lookupValue(v: V) = valueToKey(v) - def lookupValueOpt(v: V) = valueToKey.get(v) - def add(k: K, v: V): Unit = { - valueToKey(v) = k - keyToValues.getOrElseUpdate(k, new Agg.Mutable[V]()).append(v) - } - def removeAll(k: K): Agg[V] = keyToValues.get(k) match { - case None => Agg() - case Some(vs) => - vs.foreach(valueToKey.remove) - - keyToValues.remove(k) - vs - } - def addAll(k: K, vs: TraversableOnce[V]): Unit = vs.foreach(this.add(k, _)) - - def keys() = keyToValues.keysIterator - - def values() = keyToValues.valuesIterator - - def items() = keyToValues.iterator - - def keyCount = keyToValues.size - } -} |