diff --git a/build.mill b/build.mill index 9c2c741026a..5ff7c20bba0 100644 --- a/build.mill +++ b/build.mill @@ -329,9 +329,9 @@ trait MillJavaModule extends JavaModule { def testDepPaths = Task { upstreamAssemblyClasspath() ++ Seq(compile().classes) ++ resources() } def testTransitiveDeps: T[Map[String, String]] = Task { - val upstream = T.traverse(moduleDeps ++ compileModuleDeps) { + val upstream = Task.traverse(moduleDeps ++ compileModuleDeps) { case m: MillJavaModule => m.testTransitiveDeps.map(Some(_)) - case _ => T.task(None) + case _ => Task.Anon(None) }().flatten.flatten val current = Seq(testDep()) upstream.toMap ++ current @@ -342,28 +342,28 @@ trait MillJavaModule extends JavaModule { if (this == build.main) Seq(build.main) else Seq(this, build.main.test) - def writeLocalTestOverrides = T.task { + def writeLocalTestOverrides = Task.Anon { for ((k, v) <- testTransitiveDeps()) { - os.write(T.dest / "mill" / "local-test-overrides" / k, v, createFolders = true) + os.write(Task.dest / "mill" / "local-test-overrides" / k, v, createFolders = true) } - Seq(PathRef(T.dest)) + Seq(PathRef(Task.dest)) } def runClasspath = super.runClasspath() ++ writeLocalTestOverrides() - def repositoriesTask = T.task { + def repositoriesTask = Task.Anon { super.repositoriesTask() ++ Seq(MavenRepository("https://oss.sonatype.org/content/repositories/releases")) } - def mapDependencies: Task[coursier.Dependency => coursier.Dependency] = T.task { + def mapDependencies: Task[coursier.Dependency => coursier.Dependency] = Task.Anon { super.mapDependencies().andThen { dep => forcedVersions.find(f => f.dep.module.organization.value == dep.module.organization.value && f.dep.module.name.value == dep.module.name.value ).map { forced => val newDep = dep.withVersion(forced.dep.version) - T.log.debug(s"Forcing version of ${dep.module} from ${dep.version} to ${newDep.version}") + Task.log.debug(s"Forcing version of ${dep.module} from ${dep.version} to ${newDep.version}") newDep }.getOrElse(dep) } @@ -589,9 +589,9 @@ trait BridgeModule extends MillPublishJavaModule with CrossScalaModule { ivy"org.scala-lang:scala-compiler:${crossScalaVersion}" ) - def resources = T.sources { - os.copy(generatedSources().head.path / "META-INF", T.dest / "META-INF") - Seq(PathRef(T.dest)) + def resources = Task.Sources { + os.copy(generatedSources().head.path / "META-INF", Task.dest / "META-INF") + Seq(PathRef(Task.dest)) } def compilerBridgeIvyDeps: T[Agg[Dep]] = Agg( @@ -600,7 +600,7 @@ trait BridgeModule extends MillPublishJavaModule with CrossScalaModule { def compilerBridgeSourceJars: T[Agg[PathRef]] = Task { resolveDeps( - T.task { compilerBridgeIvyDeps().map(bindDependency()) }, + Task.Anon { compilerBridgeIvyDeps().map(bindDependency()) }, sources = true )() } @@ -608,10 +608,10 @@ trait BridgeModule extends MillPublishJavaModule with CrossScalaModule { def generatedSources = Task { compilerBridgeSourceJars().foreach { jar => - os.unzip(jar.path, T.dest) + os.unzip(jar.path, Task.dest) } - Seq(PathRef(T.dest)) + Seq(PathRef(Task.dest)) } } @@ -752,7 +752,7 @@ object idea extends MillPublishScalaModule { */ object dist0 extends MillPublishJavaModule { // disable scalafix here because it crashes when a module has no sources - def fix(args: String*): Command[Unit] = T.command {} + def fix(args: String*): Command[Unit] = Task.Command {} def moduleDeps = Seq(build.runner, idea) def testTransitiveDeps = build.runner.testTransitiveDeps() ++ Seq( @@ -779,7 +779,7 @@ object dist extends MillPublishJavaModule { (s"com.lihaoyi-${dist.artifactId()}", dist0.runClasspath().map(_.path).mkString("\n")) ) - def genTask(m: ScalaModule) = T.task { Seq(m.jar(), m.sourceJar()) ++ m.runClasspath() } + def genTask(m: ScalaModule) = Task.Anon { Seq(m.jar(), m.sourceJar()) ++ m.runClasspath() } def forkArgs: T[Seq[String]] = Task { val genIdeaArgs = @@ -801,7 +801,7 @@ object dist extends MillPublishJavaModule { def launcher = Task { val isWin = scala.util.Properties.isWin - val outputPath = T.dest / (if (isWin) "run.bat" else "run") + val outputPath = Task.dest / (if (isWin) "run.bat" else "run") os.write(outputPath, prependShellScript()) if (!isWin) os.perms.set(outputPath, "rwxrwxrwx") @@ -840,22 +840,22 @@ object dist extends MillPublishJavaModule { prependShellScript = launcherScript(shellArgs, cmdArgs, Agg("$0"), Agg("%~dpnx0")), assemblyRules = assemblyRules ).path, - T.dest / filename + Task.dest / filename ) - PathRef(T.dest / filename) + PathRef(Task.dest / filename) } def assembly = Task { - T.traverse(allPublishModules)(m => m.publishLocalCached)() + Task.traverse(allPublishModules)(m => m.publishLocalCached)() val raw = rawAssembly().path - os.copy(raw, T.dest / raw.last) - PathRef(T.dest / raw.last) + os.copy(raw, Task.dest / raw.last) + PathRef(Task.dest / raw.last) } def prependShellScript = Task { val (millArgs, otherArgs) = forkArgs().partition(arg => arg.startsWith("-DMILL") && !arg.startsWith("-DMILL_VERSION")) // Pass Mill options via file, due to small max args limit in Windows - val vmOptionsFile = T.dest / "mill.properties" + val vmOptionsFile = Task.dest / "mill.properties" val millOptionsContent = millArgs.map(_.drop(2).replace("\\", "/")).mkString( "\r\n" @@ -890,11 +890,11 @@ object dist extends MillPublishJavaModule { Jvm.createJar(Agg(), JarManifest(manifestEntries)) } - def run(args: Task[Args] = T.task(Args())) = Task.Command(exclusive = true) { + def run(args: Task[Args] = Task.Anon(Args())) = Task.Command(exclusive = true) { args().value match { case Nil => mill.api.Result.Failure("Need to pass in cwd as first argument to dev.run") case wd0 +: rest => - val wd = os.Path(wd0, T.workspace) + val wd = os.Path(wd0, Task.workspace) os.makeDir.all(wd) try { Jvm.runSubprocess( @@ -918,32 +918,32 @@ object dist extends MillPublishJavaModule { * @param ivyRepo The local Ivy repository where Mill modules should be published to */ def installLocal(binFile: String = DefaultLocalMillReleasePath, ivyRepo: String = null) = - T.command { - PathRef(installLocalTask(T.task(binFile), ivyRepo)()) + Task.Command { + PathRef(installLocalTask(Task.Anon(binFile), ivyRepo)()) } -def installLocalCache() = T.command { +def installLocalCache() = Task.Command { val path = installLocalTask( - T.task((os.home / ".cache" / "mill" / "download" / millVersion()).toString()) + Task.Anon((os.home / ".cache" / "mill" / "download" / millVersion()).toString()) )() - T.log.outputStream.println(path.toString()) + Task.log.outputStream.println(path.toString()) PathRef(path) } -def installLocalTask(binFile: Task[String], ivyRepo: String = null): Task[os.Path] = T.task { +def installLocalTask(binFile: Task[String], ivyRepo: String = null): Task[os.Path] = Task.Anon { val millBin = dist.assembly() - val targetFile = os.Path(binFile(), T.workspace) + val targetFile = os.Path(binFile(), Task.workspace) if (os.exists(targetFile)) - T.log.info(s"Overwriting existing local Mill binary at ${targetFile}") + Task.log.info(s"Overwriting existing local Mill binary at ${targetFile}") os.copy.over(millBin.path, targetFile, createFolders = true) - T.log.info(s"Published ${dist.allPublishModules.size} modules and installed ${targetFile}") + Task.log.info(s"Published ${dist.allPublishModules.size} modules and installed ${targetFile}") targetFile } -def millBootstrap = T.sources(T.workspace / "mill") +def millBootstrap = Task.Sources(Task.workspace / "mill") def bootstrapLauncher = Task { - val outputPath = T.dest / "mill" + val outputPath = Task.dest / "mill" val millBootstrapGrepPrefix = "(\n *DEFAULT_MILL_VERSION=)" val millDownloadUrlPrefix = "(\n *MILL_DOWNLOAD_URL=)" @@ -959,12 +959,12 @@ def bootstrapLauncher = Task { PathRef(outputPath) } -def examplePathsWithArtifactName:Task[Seq[(os.Path,String)]] = T.task{ +def examplePathsWithArtifactName:Task[Seq[(os.Path,String)]] = Task.Anon{ for { exampleMod <- build.example.exampleModules path = exampleMod.millSourcePath } yield { - val example = path.subRelativeTo(T.workspace) + val example = path.subRelativeTo(Task.workspace) val artifactName = millVersion() + "-" + example.segments.mkString("-") (path, artifactName) } @@ -973,16 +973,16 @@ def examplePathsWithArtifactName:Task[Seq[(os.Path,String)]] = T.task{ def exampleZips: T[Seq[PathRef]] = Task { examplePathsWithArtifactName().map{ case (examplePath, exampleStr) => - os.copy(examplePath, T.dest / exampleStr, createFolders = true) - os.write(T.dest / exampleStr / ".mill-version", millLastTag()) - os.copy(bootstrapLauncher().path, T.dest / exampleStr / "mill") - val zip = T.dest / s"$exampleStr.zip" - os.proc("zip", "-r", zip, exampleStr).call(cwd = T.dest) + os.copy(examplePath, Task.dest / exampleStr, createFolders = true) + os.write(Task.dest / exampleStr / ".mill-version", millLastTag()) + os.copy(bootstrapLauncher().path, Task.dest / exampleStr / "mill") + val zip = Task.dest / s"$exampleStr.zip" + os.proc("zip", "-r", zip, exampleStr).call(cwd = Task.dest) PathRef(zip) } } -def uploadToGithub(authKey: String) = T.command { +def uploadToGithub(authKey: String) = Task.Command { val vcsState = VcsVersion.vcsState() val label = vcsState.format() if (label != millVersion()) sys.error("Modified mill version detected, aborting upload") @@ -1030,8 +1030,8 @@ def validate(): Command[Unit] = { val tasks = resolveTasks("__.compile", "__.minaReportBinaryIssues") val sources = resolveTasks("__.sources") - T.command { - T.sequence(tasks)() + Task.Command { + Task.sequence(tasks)() mill.scalalib.scalafmt.ScalafmtModule.checkFormatAll(Tasks(sources))() build.docs.localPages() () diff --git a/contrib/scalapblib/readme.adoc b/contrib/scalapblib/readme.adoc index 6f1b0696e2b..bddf2e3aa5f 100644 --- a/contrib/scalapblib/readme.adoc +++ b/contrib/scalapblib/readme.adoc @@ -78,6 +78,6 @@ object example extends ScalaPBModule { def scalaVersion = "2.12.6" def scalaPBVersion = "0.7.4" override def scalaPBAdditionalArgs = - Seq(s"--zio_out=${T.dest.toIO.getCanonicalPath}") + Seq(s"--zio_out=${Task.dest.toIO.getCanonicalPath}") } ---- diff --git a/docs/modules/ROOT/images/basic/VisualizeCompiles.svg b/docs/modules/ROOT/images/basic/VisualizeCompiles.svg new file mode 100644 index 00000000000..0bcc404a14f --- /dev/null +++ b/docs/modules/ROOT/images/basic/VisualizeCompiles.svg @@ -0,0 +1,49 @@ + + +example1 + + + +bar.compile + +bar.compile + + + +bar.test.compile + +bar.test.compile + + + +bar.compile->bar.test.compile + + + + + +foo.compile + +foo.compile + + + +bar.compile->foo.compile + + + + + +foo.test.compile + +foo.test.compile + + + +foo.compile->foo.test.compile + + + + + diff --git a/docs/modules/ROOT/images/basic/VisualizeTestDeps.svg b/docs/modules/ROOT/images/basic/VisualizeTestDeps.svg new file mode 100644 index 00000000000..5bf2abaecde --- /dev/null +++ b/docs/modules/ROOT/images/basic/VisualizeTestDeps.svg @@ -0,0 +1,55 @@ + + +example1 + + + +baz.compile + +baz.compile + + + +baz.test.compile + +baz.test.compile + + + +baz.compile->baz.test.compile + + + + + +qux.compile + +qux.compile + + + +baz.compile->qux.compile + + + + + +qux.test.compile + +qux.test.compile + + + +baz.test.compile->qux.test.compile + + + + + +qux.compile->qux.test.compile + + + + + diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc index f0e9e09e4ae..6a85f69cef6 100644 --- a/docs/modules/ROOT/nav.adoc +++ b/docs/modules/ROOT/nav.adoc @@ -61,8 +61,7 @@ // either section above, it is probably an important enough topic it is worth // breaking out on its own .Extending Mill -* xref:extending/import-ivy.adoc[] -* xref:extending/using-plugins.adoc[] +* xref:extending/import-ivy-plugins.adoc[] * xref:extending/contrib-plugins.adoc[] // See also the list in Contrib_Plugins.adoc ** xref:contrib/artifactory.adoc[] diff --git a/docs/modules/ROOT/pages/depth/design-principles.adoc b/docs/modules/ROOT/pages/depth/design-principles.adoc index c3235f73169..721d71ae55d 100644 --- a/docs/modules/ROOT/pages/depth/design-principles.adoc +++ b/docs/modules/ROOT/pages/depth/design-principles.adoc @@ -66,9 +66,9 @@ is what allows us to be aggressive about caching and parallelizing the evaluation of build tasks during a build. Many kinds of build steps do require files on disk, and for that Mill provides -the `T.dest` folder. This is a folder on disk dedicated to each build task, +the `Task.dest` folder. This is a folder on disk dedicated to each build task, so that it can read and write things to it without worrying about conflicts -with other tasks that have their own `T.dest` folders. In effect, this makes +with other tasks that have their own `Task.dest` folders. In effect, this makes even file output "pure": we can know precisely where a task's output files live when we need to invalidate them, and it allows multiple tasks all reading and writing to the filesystem to do so safely even when in parallel. diff --git a/docs/modules/ROOT/pages/depth/evaluation-model.adoc b/docs/modules/ROOT/pages/depth/evaluation-model.adoc index aa877407500..fabdd3088aa 100644 --- a/docs/modules/ROOT/pages/depth/evaluation-model.adoc +++ b/docs/modules/ROOT/pages/depth/evaluation-model.adoc @@ -70,10 +70,10 @@ the overall workflow remains fast even for large projects: * ``Task``s are evaluated in dependency order - * xref:fundamentals/tasks.adoc#_targets[Target]s only re-evaluate if their input ``Task``s + * xref:fundamentals/tasks.adoc#_cached_tasks[Cached Task]s only re-evaluate if their input ``Task``s change. - * xref:fundamentals/tasks.adoc#_persistent_tasks[Task.Persistent]s preserve the `T.dest` folder on disk between runs, + * xref:fundamentals/tasks.adoc#_persistent_tasks[Task.Persistent]s preserve the `Task.dest` folder on disk between runs, allowing for finer-grained caching than Mill's default task-by-task caching and invalidation diff --git a/docs/modules/ROOT/pages/depth/large-builds.adoc b/docs/modules/ROOT/pages/depth/large-builds.adoc index 012e2fa0d88..a8d9f82bd69 100644 --- a/docs/modules/ROOT/pages/depth/large-builds.adoc +++ b/docs/modules/ROOT/pages/depth/large-builds.adoc @@ -1,6 +1,17 @@ = Structuring Large Builds :page-aliases: Structuring_Large_Builds.adoc +This section walks through Mill features and techniques used for managing large builds. +While Mill works great for small single-module projects, it is also able to work +effectively with large projects with hundreds of modules. Mill's own build for the +https://github.com/com-lihaoyi/mill[com-lihaoyi/mill] project has ~400 modules, and +other proprietary projects may have many more. + +Mill modules are cheap: having more modules does not significantly impact performance +or resource usage, so you are encouraged to break up your project into modules to manage +the layering of your codebase or benefit from parallelism. + + == Multi-file Builds include::partial$example/depth/large/10-multi-file-builds.adoc[] diff --git a/docs/modules/ROOT/pages/extending/contrib-plugins.adoc b/docs/modules/ROOT/pages/extending/contrib-plugins.adoc index 42ad5927491..ca920f8bb51 100644 --- a/docs/modules/ROOT/pages/extending/contrib-plugins.adoc +++ b/docs/modules/ROOT/pages/extending/contrib-plugins.adoc @@ -4,7 +4,7 @@ The ((plugins)) in this section are hosted in the Mill git tree and developed / maintained by the community. -For details about including plugins in your `build.mill` read xref:extending/using-plugins.adoc[Using Mill Plugins]. +For details about including plugins in your `build.mill` read xref:extending/import-ivy-plugins.adoc[Using Mill Plugins]. [CAUTION] -- @@ -28,6 +28,11 @@ import $ivy.`com.lihaoyi::mill-contrib-bloop:` -- +== Importing Contrib Modules + +include::partial$example/extending/imports/3-contrib-import.adoc[] + + == List of Contrib Plugins // See also the list in nav.adoc @@ -50,6 +55,3 @@ import $ivy.`com.lihaoyi::mill-contrib-bloop:` * xref:contrib/versionfile.adoc[] -== Importing Contrib Modules - -include::partial$example/extending/imports/3-contrib-import.adoc[] diff --git a/docs/modules/ROOT/pages/extending/using-plugins.adoc b/docs/modules/ROOT/pages/extending/import-ivy-plugins.adoc similarity index 56% rename from docs/modules/ROOT/pages/extending/using-plugins.adoc rename to docs/modules/ROOT/pages/extending/import-ivy-plugins.adoc index e335900dcbe..2f3254f5e3f 100644 --- a/docs/modules/ROOT/pages/extending/using-plugins.adoc +++ b/docs/modules/ROOT/pages/extending/import-ivy-plugins.adoc @@ -1,16 +1,40 @@ -= Using Plugins -:page-aliases: Using_Plugins.adoc -Mill plugins are ordinary jars and are loaded as any other external dependency with the xref:extending/import-ivy.adoc[`import $ivy` mechanism]. += Import Libraries and Plugins +:page-aliases: Import_File_And_Import_Ivy.adoc, Using_Plugins.adoc -There exist a large number of Mill plugins, Many of them are available on GitHub and via Maven Central. We also have a list of plugins, which is most likely not complete, but it might be a good start if you are looking for plugins: xref:Thirdparty_Plugins.adoc[]. +This page illustrates usage of `import $ivy`. +`import $ivy` lets you import JVM dependencies into your `build.mill`, so +you can use arbitrary third-party libraries at build-time. This makes +lets you perform computations at build-time rather than run-time, +speeding up your application start up. `import $ivy` can be used on any +JVM library on Maven Central. + + +== Importing Java Libraries + +include::partial$example/extending/imports/1-import-ivy.adoc[] + + +== Importing Scala Libraries + +include::partial$example/extending/imports/2-import-ivy-scala.adoc[] + +== Importing Plugins + +Mill plugins are ordinary JVM libraries jars and are loaded as any other external dependency with +the xref:extending/import-ivy-plugins.adoc[`import $ivy` mechanism]. + +There exist a large number of Mill plugins, Many of them are available on GitHub and via +Maven Central. We also have a list of plugins, which is most likely not complete, but it +might be a good start if you are looking for plugins: xref:Thirdparty_Plugins.adoc[]. Some plugin contributions are also hosted in Mill's own git tree as xref:Contrib_Plugins.adoc[]. Mill plugins are typically bound to a specific version range of Mill. -This is called the binary platform. -To ease the use of the correct versions and avoid runtime issues (caused by binary incompatible plugins, which are hard to debug) you can apply one of the following techniques: +This is called the binary platform. To ease the use of the correct versions and avoid runtime +issues (caused by binary incompatible plugins, which are hard to debug) you can apply one of the +following techniques: -== Use the specific Mill Binary Platform notation +=== Use the specific Mill Binary Platform notation [source,scala] ---- @@ -33,7 +57,7 @@ import $ivy.`:::_mill$MILL_BIN_PLATFORM:` ---- -== Use special placeholders in your `import $ivy` +=== Use special placeholders in your `import $ivy` `$MILL_VERSION` :: + diff --git a/docs/modules/ROOT/pages/extending/import-ivy.adoc b/docs/modules/ROOT/pages/extending/import-ivy.adoc deleted file mode 100644 index 44746c7507f..00000000000 --- a/docs/modules/ROOT/pages/extending/import-ivy.adoc +++ /dev/null @@ -1,19 +0,0 @@ -= import $ivy -:page-aliases: Import_File_And_Import_Ivy.adoc - -// This page illustrates usage of `import $ivy`. -// `import $ivy` lets you import JVM dependencies into your `build.mill`, so -// you can use arbitrary third-party libraries at build-time. This makes -// lets you perform computations at build-time rather than run-time, -// speeding up your application start up. `import $ivy` can be used on any -// JVM library on Maven Central. -// - -== Importing Java Libraries - -include::partial$example/extending/imports/1-import-ivy.adoc[] - - -== Importing Scala Libraries - -include::partial$example/extending/imports/2-import-ivy-scala.adoc[] \ No newline at end of file diff --git a/docs/modules/ROOT/pages/extending/meta-build.adoc b/docs/modules/ROOT/pages/extending/meta-build.adoc index 8cf595d5697..77d26f0c43a 100644 --- a/docs/modules/ROOT/pages/extending/meta-build.adoc +++ b/docs/modules/ROOT/pages/extending/meta-build.adoc @@ -2,15 +2,19 @@ :page-aliases: The_Mill_Meta_Build.adoc The meta-build manages the compilation of the `build.mill`. -If you don't configure it explicitly, a built-in synthetic meta-build is used. +Customizing the meta-build gives you greater control over how exactly your +`build.mill` evaluates. + To customize it, you need to explicitly enable it with `import $meta._`. Once enabled, the meta-build lives in the `mill-build/` directory. It needs to contain a top-level module of type `MillBuildRootModule`. +If you don't configure it explicitly, a built-in synthetic meta-build is used. Meta-builds are recursive, which means, it can itself have a nested meta-builds, and so on. -To run a task on a meta-build, you specifying the `--meta-level` option to select the meta-build level. +To run a task on a meta-build, you specify the `--meta-level` option to select +the meta-build level. == Autoformatting the `build.mill` @@ -20,7 +24,7 @@ You only need a `.scalafmt.conf` config file which at least needs configure the .Run Scalafmt on the `build.mill` (and potentially included files) ---- -$ mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/reformatAll sources +$ mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/ ---- * `--meta-level 1` selects the first meta-build. Without any customization, this is the only built-in meta-build. diff --git a/docs/modules/ROOT/pages/extending/thirdparty-plugins.adoc b/docs/modules/ROOT/pages/extending/thirdparty-plugins.adoc index 1cb7e1c097a..3684e8c70fd 100644 --- a/docs/modules/ROOT/pages/extending/thirdparty-plugins.adoc +++ b/docs/modules/ROOT/pages/extending/thirdparty-plugins.adoc @@ -5,7 +5,7 @@ The Plugins in this section are developed/maintained outside the mill git tree. This list is most likely not complete. If you wrote a Mill plugin or find that one is missing in this list, please open a {mill-github-url}/pulls[pull request] and add that plugin with a short description (in alphabetical order). -For details about including plugins in your `build.mill` read xref:extending/using-plugins.adoc[Using Mill Plugins]. +For details about including plugins in your `build.mill` read xref:extending/import-ivy-plugins.adoc[Using Mill Plugins]. CAUTION: Besides the documentation provided here, we urge you to consult the respective linked plugin documentation pages. The usage examples given here are most probably incomplete and sometimes outdated! diff --git a/docs/modules/ROOT/pages/fundamentals/cross-builds.adoc b/docs/modules/ROOT/pages/fundamentals/cross-builds.adoc index 9779f86bec9..5f4bf60f67c 100644 --- a/docs/modules/ROOT/pages/fundamentals/cross-builds.adoc +++ b/docs/modules/ROOT/pages/fundamentals/cross-builds.adoc @@ -11,7 +11,7 @@ config and building it across a variety of source folders. include::partial$example/fundamentals/cross/1-simple.adoc[] -== Default Cross Modules +== Cross Modules Defaults include::partial$example/fundamentals/cross/11-default-cross-module.adoc[] diff --git a/docs/modules/ROOT/pages/fundamentals/modules.adoc b/docs/modules/ROOT/pages/fundamentals/modules.adoc index 86a811eed4e..3036bcb3b74 100644 --- a/docs/modules/ROOT/pages/fundamentals/modules.adoc +++ b/docs/modules/ROOT/pages/fundamentals/modules.adoc @@ -1,5 +1,6 @@ = Modules :page-aliases: Modules.adoc + `mill.Module` serves two main purposes: 1. As ``object``s, they serve as namespaces that let you group related ``Task``s diff --git a/docs/modules/ROOT/pages/fundamentals/out-dir.adoc b/docs/modules/ROOT/pages/fundamentals/out-dir.adoc index a03d5da57e2..271db12464e 100644 --- a/docs/modules/ROOT/pages/fundamentals/out-dir.adoc +++ b/docs/modules/ROOT/pages/fundamentals/out-dir.adoc @@ -52,7 +52,7 @@ out/ ---- <1> The `main` directory contains all files associated with tasks and submodules of the `main` module. -<2> The `compile` task has tried to access its scratch space via `T.dest`. Here you will find the actual compile results. +<2> The `compile` task has tried to access its scratch space via `Task.dest`. Here you will find the actual compile results. <3> Two tasks printed something out while they ran. You can find these outputs in the `*.log` files. <4> Three tasks are overridden but re-use the result of their `super`-tasks in some way. You can find these result under the `*.super/` path. @@ -75,7 +75,7 @@ by `foo.json` via `PathRef` references. `foo.dest/`:: optional, a path for the `Task` to use either as a scratch space, or to place generated files that are returned using `PathRef` references. -A `Task` should only output files within its own given `foo.dest/` folder (available as `T.dest`) to avoid +A `Task` should only output files within its own given `foo.dest/` folder (available as `Task.dest`) to avoid conflicting with another `Task`, but can name files within `foo.dest/` arbitrarily. `foo.log`:: diff --git a/docs/modules/ROOT/pages/fundamentals/tasks.adoc b/docs/modules/ROOT/pages/fundamentals/tasks.adoc index e2c8702282f..4e9792396aa 100644 --- a/docs/modules/ROOT/pages/fundamentals/tasks.adoc +++ b/docs/modules/ROOT/pages/fundamentals/tasks.adoc @@ -3,7 +3,7 @@ One of Mill's core abstractions is its _Task Graph_: this is how Mill defines, orders and caches work it needs to do, and exists independently of any support -for building Scala. +for building Java, Kotlin, or Scala. Mill task graphs are primarily built using methods and macros defined on `mill.define.Task`, aliased as `T` for conciseness: @@ -17,7 +17,7 @@ different Task types: [cols="<,<,<,<,<,<,<"] |=== -| |Target |Command |Source/Input |Anonymous Task |Persistent Target |Worker +| |Target |Command |Source/Input |Anonymous Task |Persistent Task |Worker |Cached to Disk |X | | | |X | |JSON Writable |X |X |X| |X | diff --git a/docs/modules/ROOT/pages/javalib/android-examples.adoc b/docs/modules/ROOT/pages/javalib/android-examples.adoc index 28a26118678..2b3e71c40a2 100644 --- a/docs/modules/ROOT/pages/javalib/android-examples.adoc +++ b/docs/modules/ROOT/pages/javalib/android-examples.adoc @@ -15,9 +15,9 @@ a starting point for further experimentation and development. These are the main Mill Modules that are relevant for building Android apps: -* {mill-doc-url}/api/latest/mill/scalalib/AndroidSdkModule.html[`mill.scalalib.AndroidSdkModule`]: Handles Android SDK management and tools. +* {mill-doc-url}/api/latest/mill/javalib/android/AndroidSdkModule.html[`mill.javalib.android.AndroidSdkModule`]: Handles Android SDK management and tools. * {mill-doc-url}/api/latest/mill/javalib/android/AndroidAppModule.html[`mill.javalib.android.AndroidAppModule`]: Provides a framework for building Android applications. -* {mill-doc-url}/api/latest/mill/scalalib/JavaModule.html[`mill.scalalib.JavaModule`]: General Java build tasks like compiling Java code and creating JAR files. +* {mill-doc-url}/api/latest/mill/scalalib/JavaModule.html[`mill.javalib.JavaModule`]: General Java build tasks like compiling Java code and creating JAR files. == Simple Android Hello World Application diff --git a/docs/modules/ROOT/pages/javalib/dependencies.adoc b/docs/modules/ROOT/pages/javalib/dependencies.adoc index fbcb34b149c..70f86237e2f 100644 --- a/docs/modules/ROOT/pages/javalib/dependencies.adoc +++ b/docs/modules/ROOT/pages/javalib/dependencies.adoc @@ -22,9 +22,9 @@ include::partial$example/javalib/dependencies/2-run-compile-deps.adoc[] include::partial$example/javalib/dependencies/3-unmanaged-jars.adoc[] -== Downloading Non-Maven Jars +== Downloading Unmanaged Jars -include::partial$example/javalib/dependencies/4-downloading-non-maven-jars.adoc[] +include::partial$example/javalib/dependencies/4-downloading-unmanaged-jars.adoc[] == Repository Config diff --git a/docs/modules/ROOT/pages/javalib/module-config.adoc b/docs/modules/ROOT/pages/javalib/module-config.adoc index fbd934a31d4..d5bf9df68a5 100644 --- a/docs/modules/ROOT/pages/javalib/module-config.adoc +++ b/docs/modules/ROOT/pages/javalib/module-config.adoc @@ -7,6 +7,9 @@ gtag('config', 'AW-16649289906'); ++++ +:language: Java +:language-small: java + This page goes into more detail about the various configuration options for `JavaModule`. @@ -18,15 +21,6 @@ Many of the APIs covered here are listed in the API documentation: include::partial$example/javalib/module/1-common-config.adoc[] -== Custom Tasks - -include::partial$example/javalib/module/2-custom-tasks.adoc[] - -== Overriding Tasks - -include::partial$example/javalib/module/3-override-tasks.adoc[] - - == Compilation & Execution Flags include::partial$example/javalib/module/4-compilation-execution-flags.adoc[] @@ -55,8 +49,14 @@ include::partial$example/javalib/module/11-main-class.adoc[] include::partial$example/javalib/module/13-assembly-config.adoc[] +== Custom Tasks + +include::partial$example/javalib/module/2-custom-tasks.adoc[] + +== Overriding Tasks + +include::partial$example/javalib/module/3-override-tasks.adoc[] == Native C Code with JNI include::partial$example/javalib/module/15-jni.adoc[] - diff --git a/docs/modules/ROOT/pages/kotlinlib/android-examples.adoc b/docs/modules/ROOT/pages/kotlinlib/android-examples.adoc index 3ca6163e191..e45ec649f12 100644 --- a/docs/modules/ROOT/pages/kotlinlib/android-examples.adoc +++ b/docs/modules/ROOT/pages/kotlinlib/android-examples.adoc @@ -15,7 +15,7 @@ a starting point for further experimentation and development. These are the main Mill Modules that are relevant for building Android apps: -* {mill-doc-url}/api/latest/mill/scalalib/AndroidSdkModule.html[`mill.scalalib.AndroidSdkModule`]: Handles Android SDK management and tools. +* {mill-doc-url}/api/latest/mill/javalib/android/AndroidSdkModule.html[`mill.javalib.android.AndroidSdkModule`]: Handles Android SDK management and tools. * {mill-doc-url}/api/latest/mill/kotlinlib/android/AndroidAppKotlinModule.html[`mill.kotlinlib.android.AndroidAppKotlinModule`]: Provides a framework for building Android applications. * {mill-doc-url}/api/latest/mill/kotlinlib/KotlinModule.html[`mill.kotlinlib.KotlinModule`]: General Kotlin build tasks like compiling Kotlin code and creating JAR files. diff --git a/docs/modules/ROOT/pages/kotlinlib/dependencies.adoc b/docs/modules/ROOT/pages/kotlinlib/dependencies.adoc index 7660b5aed8b..fc4440e85fd 100644 --- a/docs/modules/ROOT/pages/kotlinlib/dependencies.adoc +++ b/docs/modules/ROOT/pages/kotlinlib/dependencies.adoc @@ -27,9 +27,9 @@ include::partial$example/kotlinlib/dependencies/2-run-compile-deps.adoc[] include::partial$example/kotlinlib/dependencies/3-unmanaged-jars.adoc[] -== Downloading Non-Maven Jars +== Downloading Unmanaged Jars -include::partial$example/kotlinlib/dependencies/4-downloading-non-maven-jars.adoc[] +include::partial$example/kotlinlib/dependencies/4-downloading-unmanaged-jars.adoc[] == Repository Config diff --git a/docs/modules/ROOT/pages/kotlinlib/intro.adoc b/docs/modules/ROOT/pages/kotlinlib/intro.adoc index 28426f07056..e956a775784 100644 --- a/docs/modules/ROOT/pages/kotlinlib/intro.adoc +++ b/docs/modules/ROOT/pages/kotlinlib/intro.adoc @@ -25,6 +25,13 @@ gtag('config', 'AW-16649289906'); :language-small: kotlin :language-ext: kt +NOTE: *Kotlin support in Mill is experimental*! A lot of stuff works, which is +documented under this section, but a lot of stuff doesn't. In particular, +support for Android, KotlinJS and Kotlin-Multi-Platform is still in its infancy. +The API is not yet stable and may evolve. Try it out but please be aware of its +limitations! + + include::partial$Intro_to_Mill_Header.adoc[] diff --git a/docs/modules/ROOT/pages/kotlinlib/module-config.adoc b/docs/modules/ROOT/pages/kotlinlib/module-config.adoc index 2b10af5a3bf..5c858ec0369 100644 --- a/docs/modules/ROOT/pages/kotlinlib/module-config.adoc +++ b/docs/modules/ROOT/pages/kotlinlib/module-config.adoc @@ -7,6 +7,9 @@ gtag('config', 'AW-16649289906'); ++++ +:language: Kotlin +:language-small: kotlin + This page goes into more detail about the various configuration options for `KotlinModule`. @@ -20,14 +23,6 @@ Many of the APIs covered here are listed in the API documentation: include::partial$example/kotlinlib/module/1-common-config.adoc[] -== Custom Tasks - -include::partial$example/kotlinlib/module/2-custom-tasks.adoc[] - -== Overriding Tasks - -include::partial$example/kotlinlib/module/3-override-tasks.adoc[] - == Compilation & Execution Flags @@ -41,7 +36,7 @@ include::partial$example/kotlinlib/module/7-resources.adoc[] include::partial$example/kotlinlib/module/8-kotlin-compiler-plugins.adoc[] -== Javadoc Config +== Doc-Jar Generation include::partial$example/kotlinlib/module/9-docjar.adoc[] @@ -54,7 +49,16 @@ include::partial$example/kotlinlib/module/11-main-class.adoc[] include::partial$example/kotlinlib/module/13-assembly-config.adoc[] + +== Custom Tasks + +include::partial$example/kotlinlib/module/2-custom-tasks.adoc[] + +== Overriding Tasks + +include::partial$example/kotlinlib/module/3-override-tasks.adoc[] + + == Native C Code with JNI include::partial$example/kotlinlib/module/15-jni.adoc[] - diff --git a/docs/modules/ROOT/pages/scalalib/dependencies.adoc b/docs/modules/ROOT/pages/scalalib/dependencies.adoc index ec754cab751..033e0976ff0 100644 --- a/docs/modules/ROOT/pages/scalalib/dependencies.adoc +++ b/docs/modules/ROOT/pages/scalalib/dependencies.adoc @@ -26,9 +26,9 @@ include::partial$example/scalalib/dependencies/2-run-compile-deps.adoc[] include::partial$example/scalalib/dependencies/3-unmanaged-jars.adoc[] -== Downloading Non-Maven Jars +== Downloading Unmanaged Jars -include::partial$example/scalalib/dependencies/4-downloading-non-maven-jars.adoc[] +include::partial$example/scalalib/dependencies/4-downloading-unmanaged-jars.adoc[] == Repository Config diff --git a/docs/modules/ROOT/pages/scalalib/intro.adoc b/docs/modules/ROOT/pages/scalalib/intro.adoc index d0706a2c0c2..0e807800568 100644 --- a/docs/modules/ROOT/pages/scalalib/intro.adoc +++ b/docs/modules/ROOT/pages/scalalib/intro.adoc @@ -41,7 +41,7 @@ Compared to SBT: * **Mill makes customizing the build yourself much easier**: most of what build tools do work with files and call subprocesses, and Mill makes doing that yourself easy. This means you can always make your Mill build do exactly what you want, and are not - beholden to third-party plugins that may not exist, be well maintained, or interact well + beholden to third-party plugins that may not meet your exact needs or interact well with each other. * **Mill is much more performant**: SBT has enough overhead that even a dozen diff --git a/docs/modules/ROOT/pages/scalalib/module-config.adoc b/docs/modules/ROOT/pages/scalalib/module-config.adoc index 73d1f89c79d..936169c0313 100644 --- a/docs/modules/ROOT/pages/scalalib/module-config.adoc +++ b/docs/modules/ROOT/pages/scalalib/module-config.adoc @@ -7,6 +7,8 @@ gtag('config', 'AW-16649289906'); ++++ +:language: Scala +:language-small: scala This page goes into more detail about the various configuration options for `ScalaModule`. @@ -20,15 +22,6 @@ Many of the APIs covered here are listed in the Scaladoc: include::partial$example/scalalib/module/1-common-config.adoc[] -== Custom Tasks - -include::partial$example/scalalib/module/2-custom-tasks.adoc[] - -== Overriding Tasks - -include::partial$example/scalalib/module/3-override-tasks.adoc[] - - == Compilation & Execution Flags include::partial$example/scalalib/module/4-compilation-execution-flags.adoc[] @@ -57,6 +50,14 @@ include::partial$example/scalalib/module/13-assembly-config.adoc[] include::partial$example/scalalib/module/15-unidoc.adoc[] +== Custom Tasks + +include::partial$example/scalalib/module/2-custom-tasks.adoc[] + +== Overriding Tasks + +include::partial$example/scalalib/module/3-override-tasks.adoc[] + == Using the Ammonite Repl / Scala console diff --git a/docs/modules/ROOT/partials/Intro_Maven_Gradle_Comparison.adoc b/docs/modules/ROOT/partials/Intro_Maven_Gradle_Comparison.adoc index 464f3f43778..88c63934edb 100644 --- a/docs/modules/ROOT/partials/Intro_Maven_Gradle_Comparison.adoc +++ b/docs/modules/ROOT/partials/Intro_Maven_Gradle_Comparison.adoc @@ -19,7 +19,7 @@ xref:comparisons/maven.adoc[Compared to Maven]: grow beyond just compiling a single language: needing custom code generation, linting workflows, tool integrations, output artifacts, or support for additional languages. Mill makes doing this yourself easy, so you are not beholden - to third-party plugins that may not exist or interact well with each other. + to third-party plugins that may not meet your exact needs or interact well with each other. * **Mill automatically caches and parallelizes your build**: Not just the built-in tasks that Mill ships with, but also any custom tasks or modules. diff --git a/docs/modules/ROOT/partials/Intro_to_Mill_BlogVideo.adoc b/docs/modules/ROOT/partials/Intro_to_Mill_BlogVideo.adoc index 796dc4a51b5..511d0faee47 100644 --- a/docs/modules/ROOT/partials/Intro_to_Mill_BlogVideo.adoc +++ b/docs/modules/ROOT/partials/Intro_to_Mill_BlogVideo.adoc @@ -3,7 +3,7 @@ If you're interested in the fundamental ideas behind Mill, rather than the user-facing benefits discussed above, check out the section on Mill Design Principles: -- <> +- xref:depth/design-principles.adoc[Mill Design Principles] The rest of this page contains a quick introduction to getting start with using Mill to build a simple {language} program. The other pages of this doc-site go into diff --git a/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc b/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc index 196f99f731b..bc2e131ff22 100644 --- a/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc +++ b/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc @@ -50,7 +50,7 @@ even as it grows from a small project to a large codebase or monorepo with hundr * *Flexibility*: Mill's custom tasks and modules allow anything from xref:fundamentals/tasks.adoc#primitive-tasks[adding simple build steps], up to xref:fundamentals/modules.adoc#_use_case_diy_java_modules[entire language toolchains]. - You can xref:extending/import-ivy.adoc[import any JVM library as part of your build], + You can xref:extending/import-ivy-plugins.adoc[import any JVM library as part of your build], use Mill's rich ecosystem of xref:extending/thirdparty-plugins.adoc[Third-Party Mill Plugins], - or xref:extending/writing-plugins.adoc[write plugins yourself] and publish them - on Maven Central for others to use. + or xref:extending/writing-plugins.adoc[write plugins yourself] and + xref:extending/writing-plugins.adoc#_publishing[publish them on Maven Central] for others to use. diff --git a/docs/package.mill b/docs/package.mill index b870a130a85..3768b9788fd 100644 --- a/docs/package.mill +++ b/docs/package.mill @@ -119,7 +119,9 @@ object `package` extends RootModule { if (inputs.isEmpty) output((p, i)) = graphvizLines.mkString("\n") else { outputLines.append("++++") + outputLines.append("
") outputLines.append(inputs((p, i))) + outputLines.append("
") outputLines.append("++++") } diff --git a/example/depth/large/10-multi-file-builds/build.mill b/example/depth/large/10-multi-file-builds/build.mill index a20bf39d3b5..04fb82a1cf0 100644 --- a/example/depth/large/10-multi-file-builds/build.mill +++ b/example/depth/large/10-multi-file-builds/build.mill @@ -1,3 +1,12 @@ +// Mill allows you to break up your `build.mill` file into smaller files by defining the +// build-related logic for any particular subfolder as a `package.mill` file in that subfolder. +// This can be very useful to keep large Mill builds maintainable, as each folder's build logic +// gets co-located with the files that need to be built, and speeds up compilation of the +// build logic since each `build.mill` or `package.mill` file can be compiled independently when +// it is modified without re-compiling all the others. +// +// Usage of sub-folder `package.mill` files is enabled by the magic import `import $packages._` + package build import $packages._ @@ -11,14 +20,6 @@ trait MyModule extends ScalaModule { /** See Also: bar/qux/package.mill */ -// Mill allows you to break up your `build.mill` file into smaller files by defining the -// build-related logic for any particular subfolder as a `package.mill` file in that subfolder. -// This can be very useful to keep large Mill builds maintainable, as each folder's build logic -// gets co-located with the files that need to be built, and speeds up compilation of the -// build logic since each `build.mill` or `package.mill` file can be compiled independently when -// it is modified without re-compiling all the others. -// -// Usage of sub-folder `package.mill` files is enabled by the magic import `import $packages._` // // In this example, the root `build.mill` only contains the `trait MyModule`, but it is // `foo/package.mill` and `bar/qux/package.mill` that define modules using it. The modules diff --git a/example/depth/large/11-helper-files/build.mill b/example/depth/large/11-helper-files/build.mill index 374a8764007..58de0e8eb8b 100644 --- a/example/depth/large/11-helper-files/build.mill +++ b/example/depth/large/11-helper-files/build.mill @@ -1,3 +1,8 @@ + +// Apart from having `package` files in subfolders to define modules, Mill +// also allows you to have helper code in any `*.mill` file in the same folder +// as your `build.mill` or a `package.mill`. + package build import $packages._ import mill._, scalalib._ @@ -16,9 +21,6 @@ object `package` extends RootModule with MyModule{ /** See Also: foo/versions.mill */ -// Apart from having `package` files in subfolders to define modules, Mill -// also allows you to have helper code in any `*.mill` file in the same folder -// as your `build.mill` or a `package.mill`. // // Different helper scripts and ``build.mill``/``package`` files can all refer to // each other using the `build` object, which marks the root object of your build. diff --git a/example/depth/large/12-helper-files-sc/build.sc b/example/depth/large/12-helper-files-sc/build.sc index 836719c7d71..2dc4f1881d4 100644 --- a/example/depth/large/12-helper-files-sc/build.sc +++ b/example/depth/large/12-helper-files-sc/build.sc @@ -1,24 +1,28 @@ +// To ease the migration from Mill 0.11.x, the older `.sc` file extension is also supported +// for Mill build files, and the `package` declaration is optional in such files. Note that +// this means that IDE support using `.sc` files will not be as good as IDE support using the +// current `.mill` extension with `package` declaration, so you should use `.mill` whenever +// possible + + import mill._, scalalib._ import $packages._ import $file.foo.versions import $file.util, util.MyModule object `package` extends RootModule with MyModule{ - def forkEnv = Map( - "MY_SCALA_VERSION" -> build.scalaVersion(), - "MY_PROJECT_VERSION" -> versions.myProjectVersion, - ) + def forkEnv = T{ + Map( + "MY_SCALA_VERSION" -> build.scalaVersion(), + "MY_PROJECT_VERSION" -> versions.myProjectVersion, + ) + } } /** See Also: util.sc */ /** See Also: foo/package.sc */ /** See Also: foo/versions.sc */ -// To ease the migration from Mill 0.11.x, the older `.sc` file extension is also supported -// for Mill build files, and the `package` declaration is optional in such files. Note that -// this means that IDE support using `.sc` files will not be as good as IDE support using the -// current `.mill` extension with `package` declaration, so you should use `.mill` whenever -// possible /** Usage diff --git a/example/depth/sandbox/3-breaking/build.mill b/example/depth/sandbox/3-breaking/build.mill index b1fa4c1b216..3d7c0a2739f 100644 --- a/example/depth/sandbox/3-breaking/build.mill +++ b/example/depth/sandbox/3-breaking/build.mill @@ -12,10 +12,10 @@ package build import mill._, javalib._ -def tWorkspaceTask = Task { println(Task.workspace) } +def myTask = Task { println(Task.workspace) } /** Usage -> ./mill tWorkspaceTask +> ./mill myTask */ // Whereas `MILL_WORKSPACE_ROOT` as well as in tests, which can access the diff --git a/example/extending/imports/1-import-ivy/build.mill b/example/extending/imports/1-import-ivy/build.mill index f93ad19e09b..c9c2095b710 100644 --- a/example/extending/imports/1-import-ivy/build.mill +++ b/example/extending/imports/1-import-ivy/build.mill @@ -27,7 +27,7 @@ object foo extends JavaModule { // This is a toy example: we generate a resource `snippet.txt` containing // `

hello

world

` that the application can read at runtime. // However, it demonstrates how you can easily move logic from application code at runtime -// to build logic at build time, while using the same set of JVM libraries and packages +// to build logic at build time, while using the same set of Java libraries and packages // you are already familiar with. This makes it easy to pre-compute things at build time // to reduce runtime latency or application startup times. // diff --git a/example/extending/imports/3-contrib-import/build.mill b/example/extending/imports/3-contrib-import/build.mill index eeb326c7b77..a847c3d8816 100644 --- a/example/extending/imports/3-contrib-import/build.mill +++ b/example/extending/imports/3-contrib-import/build.mill @@ -24,3 +24,13 @@ foo.BuildInfo.scalaVersion: 2.13.10 */ +// Contrib modules halfway between builtin Mill modules and +// xref:extending/thirdparty-plugins.adoc[Third-party Plugins]: +// +// * Like builtin modules, contrib modules are tested and released as part of +// Mill's own CI process, ensuring there is always a version of the plugin +// compatible with any Mill version +// +// * Like third-party plugins, contrib modules are submitted by third-parties, +// and do now maintain the same binary compatibility guarantees of Mill's +// builtin comdules \ No newline at end of file diff --git a/example/extending/metabuild/3-autoformatting/.scalafmt.conf b/example/extending/metabuild/3-autoformatting/.scalafmt.conf new file mode 100644 index 00000000000..5cfdd6768ce --- /dev/null +++ b/example/extending/metabuild/3-autoformatting/.scalafmt.conf @@ -0,0 +1,4 @@ +version = "3.8.4-RC1" + +runner.dialect = scala213 + diff --git a/example/extending/metabuild/3-autoformatting/build.mill b/example/extending/metabuild/3-autoformatting/build.mill new file mode 100644 index 00000000000..c42a1b0df9c --- /dev/null +++ b/example/extending/metabuild/3-autoformatting/build.mill @@ -0,0 +1,31 @@ +// As an example of running a task on the meta-build, you can format the `build.mill` with Scalafmt. +// Everything is already provided by Mill. +// You only need a `.scalafmt.conf` config file which at least needs configure the Scalafmt version. + +package build +import mill._ + +object foo extends Module {def task=Task{"2.13.4"}} + +/** See Also: .scalafmt.conf */ +/** Usage + +> cat build.mill # build.mill is initially poorly formatted +object foo extends Module {def task=Task{"2.13.4"}} +... + +> mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/ + +> cat build.mill # build.mill is now well formatted +object foo extends Module { def task = Task { "2.13.4" } } +... +*/ + +// +// * `--meta-level 1` selects the first meta-build. Without any customization, this is +// the only built-in meta-build. +// * `mill.scalalib.scalafmt.ScalafmtModule/reformatAll` is a generic task to format scala +// source files with Scalafmt. It requires the tasks that refer to the source files as argument +// * `sources` this selects the `sources` tasks of the meta-build, which at least contains +// the `build.mill`. + diff --git a/example/extending/plugins/7-writing-mill-plugins/build.mill b/example/extending/plugins/7-writing-mill-plugins/build.mill index 451597eb0f0..c18ba92bb62 100644 --- a/example/extending/plugins/7-writing-mill-plugins/build.mill +++ b/example/extending/plugins/7-writing-mill-plugins/build.mill @@ -1,6 +1,6 @@ // This example demonstrates how to write and test Mill plugin, and publish it to // Sonatype's Maven Central so it can be used by other developers over the internet -// via xref:extending/import-ivy.adoc[import $ivy]. +// via xref:extending/import-ivy-plugins.adoc[import $ivy]. // == Project Configuration package build @@ -10,13 +10,18 @@ import mill.main.BuildInfo.millVersion object myplugin extends ScalaModule with PublishModule { def scalaVersion = "2.13.8" + // Set the `platformSuffix` so the name indicates what Mill version it is compiled for + def platformSuffix = "_mill" + mill.main.BuildInfo.millBinPlatform + + // Depend on `mill-dist` so we can compile against Mill APIs def ivyDeps = Agg(ivy"com.lihaoyi:mill-dist:$millVersion") - // Testing Config + // Testing Config, with necessary setup for unit/integration/example tests object test extends ScalaTests with TestModule.Utest{ def ivyDeps = Agg(ivy"com.lihaoyi::mill-testkit:$millVersion") def forkEnv = Map("MILL_EXECUTABLE_PATH" -> millExecutable.assembly().path.toString) + // Create a Mill executable configured for testing our plugin object millExecutable extends JavaModule{ def ivyDeps = Agg(ivy"com.lihaoyi:mill-dist:$millVersion") def mainClass = Some("mill.runner.client.MillClientMain") @@ -51,6 +56,9 @@ object myplugin extends ScalaModule with PublishModule { // and to configure it for publishing to Maven Central via `PublishModule`. // It looks like any other Scala project, except for a few things to take note: // +// * We set the `platformSuffix` to indicate which Mill binary API version +// they are compiled against (to ensure that improper usage raises an easy to understand +// error) // * A dependency on `com.lihaoyi:mill-dist:$millVersion` // * A test dependency on `com.lihaoyi::mill-testkit:$millVersion` // * An `object millExecutable` that adds some resources to the published `mill-dist` @@ -60,6 +68,10 @@ object myplugin extends ScalaModule with PublishModule { // == Plugin Implementation +// Although Mill plugins can contain arbitrary code, the most common +// way that plugins interact with your project is by providing ``trait``s for +// your modules to inherit from. +// // Like any other `trait`, a Mill plugin's traits modules allow you to: // // * Add additional tasks to an existing module @@ -184,12 +196,13 @@ compiling 1 Scala source... > sed -i.bak 's/0.0.1/0.0.2/g' build.mill > ./mill myplugin.publishLocal -Publishing Artifact(com.lihaoyi,myplugin_2.13,0.0.2) to ivy repo... +Publishing Artifact(com.lihaoyi,myplugin_mill0.11_2.13,0.0.2) to ivy repo... */ // Mill plugins are JVM libraries like any other library written in Java or Scala. Thus they // are published the same way: by extending `PublishModule` and defining the module's `publishVersion` -// and `pomSettings`. Once done, you can publish the plugin locally via `publishLocal` below, -// or to Maven Central via `mill.scalalib.public.PublishModule/`for other developers to -// use. +// and `pomSettings`. Once done, you can publish the plugin locally via `publishLocal`, +// or to Maven Central via `mill.scalalib.public.PublishModule/` for other developers to +// use. For more details on publishing Mill projects, see the documentation for +// xref:scalalib/publishing.adoc[Publishing Scala Projects] diff --git a/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/example-test-project/build.mill b/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/example-test-project/build.mill index 5439fd2a676..0acaea4ee0e 100644 --- a/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/example-test-project/build.mill +++ b/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/example-test-project/build.mill @@ -1,5 +1,5 @@ package build -import $ivy.`com.lihaoyi::myplugin:0.0.1` +import $ivy.`com.lihaoyi::myplugin::0.0.1` import mill._, myplugin._ object `package` extends RootModule with LineCountJavaModule{ diff --git a/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/integration-test-project/build.mill b/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/integration-test-project/build.mill index 089665c7193..0c61e9288a2 100644 --- a/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/integration-test-project/build.mill +++ b/example/extending/plugins/7-writing-mill-plugins/myplugin/test/resources/integration-test-project/build.mill @@ -1,5 +1,5 @@ package build -import $ivy.`com.lihaoyi::myplugin:0.0.1` +import $ivy.`com.lihaoyi::myplugin::0.0.1` import mill._, myplugin._ object `package` extends RootModule with LineCountJavaModule{ diff --git a/example/fundamentals/cross/10-static-blog/build.mill b/example/fundamentals/cross/10-static-blog/build.mill index aeaf7dc900b..c32ac8a8cf1 100644 --- a/example/fundamentals/cross/10-static-blog/build.mill +++ b/example/fundamentals/cross/10-static-blog/build.mill @@ -1,6 +1,6 @@ // The following example demonstrates a use case: using cross modules to -// turn files on disk into blog posts. To begin with, we import two third-party -// libraries - Commonmark and Scalatags - to deal with Markdown parsing and +// turn files on disk into blog posts. To begin with, we xref:extending/import-ivy-plugins.adoc[import $ivy] +// two third-party libraries - Commonmark and Scalatags - to deal with Markdown parsing and // HTML generation respectively: package build import $ivy.`com.lihaoyi::scalatags:0.12.0`, scalatags.Text.all._ diff --git a/example/fundamentals/cross/11-default-cross-module/build.mill b/example/fundamentals/cross/11-default-cross-module/build.mill index f0d678bfb90..1457be69583 100644 --- a/example/fundamentals/cross/11-default-cross-module/build.mill +++ b/example/fundamentals/cross/11-default-cross-module/build.mill @@ -13,17 +13,18 @@ object bar extends Cross[FooModule]("2.10", "2.11", "2.12") { // For convenience, you can omit the selector for the default cross segment. -// By default, this is the first cross value specified. +// By default, this is the first cross value specified, but you can override +// it by specifying `def defaultCrossSegments` /** Usage -> mill show foo[2.10].suffix +> mill show foo[2.10].suffix # explicit cross value given "_2.10" -> mill show foo[].suffix +> mill show foo[].suffix # no cross value given, defaults to first cross value "_2.10" -> mill show bar[].suffix +> mill show bar[].suffix # no cross value given, defaults to overriden `defaultCrossSegments` "_2.12" */ diff --git a/example/fundamentals/cross/3-outside-dependency/build.mill b/example/fundamentals/cross/3-outside-dependency/build.mill index 7da447d9d18..9ed5d524274 100644 --- a/example/fundamentals/cross/3-outside-dependency/build.mill +++ b/example/fundamentals/cross/3-outside-dependency/build.mill @@ -1,4 +1,6 @@ -// You can refer to tasks defined in cross-modules as follows: +// You can refer to tasks defined in cross-modules using the `foo("2.10")` syntax, +// as given below: + package build import mill._ diff --git a/example/fundamentals/cross/4-cross-dependencies/build.mill b/example/fundamentals/cross/4-cross-dependencies/build.mill index fc68fa04087..16a12a43d2c 100644 --- a/example/fundamentals/cross/4-cross-dependencies/build.mill +++ b/example/fundamentals/cross/4-cross-dependencies/build.mill @@ -53,7 +53,7 @@ trait BarModule extends Cross.Module[String] { // } // ``` -// Rather than pssing in a literal `"2.10"` to the `foo` cross module, we pass +// Rather than passing in a literal `"2.10"` to the `foo` cross module, we pass // in the `crossValue` property that is available within every `Cross.Module`. // This ensures that each version of `bar` depends on the corresponding version // of `foo`: `bar("2.10")` depends on `foo("2.10")`, `bar("2.11")` depends on diff --git a/example/fundamentals/cross/7-inner-cross-module/build.mill b/example/fundamentals/cross/7-inner-cross-module/build.mill index f9374f5832f..ae9afaab96a 100644 --- a/example/fundamentals/cross/7-inner-cross-module/build.mill +++ b/example/fundamentals/cross/7-inner-cross-module/build.mill @@ -49,13 +49,14 @@ def baz = Task { s"hello ${foo("a").bar.param()}" } // You can use the `CrossValue` trait within any `Cross.Module` to // propagate the `crossValue` defined by an enclosing `Cross.Module` to some // nested module. In this case, we use it to bind `crossValue` so it can be -// used in `def param`. This lets you reduce verbosity by defining the `Cross` +// used in `def param`. +// +// This lets you reduce verbosity by defining the `Cross` // once for a group of modules rather than once for every single module in -// that group. There are corresponding `InnerCrossModuleN` traits for cross -// modules that take multiple inputs. +// that group. In the example above, we define the cross module once for +// `object foo extends Cross`, and then the nested modules `bar` and `qux` +// get automatically duplicated once for each `crossValue = "a"` and `crossValue = "b"` // -// You can reference the modules and tasks defined within such a -// `CrossValue` as is done in `def qux` above /** Usage diff --git a/example/fundamentals/dependencies/1-search-updates/build.mill b/example/fundamentals/dependencies/1-search-updates/build.mill index eb248e2d25b..22cb7f31046 100644 --- a/example/fundamentals/dependencies/1-search-updates/build.mill +++ b/example/fundamentals/dependencies/1-search-updates/build.mill @@ -1,4 +1,3 @@ -// == Search for dependency updates // Mill can search for updated versions of your project's dependencies, if // available from your project's configured repositories. Note that it uses diff --git a/example/fundamentals/modules/7-modules/build.mill b/example/fundamentals/modules/7-modules/build.mill index 407acc21f0d..359027f5b18 100644 --- a/example/fundamentals/modules/7-modules/build.mill +++ b/example/fundamentals/modules/7-modules/build.mill @@ -171,17 +171,17 @@ object outer extends MyModule { */ -// You can use `millSourcePath` to automatically set the source folders of your -// modules to match the build structure. You are not forced to rigidly use -// `millSourcePath` to define the source folders of all your code, but it can simplify -// the common case where you probably want your build-layout and on-disk-layout to -// be the same. +// You should use `millSourcePath` to set the source folders of your +// modules to match the build structure. In almost every case, a module's source files +// live at some relative path within the module's folder, and using `millSourcePath` +// ensures that the relative path to the module's source files remains the same +// regardless of where your module lives in the build hierarchy. // // E.g. for `mill.scalalib.ScalaModule`, the Scala source code is assumed by // default to be in `millSourcePath / "src"` while resources are automatically // assumed to be in `millSourcePath / "resources"`. // -// You can override `millSourcePath`: +// You can also override `millSourcePath`: object outer2 extends MyModule { def millSourcePath = super.millSourcePath / "nested" @@ -218,7 +218,7 @@ object outer2 extends MyModule { */ -// *Note that `os.pwd` of the Mill process is set to an empty `sandbox/` folder by default.* +// NOTE: *`os.pwd` of the Mill process is set to an empty `sandbox/` folder by default.* // When defining a module's source files, you should always use `millSourcePath` to ensure the // paths defined are relative to the module's root folder, so the module logic can continue // to work even if moved into a different subfolder. In the rare case where you need the diff --git a/example/fundamentals/out-dir/1-custom-out/build.mill b/example/fundamentals/out-dir/1-custom-out/build.mill index b36b339830b..fe36a067883 100644 --- a/example/fundamentals/out-dir/1-custom-out/build.mill +++ b/example/fundamentals/out-dir/1-custom-out/build.mill @@ -1,6 +1,8 @@ // The default location for Mill's output directory is `out/` under the project workspace. -// A task `printDest` of a module `foo` will have a default scratch space folder -// `out/foo/printDest.dest/`: +// If you'd rather use another location than `out/`, that lives +// in a faster or a writable filesystem for example, you can change the output directory +// via the `MILL_OUTPUT_DIR` environment variable. + package build import mill._ @@ -11,15 +13,6 @@ object foo extends Module { } } -/** Usage -> ./mill foo.printDest -... -.../out/foo/printDest.dest -*/ - -// If you'd rather use another location than `out/`, that lives -// in a faster or a writable filesystem for example, you can change the output directory -// via the `MILL_OUTPUT_DIR` environment variable. /** Usage > MILL_OUTPUT_DIR=build-stuff/working-dir ./mill foo.printDest diff --git a/example/fundamentals/tasks/1-task-graph/build.mill b/example/fundamentals/tasks/1-task-graph/build.mill index 4735f7c4f8e..eab8786442f 100644 --- a/example/fundamentals/tasks/1-task-graph/build.mill +++ b/example/fundamentals/tasks/1-task-graph/build.mill @@ -1,4 +1,6 @@ -// The following is a simple self-contained example using Mill to compile Java: +// The following is a simple self-contained example using Mill to compile Java, +// making use of the `Task.Source` and `Task` types to define a simple build graph +// with some input source files and intermediate build steps: package build import mill._ diff --git a/example/fundamentals/tasks/2-primary-tasks/build.mill b/example/fundamentals/tasks/2-primary-tasks/build.mill index a1b25c02847..46d7ac3ef8b 100644 --- a/example/fundamentals/tasks/2-primary-tasks/build.mill +++ b/example/fundamentals/tasks/2-primary-tasks/build.mill @@ -23,7 +23,7 @@ def resources = Task.Source { millSourcePath / "resources" } // they watch source files and folders and cause downstream tasks to // re-compute if a change is detected. -// === Targets +// === Cached Tasks def allSources = Task { os.walk(sources().path) @@ -47,7 +47,7 @@ def lineCount: T[Int] = Task { // } // ``` // -// ``Target``s are defined using the `def foo = Task {...}` syntax, and dependencies +// ``Cached Tasks``s are defined using the `def foo = Task {...}` syntax, and dependencies // on other tasks are defined using `foo()` to extract the value from them. // Apart from the `foo()` calls, the `Task {...}` block contains arbitrary code that // does some work and returns a result. @@ -59,10 +59,10 @@ def lineCount: T[Int] = Task { // // * https://github.com/com-lihaoyi/os-lib[OS-Lib Library Documentation] -// If a target's inputs change but its output does not, e.g. someone changes a +// If a cached task's inputs change but its output does not, e.g. someone changes a // comment within the source files that doesn't affect the classfiles, then // downstream tasks do not re-evaluate. This is determined using the -// `.hashCode` of the Target's return value. +// `.hashCode` of the cached task's return value. /** Usage @@ -75,7 +75,7 @@ Computing line count */ -// Furthermore, when code changes occur, targets only invalidate if the code change +// Furthermore, when code changes occur, cached tasks only invalidate if the code change // may directly or indirectly affect it. e.g. adding a comment to `lineCount` will // not cause it to recompute: @@ -88,7 +88,7 @@ Computing line count // .sum // ``` // -// But changing the code of the target or any upstream helper method will cause the +// But changing the code of the cached task or any upstream helper method will cause the // old value to be invalidated and a new value re-computed (with a new `println`) // next time it is invoked: // @@ -101,12 +101,12 @@ Computing line count // .sum // ``` // -// For more information on how the bytecode analysis necessary for invalidating targets +// For more information on how the bytecode analysis necessary for invalidating cached tasks // based on code-changes work, see https://github.com/com-lihaoyi/mill/pull/2417[PR#2417] // that implemented it. // -// The return-value of targets has to be JSON-serializable via -// {upickle-github-url}[uPickle]. You can run targets directly from the command +// The return-value of cached tasks has to be JSON-serializable via +// {upickle-github-url}[uPickle]. You can run cached tasks directly from the command // line, or use `show` if you want to see the JSON content or pipe it to // external tools. See the uPickle library documentation for more details: // @@ -163,7 +163,7 @@ Generating jar */ -// *Note that `os.pwd` of the Mill process is set to an empty `sandbox/` folder by default.* +// NOTE: *`os.pwd` of the Mill process is set to an empty `sandbox/` folder by default.* // This is to stop you from accidentally reading and writing files to the base repository root, // which would cause problems with Mill's caches not invalidating properly or files from different // tasks colliding and causing issues. @@ -259,7 +259,9 @@ def summarizeClassFileStats = Task { */ - +// For more details on how to use uPickle, check out the +// https://github.com/com-lihaoyi/upickle[uPickle library documentation] +// // === Commands def run(mainClass: String, args: String*) = Task.Command { @@ -309,11 +311,12 @@ foo.txt resource: My Example Text // arguments not parsed earlier. Default values for command line arguments are also supported. // See the mainargs documentation for more details: // -// * [MainArgs Library Documentation](https://github.com/com-lihaoyi/mainargs[MainArgs]) +// * https://github.com/com-lihaoyi/mainargs[MainArgs Library Documentation] // // By default, all command parameters need to be named, except for variadic parameters -// of type `T*` or `mainargs.Leftover[T]`. You can use the flag `--allow-positional-command-args` -// to allow arbitrary arguments to be passed positionally, as shown below: +// of type `T*` or `mainargs.Leftover[T]`, or those marked as `@arg(positional = true)`. +// You can use also the flag `--allow-positional-command-args` to globally allow +// arguments to be passed positionally, as shown below: /** Usage @@ -333,7 +336,7 @@ foo.txt resource: My Example Text // -// Like <<_targets>>, a command only evaluates after all its upstream +// Like <<_cached_tasks>>, a command only evaluates after all its upstream // dependencies have completed, and will not begin to run if any upstream // dependency has failed. // @@ -347,7 +350,8 @@ foo.txt resource: My Example Text // Tasks can be overriden, with the overriden task callable via `super`. // You can also override a task with a different type of task, e.g. below -// we override `sourceRoots` which is a `Task.Sources` with a `Task{}` target: +// we override `sourceRoots` which is a `Task.Sources` with a cached `Task{}` +// that depends on the original via `super`: // trait Foo extends Module { diff --git a/example/fundamentals/tasks/3-anonymous-tasks/build.mill b/example/fundamentals/tasks/3-anonymous-tasks/build.mill index 5655699e31d..996a22dd164 100644 --- a/example/fundamentals/tasks/3-anonymous-tasks/build.mill +++ b/example/fundamentals/tasks/3-anonymous-tasks/build.mill @@ -18,7 +18,7 @@ def printFileData(fileName: String) = Task.Command { // // Anonymous task's output does not need to be JSON-serializable, their output is // not cached, and they can be defined with or without arguments. -// Unlike <<_targets>> or <<_commands>>, anonymous tasks can be defined +// Unlike <<_cached_tasks>> or <<_commands>>, anonymous tasks can be defined // anywhere and passed around any way you want, until you finally make use of them // within a downstream task or command. // diff --git a/example/fundamentals/tasks/4-inputs/build.mill b/example/fundamentals/tasks/4-inputs/build.mill index 70facdce691..7b7c65c00b8 100644 --- a/example/fundamentals/tasks/4-inputs/build.mill +++ b/example/fundamentals/tasks/4-inputs/build.mill @@ -13,13 +13,13 @@ def myInput = Task.Input { // arbitrary block of code. // // Inputs can be used to force re-evaluation of some external property that may -// affect your build. For example, if I have a <<_targets, Target>> `bar` that +// affect your build. For example, if I have a <<_cached_task, cached task>> `bar` that // calls out to `git` to compute the latest commit hash and message directly, // that target does not have any `Task` inputs and so will never re-compute // even if the external `git` status changes: def gitStatusTask = Task { - "v-" + + "version-" + os.proc("git", "log", "-1", "--pretty=format:%h-%B ") .call(cwd = Task.workspace) .out @@ -33,12 +33,12 @@ def gitStatusTask = Task { > git commit --allow-empty -m "Initial-Commit" > ./mill show gitStatusTask -"v-...-Initial-Commit" +"version-...-Initial-Commit" > git commit --allow-empty -m "Second-Commit" > ./mill show gitStatusTask # Mill didn't pick up the git change! -"v-...-Initial-Commit" +"version-...-Initial-Commit" */ @@ -56,7 +56,7 @@ def gitStatusInput = Task.Input { .text() .trim() } -def gitStatusTask2 = Task { "v-" + gitStatusInput() } +def gitStatusTask2 = Task { "version-" + gitStatusInput() } // This makes `gitStatusInput` to always re-evaluate every build, and only if // the output of `gitStatusInput` changes will `gitStatusTask2` re-compute @@ -66,12 +66,12 @@ def gitStatusTask2 = Task { "v-" + gitStatusInput() } > git commit --allow-empty -m "Initial-Commit" > ./mill show gitStatusTask2 -"v-...-Initial-Commit" +"version-...-Initial-Commit" > git commit --allow-empty -m "Second-Commit" > ./mill show gitStatusTask2 # Mill picked up git change -"v-...-Second-Commit" +"version-...-Second-Commit" */ diff --git a/example/fundamentals/tasks/5-persistent-tasks/build.mill b/example/fundamentals/tasks/5-persistent-tasks/build.mill index c227f8b75ad..509e9ed655e 100644 --- a/example/fundamentals/tasks/5-persistent-tasks/build.mill +++ b/example/fundamentals/tasks/5-persistent-tasks/build.mill @@ -1,9 +1,15 @@ -// Persistent targets defined using `Task(Persistent = True)` are similar to normal +// Persistent targets defined using `Task(persistent = true)` are similar to normal // ``Target``s, except their `Task.dest` folder is not cleared before every // evaluation. This makes them useful for caching things on disk in a more -// fine-grained manner than Mill's own Target-level caching. +// fine-grained manner than Mill's own Task-level caching: the task can +// maintain a cache of one or more files on disk, and decide itself which files +// (or parts of which files!) need to invalidate, rather than having all generated +// files wiped out every time (which is the default behavior for normal Tasks). // -// Below is a semi-realistic example of using a `Task(Persistent = True)` target: +// +// Below is a semi-realistic example of using `Task(persistent = true)` to compress +// files in an input folder, and re-use previously-compressed files if a file in the +// input folder did not change: package build import mill._, scalalib._ import java.util.Arrays @@ -51,7 +57,7 @@ def compressBytes(input: Array[Byte]) = { // Since persistent tasks have long-lived state on disk that lives beyond a // single evaluation, this raises the possibility of the disk contents getting // into a bad state and causing all future evaluations to fail. It is left up -// to the person implementing the `Task(Persistent = True)` to ensure their implementation +// to user of `Task(persistent = true)` to ensure their implementation // is eventually consistent. You can also use `mill clean` to manually purge // the disk contents to start fresh. diff --git a/example/javalib/basic/1-simple/build.mill b/example/javalib/basic/1-simple/build.mill index 3f4e27a8b3e..62b544475fd 100644 --- a/example/javalib/basic/1-simple/build.mill +++ b/example/javalib/basic/1-simple/build.mill @@ -43,8 +43,8 @@ object `package` extends RootModule with JavaModule { // //// SNIPPET:DEPENDENCIES // -// This example project uses two third-party dependencies - ArgParse4J for CLI -// argument parsing, Apache Commons Text for HTML escaping - and uses them to wrap a +// This example project uses two third-party dependencies - https://argparse4j.github.io/[ArgParse4J] for CLI +// argument parsing, https://commons.apache.org/proper/commons-text/[Apache Commons Text] for HTML escaping - and uses them to wrap a // given input string in HTML templates with proper escaping. // // Typical usage of a `JavaModule` is shown below diff --git a/example/javalib/basic/2-custom-build-logic/build.mill b/example/javalib/basic/2-custom-build-logic/build.mill index 9c3326a0d98..cab5488b01e 100644 --- a/example/javalib/basic/2-custom-build-logic/build.mill +++ b/example/javalib/basic/2-custom-build-logic/build.mill @@ -11,6 +11,6 @@ object `package` extends RootModule with JavaModule { /** Generate resources using lineCount of sources */ override def resources = Task { os.write(Task.dest / "line-count.txt", "" + lineCount()) - Seq(PathRef(Task.dest)) + super.resources() ++ Seq(PathRef(Task.dest)) } } diff --git a/example/javalib/dependencies/4-downloading-non-maven-jars/build.mill b/example/javalib/dependencies/4-downloading-unmanaged-jars/build.mill similarity index 100% rename from example/javalib/dependencies/4-downloading-non-maven-jars/build.mill rename to example/javalib/dependencies/4-downloading-unmanaged-jars/build.mill diff --git a/example/javalib/dependencies/4-downloading-non-maven-jars/src/foo/Foo.java b/example/javalib/dependencies/4-downloading-unmanaged-jars/src/foo/Foo.java similarity index 100% rename from example/javalib/dependencies/4-downloading-non-maven-jars/src/foo/Foo.java rename to example/javalib/dependencies/4-downloading-unmanaged-jars/src/foo/Foo.java diff --git a/example/javalib/dependencies/4-downloading-non-maven-jars/textfile.txt b/example/javalib/dependencies/4-downloading-unmanaged-jars/textfile.txt similarity index 100% rename from example/javalib/dependencies/4-downloading-non-maven-jars/textfile.txt rename to example/javalib/dependencies/4-downloading-unmanaged-jars/textfile.txt diff --git a/example/javalib/linting/1-error-prone/build.mill b/example/javalib/linting/1-error-prone/build.mill index dddaf713839..8b9a656368a 100644 --- a/example/javalib/linting/1-error-prone/build.mill +++ b/example/javalib/linting/1-error-prone/build.mill @@ -1,11 +1,10 @@ -// When adding the `ErrorPromeModule` to your `JavaModule`, +// When adding the `ErrorProneModule` to your `JavaModule`, // the `error-prone` compiler plugin automatically detects various kind of programming errors. package build import mill._, javalib._, errorprone._ - object `package` extends RootModule with JavaModule with ErrorProneModule { def errorProneOptions = Seq("-XepAllErrorsAsWarnings") } diff --git a/example/javalib/linting/2-checkstyle/build.mill b/example/javalib/linting/2-checkstyle/build.mill index 6ac56a38fd3..04e6164db39 100644 --- a/example/javalib/linting/2-checkstyle/build.mill +++ b/example/javalib/linting/2-checkstyle/build.mill @@ -107,11 +107,11 @@ Audit done. // - Version `6.3` or above is required for `plain` and `xml` formats. // - Setting `checkstyleOptions` might cause failures with legacy versions. // -// == CheckstyleXsltModule +// === CheckstyleXsltModule // // This plugin extends the `mill.contrib.checkstyle.CheckstyleModule` with the ability to generate reports by applying https://www.w3.org/TR/xslt/[XSL Transformations] on a Checkstyle output report. // -// === Auto detect XSL Transformations +// ==== Auto detect XSL Transformations // // XSLT files are detected automatically provided a prescribed directory structure is followed. // [source,scala] @@ -132,7 +132,7 @@ Audit done. // */ // ---- // -// === Specify XSL Transformations manually +// ==== Specify XSL Transformations manually // // For a custom setup, adapt the following example. // [source,scala] diff --git a/example/javalib/linting/3-palantirformat/build.mill b/example/javalib/linting/3-palantirformat/build.mill index db162e3c846..0205b8d3ff1 100644 --- a/example/javalib/linting/3-palantirformat/build.mill +++ b/example/javalib/linting/3-palantirformat/build.mill @@ -17,17 +17,17 @@ object `package` extends RootModule with PalantirFormatModule /** Usage -> ./mill palantirformat --check # check should fail initially +> ./mill palantirformat --check # check should fail initially ...checking format in java sources ... ...src/A.java error: ...palantirformat aborted due to format error(s) (or invalid plugin settings/palantirformat options) -> ./mill palantirformat # format all Java source files +> ./mill palantirformat # format all Java source files ...formatting java sources ... -> ./mill palantirformat --check # check should succeed now +> ./mill palantirformat --check # check should succeed now ...checking format in java sources ... -> ./mill mill.javalib.palantirformat.PalantirFormatModule/ __.sources # alternatively, use external module to check/format +> ./mill mill.javalib.palantirformat.PalantirFormatModule/ # alternatively, use external module to check/format ...formatting java sources ... */ diff --git a/example/javalib/module/4-compilation-execution-flags/build.mill b/example/javalib/module/4-compilation-execution-flags/build.mill index da3dc450491..751eabf21c2 100644 --- a/example/javalib/module/4-compilation-execution-flags/build.mill +++ b/example/javalib/module/4-compilation-execution-flags/build.mill @@ -8,7 +8,7 @@ object `package` extends RootModule with JavaModule{ def javacOptions = Seq("-deprecation") } -// You can pass flags to the Kotlin compiler via `javacOptions`. +// You can pass flags to the Java compiler via `javacOptions`. /** Usage diff --git a/example/kotlinlib/basic/2-custom-build-logic/build.mill b/example/kotlinlib/basic/2-custom-build-logic/build.mill index f486ee06b3c..4a2d3d031a7 100644 --- a/example/kotlinlib/basic/2-custom-build-logic/build.mill +++ b/example/kotlinlib/basic/2-custom-build-logic/build.mill @@ -16,7 +16,7 @@ object `package` extends RootModule with KotlinModule { /** Generate resources using lineCount of sources */ override def resources = Task { os.write(Task.dest / "line-count.txt", "" + lineCount()) - Seq(PathRef(Task.dest)) + super.resources() ++ Seq(PathRef(Task.dest)) } object test extends KotlinTests with TestModule.Junit5 { diff --git a/example/kotlinlib/basic/4-builtin-commands/build.mill b/example/kotlinlib/basic/4-builtin-commands/build.mill index a0c34745640..522d9e6b221 100644 --- a/example/kotlinlib/basic/4-builtin-commands/build.mill +++ b/example/kotlinlib/basic/4-builtin-commands/build.mill @@ -3,8 +3,12 @@ package build import mill._, kotlinlib._ trait MyModule extends KotlinModule { - def kotlinVersion = "1.9.24" + object test extends KotlinTests with TestModule.Junit5 { + def ivyDeps = super.ivyDeps() ++ Agg( + ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" + ) + } } object foo extends MyModule { @@ -20,10 +24,4 @@ object bar extends MyModule { def ivyDeps = Agg( ivy"org.jetbrains.kotlinx:kotlinx-html-jvm:0.11.0" ) - - object test extends KotlinTests with TestModule.Junit5 { - def ivyDeps = super.ivyDeps() ++ Agg( - ivy"io.kotest:kotest-runner-junit5-jvm:5.9.1" - ) - } } diff --git a/example/kotlinlib/dependencies/4-downloading-non-maven-jars/build.mill b/example/kotlinlib/dependencies/4-downloading-unmanaged-jars/build.mill similarity index 100% rename from example/kotlinlib/dependencies/4-downloading-non-maven-jars/build.mill rename to example/kotlinlib/dependencies/4-downloading-unmanaged-jars/build.mill diff --git a/example/kotlinlib/dependencies/4-downloading-non-maven-jars/src/foo/Foo.kt b/example/kotlinlib/dependencies/4-downloading-unmanaged-jars/src/foo/Foo.kt similarity index 100% rename from example/kotlinlib/dependencies/4-downloading-non-maven-jars/src/foo/Foo.kt rename to example/kotlinlib/dependencies/4-downloading-unmanaged-jars/src/foo/Foo.kt diff --git a/example/kotlinlib/dependencies/4-downloading-non-maven-jars/textfile.txt b/example/kotlinlib/dependencies/4-downloading-unmanaged-jars/textfile.txt similarity index 100% rename from example/kotlinlib/dependencies/4-downloading-non-maven-jars/textfile.txt rename to example/kotlinlib/dependencies/4-downloading-unmanaged-jars/textfile.txt diff --git a/example/kotlinlib/linting/1-detekt/build.mill b/example/kotlinlib/linting/1-detekt/build.mill index 0cd22e9c6b8..55a2427821e 100644 --- a/example/kotlinlib/linting/1-detekt/build.mill +++ b/example/kotlinlib/linting/1-detekt/build.mill @@ -5,11 +5,14 @@ import kotlinlib.KotlinModule import kotlinlib.detekt.DetektModule object `package` extends RootModule with KotlinModule with DetektModule { - def kotlinVersion = "1.9.24" - } + +// This example shows how to use the https://github.com/detekt/detekt[Detekt] +// static code analyzer for linting a `KotlinModule`, by mixing in the trait +// `DetektModule` and calling the `detekt` task: + /** See Also: src/example/Foo.kt */ /** Usage diff --git a/example/kotlinlib/linting/2-ktlint/build.mill b/example/kotlinlib/linting/2-ktlint/build.mill index 8117f00867a..762c902c4eb 100644 --- a/example/kotlinlib/linting/2-ktlint/build.mill +++ b/example/kotlinlib/linting/2-ktlint/build.mill @@ -7,20 +7,25 @@ import kotlinlib.KotlinModule import kotlinlib.ktlint.KtlintModule object `package` extends RootModule with KotlinModule with KtlintModule { - def kotlinVersion = "1.9.24" def ktlintConfig = Some(PathRef(T.workspace / ".editorconfig")) - } +// This example shows how to use the https://github.com/pinterest/ktlint[KtLint] +// linter on a `KotlinModule`, by mixing in the trait `KtlintModule` and calling the +// `ktlint` task. `ktlint` also supports autoformatting to automatically resolve +// code formatting violations, via the `--format` flag shown below: + /** Usage > ./mill ktlint # run ktlint to produce a report, defaults to warning without error error: ...src/example/FooWrong.kt:6:28: Missing newline before ")" (standard:parameter-list-wrapping)... ...src/example/FooWrong.kt:6:28: Newline expected before closing parenthesis (standard:function-signature)... ...src/example/FooWrong.kt:6:28: Missing trailing comma before ")" (standard:trailing-comma-on-declaration-site)... + > ./mill ktlint --format true > ./mill ktlint # after fixing the violations, ktlint no longer errors */ + diff --git a/example/kotlinlib/linting/3-ktfmt/build.mill b/example/kotlinlib/linting/3-ktfmt/build.mill index b97892fd2f4..43103321a0a 100644 --- a/example/kotlinlib/linting/3-ktfmt/build.mill +++ b/example/kotlinlib/linting/3-ktfmt/build.mill @@ -7,17 +7,21 @@ import kotlinlib.KotlinModule import kotlinlib.ktfmt.KtfmtModule object `package` extends RootModule with KotlinModule with KtfmtModule { - def kotlinVersion = "1.9.24" - } +// This example demonstrates how to use the https://github.com/facebook/ktfmt[KtFmt] +// autoformatter from Facebook both to enforce and apply formatting to your `KotlinModule` +// source files. You can configure a non-default version of KtFmt by overriding `def ktfmtVersion` + /** Usage > ./mill ktfmt --format=false # run ktfmt to produce a list of files which should be formatter ...src/example/FooWrong.kt... + > ./mill ktfmt # running without arguments will format all files Done formatting ...src/example/FooWrong.kt + > ./mill ktfmt # after fixing the violations, ktfmt no longer prints any file > ./mill mill.kotlinlib.ktfmt.KtfmtModule/ __.sources # alternatively, use external module to check/format diff --git a/example/scalalib/basic/1-simple/build.mill b/example/scalalib/basic/1-simple/build.mill index 097fdc011d2..2dee802ce5d 100644 --- a/example/scalalib/basic/1-simple/build.mill +++ b/example/scalalib/basic/1-simple/build.mill @@ -146,7 +146,7 @@ error: Missing argument: --text // `mill inspect compile` to inspect a task's doc-comment documentation or what // it depends on, or `mill show foo.scalaVersion` to show the output of any task. // -// The most common *tasks* that Mill can run are cached *targets*, such as +// The most common *tasks* that Mill can run are cached tasks, such as // `compile`, and un-cached *commands* such as `foo.run`. Targets do not // re-evaluate unless one of their inputs changes, whereas commands re-run every // time. diff --git a/example/scalalib/basic/2-custom-build-logic/build.mill b/example/scalalib/basic/2-custom-build-logic/build.mill index 293f97b547b..e806d0edbdd 100644 --- a/example/scalalib/basic/2-custom-build-logic/build.mill +++ b/example/scalalib/basic/2-custom-build-logic/build.mill @@ -19,7 +19,7 @@ object `package` extends RootModule with ScalaModule { /** Generate resources using lineCount of sources */ override def resources = Task { os.write(Task.dest / "line-count.txt", "" + lineCount()) - Seq(PathRef(Task.dest)) + super.resources() ++ Seq(PathRef(Task.dest)) } } @@ -35,9 +35,9 @@ object `package` extends RootModule with ScalaModule { // rankdir=LR // node [shape=box width=0 height=0 style=filled fillcolor=white] // allSourceFiles -> lineCount -> resources -> "..." -> run -// "resources.super" -> "..." [style=invis] +// "resources.super" -> "resources" // "..." [color=white] -// "resources.super" [style=dashed] +// "resources.super" [color=white] // allSourceFiles [color=white] // run [color=white] // } @@ -64,7 +64,7 @@ Inputs: // `allSourceFiles` (an existing task) and is in-turn used in our override of // `resources` (also an existing task). `os.read.lines` and `os.write` come // from the https://github.com/com-lihaoyi/os-lib[OS-Lib] library, which is -// one of Mill's <>. This generated file can then be +// one of Mill's xref:fundamentals/bundled-libraries.adoc[Bundled Libraries]. This generated file can then be // loaded and used at runtime, as see in the output of `mill run` // // While this is a toy example, it shows how easy it is to customize your Mill @@ -72,7 +72,7 @@ Inputs: // most real-world projects. // // This customization is done in a principled fashion familiar to most -// programmers - object-orienting overrides - rather than ad-hoc +// programmers - `override` and `super` - rather than ad-hoc // monkey-patching or mutation common in other build tools. You never have // "spooky action at a distance" affecting your build / graph definition, and // your IDE can always help you find the final override of any particular build diff --git a/example/scalalib/basic/3-multi-module/build.mill b/example/scalalib/basic/3-multi-module/build.mill index d98dadee143..3919ab9924a 100644 --- a/example/scalalib/basic/3-multi-module/build.mill +++ b/example/scalalib/basic/3-multi-module/build.mill @@ -24,12 +24,16 @@ object bar extends MyModule { // We don't mark either module as top-level using `extends RootModule`, so // running tasks needs to use the module name as the prefix e.g. `foo.run` or // `bar.run`. You can define multiple modules the same way you define a single -// module, using `def moduleDeps` to define the relationship between them. +// module, using `def moduleDeps` to define the relationship between them. Modules +// can also be nested within each other, as `foo.test` and `bar.test` are nested +// within `foo` and `bar` respectively // // Note that we split out the `test` submodule configuration common to both // modules into a separate `trait MyModule`. This lets us avoid the need to // copy-paste common settings, while still letting us define any per-module -// configuration such as `ivyDeps` specific to a particular module. +// configuration such as `ivyDeps` specific to a particular module. This is a +// common pattern within Mill builds to avoid the need to copy-paste common +// configuration // // The above builds expect the following project layout: // @@ -90,6 +94,9 @@ Bar.value:

world

// Mill's evaluator will ensure that the modules are compiled in the right // order, and recompiled as necessary when source code in each module changes. +// The unique path on disk that Mill automatically assigns each task also ensures +// you do not need to worry about choosing a path on disk to cache outputs, or +// filesystem collisions if multiple tasks write to the same path. // // You can use wildcards and brace-expansion to select // multiple tasks at once or to shorten the path to deeply nested tasks. If @@ -106,8 +113,8 @@ Bar.value:

world

// |========================================================== // // -// You can use the `+` symbol to add another task with optional arguments. -// If you need to feed a `+` as argument to your task, you can mask it by +// You can use the + symbol to add another task with optional arguments. +// If you need to feed a + as argument to your task, you can mask it by // preceding it with a backslash (`\`). // @@ -121,4 +128,6 @@ Bar.value:

world

> mill __.compile + foo.__.test # Runs all `compile` tasks and all tests under `foo`. */ -// For more details on the query syntax, check out the documentation for <> +// For more details on the query syntax, check out the query syntax documentation: + +// - xref:fundamentals/query-syntax.adoc[Task Query Syntax] diff --git a/example/scalalib/basic/4-builtin-commands/build.mill b/example/scalalib/basic/4-builtin-commands/build.mill index 4b4dbc74dc9..1df3c95423e 100644 --- a/example/scalalib/basic/4-builtin-commands/build.mill +++ b/example/scalalib/basic/4-builtin-commands/build.mill @@ -15,6 +15,7 @@ import mill._, scalalib._ trait MyModule extends ScalaModule { def scalaVersion = "2.13.11" + object test extends ScalaTests with TestModule.Utest } object foo extends MyModule { @@ -81,7 +82,7 @@ foo.artifactName */ -// See the documentation for <> for more details what you +// See the documentation for xref:fundamentals/query-syntax.adoc[Task Query Syntax] for more details what you // can pass to `resolve` // == inspect @@ -305,7 +306,32 @@ foo.compileClasspath // image::basic/VisualizeJava.svg[VisualizeJava.svg] // // `visualize` can be very handy for trying to understand the dependency graph of -// tasks within your Mill build. +// tasks within your Mill build: who depends on what? Who do I need to override to affect +// a particular task? Which tasks depend on another and need to run sequentially, and which +// do not and can be run in parallel? +// +// The above example shows the outcome of using `visualize` on multiple tasks within a single +// module, but you can also use `visualize` on a single task in multiple modules to see how they are related: +// +/** Usage +> mill visualize __.compile + +> cat out/visualize.dest/out.dot +digraph "example1" { +graph ["rankdir"="LR"] +"bar.compile" ["style"="solid","shape"="box"] +"bar.test.compile" ["style"="solid","shape"="box"] +"foo.compile" ["style"="solid","shape"="box"] +"foo.test.compile" ["style"="solid","shape"="box"] +"bar.compile" -> "foo.compile" +"bar.compile" -> "bar.test.compile" +"foo.compile" -> "foo.test.compile" +} +*/ + +// image::basic/VisualizeCompiles.svg[VisualizeCompiles.svg] +// +// // // == visualizePlan // diff --git a/example/scalalib/dependencies/1-ivy-deps/build.mill b/example/scalalib/dependencies/1-ivy-deps/build.mill index 94313567825..79c07a78feb 100644 --- a/example/scalalib/dependencies/1-ivy-deps/build.mill +++ b/example/scalalib/dependencies/1-ivy-deps/build.mill @@ -23,6 +23,7 @@ object `package` extends RootModule with ScalaModule { // dependencies // // * Triple `:::` syntax (e.g. `ivy"org.scalamacros:::paradise:2.1.1"`) defines +// * Triple `:::` syntax (e.g. `ivy"org.scalamacros:::paradise:2.1.1"`) defines // dependencies cross-published against the full Scala version e.g. `2.12.4` // instead of just `2.12`. These are typically Scala compiler plugins or // similar. @@ -33,7 +34,7 @@ object `package` extends RootModule with ScalaModule { // // * `ivy"org.apache.spark::spark-sql:2.4.0;classifier=tests`. // -// Please consult the <> section for even more details. +// Please consult the xref:fundamentals/library-deps.adoc[Library Dependencies in Mill] section for more details. //// SNIPPET:USAGE diff --git a/example/scalalib/dependencies/2-run-compile-deps/build.mill b/example/scalalib/dependencies/2-run-compile-deps/build.mill index 233b8f51b34..c2fd26eea45 100644 --- a/example/scalalib/dependencies/2-run-compile-deps/build.mill +++ b/example/scalalib/dependencies/2-run-compile-deps/build.mill @@ -20,7 +20,7 @@ object foo extends ScalaModule { //// SNIPPET:END // You can also declare compile-time-only dependencies with `compileIvyDeps`. -// These are present in the compile classpath, but will not propagated to the +// These are present in the compile classpath, but will not propagate to the // transitive dependencies. //// SNIPPET:BUILD2 diff --git a/example/scalalib/dependencies/3-unmanaged-jars/build.mill b/example/scalalib/dependencies/3-unmanaged-jars/build.mill index 9b6bda3d581..8bbc09f3b3a 100644 --- a/example/scalalib/dependencies/3-unmanaged-jars/build.mill +++ b/example/scalalib/dependencies/3-unmanaged-jars/build.mill @@ -22,4 +22,11 @@ object `package` extends RootModule with ScalaModule { Key: name, Value: John Key: age, Value: 30 -*/ \ No newline at end of file +*/ + +// in most scenarios you should rely on `ivyDeps`/`moduleDeps` and let Mill manage +// the compilation/downloading/caching of classpath jars for you, as Mill will +// automatically pull in transitive dependencies which are generally needed for things +// to work. But in the rare case you receive a jar or folder-full-of-classfiles +// from somewhere and need to include it in your project, `unmanagedClasspath` is the +// way to do it. \ No newline at end of file diff --git a/example/scalalib/dependencies/4-downloading-non-maven-jars/build.mill b/example/scalalib/dependencies/4-downloading-unmanaged-jars/build.mill similarity index 66% rename from example/scalalib/dependencies/4-downloading-non-maven-jars/build.mill rename to example/scalalib/dependencies/4-downloading-unmanaged-jars/build.mill index c28ba3b23d1..a79e2089f51 100644 --- a/example/scalalib/dependencies/4-downloading-non-maven-jars/build.mill +++ b/example/scalalib/dependencies/4-downloading-unmanaged-jars/build.mill @@ -17,10 +17,10 @@ object `package` extends RootModule with ScalaModule { //// SNIPPET:END // You can also override `unmanagedClasspath` to point it at jars that you want to -// download from arbitrary URLs. Note that tasks like `unmanagedClasspath` are -// cached, so your jar is downloaded only once and re-used indefinitely after that. +// download from arbitrary URLs. // `requests.get` comes from the https://github.com/com-lihaoyi/requests-scala[Requests-Scala] -// library, one of Mill's <>. +// library, one of Mill's xref:fundamentals/bundled-libraries.adoc[Bundled Libraries]. +// /** Usage @@ -30,3 +30,9 @@ hear me moo I weigh twice as much as you */ + +// Note that tasks like `unmanagedClasspath` are +// cached, so your jar is downloaded only once and re-used indefinitely after that. +// This is usually not a problem, because usually URLs follow the rule that +// https://www.w3.org/Provider/Style/URI[Cool URIs don't change], and so jars +// downloaded from the same URL will always contain the same contents. diff --git a/example/scalalib/dependencies/4-downloading-non-maven-jars/src/Foo.scala b/example/scalalib/dependencies/4-downloading-unmanaged-jars/src/Foo.scala similarity index 100% rename from example/scalalib/dependencies/4-downloading-non-maven-jars/src/Foo.scala rename to example/scalalib/dependencies/4-downloading-unmanaged-jars/src/Foo.scala diff --git a/example/scalalib/dependencies/4-downloading-non-maven-jars/textfile.txt b/example/scalalib/dependencies/4-downloading-unmanaged-jars/textfile.txt similarity index 100% rename from example/scalalib/dependencies/4-downloading-non-maven-jars/textfile.txt rename to example/scalalib/dependencies/4-downloading-unmanaged-jars/textfile.txt diff --git a/example/scalalib/dependencies/5-repository-config/build.mill b/example/scalalib/dependencies/5-repository-config/build.mill index 05249d8fe42..b7ca0d0ba61 100644 --- a/example/scalalib/dependencies/5-repository-config/build.mill +++ b/example/scalalib/dependencies/5-repository-config/build.mill @@ -1,5 +1,6 @@ -// By default, dependencies are resolved from maven central, but you can add -// your own resolvers by overriding the `repositoriesTask` task in the module: +// By default, dependencies are resolved from https://central.sonatype.com/[Maven Central], +// the standard package repository for JVM languages like Java, Kotlin, or Scala. You +// can also add your own resolvers by overriding the `repositoriesTask` task in the module: //// SNIPPET:BUILD1 package build @@ -26,9 +27,12 @@ object foo extends ScalaModule { //// SNIPPET:END -// Mill read https://get-coursier.io/[coursier] config files automatically. +// Mill uses the https://get-coursier.io/[Coursier] dependency resolver, and reads +// Coursier config files automatically. +// +// You can configure Coursier to use an alternate download location for Maven Central +// artifacts via a `mirror.properties` file: // -// It is possible to setup mirror with `mirror.properties` // [source,properties] // ---- // central.from=https://repo1.maven.org/maven2 diff --git a/example/scalalib/module/2-custom-tasks/build.mill b/example/scalalib/module/2-custom-tasks/build.mill index 37b9f97b543..c4fcc9c9c17 100644 --- a/example/scalalib/module/2-custom-tasks/build.mill +++ b/example/scalalib/module/2-custom-tasks/build.mill @@ -78,9 +78,9 @@ object `package` extends RootModule with ScalaModule { // depending on existing Tasks e.g. `foo.sources` via the `foo.sources()` // syntax to extract their current value, as shown in `lineCount` above. The // return-type of a Task has to be JSON-serializable (using -// https://github.com/lihaoyi/upickle[uPickle], one of Mill's <>) +// https://github.com/lihaoyi/upickle[uPickle], one of Mill's xref:fundamentals/bundled-libraries.adoc[Bundled Libraries]) // and the Task is cached when first run until its inputs change (in this case, if -// someone edits the `foo.sources` files which live in `foo/src`. Cached Tasks +// someone edits the `foo.sources` files which live in `foo/src`). Cached Tasks // cannot take parameters. // // Note that depending on a task requires use of parentheses after the task @@ -113,13 +113,25 @@ my.line.count: 14 // to compile some Javascript, generate sources to feed into a compiler, or // create some custom jar/zip assembly with the files you want , all of these // can simply be custom tasks with your code running in the `Task {...}` block. +// You can also import arbitrary Java or Scala libraries from Maven Central via +// xref:extending/import-ivy-plugins.adoc[import $ivy] to use in your build. // // You can create arbitrarily long chains of dependent tasks, and Mill will // handle the re-evaluation and caching of the tasks' output for you. // Mill also provides you a `Task.dest` folder for you to use as scratch space or -// to store files you want to return: all files a task creates should live -// within `Task.dest`, and any files you want to modify should be copied into -// `Task.dest` before being modified. That ensures that the files belonging to a -// particular task all live in one place, avoiding file-name conflicts and +// to store files you want to return: +// +// * Any files a task creates should live +// within `Task.dest` +// +// * Any files a task modifies should be copied into +// `Task.dest` before being modified. +// +// * Any files that a task returns should be returned as a `PathRef` to a path +// within `Task.dest` +// +// That ensures that the files belonging to a +// particular task all live in one place, avoiding file-name conflicts, +// preventing race conditions when tasks evaluate in parallel, and // letting Mill automatically invalidate the files when the task's inputs // change. \ No newline at end of file diff --git a/example/scalalib/testing/2-test-deps/build.mill b/example/scalalib/testing/2-test-deps/build.mill index 72a8e10e31f..217cc30bea0 100644 --- a/example/scalalib/testing/2-test-deps/build.mill +++ b/example/scalalib/testing/2-test-deps/build.mill @@ -37,7 +37,11 @@ object baz extends ScalaModule { //// SNIPPET:END // In this example, not only does `qux` depend on `baz`, but we also make -// `qux.test` depend on `baz.test`. That lets `qux.test` make use of the +// `qux.test` depend on `baz.test`. +// +// image::basic/VisualizeTestDeps.svg[VisualizeTestDeps.svg] +// +// That lets `qux.test` make use of the // `BazTestUtils` class that `baz.test` defines, allowing us to re-use this // test helper throughout multiple modules' test suites diff --git a/kotlinlib/src/mill/kotlinlib/PlatformKotlinModule.scala b/kotlinlib/src/mill/kotlinlib/PlatformKotlinModule.scala index 23ff797e7ad..84229d5b756 100644 --- a/kotlinlib/src/mill/kotlinlib/PlatformKotlinModule.scala +++ b/kotlinlib/src/mill/kotlinlib/PlatformKotlinModule.scala @@ -1,7 +1,6 @@ package mill.kotlinlib -import mill._ -import os.Path +import mill.scalalib.PlatformModuleBase /** * A [[KotlinModule]] intended for defining `.jvm`/`.js`/etc. submodules @@ -13,25 +12,4 @@ import os.Path * built against and not something that should affect the filesystem path or * artifact name */ -trait PlatformKotlinModule extends KotlinModule { - override def millSourcePath: Path = super.millSourcePath / os.up - - /** - * The platform suffix of this [[PlatformKotlinModule]]. Useful if you want to - * further customize the source paths or artifact names. - */ - def platformKotlinSuffix: String = millModuleSegments - .value - .collect { case l: mill.define.Segment.Label => l.value } - .last - - override def sources: T[Seq[PathRef]] = Task.Sources { - super.sources().flatMap { source => - val platformPath = - PathRef(source.path / _root_.os.up / s"${source.path.last}-$platformKotlinSuffix") - Seq(source, platformPath) - } - } - - override def artifactNameParts: T[Seq[String]] = super.artifactNameParts().dropRight(1) -} +trait PlatformKotlinModule extends PlatformModuleBase with KotlinModule diff --git a/kotlinlib/src/mill/kotlinlib/ktfmt/KtfmtModule.scala b/kotlinlib/src/mill/kotlinlib/ktfmt/KtfmtModule.scala index 281f0a9fa47..dcf4f33cb55 100644 --- a/kotlinlib/src/mill/kotlinlib/ktfmt/KtfmtModule.scala +++ b/kotlinlib/src/mill/kotlinlib/ktfmt/KtfmtModule.scala @@ -1,7 +1,6 @@ package mill.kotlinlib.ktfmt import mill._ -import mainargs.Leftover import mill.api.{Loose, PathRef} import mill.define.{Discover, ExternalModule} import mill.kotlinlib.{DepSyntax, Versions} @@ -49,13 +48,12 @@ trait KtfmtModule extends KtfmtBaseModule { */ def ktfmt( @mainargs.arg ktfmtArgs: KtfmtArgs, - @mainargs.arg(positional = true) sources: Leftover[String] + @mainargs.arg(positional = true) sources: Tasks[Seq[PathRef]] = + Tasks.resolveMainDefault("__.sources") ): Command[Unit] = Task.Command { - val _sources = if (sources.value.isEmpty) { + val _sources: Seq[PathRef] = if (sources.value.isEmpty) { this.sources() - } else { - sources.value.iterator.map(rel => PathRef(millSourcePath / os.RelPath(rel))) - } + } else T.sequence(sources.value)().flatten KtfmtModule.ktfmtAction( ktfmtArgs.style, ktfmtArgs.format, diff --git a/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala b/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala index 3b9677a4218..75fbad0890b 100644 --- a/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala +++ b/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala @@ -1,9 +1,7 @@ package mill.kotlinlib.ktfmt -import mainargs.Leftover -import mill.{T, api} +import mill.{PathRef, T, Task, api} import mill.kotlinlib.KotlinModule -import mill.kotlinlib.ktfmt.{KtfmtArgs, KtfmtModule} import mill.main.Tasks import mill.testkit.{TestBaseModule, UnitTester} import utest.{TestSuite, Tests, assert, test} @@ -12,6 +10,10 @@ object KtfmtModuleTests extends TestSuite { val kotlinVersion = "1.9.24" + object module extends TestBaseModule with KotlinModule with KtfmtModule { + override def kotlinVersion: T[String] = KtfmtModuleTests.kotlinVersion + } + def tests: Tests = Tests { val (before, after) = { @@ -62,7 +64,7 @@ object KtfmtModuleTests extends TestSuite { test("ktfmt - explicit files") { checkState( - afterFormat(before, sources = Seq("src/Example.kt")), + afterFormat(before, sources = Seq(module.sources)), after / "style" / "kotlin" ) } @@ -94,13 +96,9 @@ object KtfmtModuleTests extends TestSuite { style: String = "kotlin", format: Boolean = true, removeUnusedImports: Boolean = true, - sources: Seq[String] = Seq.empty + sources: Seq[mill.define.NamedTask[Seq[PathRef]]] = Seq.empty ): Seq[os.Path] = { - object module extends TestBaseModule with KotlinModule with KtfmtModule { - override def kotlinVersion: T[String] = KtfmtModuleTests.kotlinVersion - } - val eval = UnitTester(module, moduleRoot) eval(module.ktfmt( @@ -109,7 +107,7 @@ object KtfmtModuleTests extends TestSuite { format = format, removeUnusedImports = removeUnusedImports ), - sources = Leftover(sources: _*) + sources = Tasks(sources) )).fold( { case api.Result.Exception(cause, _) => throw cause diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index 1771b96608b..7b7ff6bbeeb 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -531,11 +531,11 @@ trait MainModule extends BaseModule0 { planTasks: Option[List[NamedTask[_]]] = None ): Result[Seq[PathRef]] = { def callVisualizeModule( - rs: List[NamedTask[Any]], - allRs: List[NamedTask[Any]] + tasks: List[NamedTask[Any]], + transitiveTasks: List[NamedTask[Any]] ): Result[Seq[PathRef]] = { val (in, out) = vizWorker - in.put((rs, allRs, ctx.dest)) + in.put((tasks, transitiveTasks, ctx.dest)) val res = out.take() res.map { v => println(upickle.default.write(v.map(_.path.toString()), indent = 2)) @@ -550,9 +550,7 @@ trait MainModule extends BaseModule0 { ) match { case Left(err) => Result.Failure(err) case Right(rs) => planTasks match { - case Some(allRs) => { - callVisualizeModule(rs, allRs) - } + case Some(allRs) => callVisualizeModule(rs, allRs) case None => callVisualizeModule(rs, rs) } } diff --git a/main/src/mill/main/VisualizeModule.scala b/main/src/mill/main/VisualizeModule.scala index f1269795815..f73e0a84e5e 100644 --- a/main/src/mill/main/VisualizeModule.scala +++ b/main/src/mill/main/VisualizeModule.scala @@ -4,13 +4,14 @@ import java.util.concurrent.LinkedBlockingQueue import coursier.LocalRepositories import coursier.core.Repository import coursier.maven.MavenRepository -import mill.define.{Discover, ExternalModule, Target, NamedTask} +import mill.define.{Discover, ExternalModule, NamedTask, Target} import mill.util.Util.millProjectModule -import mill.api.{Loose, Result, PathRef} +import mill.api.{Loose, PathRef, Result} import mill.define.Worker import org.jgrapht.graph.{DefaultEdge, SimpleDirectedGraph} import guru.nidi.graphviz.attribute.Rank.RankDir import guru.nidi.graphviz.attribute.{Rank, Shape, Style} +import mill.eval.Graph object VisualizeModule extends ExternalModule with VisualizeModule { def repositories: Seq[Repository] = Seq( @@ -45,10 +46,15 @@ trait VisualizeModule extends mill.define.TaskModule { val visualizeThread = new java.lang.Thread(() => while (true) { val res = Result.Success { - val (targets, rs, dest) = in.take() - val (sortedGroups, transitive) = mill.eval.Plan.plan(rs) + val (tasks, transitiveTasks, dest) = in.take() + val transitive = Graph.transitiveTargets(tasks) + val topoSorted = Graph.topoSorted(transitive) + val sortedGroups = Graph.groupAroundImportantTargets(topoSorted) { + case x: NamedTask[Any] if transitiveTasks.contains(x) => x + } + val (plannedForRender, _) = mill.eval.Plan.plan(transitiveTasks) - val goalSet = rs.toSet + val goalSet = transitiveTasks.toSet import guru.nidi.graphviz.model.Factory._ val edgesIterator = for ((k, vs) <- sortedGroups.items()) @@ -63,21 +69,21 @@ trait VisualizeModule extends mill.define.TaskModule { val edges = edgesIterator.map { case (k, v) => (k, v.toArray.distinct) }.toArray - val indexToTask = edges.flatMap { case (k, vs) => Iterator(k.task) ++ vs }.distinct + val indexToTask = edges.flatMap { case (k, vs) => Iterator(k) ++ vs }.distinct val taskToIndex = indexToTask.zipWithIndex.toMap val jgraph = new SimpleDirectedGraph[Int, DefaultEdge](classOf[DefaultEdge]) for (i <- indexToTask.indices) jgraph.addVertex(i) for ((src, dests) <- edges; dest <- dests) { - jgraph.addEdge(taskToIndex(src.task), taskToIndex(dest)) + jgraph.addEdge(taskToIndex(src), taskToIndex(dest)) } org.jgrapht.alg.TransitiveReduction.INSTANCE.reduce(jgraph) val nodes = indexToTask.map(t => - node(sortedGroups.lookupValue(t).render) + node(plannedForRender.lookupValue(t).render) .`with` { - if (targets.contains(t)) Style.SOLID + if (tasks.contains(t)) Style.SOLID else Style.DASHED } .`with`(Shape.BOX) diff --git a/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala b/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala index cd089c50af4..a5ee4b63380 100644 --- a/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala +++ b/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala @@ -97,7 +97,8 @@ object PalantirFormatModule extends ExternalModule with PalantirFormatBaseModule */ def formatAll( check: mainargs.Flag = mainargs.Flag(value = false), - @mainargs.arg(positional = true) sources: Tasks[Seq[PathRef]] + @mainargs.arg(positional = true) sources: Tasks[Seq[PathRef]] = + Tasks.resolveMainDefault("__.sources") ): Command[Unit] = Task.Command { val _sources = T.sequence(sources.value)().iterator.flatten diff --git a/scalalib/src/mill/scalalib/PlatformModuleBase.scala b/scalalib/src/mill/scalalib/PlatformModuleBase.scala new file mode 100644 index 00000000000..f9c6e4ca111 --- /dev/null +++ b/scalalib/src/mill/scalalib/PlatformModuleBase.scala @@ -0,0 +1,27 @@ +package mill.scalalib + +import mill._ +import os.Path + +trait PlatformModuleBase extends JavaModule { + override def millSourcePath: Path = super.millSourcePath / os.up + + /** + * The platform suffix of this [[PlatformModuleBase]]. Useful if you want to + * further customize the source paths or artifact names. + */ + def platformCrossSuffix: String = millModuleSegments + .value + .collect { case l: mill.define.Segment.Label => l.value } + .last + + override def sources: T[Seq[PathRef]] = Task.Sources { + super.sources().flatMap { source => + val platformPath = + PathRef(source.path / _root_.os.up / s"${source.path.last}-${platformCrossSuffix}") + Seq(source, platformPath) + } + } + + override def artifactNameParts: T[Seq[String]] = super.artifactNameParts().dropRight(1) +} diff --git a/scalalib/src/mill/scalalib/PlatformScalaModule.scala b/scalalib/src/mill/scalalib/PlatformScalaModule.scala index 82b15306ebd..2655d904979 100644 --- a/scalalib/src/mill/scalalib/PlatformScalaModule.scala +++ b/scalalib/src/mill/scalalib/PlatformScalaModule.scala @@ -1,7 +1,6 @@ package mill.scalalib -import mill._ -import os.Path +import mill.{PathRef, T, Task} /** * A [[ScalaModule]] intended for defining `.jvm`/`.js`/`.native` submodules @@ -14,8 +13,10 @@ import os.Path * built against and not something that should affect the filesystem path or * artifact name */ -trait PlatformScalaModule extends ScalaModule { - override def millSourcePath: Path = super.millSourcePath / os.up +trait PlatformScalaModule extends /* PlatformModuleBase with*/ ScalaModule { + // Cannot move stuff to PlatformModuleBase due to bincompat concerns + + override def millSourcePath: os.Path = super.millSourcePath / os.up /** * The platform suffix of this [[PlatformScalaModule]]. Useful if you want to