From 730a40d0324c14adfeb3837604c4a65c8e1941b2 Mon Sep 17 00:00:00 2001 From: Guillaume Galy Date: Wed, 16 May 2018 06:19:47 +0200 Subject: [PATCH] [WIP] Fixes #227; add `mill clean` (#315) * Adding clean as a default task * [WIP] Improve 'clean' paths resolution * Improve clean targets resolution mechanism * fix error on clean all * update "clean all" to keep all 'out/mill-*' paths * fix cross module resolution in clean task * Add documentation for "clean" task --- docs/pages/1 - Intro to Mill.md | 20 +++++++++ main/src/mill/main/MainModule.scala | 42 ++++++++++++++++- main/src/mill/main/Resolve.scala | 70 +++++++++++++++++++++++++++++ readme.md | 11 +++++ 4 files changed, 142 insertions(+), 1 deletion(-) diff --git a/docs/pages/1 - Intro to Mill.md b/docs/pages/1 - Intro to Mill.md index 5eab7141bac..f6854ba2c9b 100644 --- a/docs/pages/1 - Intro to Mill.md +++ b/docs/pages/1 - Intro to Mill.md @@ -456,6 +456,26 @@ $ mill show foo.compileDepClasspath `show` is also useful for interacting with Mill from external tools, since the JSON it outputs is structured and easily parsed & manipulated. +### clean + +```bash +$ mill clean +``` + +`clean` deletes all the cached outputs of previously executed tasks. It can +apply to the entire project, entire modules, or specific tasks. + +```bash +mill clean # clean all outputs +mill clean foo # clean all outputs for module 'foo' (including nested modules) +mill clean foo.compile # only clean outputs for task 'compile' in module 'foo' +mill clean foo.{compile,run} +mill clean "foo.{compile,run}" +mill clean foo.compile foo.run +mill clean _.compile +mill clean __.compile +``` + ## IntelliJ Support Mill supports IntelliJ by default. Use `mill mill.scalalib.GenIdea/idea` to diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index 32281407f3a..4fed5ec5c67 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -1,10 +1,12 @@ package mill.main +import ammonite.ops.Path import mill.define.{NamedTask, Task} import mill.eval.{Evaluator, Result} -import mill.util.{EitherOps, ParseArgs, PrintLogger, Watched} +import mill.util.{PrintLogger, Watched} import pprint.{Renderer, Truncated} import upickle.Js + object MainModule{ def resolveTasks[T](evaluator: Evaluator[Any], targets: Seq[String], multiSelect: Boolean) (f: List[NamedTask[Any]] => T) = { @@ -35,6 +37,9 @@ trait MainModule extends mill.Module{ println(res) res } + + private val OutDir: String = "out" + /** * Resolves a mill query string and prints out the tasks it resolves to. */ @@ -177,4 +182,39 @@ trait MainModule extends mill.Module{ } } } + + /** + * Deletes the given targets from the out directory. Providing no targets + * will clean everything. + */ + def clean(evaluator: Evaluator[Any], targets: String*) = mill.T.command { + val rootDir = ammonite.ops.pwd / OutDir + + val KeepPattern = "(mill-.+)".r.anchored + + def keepPath(path: Path) = path.segments.lastOption match { + case Some(KeepPattern(_)) => true + case _ => false + } + + val pathsToRemove = + if (targets.isEmpty) + Right(ammonite.ops.ls(rootDir).filterNot(keepPath)) + else + RunScript.resolveTasks( + mill.main.ResolveSegments, evaluator, targets, multiSelect = true + ).map( + _.map { segments => + Evaluator.resolveDestPaths(rootDir, segments).out + }) + + pathsToRemove match { + case Left(err) => + Result.Failure(err) + case Right(paths) => + paths.foreach(ammonite.ops.rm) + Result.Success(()) + } + } + } diff --git a/main/src/mill/main/Resolve.scala b/main/src/mill/main/Resolve.scala index ccb5ec04e44..4b0ae27f1db 100644 --- a/main/src/mill/main/Resolve.scala +++ b/main/src/mill/main/Resolve.scala @@ -86,6 +86,76 @@ object ResolveMetadata extends Resolve[String]{ } } +object ResolveSegments extends Resolve[Segments] { + + override def endResolveCross(obj: Module, + revSelectorsSoFar: List[Segment], + last: List[String], + discover: Discover[_], + rest: Seq[String]): Either[String, Seq[Segments]] = { + obj match{ + case c: Cross[Module] => + last match{ + case List("__") => Right(c.items.map(_._2.millModuleSegments)) + case items => + c.items + .filter(_._1.length == items.length) + .filter(_._1.zip(last).forall{case (a, b) => b == "_" || a.toString == b}) + .map(_._2.millModuleSegments) match { + case Nil => + Resolve.errorMsgCross( + c.items.map(_._1.map(_.toString)), + last, + revSelectorsSoFar + ) + case res => Right(res) + } + } + case _ => + Left( + Resolve.unableToResolve(Segment.Cross(last), revSelectorsSoFar) + + Resolve.hintListLabel(revSelectorsSoFar) + ) + } + } + + def endResolveLabel(obj: Module, + revSelectorsSoFar: List[Segment], + last: String, + discover: Discover[_], + rest: Seq[String]): Either[String, Seq[Segments]] = { + val target = + obj + .millInternal + .reflect[Target[_]] + .find(_.label == last) + .map(t => Right(t.ctx.segments)) + + val command = + Resolve + .invokeCommand(obj, last, discover, rest) + .headOption + .map(_.map(_.ctx.segments)) + + val module = + obj.millInternal + .reflectNestedObjects[Module] + .find(_.millOuterCtx.segment == Segment.Label(last)) + .map(m => Right(m.millModuleSegments)) + + command orElse target orElse module match { + case None => + Resolve.errorMsgLabel( + singleModuleMeta(obj, discover, revSelectorsSoFar.isEmpty), + last, + revSelectorsSoFar + ) + + case Some(either) => either.right.map(Seq(_)) + } + } +} + object ResolveTasks extends Resolve[NamedTask[Any]]{ diff --git a/readme.md b/readme.md index b2ef81a0449..20a577a0165 100644 --- a/readme.md +++ b/readme.md @@ -67,6 +67,17 @@ You can get Mill to show the JSON-structured output for a particular `Target` or Output will be generated into a the `./out` folder. +You can clean the project using `clean`: + +```bash +# Clean entire project. +mill clean +# Clean a single target. +mill clean main +# Clean multiple targets. +mill clean main core +``` + If you are repeatedly testing Mill manually by running it against the `build.sc` file in the repository root, you can skip the assembly process and directly run it via: