diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc
index 1dd014f43c4..66a0c2f2a29 100644
--- a/CONTRIBUTING.adoc
+++ b/CONTRIBUTING.adoc
@@ -1,6 +1,6 @@
= Contributing to Mill
:link-github: https://github.com/com-lihaoyi/mill
-:link-chat: https://discord.gg/xJCYRVMS
+
Thank you for considering contributing to Mill.
@@ -16,6 +16,6 @@ Here are some direct links:
* {link-github}/discussions[Discussion Forum on GitHub] - A place to ask question and discuss all kind of questions around Mill
* {link-github}/issues[Issue Tracker on GitHub] - Our issue tracker for bugs and features
* {link-github}/pulls[Pull Requests on GitHub] - All new features and bug fixes find their way into Mill via a pull request. You can also sketch new ideas by creating a draft pull requests.
-{link-chat}[Discord Chat] - You can also join our chat room if you like more direct communication or to just say hello
+
To build docs locally, `mill docs.localPages`. The last line of the command tells you where to browse the generated pages. From there you can follow the breadcrumbs.
diff --git a/docs/modules/ROOT/images/index/gradle.svg b/docs/modules/ROOT/images/index/gradle.svg
new file mode 100644
index 00000000000..8e7431e8230
--- /dev/null
+++ b/docs/modules/ROOT/images/index/gradle.svg
@@ -0,0 +1,19 @@
+
diff --git a/docs/modules/ROOT/images/index/iconscout-java.svg b/docs/modules/ROOT/images/index/iconscout-java.svg
new file mode 100644
index 00000000000..acf25c47343
--- /dev/null
+++ b/docs/modules/ROOT/images/index/iconscout-java.svg
@@ -0,0 +1,7 @@
+
diff --git a/docs/modules/ROOT/images/index/iconscout-kotlin.svg b/docs/modules/ROOT/images/index/iconscout-kotlin.svg
new file mode 100644
index 00000000000..48ff1667158
--- /dev/null
+++ b/docs/modules/ROOT/images/index/iconscout-kotlin.svg
@@ -0,0 +1,26 @@
+
diff --git a/docs/modules/ROOT/images/index/iconscout-scala.svg b/docs/modules/ROOT/images/index/iconscout-scala.svg
new file mode 100644
index 00000000000..279070c5eec
--- /dev/null
+++ b/docs/modules/ROOT/images/index/iconscout-scala.svg
@@ -0,0 +1,21 @@
+
diff --git a/docs/modules/ROOT/images/index/maven.png b/docs/modules/ROOT/images/index/maven.png
new file mode 100644
index 00000000000..5b57ab516c9
Binary files /dev/null and b/docs/modules/ROOT/images/index/maven.png differ
diff --git a/docs/modules/ROOT/images/index/sbt.png b/docs/modules/ROOT/images/index/sbt.png
new file mode 100644
index 00000000000..d684bbca7d1
Binary files /dev/null and b/docs/modules/ROOT/images/index/sbt.png differ
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 3cbe43df906..648c969b58f 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -3,108 +3,99 @@
// but we intentionally skim over them and do not go into depth: the focus is
// on end user goals and how to achieve them.
-.xref:index.adoc[]
-
-.xref:javalib/intro.adoc[]
-* xref:javalib/builtin-commands.adoc[]
-* xref:javalib/module-config.adoc[]
-* xref:javalib/dependencies.adoc[]
-* xref:javalib/testing.adoc[]
-* xref:javalib/linting.adoc[]
-* xref:javalib/publishing.adoc[]
-* xref:javalib/build-examples.adoc[]
-* xref:javalib/web-examples.adoc[]
-* xref:javalib/android-examples.adoc[]
-
-.xref:scalalib/intro.adoc[]
-* xref:scalalib/builtin-commands.adoc[]
-* xref:scalalib/module-config.adoc[]
-* xref:scalalib/dependencies.adoc[]
-* xref:scalalib/testing.adoc[]
-* xref:scalalib/linting.adoc[]
-* xref:scalalib/publishing.adoc[]
-* xref:scalalib/build-examples.adoc[]
-* xref:scalalib/web-examples.adoc[]
-
-.xref:kotlinlib/intro.adoc[]
-* xref:kotlinlib/builtin-commands.adoc[]
-* xref:kotlinlib/module-config.adoc[]
-* xref:kotlinlib/dependencies.adoc[]
-* xref:kotlinlib/testing.adoc[]
-* xref:kotlinlib/linting.adoc[]
-* xref:kotlinlib/publishing.adoc[]
-// * xref:kotlinlib/build-examples.adoc[]
-* xref:kotlinlib/web-examples.adoc[]
-* xref:kotlinlib/android-examples.adoc[]
-
-.Build Tool Comparisons
-* xref:comparisons/maven.adoc[]
-* xref:comparisons/gradle.adoc[]
-* xref:comparisons/sbt.adoc[]
-
-.The Mill CLI
-* xref:cli/installation-ide.adoc[]
-* xref:cli/flags.adoc[]
-* xref:cli/alternate-installation.adoc[]
-
+* xref:why-mill.adoc[]
+* xref:javalib/intro.adoc[]
+** xref:javalib/builtin-commands.adoc[]
+** xref:javalib/module-config.adoc[]
+** xref:javalib/dependencies.adoc[]
+** xref:javalib/testing.adoc[]
+** xref:javalib/linting.adoc[]
+** xref:javalib/publishing.adoc[]
+** xref:javalib/build-examples.adoc[]
+** xref:javalib/web-examples.adoc[]
+** xref:javalib/android-examples.adoc[]
+* xref:scalalib/intro.adoc[]
+** xref:scalalib/builtin-commands.adoc[]
+** xref:scalalib/module-config.adoc[]
+** xref:scalalib/dependencies.adoc[]
+** xref:scalalib/testing.adoc[]
+** xref:scalalib/linting.adoc[]
+** xref:scalalib/publishing.adoc[]
+** xref:scalalib/build-examples.adoc[]
+** xref:scalalib/web-examples.adoc[]
+* xref:kotlinlib/intro.adoc[]
+** xref:kotlinlib/builtin-commands.adoc[]
+** xref:kotlinlib/module-config.adoc[]
+** xref:kotlinlib/dependencies.adoc[]
+** xref:kotlinlib/testing.adoc[]
+** xref:kotlinlib/linting.adoc[]
+** xref:kotlinlib/publishing.adoc[]
+// ** xref:kotlinlib/build-examples.adoc[]
+** xref:kotlinlib/web-examples.adoc[]
+** xref:kotlinlib/android-examples.adoc[]
+* Build Tool Comparisons
+** xref:comparisons/maven.adoc[]
+** xref:comparisons/gradle.adoc[]
+** xref:comparisons/sbt.adoc[]
+* The Mill CLI
+** xref:cli/installation-ide.adoc[]
+** xref:cli/flags.adoc[]
+** xref:cli/alternate-installation.adoc[]
// This section gives a tour of the various user-facing features of Mill:
// library deps, out folder, queries, tasks, etc.. These are things that
// every Mill user will likely encounter, and are touched upon in the various
// language-specific sections, but here we go into a deeper language-agnostic
// discussion of what these Mill features ar and how they work
-.Mill Fundamentals
-* xref:fundamentals/tasks.adoc[]
-* xref:fundamentals/modules.adoc[]
-* xref:fundamentals/out-dir.adoc[]
-* xref:fundamentals/query-syntax.adoc[]
-* xref:fundamentals/library-deps.adoc[]
-* xref:fundamentals/cross-builds.adoc[]
-* xref:fundamentals/bundled-libraries.adoc[]
-
+* Mill Fundamentals
+** xref:fundamentals/tasks.adoc[]
+** xref:fundamentals/modules.adoc[]
+** xref:fundamentals/out-dir.adoc[]
+** xref:fundamentals/query-syntax.adoc[]
+** xref:fundamentals/library-deps.adoc[]
+** xref:fundamentals/cross-builds.adoc[]
+** xref:fundamentals/bundled-libraries.adoc[]
// This section talks about Mill plugins. While it could theoretically fit in
// either section above, it is probably an important enough topic it is worth
// breaking out on its own
-.Extending Mill
-* xref:extending/import-ivy-plugins.adoc[]
-* xref:extending/contrib-plugins.adoc[]
+* Extending Mill
+** xref:extending/import-ivy-plugins.adoc[]
+** xref:extending/contrib-plugins.adoc[]
// See also the list in Contrib_Plugins.adoc
-** xref:contrib/artifactory.adoc[]
-** xref:contrib/bintray.adoc[]
-** xref:contrib/bloop.adoc[]
-** xref:contrib/buildinfo.adoc[]
-** xref:contrib/codeartifact.adoc[]
-** xref:contrib/docker.adoc[]
-** xref:contrib/flyway.adoc[]
-** xref:contrib/gitlab.adoc[]
-** xref:contrib/jmh.adoc[]
-** xref:contrib/playlib.adoc[]
-** xref:contrib/proguard.adoc[]
-** xref:contrib/scalapblib.adoc[]
-** xref:contrib/scoverage.adoc[]
-** xref:contrib/sonatypecentral.adoc[]
-** xref:contrib/testng.adoc[]
-** xref:contrib/twirllib.adoc[]
-** xref:contrib/versionfile.adoc[]
-* xref:extending/thirdparty-plugins.adoc[]
-* xref:extending/running-jvm-code.adoc[]
-* xref:extending/writing-plugins.adoc[]
-* xref:extending/meta-build.adoc[]
-* xref:extending/new-language.adoc[]
-
+*** xref:contrib/artifactory.adoc[]
+*** xref:contrib/bintray.adoc[]
+*** xref:contrib/bloop.adoc[]
+*** xref:contrib/buildinfo.adoc[]
+*** xref:contrib/codeartifact.adoc[]
+*** xref:contrib/docker.adoc[]
+*** xref:contrib/flyway.adoc[]
+*** xref:contrib/gitlab.adoc[]
+*** xref:contrib/jmh.adoc[]
+*** xref:contrib/playlib.adoc[]
+*** xref:contrib/proguard.adoc[]
+*** xref:contrib/scalapblib.adoc[]
+*** xref:contrib/scoverage.adoc[]
+*** xref:contrib/sonatypecentral.adoc[]
+*** xref:contrib/testng.adoc[]
+*** xref:contrib/twirllib.adoc[]
+*** xref:contrib/versionfile.adoc[]
+** xref:extending/thirdparty-plugins.adoc[]
+** xref:extending/running-jvm-code.adoc[]
+** xref:extending/writing-plugins.adoc[]
+** xref:extending/meta-build.adoc[]
+** xref:extending/new-language.adoc[]
// This section focuses on diving into deeper, more advanced topics for Mill.
// These are things that most Mill developers would not encounter day to day,
// but people developing Mill plugins or working on particularly large or
// sophisticated Mill builds will need to understand.
-.Mill In Depth
-* xref:depth/large-builds.adoc[]
-* xref:depth/sandboxing.adoc[]
-* xref:depth/evaluation-model.adoc[]
-* xref:depth/design-principles.adoc[]
-* xref:depth/why-scala.adoc[]
-
+* Mill In Depth
+** xref:depth/large-builds.adoc[]
+** xref:depth/sandboxing.adoc[]
+** xref:depth/evaluation-model.adoc[]
+** xref:depth/design-principles.adoc[]
+** xref:depth/why-scala.adoc[]
// Reference pages that a typical user would not typically read top-to-bottom,
// but may need to look up once in a while, and thus should be written down
// *somewhere*.
-.Reference
-* {mill-doc-url}/api/latest/mill/index.html[Mill Scaladoc]
-* xref:reference/changelog.adoc[]
+* Reference
+** {mill-doc-url}/api/latest/mill/index.html[Mill Scaladoc]
+** xref:reference/changelog.adoc[]
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
index ac3570125f5..e088bb05d6a 100644
--- a/docs/modules/ROOT/pages/index.adoc
+++ b/docs/modules/ROOT/pages/index.adoc
@@ -70,19 +70,27 @@ xref:extending/writing-plugins.adoc#_publishing[publish them] to Maven Central f
To begin using Mill, check out the introductory documentation for each language:
-* xref:javalib/intro.adoc[]
-* xref:scalalib/intro.adoc[]
-* xref:kotlinlib/intro.adoc[]
-If you prefer a video introduction, see the following tech talk that was presented
-at the Japan Java User Group Fall Conference Oct 2024:
+[cols="1a,1a,1a"]
+|===
+| xref:javalib/intro.adoc[image:index/iconscout-java.svg[java,32] Java with Mill]
+| xref:scalalib/intro.adoc[image:index/iconscout-scala.svg[scala,20] Scala with Mill]
+| xref:kotlinlib/intro.adoc[image:index/iconscout-kotlin.svg[kotlin,32] Kotlin with Mill]
+|===
-* https://www.youtube.com/watch?v=Dry6wMRN6MI[Video: Better Java Builds with the Mill Build Tool]
+For a quick introduction of why you may care about Mill, see the following page:
+
+* xref:why-mill.adoc[]
+
+Or if you prefer a video introduction:
+
+* https://www.youtube.com/watch?v=Dry6wMRN6MI[Video: Better Java Builds with the Mill Build Tool],
+ Japan Java User Group Fall Conference Oct 2024
Mill is used to build many real-world projects, such as the
https://github.com/swaldman/c3p0[C3P0 JDBC Connection Pool],
-https://github.com/coursier/coursier[Coursier JVM dependency resolver],
+https://github.com/coursier/coursier[Coursier dependency resolver],
https://github.com/com-lihaoyi/Ammonite[Ammonite REPL], and the
https://github.com/SpinalHDL/SpinalHDL[SpinalHDL] and
https://github.com/chipsalliance/chisel[Chisel] hardware design frameworks.
@@ -95,9 +103,15 @@ https://gradle.org/[Gradle], https://bazel.build/[Bazel], but tries to learn fro
strengths of each tool and improve on their weaknesses. For comparisons with existing
build tools, check out these pages:
-* xref:comparisons/maven.adoc[Mill vs Maven]
-* xref:comparisons/gradle.adoc[Mill vs Gradle]
-* xref:comparisons/sbt.adoc[Mill vs SBT]
+
+[cols="1a,1a,1a"]
+|===
+| xref:comparisons/maven.adoc[image:index/maven.png[maven,24] Mill vs Maven]
+| xref:comparisons/gradle.adoc[image:index/gradle.svg[gradle,32] Mill vs Gradle]
+| xref:comparisons/sbt.adoc[image:index/sbt.png[sbt,32] Mill vs SBT]
+|===
+
+
If you want to contribute to Mill, or are interested in the fundamental ideas behind
Mill rather than the user-facing benefits discussed above, check out the page on
@@ -107,3 +121,13 @@ they are interesting:
* https://www.youtube.com/watch?v=UsXgCeU-ovI[Video: A Deep Dive into the Mill Build Tool]
+To engage Mill with the community, you can use the channels below:
+
+* https://github.com/com-lihaoyi/mill/discussions[Mill Github Discussions]
+* https://discord.com/invite/scala[Scala Discord], in the TOOLING#mill channel
+
+Mill maintains an open list of issue bounties below, with payouts ranging from
+500USD to 3000USD per issue. Feel free to look through the list and try your
+hand at any bounties that may interest you
+
+* https://github.com/orgs/com-lihaoyi/discussions/6[Open Issue Bounties]
\ No newline at end of file
diff --git a/docs/modules/ROOT/pages/javalib/intro.adoc b/docs/modules/ROOT/pages/javalib/intro.adoc
index 9d96e594466..bd497c00ac5 100644
--- a/docs/modules/ROOT/pages/javalib/intro.adoc
+++ b/docs/modules/ROOT/pages/javalib/intro.adoc
@@ -12,7 +12,7 @@
// what Mill is, hopefully have downloaded an example to try out, and be
// interested in learning more about the Mill build tool
-= Building Java Projects with Mill
+= Building Java with Mill
:page-aliases: Intro_to_Mill.adoc, Intro_to_Mill_for_Java.adoc, Java_Intro_to_Mill.adoc
include::partial$gtag-config.adoc[]
diff --git a/docs/modules/ROOT/pages/scalalib/intro.adoc b/docs/modules/ROOT/pages/scalalib/intro.adoc
index a1fb3bbb050..7b6373b188b 100644
--- a/docs/modules/ROOT/pages/scalalib/intro.adoc
+++ b/docs/modules/ROOT/pages/scalalib/intro.adoc
@@ -11,7 +11,7 @@
// what Mill is, hopefully have downloaded an example to try out, and be
// interested in learning more about the Mill build tool
-= Building Scala Projects with Mill
+= Building Scala with Mill
:page-aliases: Intro_to_Mill_for_Scala.adoc, Scala_Intro_to_Mill.adoc
include::partial$gtag-config.adoc[]
diff --git a/docs/modules/ROOT/pages/why-mill.adoc b/docs/modules/ROOT/pages/why-mill.adoc
new file mode 100644
index 00000000000..1a083e1580a
--- /dev/null
+++ b/docs/modules/ROOT/pages/why-mill.adoc
@@ -0,0 +1,527 @@
+= Why Mill?
+
+Mill is a fast build tool for Java, Scala, and Kotlin. Although the Java
+compiler is very fast and the Java language is easy to learn, JVM build tools are
+known to be slow and hard to use. Mill tries to offer a better alternative: 2-10x faster
+than Maven or Gradle, better IDE support, and extensibility without needing plugins.
+This results in time savings due to less time waiting for your build tool, as well as less
+time struggling with your build tool, and more time to focus on the actual work you need to do.
+
+At a first glance, Mill looks like any other build tool. You have build files, you configure
+dependencies, you can compile, run, or test your project:
+
+```scala
+// build.mill
+package build
+import mill._, javalib._
+
+object foo extends JavaModule {
+ def ivyDeps = Agg(
+ ivy"net.sourceforge.argparse4j:argparse4j:0.9.0",
+ ivy"org.thymeleaf:thymeleaf:3.1.1.RELEASE"
+ )
+
+ object test extends JavaTests with TestModule.Junit4
+}
+```
+```bash
+> /mill foo.compile
+compiling 1 Java source...
+
+> /mill foo.run --text hello
+
hello
+
+> ./mill foo.test
+Test foo.FooTest.testEscaping finished, ...
+Test foo.FooTest.testSimple finished, ...
+0 failed, 0 ignored, 2 total, ...
+```
+
+Beyond the basics, Mill provides 3 major advantages over other build tools. The comparison pages
+for the respective build tool go into more detail (for xref:comparisons/maven.adoc[Maven],
+xref:comparisons/gradle.adoc[Gradle], and xref:comparisons/sbt.adoc[SBT]), but at a high level
+these advantages are:
+
+1. *Performance*: Mill offers a 2-10x speedup means less time waiting for your build tool,
+ meaning less time waiting for your build and more time doing useful work
+
+2. *Ease of Use*: Mill has better IDE support in IntelliJ and VSCode and richer
+ visualization tools that other tools, to help understand your build and what it is doing
+
+3. *Extensibility*: Mill lets you write code or use any published JVM library in your build,
+ customizing it to your needs without being limited by third-party plugins
+
+We will discuss each one in turn.
+
+## Performance
+
+### Maven
+
+Overall across our benchmarks, Mill is 5-10x faster than Maven for clean compiles,
+both parallel and sequential, and for many modules or for a single module:
+
+|===
+| Benchmark | Maven | Mill | Speedup
+| xref:comparisons/maven.adoc#_sequential_clean_compile_all[Sequential Clean Compile All] | 2m 31.12s | 0m 22.19s | 6.8x
+| xref:comparisons/maven.adoc#_parallel_clean_compile_all[Parallel Clean Compile All] | 1m 16.45s | 0m 09.95s | 7.7x
+| xref:comparisons/maven.adoc#_clean_compile_single_module[Clean Compile Single Module] | 0m 19.62s | 0m 02.17s | 9.0x
+| xref:comparisons/maven.adoc#_incremental_compile_single_module[Incremental Compile Single Module] | 0m 21.10s | 0m 00.54s | 39.1x
+| xref:comparisons/maven.adoc#_no_op_compile_single_module[No-Op Compile Single Module] | 0m 17.34s | 0m 00.47s | 36.9x
+|===
+
+For the purposes of this page, we will focus on two benchmarks
+
+#### Parallel Clean Compile All
+
+|===
+| Benchmark | Maven | Mill | Speedup
+| xref:comparisons/maven.adoc#_parallel_clean_compile_all[Parallel Clean Compile All] | 1m 16.45s | 0m 09.95s | 7.7x
+|===
+
+
+```bash
+> time ./mvnw -T 10 -DskipTests -Dcheckstyle.skip -Denforcer.skip=true clean install
+
+> ./mill clean; time ./mill __.compile
+```
+
+
+This benchmark involves running `clean` to delete all generated files and re-compiling
+everything in parallel.
+
+* For Maven, parallelism is opt-in via `-T 10`, while for Mill it is enabled by default.
+* For Maven, tests and linters are opt-out via the `-D` properties, while in Mill
+ tests an linters are opt-in
+
+Mill sees a significant ~8x speedup over Maven for this benchmark.
+
+#### Incremental Compile Single-Module
+
+|===
+| Benchmark | Maven | Mill | Speedup
+| xref:comparisons/maven.adoc#_incremental_compile_single_module[Incremental Compile Single Module] | 0m 21.10s | 0m 00.54s | 39.1x
+|===
+
+```bash
+$ echo "" >> common/src/main/java/io/netty/util/AbstractConstant.java
+$ time ./mvnw -pl common -DskipTests -Dcheckstyle.skip -Denforcer.skip=true install
+Compiling 174 source files to /Users/lihaoyi/Github/netty/common/target/classes
+Compiling 60 source files to /Users/lihaoyi/Github/netty/common/target/test-classes
+
+
+$ echo "" >> common/src/main/java/io/netty/util/AbstractConstant.java
+$ time ./mill common.test.compile
+compiling 1 Java source to /Users/lihaoyi/Github/netty/out/common/compile.dest/classes ...
+```
+
+This benchmark involves making a single edit to a single already-compiled file in `common` -
+adding a single newline to the end of the file - and re-compiling `common` and `common.test`.
+
+Mill sees a huge ~39x speedup for this benchmark, because Mill's incremental compiler
+(https://github.com/sbt/zinc[Zinc]) is able to detect that only one file in one module
+has changed, and that the change is small enough
+to not require other files to re-compile. In contrast, Maven re-compiles all files in both
+modules, even though only one file was touched and the change was trivial.
+
+
+### Gradle
+
+The comparison with Gradle is less stark, but still significant. Mill is 3-4x faster than Gradle
+across the various workflows:
+
+
+|===
+| Benchmark | Gradle | Mill | Speedup
+| xref:comparisons/maven.adoc#_sequential_clean_compile_all[Sequential Clean Compile All] | 17.6s | 5.40s | 3.3x
+| xref:comparisons/maven.adoc#_parallel_clean_compile_all[Parallel Clean Compile All] | 12.3s | 3.57s | 3.4x
+| xref:comparisons/maven.adoc#_clean_compile_single_module[Clean Compile Single Module] | 4.41s | 1.20s | 3.7x
+| xref:comparisons/maven.adoc#_incremental_compile_single_module[Incremental Compile Single Module] | 1.37s | 0.51s | 2.7x
+| xref:comparisons/maven.adoc#_no_op_compile_single_module[No-Op Compile Single Module] | 0.94s | 0.46s | 2.0x
+|===
+
+Again, for the purposes of this page, we will focus on two benchmarks
+
+#### Parallel Clean Compile All
+
+|===
+| Benchmark | Gradle | Mill | Speedup
+| xref:comparisons/maven.adoc#_parallel_clean_compile_all[Parallel Clean Compile All] | 12.3s | 3.57s | 3.4x
+|===
+
+```bash
+$ ./gradlew clean; time ./gradlew classes testClasses --no-build-cache
+
+$ ./mill clean; time ./mill __.compile
+```
+
+Here we only run compilation for classes and test classes, without linting or testing or anything else.
+Both Mill and Gradle are parallel by default, with 1 thread per core. For Gradle we disabled the global
+build cache to ensure we can benchmark the actual compilation time.
+
+We measure Mill being ~3.4x faster than Gradle for this benchmark.
+
+#### Incremental Compile Single-Module
+
+|===
+| Benchmark | Gradle | Mill | Speedup
+| xref:comparisons/maven.adoc#_incremental_compile_single_module[Incremental Compile Single Module] | 1.37s | 0.51s | 2.7x
+|===
+
+```bash
+$ echo "" >> src/main/java/org/mockito/BDDMockito.java; time ./gradlew :classes
+
+$ echo "" >> src/main/java/org/mockito/BDDMockito.java; time ./mill compile
+compiling 1 Java source to /Users/lihaoyi/Github/netty/out/common/compile.dest/classes ...
+```
+
+
+Again, this benchmark involves making a single edit to a single already-compiled file in the
+root module - adding a single newline to the end of the file - and re-compiling it along with
+its tests.
+
+Both Gradle and Mill appear to do a good job limiting the compilation to only the changed
+file, but Mill has less fixed overhead than Gradle does, finishing in about ~0.5s
+rather than ~1.5 seconds.
+
+
+## Ease of Use
+
+The second area that Mill does well compared to tools like Maven or Gradle is in its ease
+of use.This is not just in superficial things like the build file or command-line syntax,
+but also in how Mill exposes how your build works and what your build is doing so you can
+understand it and confidently make changes. We will consider three cases: the Mill Chrome
+Profile, Mill Visualize, and Mill's IDE support
+
+### Chrome Profiles
+
+All Mill runs generate some debugging metadata files in `out/mill-*`. One of these
+is `out/mill-chrome-profile.json`, which is a file following the Chrome Profiling format.
+It can be loaded into any Chrome browser's built in `chrome://tracing` UI, to let you
+interactively explore what Mill was doing during its last run. e.g. when performing a
+clean compile on the Netty codebase, the profile ends up looking like this:
+
+image::comparisons/NettyCompileProfile.png[]
+
+The Chrome profile shows what task each Mill thread was executing throughout the run.
+The Chrome profiling UI is interactive, so you can zoom in and out, or click on individual
+tasks to show the exact duration and other metadata.
+
+But the real benefit of the Chrome profile isn't the low-level data it provides, but the
+high-level view:
+
+* In the profile above, it is clear that for the first ~700ms, Mill is able
+ to use all cores on 10 cores on my laptop to do useful work.
+* But after that, utilization is
+ much more sparse: `common.compile`, `buffer.compile`, `transport.compile`, `codec.compile`,
+ appear to wait for one another and run sequentially one after another.
+
+
+This waiting is likely due to dependencies between them, and they take long enough that all
+the other tasks depending on them get held up. For example, when `codec.compile` finishes
+above, we can see a number of downstream tasks immediately start running.
+
+This understanding of your build's performance profile is not just an academic exercise, but
+provides actionable information:
+
+* If I wanted faster Netty clean compiles, speeding up `common.compile`, `buffer.compile`,
+ `transport.compile`, or `codec.compile` would make the most impact.
+
+* On the other hand, time speeding up the various `codec-*.compile` tasks would help not at all:
+ these tasks are already running at a time where the CPUs are mostly idle.
+
+Most build tools do provide some way of analyzing build performance, but none of them provide
+it as easily as Mill does: any Mill run generates a profile automatically, and any computer
+with Chrome on it is able to load and let you explore that profile. That is a powerful tool to
+help engineers understand what the build is doing: any engineer who felt a build was slow
+can trivially load it into their Chrome browser to analyze and figure out what.
+
+### Mill Visualize
+
+Apart from the Mill Chrome Profile, Mill also provides the `./mill visualize` command, which
+is useful to show the logical dependency graph between tasks. For example, we can use
+`./mill visualize __.compile` (double `__` means wildcard) to
+show the dependency graph between the modules of the Netty build below:
+
+image::comparisons/NettyCompileGraph.svg[]
+
+(_Right-click open-image-in-new-tab to see full size_)
+
+In this graph, we can clearly see that `common.compile`, `buffer.compile`,`transport.compile`,
+and `codec.compile` depend on each other in a linear fashion. This explains why they each must
+wait for the prior task to complete before starting, and cannot run in parallel with one another.
+Furthermore, we can again confirm that many of the `codec-*.compile` tasks depend on `codec.compile`,
+which is in the profile why we saw them waiting for the upstream task to complete before starting.
+
+Although these are things we could have _guessed_ from looking at the Chrome Profile above,
+`./mill visualize` gives you a separate angle from which to look at your build. Together these
+tools can help give greater understanding of what your build is doing and why it is doing that:
+something that can be hard to come by with build tools that are often considered confusing and
+inscrutable.
+
+### IDE Support
+
+One area that Mill does better than Gradle is providing a seamless IDE experience. For example,
+consider the snippet below where we are using Gradle to configure the javac compiler options.
+Due to `.gradle` files being untyped Groovy, the autocomplete and code-assist experience working
+with these files is hit-or-miss. In the example below, we can see that IntelliJ is able to identify
+that `compileArgs` exists and has the type `List`:
+
+image::comparisons/IntellijMockitoGradleCompileOptions.png[]
+
+But if you try to jump to definition or find out anything else about it you hit a wall:
+
+image::comparisons/IntellijMockitoGradleCompileOptions2.png[]
+
+Often working with build configurations feels like hitting dead ends: if you don't have
+`options.compilerArgs` memorized in your head, there is literally nothing you can do in your editor to
+make progress to figure out what it is or what it is used for. That leaves you googling
+for answers, which can be a frustrating experience that distracts you from the task at hand.
+
+The fundamental problem with tools like Gradle is that the code you write does not
+actually perform the build: rather, you are just setting up some data structure that
+is used to configure the _real_ build engine that runs later. Thus when you explore
+the Gradle build in an IDE, the IDE can only explore the configuration logic (which
+is usually un-interesting) and is unable to explore the actual build logic (which
+is what you actually care about!)
+
+In comparison, Mill's `.mill` files are all statically typed, and as a result IntelliJ is easily able to
+pull up the documentation for `def javacOptions`, even though it doesn't have any special support
+for Mill built into the IDE:
+
+image::comparisons/IntellijMockitoMillJavacOptionsDocs.png[]
+
+Apart from static typing, the way Mill builds are structured also helps the IDE: Mill
+code _actually performs your build_, rather than configuring some opaque build engine.
+While that sounds academic, one concrete consequence is that IntelliJ is able to take
+your `def javacOptions` override and
+find the original definitions that were overridden, and show you where they are defined:
+
+image::comparisons/IntellijMockitoMillJavacOptionsParents.png[]
+
+image::comparisons/IntellijMockitoMillJavacOptionsDef.png[]
+
+Furthermore, because task dependencies in Mill are just normal method calls, IntelliJ is
+able to _find usages_, showing you where the task is used. Below, we can see the method
+call in the `def compile` task, which uses `javacOptions()` along with a number of other tasks:
+
+image::comparisons/IntellijMockitoMillCompile.png[]
+
+From there, if you are curious about any of the other tasks used alongside `javacOptions`, it's
+easy for you to pull up _their_ documentation, jump to _their_
+definition, or find _their_ usages. For example we can pull up the docs of
+
+`compileClasspath()` below, jump to _its_ implementation, and continue
+interactively exploring your build logic:
+
+image::comparisons/IntellijMockitoMillCompileClasspath.png[]
+
+Unlike most other build tools, Mill builds can be explored interactively in your
+IDE. If you do not know what something does, it's documentation, definition, or usages is always
+one click away in IntelliJ or VSCode. This isn't a new experience for Java developers, as it
+is what you would be used to day-to-day in your application code! But Mill brings that same
+polished experience to your build system - traditionally some that that has been opaque
+and hard to understand - and does so in a way that no other build tool does.
+
+
+## Extensibility
+
+Mill allows you to directly write code to configure your build, and even download libraries
+from Maven Central.
+
+Most build tools need plugins to do anything: if you want to Foo you need a
+Foo plugin, if you want to Bar you need a Bar plugin, for any possible Foo or Bar. These could
+be simple tasks - zipping up files, pre-rendering web templates, preparing static assets for
+deployment - but even a tasks that would be trivial to implement in a few lines of code requires
+you to Google for third-party plugins, dig through their Github to see which one is best
+maintained, and hope for the best when you include it in your build. And while you could
+write plugins yourself, doing so is usually challenging and non-trivial.
+
+Mill is different. Although it does have plugins for more advanced integrations, for most
+simple things you can directly write code to achieve what you want, using the bundled
+filesystem, subprocess, and dependency-management libraries. And even if you need third-party
+libraries from Maven Central to do Foo, you can directly import the "Foo" library and use it
+directly, without having to find a "Foo build plugin" wrapper.
+
+### Simple Custom Tasks
+
+The following Mill build is a minimal Java module `foo`. It contains no custom configuration, and
+so inherits all the defaults from `mill.javalib.JavaModule`: default source folder layout, default
+assembly configuration, default compiler flags, and so on.
+
+```scala
+package build
+import mill._, javalib._
+
+object foo extends JavaModule {
+}
+```
+```bash
+> mill compile
+Compiling 1 Java source...
+```
+
+If you want to add a custom task, this is as simple as defining a method e.g.
+`def lineCount = Task { ... }`. The body of `Task` performs the action we want, and
+can depend on other tasks such as `allSourceFiles()` below:
+
+```scala
+package build
+import mill._, javalib._
+
+object foo extends JavaModule {
+ /** Total number of lines in module source files */
+ def lineCount = Task {
+ allSourceFiles().map(f => os.read.lines(f.path).size).sum
+ }
+}
+```
+
+Once we define a new task, we can immediately begin using it in our build.
+`lineCount` is not used by any existing `JavaModule` tasks, but we can still
+show its value via the Mill command line to force it to evaluate:
+
+```bash
+> mill show foo.lineCount
+17
+```
+
+
+### Overriding Tasks
+
+To wire up `lineCount` into our main `JavaModule` `compile`/`test`/`run` tasks,
+one way is to take the line count value and write it to a file in `def resources`.
+This file can then be read at runtime as a JVM resource. We do that below
+by overriding `def resources` and making it depend on `lineCount`, in addition
+to its existing value `super.resources()`:
+
+```scala
+package build
+import mill._, javalib._
+
+object foo extends JavaModule {
+ /** Total number of lines in module source files */
+ def lineCount = Task {
+ allSourceFiles().map(f => os.read.lines(f.path).size).sum
+ }
+
+ /** Generate resources using lineCount of sources */
+ override def resources = Task {
+ os.write(Task.dest / "line-count.txt", "" + lineCount())
+ super.resources() ++ Seq(PathRef(Task.dest))
+ }
+}
+```
+
+
+Because `override def resources` overrides the existing `resources` method used
+in the rest of `JavaModule`, the downstream tasks automatically now use the new
+override instead, as that is how overrides work. That means if you call `mill foo.run`,
+it will automatically pick up the new `line-count.txt` file and make it available to
+the application code to use e.g. below, where we just print it out:
+
+```bash
+> mill foo.run
+Line Count: 17
+```
+
+Most developers do not need to embed the line-count of their codebase in a resource
+file to look up at runtime, but nevertheless this example shows how easy it is to write
+code to perform ad-hoc tasks without needing to pull in and configure some third-party
+plugin. And we get full IDE support with autocomplete/navigation/documentation/etc.
+while we are writing our custom task.
+
+
+While most build tools do allow writing and wiring up custom tasks, none of them
+have a workflow as simple as Mill. Next, we'll look at a more realistic example,
+which includes usage of third-party libraries in the build.
+
+### Using Libraries from Maven Central in Tasks
+
+Earlier on we discussed possibly pre-rendering HTML pages in the build so they can be
+served at runtime. The use case for this are obvious: if a page never changes, rendering
+it on every request is wasteful, and even rendering it once and then caching it can impact
+your application startup time. Thus, you may want to move some HTML rendering to build-time,
+but with traditional build tools such a move is sufficiently inconvenient and complicated
+that people do not do it.
+
+With Mill, pre-rendering HTML at build time is really easy, even if you need a third-party
+library. Mill does not ship with a bundled HTML templating engine, but you can use the
+`import $ivy` syntax to include one such as Thymeleaf, which would immediately make the
+Thymeleaf classes available for you to import and use in your build as below:
+
+
+```scala
+package build
+import mill._, javalib._
+import $ivy.`org.thymeleaf:thymeleaf:3.1.1.RELEASE`
+import org.thymeleaf.TemplateEngine
+import org.thymeleaf.context.Context
+object foo extends JavaModule {
+ def htmlSnippet = Task {
+ val context = new Context()
+ context.setVariable("heading", "hello")
+ new TemplateEngine().process(
+ "",
+ context
+ )
+ }
+ def resources = Task.Sources{
+ os.write(Task.dest / "snippet.txt", htmlSnippet())
+ super.resources() ++ Seq(PathRef(Task.dest))
+ }
+}
+```
+
+Once we have run `import $ivy`, we can import `TemplateEngine`, `Context`, and replace our
+`def lineCount` with a `def htmlSnippet` task that uses Thymeleaf to render HTML. Again,
+we get full IDE support for working with the Thymeleaf Java API, the new `htmlSnippet` task
+is inspectable from the Mill command line via `show`, and we wire it up into
+`def resources` so it can be inspected and used at runtime by the application
+(in this case just printed out):
+
+
+```bash
+> mill show foo.htmlSnippet
+"
+```
+
+Rendering HTML using the Thymeleaf templating engine is not rocket science, but what is
+interesting here is what we did _not_ need to do:
+
+* We did _not_ need to find a Thymeleaf-Mill plugin in order to include Thymeleaf in our
+ build
+
+* We did _not_ need to learn a special API or framework for authoring build plugins ourselves
+ to write a plugin to include Thymeleaf in our build
+
+Instead, we could simply import Thymeleaf directly from Maven Central and use it just
+like we would use it in any Java application, complete with full IDE support for
+autocomplete and code navigation, with the same experience you probably are already
+used to for your application code. This makes it an order of magnitude easier for
+non-experts to configure their build to do exactly what they need, rather than be
+limited by what some unmaintained third-party plugin might support. And although
+the configuration is done in the Scala language, the syntax should be relatively
+familiar ("Java without Semicolons" it is sometimes called) and the JVM libraries
+and tools (e.g. Thymeleaf, IntelliJ, VSCode) are the exact same libraries and tools
+you are already used to.
+
+## Conclusion
+
+To wrap up, Mill does all the same things that other build tools like Maven or Gradle do,
+but aims to do them better: faster, easier to use, and easier to extend.
+This means both time saved waiting for your build tool to run in day-to-day work, as well
+as time saved when you inevitably need to evolve or adjust your build system to accommodate
+changing requirements.
+
+With Mill, you can therefore spend less time waiting for or fighting with your build tool,
+and more time on the actual work you are trying to accomplish. And while traditionally build
+systems were often mysterious black boxes that only experts could work with, Mill's ease
+of use and ease of extension democratize the build system so any developer can figure out
+what its doing or extend it to do exactly what they need.
diff --git a/docs/package.mill b/docs/package.mill
index e43370f5272..baa449acf7d 100644
--- a/docs/package.mill
+++ b/docs/package.mill
@@ -3,7 +3,7 @@ import org.jsoup._
import mill.util.Jvm
import mill._, scalalib._
import de.tobiasroeser.mill.vcs.version.VcsVersion
-import collection.JavaConverters._
+import scala.jdk.CollectionConverters._
/** Generates the mill documentation with Antora. */
object `package` extends RootModule {
@@ -28,6 +28,7 @@ object `package` extends RootModule {
Jvm.runSubprocess(
commandArgs = Seq(
npmExe,
+ "--no-audit",
"install",
"@antora/cli@3.1.9",
"@antora/site-generator-default@3.1.9",
@@ -192,6 +193,7 @@ object `package` extends RootModule {
val taggedSources = for (path <- extraSources) yield {
s""" - url: ${build.baseDir}
| start_path: ${path.relativeTo(build.baseDir)}
+ | edit_url: false
|""".stripMargin
}
s"""site:
@@ -206,6 +208,7 @@ object `package` extends RootModule {
| - url: ${if (authorMode) build.baseDir else build.Settings.projectUrl}
| branches: []
| tags: ${build.Settings.legacyDocTags.filter(_ => !authorMode).map("'" + _ + "'").mkString("[", ",", "]")}
+ | edit_url: false
| start_path: docs/antora
|
|${taggedSources.mkString("\n\n")}
@@ -237,7 +240,7 @@ object `package` extends RootModule {
|""".stripMargin
}
- def oldDocSources = Task {
+ def oldDocSources: T[Seq[PathRef]] = Task {
for (oldVersion <- build.Settings.docTags) yield {
val checkout = T.dest / oldVersion
os.proc("git", "clone", T.workspace / ".git", checkout).call(stdout = os.Inherit)
@@ -254,21 +257,22 @@ object `package` extends RootModule {
generatePages(authorMode = false)().apply(oldDocSources().map(_.path))
}
- def localPages = Task {
+ def localPages: T[PathRef] = Task {
val pages = generatePages(authorMode = true)().apply(oldDocSources().map(_.path))
T.log.outputStream.println(
- s"You can browse the local pages at: ${(pages.path / "index.html").toNIO.toUri()}"
+ s"You can browse the pages at: ${(pages.path / "index.html").toNIO.toUri()}"
)
+ pages
}
- def fastPages = Task {
+ def fastPages: T[PathRef] = Task {
val pages = generatePages(authorMode = true)().apply(Nil)
T.log.outputStream.println(
- s"You can browse the local pages at: ${(pages.path / "index.html").toNIO.toUri()}"
+ s"You can browse the pages at: ${(pages.path / "index.html").toNIO.toUri()}"
)
pages
}
- def generatePages(authorMode: Boolean) = T.task { extraSources: Seq[os.Path] =>
+ def generatePages(authorMode: Boolean) = Task.Anon { extraSources: Seq[os.Path] =>
T.log.errorStream.println("Creating Antora playbook ...")
// dependency to sources
source()
diff --git a/docs/supplemental-ui/partials/header-content.hbs b/docs/supplemental-ui/partials/header-content.hbs
index 5f134d0e22b..e3a6630f666 100644
--- a/docs/supplemental-ui/partials/header-content.hbs
+++ b/docs/supplemental-ui/partials/header-content.hbs
@@ -20,7 +20,7 @@
APIIssuesDiscuss
- Chat
+