diff --git a/bsp/src/mill/bsp/BSP.scala b/bsp/src/mill/bsp/BSP.scala index 3442ee7e6af..dfa143fe147 100644 --- a/bsp/src/mill/bsp/BSP.scala +++ b/bsp/src/mill/bsp/BSP.scala @@ -10,7 +10,7 @@ import mill.scalalib.CoursierModule object BSP extends ExternalModule with CoursierModule { - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] private def bspWorkerLibs: T[Agg[PathRef]] = T { millProjectModule("mill-bsp-worker", repositoriesTask()) diff --git a/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala b/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala index e10f60c8702..0d846b33d85 100644 --- a/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala +++ b/bsp/worker/src/mill/bsp/worker/MillBuildServer.scala @@ -34,7 +34,7 @@ private class MillBuildServer( ) extends ExternalModule with BuildServer { - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] private[worker] var cancellator: Boolean => Unit = shutdownBefore => () private[worker] var onSessionEnd: Option[BspServerResult => Unit] = None @@ -567,7 +567,7 @@ private class MillBuildServer( case ((msg, cleaned), targetId) => val (module, ev) = state.bspModulesById(targetId) val mainModule = new MainModule { - override implicit def millDiscover: Discover[_] = Discover[this.type] + override implicit def millDiscover: Discover = Discover[this.type] } val compileTargetName = (module.millModuleSegments ++ Label("compile")).render debug(s"about to clean: ${compileTargetName}") diff --git a/contrib/artifactory/src/mill/contrib/artifactory/ArtifactoryPublishModule.scala b/contrib/artifactory/src/mill/contrib/artifactory/ArtifactoryPublishModule.scala index 1c2234edc5d..4c3e40b8787 100644 --- a/contrib/artifactory/src/mill/contrib/artifactory/ArtifactoryPublishModule.scala +++ b/contrib/artifactory/src/mill/contrib/artifactory/ArtifactoryPublishModule.scala @@ -93,5 +93,5 @@ object ArtifactoryPublishModule extends ExternalModule { } } - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/contrib/bintray/src/mill/contrib/bintray/BintrayPublishModule.scala b/contrib/bintray/src/mill/contrib/bintray/BintrayPublishModule.scala index 91cfd99164c..1fd35ceb8cd 100644 --- a/contrib/bintray/src/mill/contrib/bintray/BintrayPublishModule.scala +++ b/contrib/bintray/src/mill/contrib/bintray/BintrayPublishModule.scala @@ -100,5 +100,5 @@ object BintrayPublishModule extends ExternalModule { } } - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala b/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala index 3d27d323cfd..1327051c7af 100644 --- a/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala +++ b/contrib/bloop/src/mill/contrib/bloop/BloopImpl.scala @@ -442,5 +442,5 @@ class BloopImpl(evs: () => Seq[Evaluator], wd: os.Path) extends ExternalModule { } } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/contrib/codeartifact/src/mill/contrib/codeartifact/CodeartifactPublishModule.scala b/contrib/codeartifact/src/mill/contrib/codeartifact/CodeartifactPublishModule.scala index bd1dbd34464..6a16297562f 100644 --- a/contrib/codeartifact/src/mill/contrib/codeartifact/CodeartifactPublishModule.scala +++ b/contrib/codeartifact/src/mill/contrib/codeartifact/CodeartifactPublishModule.scala @@ -57,6 +57,6 @@ object CodeartifactPublishModule extends ExternalModule { ) } - lazy val millDiscover: mill.define.Discover[this.type] = + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/contrib/gitlab/src/mill/contrib/gitlab/GitlabPublishModule.scala b/contrib/gitlab/src/mill/contrib/gitlab/GitlabPublishModule.scala index 2efd3035ba6..5dac6430268 100644 --- a/contrib/gitlab/src/mill/contrib/gitlab/GitlabPublishModule.scala +++ b/contrib/gitlab/src/mill/contrib/gitlab/GitlabPublishModule.scala @@ -76,5 +76,5 @@ object GitlabPublishModule extends ExternalModule { ) } - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/contrib/playlib/src/mill/playlib/RouteCompilerWorkerModule.scala b/contrib/playlib/src/mill/playlib/RouteCompilerWorkerModule.scala index b4928e6b35a..854be532ff0 100644 --- a/contrib/playlib/src/mill/playlib/RouteCompilerWorkerModule.scala +++ b/contrib/playlib/src/mill/playlib/RouteCompilerWorkerModule.scala @@ -12,5 +12,5 @@ trait RouteCompilerWorkerModule extends Module { private[playlib] object RouteCompilerWorkerModule extends ExternalModule with RouteCompilerWorkerModule { - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBWorker.scala b/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBWorker.scala index 74ba7a5f763..4472ca77a00 100644 --- a/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBWorker.scala +++ b/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBWorker.scala @@ -127,5 +127,5 @@ trait ScalaPBWorkerApi { object ScalaPBWorkerApi extends ExternalModule { def scalaPBWorker: Worker[ScalaPBWorker] = T.worker { new ScalaPBWorker() } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/contrib/scoverage/src/mill/contrib/scoverage/ScoverageReportWorker.scala b/contrib/scoverage/src/mill/contrib/scoverage/ScoverageReportWorker.scala index f45621802c0..e9fb93b2722 100644 --- a/contrib/scoverage/src/mill/contrib/scoverage/ScoverageReportWorker.scala +++ b/contrib/scoverage/src/mill/contrib/scoverage/ScoverageReportWorker.scala @@ -40,5 +40,5 @@ object ScoverageReportWorker extends ExternalModule { def scoverageReportWorker: Worker[ScoverageReportWorker] = T.worker { new ScoverageReportWorker() } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublishModule.scala b/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublishModule.scala index 9463761b3ba..807100af3f4 100644 --- a/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublishModule.scala +++ b/contrib/sonatypecentral/src/mill/contrib/sonatypecentral/SonatypeCentralPublishModule.scala @@ -143,5 +143,5 @@ object SonatypeCentralPublishModule extends ExternalModule { Result.Success(SonatypeCredentials(username, password)) } - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/contrib/versionfile/src/mill/contrib/versionfile/VersionFileModule.scala b/contrib/versionfile/src/mill/contrib/versionfile/VersionFileModule.scala index d538ce6958e..e476e1383fe 100644 --- a/contrib/versionfile/src/mill/contrib/versionfile/VersionFileModule.scala +++ b/contrib/versionfile/src/mill/contrib/versionfile/VersionFileModule.scala @@ -89,5 +89,5 @@ object VersionFileModule extends define.ExternalModule { } yield proc.call() } - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/docs/modules/ROOT/pages/Bundled_Libraries.adoc b/docs/modules/ROOT/pages/Bundled_Libraries.adoc index d66707344ff..f8090b0951d 100644 --- a/docs/modules/ROOT/pages/Bundled_Libraries.adoc +++ b/docs/modules/ROOT/pages/Bundled_Libraries.adoc @@ -6,47 +6,33 @@ Mill comes bundled with a set of external Open Source libraries and projects. == OS-lib -OS-Lib is a simple, flexible, high-performance Scala interface to common OS -filesystem and subprocess APIs. - -Mill uses OS-Lib for all of its file system operations. - Project page:: https://github.com/com-lihaoyi/os-lib ScalaDoc:: https://javadoc.io/doc/com.lihaoyi/os-lib_2.13/latest/index.html +include::example/external/libraries/1-oslib.adoc[] == uPickle -uPickle: a simple Scala JSON and Binary (MessagePack) serialization library - -Mill uses uPickle to cache target output to disk as JSON, and to output JSON -for third-party tools to consume. - Project page:: https://github.com/com-lihaoyi/upickle ScalaDoc:: https://javadoc.io/doc/com.lihaoyi/upickle_2.13/latest/index.html -== Requests-Scala - -Requests-Scala is a Scala port of the popular Python Requests HTTP client. -Requests-Scala aims to provide the same API and user-experience as the -original Requests: flexible, intuitive, and straightforward to use. +include::example/external/libraries/2-upickle.adoc[] -Mill bundles Requests for you to use to make HTTP requests. +== Requests-Scala Project page:: https://github.com/com-lihaoyi/requests-scala ScalaDoc:: https://javadoc.io/doc/com.lihaoyi/requests_2.13/latest/index.html -== MainArgs +include::example/external/libraries/3-requests.adoc[] -MainArgs is a small, dependency-free library for command line argument parsing -in Scala. +== MainArgs -Mill uses MainArgs to handle argument parsing for ``T.command``s that are run -from the command line. Project page:: https://github.com/com-lihaoyi/mainargs Scaladoc:: https://javadoc.io/doc/com.lihaoyi/mainargs_2.13/latest/index.html +include::example/external/libraries/4-mainargs.adoc[] + == Coursier Coursier is the Scala application and artifact manager. Mill uses Coursier for diff --git a/docs/modules/ROOT/pages/Tasks.adoc b/docs/modules/ROOT/pages/Tasks.adoc index 0cabefdd335..d6c8836f039 100644 --- a/docs/modules/ROOT/pages/Tasks.adoc +++ b/docs/modules/ROOT/pages/Tasks.adoc @@ -51,35 +51,6 @@ include::example/depth/tasks/5-persistent-targets.adoc[] include::example/depth/tasks/6-workers.adoc[] -=== Evaluator Commands (experimental) - -_Evaluator Command are experimental and suspected to change. -See {mill-github-url}/issues/502[issue #502] for details._ - -You can define a command that takes in the current `Evaluator` as an argument, -which you can use to inspect the entire build, or run arbitrary tasks. -For example, here is the `mill.scalalib.GenIdea/idea` command which uses this -to traverse the module-tree and generate an Intellij project config for your -build. - -[source,scala] ----- -def idea(ev: Evaluator) = T.command { - mill.scalalib.GenIdea( - implicitly, - ev.rootModule, - ev.discover - ) -} ----- - -Many built-in tools are implemented as custom evaluator commands: -xref:Scala_Builtin_Commands.adoc#_inspect[inspect], -xref:Scala_Builtin_Commands.adoc#_resolve[resolve], -xref:Scala_Builtin_Commands.adoc#_show[show]. -If you want a way to run Mill commands and programmatically manipulate the -tasks and outputs, you do so with your own evaluator command. - == Using ScalaModule.run as a task include::example/depth/tasks/11-module-run-task.adoc[] diff --git a/example/external/libraries/1-oslib/build.mill b/example/external/libraries/1-oslib/build.mill new file mode 100644 index 00000000000..ff811cb6713 --- /dev/null +++ b/example/external/libraries/1-oslib/build.mill @@ -0,0 +1,43 @@ +// Mill uses OS-Lib for all of its file system and subprocess operations. +// +// === Sandbox Working Directories +// +// One thing to note about Mill's usage of OS-Lib is that Mill sets the `os.pwd` +// and for filesystem operations and subprocesses to each task's `.dest` folder, +// as part of its xref:Mill_Sandboxing.adoc[] efforts to prevent accidental +// interference between tasks: + +import mill._ + +def task1 = T{ + os.write(os.pwd / "file.txt", "hello") + PathRef(os.pwd / "file.txt") +} + +def task2 = T{ + os.call(("bash", "-c", "echo 'world' >> file.txt")) + PathRef(os.pwd / "file.txt") +} + +def command = T{ + println(task1().path) + println(os.read(task1().path)) + println(task2().path) + println(os.read(task2().path)) +} + +// Thus although both `task1` and `task2` above write to `os.pwd / "file.txt"` - +// one via `os.write` and one via a Bash subprocess - each task gets its own +// working directory that prevents the files from colliding on disk. Thus the final +// `command` can depend on both tasks and read each task's `file.txt` separately +// without conflict + +/** Usage + +> ./mill command # mac/linux +.../out/task1.dest/file.txt +hello +.../out/task2.dest/file.txt +world + +*/ \ No newline at end of file diff --git a/example/external/libraries/2-upickle/build.mill b/example/external/libraries/2-upickle/build.mill new file mode 100644 index 00000000000..4bd24df313b --- /dev/null +++ b/example/external/libraries/2-upickle/build.mill @@ -0,0 +1,102 @@ +// Mill uses uPickle to cache target output to disk as JSON, and to output JSON +// for third-party tools to consume. The output of every Mill target must be JSON +// serializable via uPickle. +// +// The uPickle serialized return of every Mill task is used for multiple purposes: +// +// - As the format for caching things on disk +// +// - The output format for `show`, which can be used for manual inspection piped +// to external tools +// +// - Decided whether downstream results can be read from the cache or whether they +// need to be recomputed +// +// === Primitives and Collections +// +// Most Scala primitive types (``String``s, ``Int``s, ``Boolean``s, etc.) and +// collections types (``Seq``s, ``List``s, ``Tuple``s, etc.) are serializable by default. + +import mill._ + +def taskInt = T{ 123 } +def taskBoolean = T{ true } +def taskString = T{ "hello " + taskInt() + " world " + taskBoolean() } + + +/** Usage + +> ./mill show taskInt +123 + +> ./mill show taskBoolean +true + +> ./mill show taskString +"hello 123 world true" + +> ./mill show taskTuple +[ + 123, + true, + "hello 123 world true" +] +*/ + +def taskTuple = T{ (taskInt(), taskBoolean(), taskString())} +def taskSeq = T{ Seq(taskInt(), taskInt() * 2, taskInt() * 3)} +def taskMap = T{ Map("int" -> taskInt().toString, "boolean" -> taskBoolean().toString) } + + +/** Usage +> ./mill show taskSeq +[ + 123, + 246, + 369 +] + +> ./mill show taskMap +{ + "int": "123", + "boolean": "true" +} + +*/ + +// === Paths and PathRef +// +// ``os.Path``s from OS-Lib are also serializable as strings. + +def taskPath = T{ + os.write(os.pwd / "file.txt", "hello") + os.pwd / "file.txt" +} + +/** Usage + +> ./mill show taskPath +".../out/taskPath.dest/file.txt" + +*/ + +// Note that returning an `os.Path` from a task will only invalidate downstream +// tasks on changes to the path itself (e.g. from returning `file.txt` to `file2.txt`), +// and not to changes to the contents of any file or folder at that path. If you want +// to invalidate downstream tasks depending on the contents of a file or folder, you +// should return a `PathRef`: + +def taskPathRef = T{ + os.write(os.pwd / "file.txt", "hello") + PathRef(os.pwd / "file.txt") +} + +/** Usage + +> ./mill show taskPathRef +"ref.../out/taskPathRef.dest/file.txt" + +*/ + +// The serialized `PathRef` contains a hexadecimal hash signature of the file or +// folder referenced on disk, computed from its contents. diff --git a/example/external/libraries/3-requests/build.mill b/example/external/libraries/3-requests/build.mill new file mode 100644 index 00000000000..37bca888162 --- /dev/null +++ b/example/external/libraries/3-requests/build.mill @@ -0,0 +1,117 @@ +// Mill bundles Requests-Scala for you to use to make HTTP requests. +// `Requests-Scala` lets you integrate your build with the world beyond your local +// filesystem. +// +// Requests-Scala is mostly used in Mill for downloading files as part of your +// build. These can either be data files or executables, and in either case they +// are downloaded once and cached for later use. +// +// === Downloading Compilers and Source Code +// +// In the example below, we download the +// https://github.com/bazelbuild/remote-apis[Remote APIs] source zip, download a +// https://github.com/bazelbuild/bazel[Bazel Build Tool] binary, and use Bazel to +// compile the Remote APIs source code as part of our build: + +import mill._ + +def remoteApisZip = T{ + println("downloading bazel-remote-apis sources...") + os.write( + T.dest / "source.zip", + requests.get("https://github.com/bazelbuild/remote-apis/archive/refs/tags/v2.2.0.zip") + ) + PathRef(T.dest / "source.zip") +} + +def bazel = T{ + println("downloading bazel...") + val fileName = + if (System.getProperty("os.name") == "Mac OS X") "bazel-5.4.1-darwin-arm64" + else "bazel-5.4.1-linux-x86_64" + + os.write( + T.dest / "bazel", + requests.get(s"https://github.com/bazelbuild/bazel/releases/download/5.4.1/$fileName") + ) + os.perms.set(T.dest / "bazel", "rwxrwxrwx") + PathRef(T.dest / "bazel") +} + +def compiledRemoteApis = T{ + val javaBuildTarget = "build/bazel/remote/execution/v2:remote_execution_java_proto" + os.call(("unzip", remoteApisZip().path, "-d", T.dest)) + os.call((bazel().path, "build", javaBuildTarget), cwd = T.dest / "remote-apis-2.2.0") + + val queried = os.call( + (bazel().path, "cquery", javaBuildTarget, "--output=files"), + cwd = T.dest / "remote-apis-2.2.0" + ) + + queried + .out + .lines() + .map(line => PathRef(T.dest / "remote-apis-2.2.0" / os.SubPath(line))) +} + +// In the execution example below, we can see the first time we ask for `compiledRemoteApis`, +// Mill downloads the Bazel build tool, downloads the Remote APIs source code, and then +// invokes Bazel to compile them: + +/** Usage + +> ./mill show compiledRemoteApis +downloading bazel... +downloading bazel-remote-apis sources... +Loading: ... +Analyzing: ... +... +INFO: Build completed successfully... +[ + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/build/bazel/semver/libsemver_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libduration_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libtimestamp_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libwrappers_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_api_http_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libdescriptor_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_api_annotations_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libany_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_rpc_status_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libempty_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_longrunning_operations_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/build/bazel/remote/execution/v2/libremote_execution_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/build/bazel/remote/execution/v2/remote_execution_proto-speed-src.jar" +] +*/ + +// However, in subsequent evaluations of `compiledRemoteApis`, the two downloads and +// the Bazel invocation are skipped and the earlier output directly and immediately re-used: + +/** Usage +> ./mill show compiledRemoteApis +[ + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/build/bazel/semver/libsemver_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libduration_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libtimestamp_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libwrappers_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_api_http_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libdescriptor_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_api_annotations_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libany_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_rpc_status_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/com_google_protobuf/libempty_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/external/googleapis/libgoogle_longrunning_operations_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/build/bazel/remote/execution/v2/libremote_execution_proto-speed.jar", + "ref:.../bazel-out/darwin_arm64-fastbuild/bin/build/bazel/remote/execution/v2/remote_execution_proto-speed-src.jar" +] + +*/ + +// The various tasks will only be re-evaluated if there are code changes in your `build.mill` +// file that affect them. +// +// In general, Using `requests.get` to download files as part of your build is only safe +// as long as the files you download are immutable. Mill cannot know whether the remote +// HTTP endpoint has been changed or not. However, empirically most URLs you may want +// to download files from do turn out to be immutable: from package repositories, artifact +// servers, and so on. So this works out surprisingly well in practice. diff --git a/example/external/libraries/4-mainargs/build.mill b/example/external/libraries/4-mainargs/build.mill new file mode 100644 index 00000000000..d0c36789126 --- /dev/null +++ b/example/external/libraries/4-mainargs/build.mill @@ -0,0 +1,165 @@ +// Mill uses MainArgs to handle argument parsing for ``T.command``s that +// are run from the command line. +import mill._ + +def commandSimple(str: String, i: Int, bool: Boolean = true) = T.command{ + println(s"$str $i $bool") +} + +// Mill uses MainArgs to let you parse most common Scala primitive types as command +// parameters: `String`s, `Int`s, `Boolean`s, etc. Single-character parameter names +// are treated as short arguments called with one dash `-` rather than two dashes `--`. +// Default values work as you would expect, and are substituted in if a value is not +// given at the command line + +/** Usage + +> ./mill commandSimple --str hello -i 123 +hello 123 true + +*/ + + +// === `os.Path` +// +// In addition to the builtin set of types that MainArgs supports, Mill also +// supports parsing OS-Lib ``os.Path``s from the command line: + + +def commandTakingPath(path: os.Path) = T.command{ + println(path) +} + +/** Usage + +> ./mill commandTakingPath --path foo/bar/baz.txt +...foo/bar/baz.txt + +*/ + + +// === Custom Main Argument Parsers +// +// You can define your own custom types and use them as command line arguments, +// as long as you define an `implicit mainargs.TokensReader[T]` for that type. + +class LettersOrDigits(val value: String) + +implicit object LettersOrDigitsTokensReader + extends mainargs.TokensReader.Simple[LettersOrDigits] { + def shortName = "letters-or-digits" + def read(strs: Seq[String]) = { + if (strs.last.forall(_.isLetterOrDigit)) Right(new LettersOrDigits(strs.last)) + else Left("non-letter/digit characters") + } +} + +def commandCustomArg(custom: LettersOrDigits) = T.command{ + println("hello " + custom.value) +} + +// Above, we show an example where we have a custom `class LettersOrDigits` type, +// where the `implicit object LettersOrDigitsTokensReader` does some basic validation +// to raise an error if the argument contains characters that are not etters or digits: + +/** Usage + +> ./mill commandCustomArg --custom 123abc +hello 123abc + +> ./mill commandCustomArg --custom 123?abc +error: Invalid argument --custom failed to parse "123?abc"... +...due to non-letter/digit characters +... + +*/ + + +// === `Task` +// +// Mill allows commands to take ``Task[T]``s as parameters anywhere they can +// take an unboxed `T`. This can be handy later on if you want to call the +// command as part of another target, while passing it the value of an upstream +// target: + +def commandTakingTask(str: Task[String]) = T.command{ + val result = "arg: " + str() + println(result) + result +} + +/** Usage + +> ./mill commandTakingTask --str helloworld +arg: helloworld + +*/ + +def upstreamTarget = T{ + "HELLO" +} + +def targetCallingCommand = T{ + commandTakingTask(upstreamTarget)() +} + + +/** Usage + +> ./mill show targetCallingCommand +"arg: HELLO" + +*/ + + +// === Evaluator (experimental) +// +// _Evaluator Command are experimental and suspected to change. +// See {mill-github-url}/issues/502[issue #502] for details._ +// +// You can define a command that takes in the current `Evaluator` as an argument, +// which you can use to inspect the entire build, or run arbitrary tasks. +// For example, here is a `customPlanCommand` command which uses this +// to traverse the module tree to find the targets specified by the `targets` strings, +// and plan out what would be necessary to run them +import mill.eval.{Evaluator, Terminal} +import mill.resolve.{Resolve, SelectMode} + +def customPlanCommand(evaluator: Evaluator, targets: String*) = T.command { + Resolve.Tasks.resolve( + evaluator.rootModule, + targets, + SelectMode.Multi + ) match{ + case Left(err) => Left(err) + case Right(resolved) => + val (sortedGroups, _) = evaluator.plan(resolved) + val plan = sortedGroups + .keys() + .collect { case r: Terminal.Labelled[_] => r.render } + .toArray + + plan.foreach(println) + Right(()) + } +} + +// We can call our `customPlanCommand` from the command line and pass it the +// `targetCallingCommand` we saw earlier, and it prints out the list of tasks +// it needs to run in the order necessary to reach `targetCallingCommand + +/** Usage + +> ./mill customPlanCommand targetCallingCommand +upstreamTarget +commandTakingTask +targetCallingCommand + +*/ + +// Many built-in tools are implemented as custom evaluator commands: +// xref:Scala_Builtin_Commands.adoc#_inspect[inspect], +// xref:Scala_Builtin_Commands.adoc#_resolve[resolve], +// xref:Scala_Builtin_Commands.adoc#_show[show]. +// If you want a way to run Mill commands and programmatically manipulate the +// tasks and outputs, you do so with your own evaluator command. diff --git a/example/package.mill b/example/package.mill index df1392d1565..0ab4f788c8b 100644 --- a/example/package.mill +++ b/example/package.mill @@ -57,6 +57,10 @@ object `package` extends RootModule with Module { object plugins extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "plugins")) } + object external extends Module { + object libraries extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "libraries")) + } + trait ExampleCrossModuleJava extends ExampleCrossModule { def upstreamCross(s: String) = s match { diff --git a/idea/src/mill/idea/GenIdea.scala b/idea/src/mill/idea/GenIdea.scala index f3f70d990b1..b24504d4190 100644 --- a/idea/src/mill/idea/GenIdea.scala +++ b/idea/src/mill/idea/GenIdea.scala @@ -21,5 +21,5 @@ object GenIdea extends ExternalModule { } } - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } diff --git a/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala b/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala index d84b9483a14..da51bf92de7 100644 --- a/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala +++ b/integration/failure/root-module-compile-error/src/RootModuleCompileErrorTests.scala @@ -12,14 +12,22 @@ object RootModuleCompileErrorTests extends UtestIntegrationTestSuite { assert(res.isSuccess == false) // For now these error messages still show generated/mangled code; not ideal, but it'll do - assert(res.err.contains("""build.mill:6:42: not found: type UnknownRootModule""")) - assert(res.err.contains("""class package_ extends RootModule with UnknownRootModule {""")) - assert(res.err.replace('\\', '/').contains( - """foo/package.mill:6:59: not found: type UnknownFooModule""" - )) - assert(res.err.contains( - """class package_ extends RootModule.Subfolder("foo") with UnknownFooModule {""" - )) + assert(res.err.contains("""build.mill:6:50: not found: type UnknownRootModule""")) + assert( + res.err.contains( + """abstract class package_ extends RootModule with UnknownRootModule {""" + ) + ) + assert( + res.err.replace('\\', '/').contains( + """foo/package.mill:6:60: not found: type UnknownFooModule""" + ) + ) + assert( + res.err.contains( + """abstract class package_ extends RootModule.Subfolder with UnknownFooModule {""" + ) + ) assert(res.err.contains("""build.mill:7:22: not found: value unknownRootInternalDef""")) assert(res.err.contains("""def scalaVersion = unknownRootInternalDef""")) diff --git a/main/api/src/mill/api/JsonFormatters.scala b/main/api/src/mill/api/JsonFormatters.scala index 6ed4408f027..dda6a1c3cc1 100644 --- a/main/api/src/mill/api/JsonFormatters.scala +++ b/main/api/src/mill/api/JsonFormatters.scala @@ -1,16 +1,29 @@ package mill.api +import os.Path import upickle.default.{ReadWriter => RW} import scala.reflect.ClassTag import scala.util.matching.Regex -object JsonFormatters extends JsonFormatters +object JsonFormatters extends JsonFormatters { + private object PathTokensReader0 extends mainargs.TokensReader.Simple[os.Path] { + def shortName = "path" + def read(strs: Seq[String]): Either[String, Path] = + Right(os.Path(strs.last, WorkspaceRoot.workspaceRoot)) + } +} /** * Defines various default JSON formatters used in mill. */ trait JsonFormatters { + + /** + * Additional [[mainargs.TokensReader]] instance to teach it how to read Ammonite paths + */ + implicit def PathTokensReader: mainargs.TokensReader[os.Path] = JsonFormatters.PathTokensReader0 + implicit val pathReadWrite: RW[os.Path] = upickle.default.readwriter[String] .bimap[os.Path]( _.toString, diff --git a/main/define/src/mill/define/BaseModule.scala b/main/define/src/mill/define/BaseModule.scala index 3235f09fb21..9023407c044 100644 --- a/main/define/src/mill/define/BaseModule.scala +++ b/main/define/src/mill/define/BaseModule.scala @@ -38,12 +38,12 @@ abstract class BaseModule( override def millSourcePath = millOuterCtx.millSourcePath override implicit def millModuleBasePath: Ctx.BasePath = Ctx.BasePath(millSourcePath) implicit def millImplicitBaseModule: BaseModule.Implicit = BaseModule.Implicit(this) - def millDiscover: Discover[this.type] + def millDiscover: Discover } trait BaseModule0 extends Module { - implicit def millDiscover: Discover[_] + implicit def millDiscover: Discover protected[mill] val watchedValues: mutable.Buffer[Watchable] = mutable.Buffer.empty[Watchable] protected[mill] val evalWatchedValues: mutable.Buffer[Watchable] = mutable.Buffer.empty[Watchable] @@ -86,7 +86,7 @@ abstract class ExternalModule(implicit Caller(null) ) { - implicit def millDiscoverImplicit: Discover[_] = millDiscover + implicit def millDiscoverImplicit: Discover = millDiscover assert( !" #".exists(millModuleEnclosing0.value.contains(_)), "External modules must be at a top-level static path, not " + millModuleEnclosing0.value diff --git a/main/define/src/mill/define/Discover.scala b/main/define/src/mill/define/Discover.scala index 6dcca7e8f67..7104cbe4aa0 100644 --- a/main/define/src/mill/define/Discover.scala +++ b/main/define/src/mill/define/Discover.scala @@ -16,7 +16,7 @@ import scala.reflect.macros.blackbox * the `T.command` methods we find. This mapping from `Class[_]` to `MainData` * can then be used later to look up the `MainData` for any module. */ -case class Discover[T] private ( +case class Discover private ( value: Map[ Class[_], (Seq[String], Seq[mainargs.MainData[_, _]]) @@ -33,27 +33,27 @@ case class Discover[T] private ( (Seq[String], Seq[mainargs.MainData[_, _]]) ] = value, dummy: Int = dummy /* avoid conflict with Discover.apply(value: Map) below*/ - ): Discover[T] = new Discover[T](value, dummy) + ): Discover = new Discover(value, dummy) @deprecated("Binary compatibility shim", "Mill 0.11.4") - private[define] def copy[T](value: Map[Class[_], Seq[mainargs.MainData[_, _]]]): Discover[T] = { - new Discover[T](value.view.mapValues((Nil, _)).toMap, dummy) + private[define] def copy(value: Map[Class[_], Seq[mainargs.MainData[_, _]]]): Discover = { + new Discover(value.view.mapValues((Nil, _)).toMap, dummy) } } object Discover { - def apply2[T](value: Map[Class[_], (Seq[String], Seq[mainargs.MainData[_, _]])]): Discover[T] = - new Discover[T](value) + def apply2[T](value: Map[Class[_], (Seq[String], Seq[mainargs.MainData[_, _]])]): Discover = + new Discover(value) @deprecated("Binary compatibility shim", "Mill 0.11.4") - def apply[T](value: Map[Class[_], Seq[mainargs.MainData[_, _]]]): Discover[T] = - new Discover[T](value.view.mapValues((Nil, _)).toMap) + def apply[T](value: Map[Class[_], Seq[mainargs.MainData[_, _]]]): Discover = + new Discover(value.view.mapValues((Nil, _)).toMap) - def apply[T]: Discover[T] = macro Router.applyImpl[T] + def apply[T]: Discover = macro Router.applyImpl[T] private class Router(val ctx: blackbox.Context) extends mainargs.Macros(ctx) { import c.universe._ - def applyImpl[T: WeakTypeTag]: Expr[Discover[T]] = { + def applyImpl[T: WeakTypeTag]: Expr[Discover] = { val seen = mutable.Set.empty[Type] def rec(tpe: Type): Unit = { if (!seen(tpe)) { @@ -141,8 +141,8 @@ object Discover { q"$lhs -> $overridesLambda" } - c.Expr[Discover[T]]( - q"import _root_.mill.main.TokenReaders._; _root_.mill.define.Discover.apply2(_root_.scala.collection.immutable.Map(..$mapping))" + c.Expr[Discover]( + q"_root_.mill.define.Discover.apply2(_root_.scala.collection.immutable.Map(..$mapping))" ) } } diff --git a/main/package.mill b/main/package.mill index f7a2522255a..11b8baf1d0d 100644 --- a/main/package.mill +++ b/main/package.mill @@ -11,8 +11,8 @@ object `package` extends RootModule with build.MillStableScalaModule with BuildI def moduleDeps = Seq(eval, resolve, client) def ivyDeps = Agg( build.Deps.windowsAnsi, - build.Deps.mainargs, build.Deps.coursierInterface, + build.Deps.mainargs, build.Deps.requests, build.Deps.logback ) @@ -76,6 +76,7 @@ object `package` extends RootModule with build.MillStableScalaModule with BuildI def ivyDeps = Agg( build.Deps.osLib, + build.Deps.mainargs, build.Deps.upickle, build.Deps.pprint, build.Deps.fansi, diff --git a/main/resolve/src/mill/resolve/Resolve.scala b/main/resolve/src/mill/resolve/Resolve.scala index a4d4fdca73f..393346a8733 100644 --- a/main/resolve/src/mill/resolve/Resolve.scala +++ b/main/resolve/src/mill/resolve/Resolve.scala @@ -126,7 +126,7 @@ object Resolve { val invoked = invokeCommand0( p, r.segments.parts.last, - rootModule.millDiscover.asInstanceOf[Discover[mill.define.Module]], + rootModule.millDiscover.asInstanceOf[Discover], args, nullCommandDefaults, allowPositionalCommandArgs @@ -139,7 +139,7 @@ object Resolve { private def invokeCommand0( target: mill.define.Module, name: String, - discover: Discover[mill.define.Module], + discover: Discover, rest: Seq[String], nullCommandDefaults: Boolean, allowPositionalCommandArgs: Boolean diff --git a/main/src/mill/main/RootModule.scala b/main/src/mill/main/RootModule.scala index e086234c87c..ae2f87699b9 100644 --- a/main/src/mill/main/RootModule.scala +++ b/main/src/mill/main/RootModule.scala @@ -22,38 +22,32 @@ abstract class RootModule()(implicit millModuleLine0, millFile0, Caller(null) - ) with mill.main.MainModule { - - // Make BaseModule take the `millDiscover` as an implicit param, rather than - // defining it itself. That is so we can define it externally in the wrapper - // code and it have it automatically passed to both the wrapper BaseModule as - // well as any user-defined BaseModule that may be present, so the - // user-defined BaseModule can have a complete Discover[_] instance without - // needing to tediously call `override lazy val millDiscover = Discover[this.type]` - override lazy val millDiscover: Discover[this.type] = - baseModuleInfo.discover.asInstanceOf[Discover[this.type]] -} + ) with mill.main.MainModule @internal object RootModule { - case class Info(millSourcePath0: os.Path, discover: Discover[_]) + case class Info(millSourcePath0: os.Path, discover: Discover) + case class SubFolderInfo(value: Seq[String]) - abstract class Subfolder(path: String*)(implicit + abstract class Subfolder()(implicit baseModuleInfo: RootModule.Info, millModuleLine0: sourcecode.Line, - millFile0: sourcecode.File + millFile0: sourcecode.File, + subFolderInfo: SubFolderInfo ) extends Module.BaseClass()( Ctx.make( - millModuleEnclosing0 = path.mkString("."), + millModuleEnclosing0 = subFolderInfo.value.mkString("."), millModuleLine0 = millModuleLine0, millModuleBasePath0 = Ctx.BasePath(baseModuleInfo.millSourcePath0 / os.up), - segments0 = Segments.labels(path.init: _*), + segments0 = Segments.labels(subFolderInfo.value.init: _*), external0 = Ctx.External(false), foreign0 = Ctx.Foreign(None), fileName = millFile0, enclosing = Caller(null) ) - ) with Module + ) with Module { + def millDiscover: Discover + } @deprecated abstract class Foreign(foreign0: Option[Segments])(implicit @@ -66,8 +60,5 @@ object RootModule { millModuleLine0, millFile0, Caller(null) - ) with mill.main.MainModule { - - override implicit lazy val millDiscover: Discover[this.type] = Discover[this.type] - } + ) with mill.main.MainModule } diff --git a/main/src/mill/main/TokenReaders.scala b/main/src/mill/main/TokenReaders.scala index 50f7785313f..a23584f2480 100644 --- a/main/src/mill/main/TokenReaders.scala +++ b/main/src/mill/main/TokenReaders.scala @@ -39,7 +39,8 @@ private class LeftoverTaskTokenReader[T](tokensReaderOfT: TokensReader.Leftover[ def shortName = tokensReaderOfT.shortName } -object TokenReaders { +object TokenReaders extends TokenReaders0 +trait TokenReaders0 { implicit def millEvaluatorTokenReader[T]: mainargs.TokensReader[Evaluator] = new mill.main.EvaluatorTokenReader[T]() diff --git a/main/src/mill/main/VisualizeModule.scala b/main/src/mill/main/VisualizeModule.scala index c2e8e274ce3..44adcbde07e 100644 --- a/main/src/mill/main/VisualizeModule.scala +++ b/main/src/mill/main/VisualizeModule.scala @@ -18,7 +18,7 @@ object VisualizeModule extends ExternalModule with VisualizeModule { MavenRepository("https://oss.sonatype.org/content/repositories/releases") ) - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } trait VisualizeModule extends mill.define.TaskModule { def repositories: Seq[Repository] diff --git a/main/src/mill/package.scala b/main/src/mill/package.scala index 071f608e5ec..766d6986ce8 100644 --- a/main/src/mill/package.scala +++ b/main/src/mill/package.scala @@ -1,4 +1,4 @@ -package object mill extends mill.api.JsonFormatters { +package object mill extends mill.api.JsonFormatters with mill.main.TokenReaders0 { val T = define.Target type T[+T] = define.Target[T] val Target = define.Target diff --git a/main/test/src/mill/main/MainModuleTests.scala b/main/test/src/mill/main/MainModuleTests.scala index 57f0d777d65..6261df99f65 100644 --- a/main/test/src/mill/main/MainModuleTests.scala +++ b/main/test/src/mill/main/MainModuleTests.scala @@ -27,7 +27,7 @@ object MainModuleTests extends TestSuite { Map("1" -> "hello", "2" -> "world") } def helloCommand(x: Int, y: Task[String]) = T.command { (x, y(), hello()) } - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } object cleanModule extends TestBaseModule with MainModule { diff --git a/main/test/src/mill/util/TestGraphs.scala b/main/test/src/mill/util/TestGraphs.scala index 3be987128a6..25cb8462705 100644 --- a/main/test/src/mill/util/TestGraphs.scala +++ b/main/test/src/mill/util/TestGraphs.scala @@ -323,7 +323,7 @@ object TestGraphs { object canOverrideSuper extends TestBaseModule with BaseModule { override def foo = T { super.foo() ++ Seq("object") } override def cmd(i: Int) = T.command { super.cmd(i)() ++ Seq("object" + i) } - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } trait TraitWithModule extends Module { outer => @@ -335,7 +335,7 @@ object TestGraphs { // Make sure nested objects inherited from traits work object TraitWithModuleObject extends TestBaseModule with TraitWithModule { - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } object nullTasks extends TestBaseModule { @@ -353,7 +353,7 @@ object TestGraphs { def nullCommand3() = T.command { nullTask1() } def nullCommand4() = T.command { nullTask2() } - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } object duplicates extends TestBaseModule { @@ -377,7 +377,7 @@ object TestGraphs { def test4() = T.command {} } - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } object singleCross extends TestBaseModule { @@ -511,7 +511,7 @@ object TestGraphs { object nestedTaskCrosses extends TestBaseModule { // this is somehow necessary to let Discover see our inner (default) commands // I expected, that the identical inherited `millDiscover` is enough, but it isn't - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] object cross1 extends mill.Cross[Cross1]("210", "211", "212") trait Cross1 extends mill.Cross.Module[String] { def scalaVersion = crossValue diff --git a/runner/src/mill/runner/CodeGen.scala b/runner/src/mill/runner/CodeGen.scala index dbe14739c82..57ae17d81f6 100644 --- a/runner/src/mill/runner/CodeGen.scala +++ b/runner/src/mill/runner/CodeGen.scala @@ -120,12 +120,15 @@ object CodeGen { scriptCode: String, markerComment: String ) = { + val segments = scriptFolderPath.relativeTo(projectRoot).segments + val prelude = topBuildPrelude( + segments, scriptFolderPath, enclosingClasspath, millTopLevelProjectRoot ) - val segments = scriptFolderPath.relativeTo(projectRoot).segments + val instrument = new ObjectDataInstrument(scriptCode) fastparse.parse(scriptCode, Parsers.CompilationUnit(_), instrument = instrument) val objectData = instrument.objectData @@ -147,17 +150,12 @@ object CodeGen { o.name.text == "`package`" && (o.parent.text == "RootModule" || o.parent.text == "MillBuildRootModule") ) match { case Some(objectData) => - val newParent = // Use whitespace to try and make sure stuff to the right has the same column offset - if (segments.isEmpty) expectedParent - else { - val segmentsStr = segments.map(pprint.Util.literalize(_)).mkString(", ") - s"RootModule.Subfolder($segmentsStr)" - } + val newParent = if (segments.isEmpty) expectedParent else s"RootModule.Subfolder" var newScriptCode = scriptCode - newScriptCode = objectData.obj.applyTo(newScriptCode, "class") - newScriptCode = objectData.name.applyTo(newScriptCode, wrapperObjectName) newScriptCode = objectData.parent.applyTo(newScriptCode, newParent) + newScriptCode = objectData.name.applyTo(newScriptCode, wrapperObjectName) + newScriptCode = objectData.obj.applyTo(newScriptCode, "abstract class") s"""$pkgLine |$aliasImports @@ -166,6 +164,7 @@ object CodeGen { |$newScriptCode |object $wrapperObjectName extends $wrapperObjectName { | $childAliases + | override lazy val millDiscover: _root_.mill.define.Discover = _root_.mill.define.Discover[this.type] |}""".stripMargin case None => s"""$pkgLine @@ -180,6 +179,7 @@ object CodeGen { } def topBuildPrelude( + segments: Seq[String], scriptFolderPath: os.Path, enclosingClasspath: Seq[os.Path], millTopLevelProjectRoot: os.Path @@ -190,35 +190,36 @@ object CodeGen { | ${enclosingClasspath.map(p => literalize(p.toString))}, | ${literalize(scriptFolderPath.toString)}, | ${literalize(millTopLevelProjectRoot.toString)}, - | _root_.mill.define.Discover[$wrapperObjectName.type] + | _root_.scala.Seq(${segments.map(pprint.Util.literalize(_)).mkString(", ")}) |) |import MillMiscInfo._ |""".stripMargin } def topBuildHeader( - segs: Seq[String], + segments: Seq[String], scriptFolderPath: os.Path, millTopLevelProjectRoot: os.Path, childAliases: String ): String = { - val extendsClause = if (segs.isEmpty) { + val extendsClause = if (segments.isEmpty) { if (millTopLevelProjectRoot == scriptFolderPath) { s"extends _root_.mill.main.RootModule() " } else { s"extends _root_.mill.runner.MillBuildRootModule() " } } else { - val segsList = segs.map(pprint.Util.literalize(_)).mkString(", ") - s"extends _root_.mill.main.RootModule.Subfolder($segsList) " + + s"extends _root_.mill.main.RootModule.Subfolder " } // User code needs to be put in a separate class for proper submodule // object initialization due to https://github.com/scala/scala3/issues/21444 s"""object $wrapperObjectName extends $wrapperObjectName{ | $childAliases + | override lazy val millDiscover: _root_.mill.define.Discover = _root_.mill.define.Discover[this.type] |} - |class $wrapperObjectName $extendsClause {""".stripMargin + |abstract class $wrapperObjectName $extendsClause {""".stripMargin } diff --git a/runner/src/mill/runner/MillBuild.scala b/runner/src/mill/runner/MillBuild.scala index 5bbeba1c3c7..0b639af203f 100644 --- a/runner/src/mill/runner/MillBuild.scala +++ b/runner/src/mill/runner/MillBuild.scala @@ -17,5 +17,5 @@ trait MillBuild extends Module { } object MillBuild extends ExternalModule with MillBuild { - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } diff --git a/runner/src/mill/runner/MillBuildRootModule.scala b/runner/src/mill/runner/MillBuildRootModule.scala index f09a50583df..40931ffe42c 100644 --- a/runner/src/mill/runner/MillBuildRootModule.scala +++ b/runner/src/mill/runner/MillBuildRootModule.scala @@ -23,7 +23,7 @@ import scala.util.Try * calls within the scripts. */ @internal -class MillBuildRootModule()(implicit +abstract class MillBuildRootModule()(implicit baseModuleInfo: RootModule.Info, millBuildRootModuleInfo: MillBuildRootModule.Info ) extends RootModule() with ScalaModule { @@ -274,8 +274,7 @@ object MillBuildRootModule { ) ) { - override lazy val millDiscover: Discover[this.type] = - baseModuleInfo.discover.asInstanceOf[Discover[this.type]] + override lazy val millDiscover: Discover = baseModuleInfo.discover } case class Info( @@ -295,16 +294,19 @@ object MillBuildRootModule { enclosingClasspath: Seq[String], projectRoot: String, topLevelProjectRoot: String, - discover: Discover[_] + segments: Seq[String] ) { - implicit val millBuildRootModuleInfo: MillBuildRootModule.Info = MillBuildRootModule.Info( + implicit lazy val millBuildRootModuleInfo: MillBuildRootModule.Info = MillBuildRootModule.Info( enclosingClasspath.map(os.Path(_)), os.Path(projectRoot), os.Path(topLevelProjectRoot) ) - implicit val millBaseModuleInfo: RootModule.Info = RootModule.Info( + implicit lazy val millBaseModuleInfo: RootModule.Info = RootModule.Info( millBuildRootModuleInfo.projectRoot, - discover + null + ) + implicit lazy val subfolderInfo: RootModule.SubFolderInfo = RootModule.SubFolderInfo( + segments ) } } diff --git a/runner/src/mill/runner/MillCliConfig.scala b/runner/src/mill/runner/MillCliConfig.scala index 99cefc1d712..7d7a8c39f02 100644 --- a/runner/src/mill/runner/MillCliConfig.scala +++ b/runner/src/mill/runner/MillCliConfig.scala @@ -1,8 +1,6 @@ package mill.runner import mainargs.{Flag, Leftover, arg} -import mill.api.WorkspaceRoot -import os.Path class MillCliConfig private ( @arg( @@ -317,14 +315,7 @@ object MillCliConfigParser { val customName: String = s"Mill Build Tool, version ${mill.main.BuildInfo.millVersion}" val customDoc = "usage: mill [options] [[target [target-options]] [+ [target ...]]]" - /** - * Additional [[mainargs.TokensReader]] instance to teach it how to read Ammonite paths - */ - implicit object PathRead extends mainargs.TokensReader.Simple[os.Path] { - def shortName = "path" - def read(strs: Seq[String]): Either[String, Path] = - Right(os.Path(strs.last, WorkspaceRoot.workspaceRoot)) - } + import mill.api.JsonFormatters._ private[this] lazy val parser: ParserForClass[MillCliConfig] = mainargs.ParserForClass[MillCliConfig] diff --git a/scalajslib/src/mill/scalajslib/worker/ScalaJSWorker.scala b/scalajslib/src/mill/scalajslib/worker/ScalaJSWorker.scala index 840a9cac005..a4d3d75e2d3 100644 --- a/scalajslib/src/mill/scalajslib/worker/ScalaJSWorker.scala +++ b/scalajslib/src/mill/scalajslib/worker/ScalaJSWorker.scala @@ -220,5 +220,5 @@ private[scalajslib] class ScalaJSWorker extends AutoCloseable { private[scalajslib] object ScalaJSWorkerExternalModule extends mill.define.ExternalModule { def scalaJSWorker: Worker[ScalaJSWorker] = T.worker { new ScalaJSWorker() } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/scalalib/src/mill/scalalib/Dependency.scala b/scalalib/src/mill/scalalib/Dependency.scala index 097c2a1c221..6cc643fd163 100644 --- a/scalalib/src/mill/scalalib/Dependency.scala +++ b/scalalib/src/mill/scalalib/Dependency.scala @@ -36,5 +36,5 @@ object Dependency extends ExternalModule { def showUpdates(ev: Evaluator, allowPreRelease: Boolean): Command[Unit] = Dependency.showUpdates(ev, allowPreRelease, Format.PerModule) - lazy val millDiscover: Discover[Dependency.this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/scalalib/src/mill/scalalib/GenIdea.scala b/scalalib/src/mill/scalalib/GenIdea.scala index 9bdf16fe094..733b42043b7 100644 --- a/scalalib/src/mill/scalalib/GenIdea.scala +++ b/scalalib/src/mill/scalalib/GenIdea.scala @@ -23,5 +23,5 @@ object GenIdea extends ExternalModule { () } - override lazy val millDiscover: Discover[this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } diff --git a/scalalib/src/mill/scalalib/GenIdeaImpl.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala index cd53a902cfb..9863c23eb6b 100755 --- a/scalalib/src/mill/scalalib/GenIdeaImpl.scala +++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala @@ -18,7 +18,7 @@ case class GenIdeaImpl( evaluator: Evaluator, ctx: Log with Home, rootModule: BaseModule, - discover: Discover[_] + discover: Discover ) { import GenIdeaImpl._ diff --git a/scalalib/src/mill/scalalib/PublishModule.scala b/scalalib/src/mill/scalalib/PublishModule.scala index edc92dbdeab..5a22eccc38c 100644 --- a/scalalib/src/mill/scalalib/PublishModule.scala +++ b/scalalib/src/mill/scalalib/PublishModule.scala @@ -379,5 +379,5 @@ object PublishModule extends ExternalModule { } } - lazy val millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] + lazy val millDiscover: mill.define.Discover = mill.define.Discover[this.type] } diff --git a/scalalib/src/mill/scalalib/dependency/DependencyUpdatesImpl.scala b/scalalib/src/mill/scalalib/dependency/DependencyUpdatesImpl.scala index ef22bed789d..17a22e4cd11 100644 --- a/scalalib/src/mill/scalalib/dependency/DependencyUpdatesImpl.scala +++ b/scalalib/src/mill/scalalib/dependency/DependencyUpdatesImpl.scala @@ -16,7 +16,7 @@ object DependencyUpdatesImpl { evaluator: Evaluator, ctx: Log with Home, rootModule: BaseModule, - discover: Discover[_], + discover: Discover, allowPreRelease: Boolean ): Seq[ModuleDependenciesUpdates] = { diff --git a/scalalib/src/mill/scalalib/giter8/Giter8Module.scala b/scalalib/src/mill/scalalib/giter8/Giter8Module.scala index 900e3974a3e..ca7688d2665 100644 --- a/scalalib/src/mill/scalalib/giter8/Giter8Module.scala +++ b/scalalib/src/mill/scalalib/giter8/Giter8Module.scala @@ -9,7 +9,7 @@ import mill.main.BuildInfo import mill.api.Loose object Giter8Module extends ExternalModule with Giter8Module { - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } trait Giter8Module extends CoursierModule { diff --git a/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala b/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala index f28a17624ea..eabc265fd3d 100644 --- a/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala +++ b/scalalib/src/mill/scalalib/scalafmt/ScalafmtModule.scala @@ -95,5 +95,5 @@ object ScalafmtModule extends ExternalModule with ScalafmtModule { ) } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala b/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala index e141581d491..5b5f6562db4 100644 --- a/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala +++ b/scalalib/src/mill/scalalib/scalafmt/ScalafmtWorker.scala @@ -11,7 +11,7 @@ import mill.api.Result object ScalafmtWorkerModule extends ExternalModule { def worker: Worker[ScalafmtWorker] = T.worker { new ScalafmtWorker() } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } private[scalafmt] class ScalafmtWorker extends AutoCloseable { diff --git a/scalanativelib/src/mill/scalanativelib/worker/ScalaNativeWorker.scala b/scalanativelib/src/mill/scalanativelib/worker/ScalaNativeWorker.scala index ed731eb20bb..76ffaf05cec 100644 --- a/scalanativelib/src/mill/scalanativelib/worker/ScalaNativeWorker.scala +++ b/scalanativelib/src/mill/scalanativelib/worker/ScalaNativeWorker.scala @@ -42,5 +42,5 @@ private[scalanativelib] class ScalaNativeWorker extends AutoCloseable { private[scalanativelib] object ScalaNativeWorkerExternalModule extends mill.define.ExternalModule { def scalaNativeWorker: Worker[ScalaNativeWorker] = T.worker { new ScalaNativeWorker() } - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/scalanativelib/test/src/mill/scalanativelib/ExclusionsTests.scala b/scalanativelib/test/src/mill/scalanativelib/ExclusionsTests.scala index d6baf7734f7..4947f2962cc 100644 --- a/scalanativelib/test/src/mill/scalanativelib/ExclusionsTests.scala +++ b/scalanativelib/test/src/mill/scalanativelib/ExclusionsTests.scala @@ -23,7 +23,7 @@ object ExclusionsTests extends TestSuite { ivy"com.github.scopt:scopt_native0.4_2.13:4.0.1" ) } - override lazy val millDiscover: Discover[Exclusions.this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } val exclusionsEvaluator = UnitTester(Exclusions, null) diff --git a/scalanativelib/test/src/mill/scalanativelib/FeaturesTests.scala b/scalanativelib/test/src/mill/scalanativelib/FeaturesTests.scala index 3418092d136..c151c559662 100644 --- a/scalanativelib/test/src/mill/scalanativelib/FeaturesTests.scala +++ b/scalanativelib/test/src/mill/scalanativelib/FeaturesTests.scala @@ -10,7 +10,7 @@ object FeaturesTests extends TestSuite { def scalaNativeVersion = "0.5.0" def scalaVersion = "2.13.10" def nativeIncrementalCompilation = true - override lazy val millDiscover: Discover[Features.this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } val millSourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "features" diff --git a/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala b/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala index d2569ab3cf2..a632abc0f2b 100644 --- a/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala +++ b/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala @@ -76,7 +76,7 @@ object HelloNativeWorldTests extends TestSuite { object test extends ScalaNativeTests with TestModule.Utest } - override lazy val millDiscover: Discover[HelloNativeWorld.this.type] = Discover[this.type] + override lazy val millDiscover: Discover = Discover[this.type] } val millSourcePath = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "hello-native-world" diff --git a/testkit/src/mill/testkit/TestBaseModule.scala b/testkit/src/mill/testkit/TestBaseModule.scala index 86d609cfc5f..5d07a2b2f67 100644 --- a/testkit/src/mill/testkit/TestBaseModule.scala +++ b/testkit/src/mill/testkit/TestBaseModule.scala @@ -20,5 +20,5 @@ class TestBaseModule(implicit millModuleFile0, Caller(null) ) { - lazy val millDiscover: Discover[this.type] = Discover[this.type] + lazy val millDiscover: Discover = Discover[this.type] } diff --git a/testkit/src/mill/testkit/UtestExampleTestSuite.scala b/testkit/src/mill/testkit/UtestExampleTestSuite.scala index c2fac9be206..0a95dee6fb2 100644 --- a/testkit/src/mill/testkit/UtestExampleTestSuite.scala +++ b/testkit/src/mill/testkit/UtestExampleTestSuite.scala @@ -12,7 +12,15 @@ object UtestExampleTestSuite extends TestSuite { val tests: Tests = Tests { test("exampleTest") { - Retry(count = 3, timeoutMillis = 5.minutes.toMillis) { + if (sys.env.contains("CI")) { + Retry(count = 3, timeoutMillis = 5.minutes.toMillis) { + ExampleTester.run( + clientServerMode, + workspaceSourcePath, + millExecutable + ) + } + } else { ExampleTester.run( clientServerMode, workspaceSourcePath,