diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml
index a2fecdaee51..162cc87bed7 100644
--- a/.github/workflows/actions.yml
+++ b/.github/workflows/actions.yml
@@ -80,7 +80,7 @@ jobs:
- java-version: 11
millargs: "'example.thirdparty[{mockito,acyclic,commons-io}].local.test'"
- java-version: 17
- millargs: "'example.thirdparty[{fansi,jimfs,netty}].local.test'"
+ millargs: "'example.thirdparty[{fansi,jimfs,netty,gatling}].local.test'"
- java-version: 11
millargs: "'example.depth.__.local.test'"
- java-version: 17
diff --git a/build.sc b/build.sc
index 743505015c0..3ca7d4068c3 100644
--- a/build.sc
+++ b/build.sc
@@ -1359,7 +1359,8 @@ object example extends Module {
"jimfs" -> ("google/jimfs", "5b60a42eb9d3cd7a2073d549bd0cb833f5a7e7e9"),
"commons-io" -> ("apache/commons-io", "b91a48074231ef813bc9b91a815d77f6343ff8f0"),
"netty" -> ("netty/netty", "20a790ed362a3c11e0e990b58598e4ac6aa88bef"),
- "mockito" -> ("mockito/mockito", "97f3574cc07fdf36f1f76ba7332ac57675e140b1")
+ "mockito" -> ("mockito/mockito", "97f3574cc07fdf36f1f76ba7332ac57675e140b1"),
+ "gatling" -> ("gatling/gatling", "3870fda86e6bca005fbd53108c60a65db36279b6")
)
object thirdparty extends Cross[ThirdPartyModule](listIn(millSourcePath / "thirdparty"))
trait ThirdPartyModule extends ExampleCrossModule {
diff --git a/docs/modules/ROOT/images/GatlingCompileGraph.svg b/docs/modules/ROOT/images/GatlingCompileGraph.svg
new file mode 100644
index 00000000000..ee35599c859
--- /dev/null
+++ b/docs/modules/ROOT/images/GatlingCompileGraph.svg
@@ -0,0 +1,649 @@
+
diff --git a/docs/modules/ROOT/images/GatlingCompileProfile.png b/docs/modules/ROOT/images/GatlingCompileProfile.png
new file mode 100644
index 00000000000..ad7259372d8
Binary files /dev/null and b/docs/modules/ROOT/images/GatlingCompileProfile.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingMillPlugin1.png b/docs/modules/ROOT/images/IntellijGatlingMillPlugin1.png
new file mode 100644
index 00000000000..c99ec5e2286
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingMillPlugin1.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingMillPlugin2.png b/docs/modules/ROOT/images/IntellijGatlingMillPlugin2.png
new file mode 100644
index 00000000000..f1b59ea62b4
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingMillPlugin2.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingMillTask1.png b/docs/modules/ROOT/images/IntellijGatlingMillTask1.png
new file mode 100644
index 00000000000..3767fee4c78
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingMillTask1.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingMillTask2.png b/docs/modules/ROOT/images/IntellijGatlingMillTask2.png
new file mode 100644
index 00000000000..23d271fe36e
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingMillTask2.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingMillTask3.png b/docs/modules/ROOT/images/IntellijGatlingMillTask3.png
new file mode 100644
index 00000000000..dd79596d3d2
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingMillTask3.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingSbtPlugin1.png b/docs/modules/ROOT/images/IntellijGatlingSbtPlugin1.png
new file mode 100644
index 00000000000..0e5ba930036
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingSbtPlugin1.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingSbtPlugin2.png b/docs/modules/ROOT/images/IntellijGatlingSbtPlugin2.png
new file mode 100644
index 00000000000..010f6728954
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingSbtPlugin2.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingSbtTask1.png b/docs/modules/ROOT/images/IntellijGatlingSbtTask1.png
new file mode 100644
index 00000000000..b4ae27fc507
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingSbtTask1.png differ
diff --git a/docs/modules/ROOT/images/IntellijGatlingSbtTask2.png b/docs/modules/ROOT/images/IntellijGatlingSbtTask2.png
new file mode 100644
index 00000000000..65ed87dc398
Binary files /dev/null and b/docs/modules/ROOT/images/IntellijGatlingSbtTask2.png differ
diff --git a/docs/modules/ROOT/images/MockitoCompileGraph.svg b/docs/modules/ROOT/images/MockitoCompileGraph.svg
new file mode 100644
index 00000000000..b71abdaf056
--- /dev/null
+++ b/docs/modules/ROOT/images/MockitoCompileGraph.svg
@@ -0,0 +1,319 @@
+
diff --git a/docs/modules/ROOT/images/MockitoCompileProfile.png b/docs/modules/ROOT/images/MockitoCompileProfile.png
new file mode 100644
index 00000000000..44db291b271
Binary files /dev/null and b/docs/modules/ROOT/images/MockitoCompileProfile.png differ
diff --git a/docs/modules/ROOT/images/NettyCompileGraph.svg b/docs/modules/ROOT/images/NettyCompileGraph.svg
new file mode 100644
index 00000000000..95bf3f46f09
--- /dev/null
+++ b/docs/modules/ROOT/images/NettyCompileGraph.svg
@@ -0,0 +1,1279 @@
+
diff --git a/docs/modules/ROOT/images/NettyCompileProfile.png b/docs/modules/ROOT/images/NettyCompileProfile.png
new file mode 100644
index 00000000000..a82631db20a
Binary files /dev/null and b/docs/modules/ROOT/images/NettyCompileProfile.png differ
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index e7f33e68d81..3bffd3ad9de 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -22,6 +22,7 @@
* xref:Testing_Scala_Projects.adoc[]
// * xref:Publishing_Scala_Projects.adoc[]
* xref:Scala_Web_Examples.adoc[]
+* xref:Case_Study_Mill_vs_SBT.adoc[]
// This section is all about developing a deeper understanding of specific
// topics in Mill. This is the opposite of `Quick Start` above: while we touch
diff --git a/docs/modules/ROOT/pages/Case_Study_Mill_vs_Gradle.adoc b/docs/modules/ROOT/pages/Case_Study_Mill_vs_Gradle.adoc
index 9353537bc11..ca2dbd90dce 100644
--- a/docs/modules/ROOT/pages/Case_Study_Mill_vs_Gradle.adoc
+++ b/docs/modules/ROOT/pages/Case_Study_Mill_vs_Gradle.adoc
@@ -181,6 +181,43 @@ For both Mill and Gradle, we see small speedups relative to the <
+gtag('config', 'AW-16649289906');
+
+++++
+
+This page compares using Mill to SBT, using the https://github.com/gatling/gatling[Gatling Load Testing Framework]
+codebase as the example. Gatling is a medium sized codebase, 40,000 lines of Scala split over 21
+subprojects. By porting it to Mill, this case study should give you an idea of how Mill compares
+to SBT in more realistic, real-world projects.
+
+To do this, we have written a Mill `build.sc` file for the Gatling project. This can be used
+with Mill to build and test the various submodules of the Gatling project without needing to
+change any other files in the repository:
+
+- https://github.com/com-lihaoyi/mill/blob/main/example/thirdparty/gatling/build.sc[Gatling build.sc file]
+
+== Completeness
+
+The Mill build for Gatling is not 100% complete, but it covers most of the major parts of Gatling:
+compiling Scala, running tests. It does not currently cover linting via
+https://github.com/diffplug/spotless[Spotless], as that is not built-in to Mill.
+
+The goal of this exercise is not to be 100% feature complete enough to replace the SBT build
+today. It is instead meant to provide a realistic comparison of how using Mill in a realistic,
+real-world project compares to using SBT.
+
+== Performance
+
+The Mill build for Gatling is generally snappier than the SBT build. This applies to
+most workflows, but the difference matters most for workflows which are short-lived,
+where the difference in the fixed overhead of the build tool is most noticeable.
+
+For a fair comparison, we disabled `gatling-build-plugin` in the sbt setup, which bundles the various scalafmt/scalafix/etc. linters as part of `compile`,
+since Mill doesn't bundle them and instead expects them to be invoked separately.
+
+For the benchmarks below, each provided number is the median wall time of three consecutive runs
+on my M1 Macbook Pro. While ad-hoc, these benchmarks are enough to give you a flavor of how
+Mill's performance compares to SBT:
+
+[cols="1,1,1,1"]
+|===
+| Benchmark | Cold SBT | Hot SBT | Mill
+
+| <> | 28.7s | 12s | 10.4s
+| <> | 10.1s | 1s | 0.96s
+| <> | 6.2s | 0s | 0.48s
+| <> | 4.2s | 0s | 0.40s
+|===
+
+SBT can be used in two modes, either "cold" run directly from the command line, or "hot"
+where an SBT session is kept open and commands are run within in. I provide the timings for
+both scenarios above, along with the time taken for Mill commands. Mill does not have this
+distinction, and can only be run directly from the command line. The `Hot SBT` mode only
+reports timings to the nearest second, so that is the number used in this comparison.
+
+In general, we can see that Mill's performance is very close to the "Hot SBT" benchmarks,
+and both "Hot SBT" and "Mill" are much faster than "Cold SBT". In general, Mill does not
+provide any performance improvement over the "ideal" scenario of running SBT hot from a
+live SBT session, but it provides the same level of performance without the complexity
+of maintaining an open SBT session that needs to be managed.
+
+=== Parallel Clean Compile All
+
+```bash
+$ sbt clean; time sbt compile
+28.7s
+27.6s
+31.2s
+
+$ sbt
+
+sbt> clean; compile
+12s
+12s
+10s
+
+$ ./mill clean; time ./mill -j 10 __.compile
+10.7s
+9.4s
+10.4s
+```
+
+This benchmark measures the time taken to sequentially compiled all the Java and Scala code in
+the Gatling code base. The goal of this benchmark is to try and measure the "clean compile
+everything" step, without the effects of parallelism that can be nondeterministic and vary
+wildly from machine to machine depending on the number of cores available.
+
+We configure Mill to do the same using the same number of threads (10) as SBT uses. As SBT runs
+in parallel by default, we do not have a comparison for sequential execution times.
+
+=== Clean Compile Single-Module
+
+```bash
+> sbt clean; time sbt gatling-commons/compile
+10.1
+10.7
+10.1
+
+sbt> clean; gatling-common/compile
+1s
+1s
+1s
+
+$ ./mill clean; time ./mill gatling-common.compile
+0.96s
+0.95s
+0.96s
+```
+
+This benchmark indicates the use case of clean-compiling a single module. In this case,
+the `gatling-commons` module's application code in `commons/, _exluding_ the test code in
+and all the downstream submodules.
+
+=== Incremental Compile Single-Module
+
+```bash
+$ echo "" >> gatling-commons/src/main/scala/io/gatling/commons/util/Arrays.scala
+$ time sbt gatling-commons/compile
+6.6s
+6.2s
+6.0s
+
+sbt> gatling-commons/compile
+0s
+0s
+0s
+
+$ echo "" >> gatling-commons/src/main/scala/io/gatling/commons/util/Arrays.scala
+$ time ./mill gatling-commons.compile
+0.49s
+0.48s
+0.47s
+```
+
+This benchmark measures the common case of making a tiny change to a single file and
+re-compiling just that module. This is the common workflow that most software developers
+do over and over day-in and day-out. We simulate this by appending a new line to the
+file `gatling-commons/src/main/scala/io/gatling/commons/util/Arrays.scala`.
+
+Both Mill and SBT are able to take advantage of the small code change and re-compile
+only the single files needing re-compilation, demonstrating substantial speedups over
+the <> benchmark above. Both "Hot SBT" and "Mill" finish in
+a fraction of a second, while "Cold SBT" has substantial overhead.
+
+=== No-Op Compile Single-Module
+
+```bash
+$ time sbt gatling-commons/compile
+4.2s
+4.2s
+4.2s
+
+sbt> gatling-commons/compile
+0s
+0s
+0s
+
+$ time ./mill gatling-commons.compile
+0.39s
+0.41s
+0.40s
+```
+
+This benchmark is meant to measure the pure overhead of running the build tool: given a single
+module that did _not_ change, the build tool should need to do _nothing_ in response, and so
+any time taken is pure overhead.
+
+Again, we see both "Hot SBT" and "Mill" finish in a fraction of a second, with the Mill numbers
+showing a ~0.4s overhead to run Mill even when there is no work to do, and the "Cold SBT" has
+in comparison substantial >4s overhead.
+
+== IDE Support
+
+One area that Mill does significantly better than SBT is in the IDE support. For example, although
+IDEs like IntelliJ are nominally able to parse and analyze your SBT files, the assistance they can
+provide is often not very useful. For example, consider the inspection and jump-to-definition experience
+of looking into an SBT Task:
+
+image::IntellijGatlingSbtTask1.png[]
+image::IntellijGatlingSbtTask2.png[]
+
+Or an SBT plugin:
+
+image::IntellijGatlingSbtPlugin1.png[]
+image::IntellijGatlingSbtPlugin2.png[]
+
+In general, although your IDE can make sure the name of the task exists, and the type is correct, it
+is unable to pull up any further information about the task: its documentation, its implementation,
+usages, any upstream overridden implementations, etc.. Some of this is the limitations of the IDE,
+but some of it is fundamental: because SBT makes the developer define the `val myTask` separate
+from the assignment of `myTask := something`, jumping to the definition of `myTask` tells you nothing
+at all: what it does, where it is assigned, etc.
+
+In comparison, for Mill, IDEs like Intellij are able to provide much more intelligence. e.g. when
+inspecting a task, it is able to pull up the documentation comment:
+
+image::IntellijGatlingMillTask1.png[]
+
+It is able to pull up any overriden implementations of task, directly in the editor:
+
+image::IntellijGatlingMillTask2.png[]
+
+And you can easily navigate to the overriden implementations to see where they are defined and
+what you are overriding:
+
+image::IntellijGatlingMillTask3.png[]
+
+Mill's equivalent of SBT plugins are just Scala traits, and again you can easily pull up their
+documentation in-line in the editor or jump to their full implementation:
+
+image::IntellijGatlingMillPlugin1.png[]
+image::IntellijGatlingMillPlugin2.png[]
+
+In general, navigating around your build in Mill is much more straightforward than
+navigating around your build in SBT. All your normal IDE functionality works perfectly:
+jump-to-definition, find-usages, peek-at-documentation, and so on. Although the Mill
+and SBT builds end up doing the same basic things - compiling Scala, running tests,
+zipping up jars - Mill helps de-mystify things considerably so you are never blocked
+wondering what your build tool is doing.
+
+== Debugging Tooling
+
+Another area that Mill does better than SBT is providing builtin tools for you to understand
+what your build is doing. For example, the Gatling project build discussed has 21 submodules
+and associated test suites, but how do these different modules depend on each other? With
+Mill, you can run `./mill -i show visualize __.compile`, and it will show you how the
+`compile` task of each module depends on the others:
+
+image::GatlingCompileGraph.svg[]
+
+Apart from the static dependency graph, another thing of interest may be the performance
+profile and timeline: where the time is spent when you actually compile everything. With
+Mill, when you run a compilation using `./mill -j 10 __.compile`, you automatically get a
+`out/mill-chrome-profile.json` file that you can load into your `chrome://tracing` page and
+visualize where your build is spending time and where the performance bottlenecks are:
+
+image::GatlingCompileProfile.png[]
+
+If you want to inspect the tree of third-party dependencies used by any module, the
+built in `ivyDepsTree` command lets you do that easily:
+
+```bash
+$ ./mill gatling-app.ivyDepsTree
+[137/137] gatling-app.ivyDepsTree
+├─ org.scala-lang:scala-library:2.13.14
+├─ io.gatling:gatling-shared-model_2.13:0.0.6
+│ ├─ io.gatling:gatling-shared-util_2.13:0.0.8
+│ │ ├─ org.scala-lang:scala-library:2.13.14
+│ │ └─ org.scala-lang.modules:scala-collection-compat_2.13:2.11.0
+│ │ └─ org.scala-lang:scala-library:2.13.14
+│ ├─ io.suzaku:boopickle_2.13:1.3.3
+│ │ └─ org.scala-lang:scala-library:2.13.14
+│ └─ org.scala-lang:scala-library:2.13.14
+├─ io.gatling:gatling-shared-cli:0.0.3
+│ └─ com.github.spotbugs:spotbugs-annotations:4.8.4 -> 4.8.6
+│ └─ com.google.code.findbugs:jsr305:3.0.2
+├─ org.simpleflatmapper:lightning-csv:8.2.3
+│ └─ org.simpleflatmapper:sfm-util:8.2.3
+├─ com.github.ben-manes.caffeine:caffeine:3.1.8
+│ ├─ com.google.errorprone:error_prone_annotations:2.21.1
+│ └─ org.checkerframework:checker-qual:3.37.0
+...
+```
+
+None of these tools are rocket science, but Mill provides all of them out of the
+box in a convenient package for you to use. Whether you want a visual graph layout,
+a parallel performance profile, or a third-party dependency tree of your project,
+Mill makes it easy and convenient without needing to fiddle with custom configuration
+or third party plugins. This helps make it easy for you to explore, understand, and
+take ownership of the build tool.
+
+== Conclusion
+
+Both the Mill and SBT builds we discussed in this case study do the same thing: they
+compile Java and Scala code and run tests. If set up and used properly, SBT builds
+are performant and do what needs to be done.
+
+Where Mill has an advantage over SBT is in its simplicity and understandability. You
+do not need to worry about using it "the wrong way" and ending up with workflows running
+slower than necessary. You can explore your build using your IDE like you would any other
+project, tracing task dependencies using the same jump-to-definition you use to trace
+method calls in your application code. Mill provides builtin tools to help you navigate,
+visualize, and understand your build, turning a normally opaque "build config" into
+something that's transparent and easily understandable.
+
diff --git a/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc b/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc
index 1b91a88343a..b0d6d9fe5f5 100644
--- a/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc
+++ b/docs/modules/ROOT/partials/Intro_to_Mill_Header.adoc
@@ -26,9 +26,10 @@ digraph G {
}
....
-{mill-github-url}[Mill] is a fast multi-language build tool that supports {language}, speeding
-up common development workflows by 2-10x xref:Case_Study_Mill_vs_Maven.adoc[compared to Maven],
-xref:Case_Study_Mill_vs_Gradle.adoc[Gradle], or SBT.
+{mill-github-url}[Mill] is a fast multi-language build tool that supports {language}, making your
+common development workflows xref:Case_Study_Mill_vs_Maven.adoc[5-10x faster to Maven], or
+xref:Case_Study_Mill_vs_Gradle.adoc[2-4x faster than Gradle], with
+xref:Case_Study_Mill_vs_SBT.adoc[an easier experience than SBT].
Mill aims to make your JVM project's build process performant, maintainable, and flexible
even as it grows from a small project to a large codebase or monorepo with hundreds of modules:
diff --git a/example/thirdparty/gatling/build.sc b/example/thirdparty/gatling/build.sc
new file mode 100644
index 00000000000..b007b99d469
--- /dev/null
+++ b/example/thirdparty/gatling/build.sc
@@ -0,0 +1,387 @@
+import mill._, scalalib._
+
+
+object Dependencies {
+ // Compile dependencies
+
+ // format: OFF
+ private def scalaReflect(version: String) = ivy"org.scala-lang:scala-reflect:$version"
+ private val gatlingSharedUtil = ivy"io.gatling::gatling-shared-util:0.0.8"
+ private val gatlingSharedModel = ivy"io.gatling::gatling-shared-model:0.0.6"
+ private val gatlingSharedCli = ivy"io.gatling:gatling-shared-cli:0.0.3"
+ private val scalaSwing = ivy"org.scala-lang.modules::scala-swing:3.0.0"
+ private val scalaParserCombinators = ivy"org.scala-lang.modules::scala-parser-combinators:2.4.0"
+ private val netty = ivy"io.netty:netty-codec-http:4.1.112.Final"
+ private val nettyBuffer = ivy"io.netty:netty-buffer:4.1.112.Final"
+ private val nettyHandler = ivy"io.netty:netty-handler:4.1.112.Final"
+ private val nettyProxy = ivy"io.netty:netty-handler-proxy:4.1.112.Final"
+ private val nettyDns = ivy"io.netty:netty-resolver-dns:4.1.112.Final"
+ private val nettyEpollLinuxX86 = ivy"io.netty:netty-transport-native-epoll:4.1.112.Final;classifier=linux-x86_64"
+ private val nettyEpollLinuxArm = ivy"io.netty:netty-transport-native-epoll:4.1.112.Final;classifier=linux-aarch_64"
+ private val nettyIoUringLinuxX86 = ivy"io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.25.Final;classifier=linux-x86_64"
+ private val nettyIoUringLinuxArm = ivy"io.netty.incubator:netty-incubator-transport-native-io_uring:0.0.25.Final;classifier=linux-aarch_64"
+ private val nettyHttp2 = ivy"io.netty:netty-codec-http2:4.1.112.Final"
+ private val nettyResolverNativeOsXX86 = ivy"io.netty:netty-resolver-dns-native-macos:4.1.112.Final;classifier=osx-x86_64"
+ private val nettyResolverNativeOsXArm = ivy"io.netty:netty-resolver-dns-native-macos:4.1.112.Final;classifier=osx-aarch_64"
+ private val nettyTcNative = ivy"io.netty:netty-tcnative-classes:2.0.65.Final"
+ private val nettyTcNativeBoringSsl = ivy"io.netty:netty-tcnative-boringssl-static:2.0.65.Final"
+ private val nettyTcNativeBoringSslLinuxX86 = ivy"io.netty:netty-tcnative-boringssl-static:2.0.65.Final;classifier=linux-x86_64"
+ private val nettyTcNativeBoringSslLinuxArm = ivy"io.netty:netty-tcnative-boringssl-static:2.0.65.Final;classifier=linux-aarch_64"
+ private val nettyTcNativeBoringSslOsXX86 = ivy"io.netty:netty-tcnative-boringssl-static:2.0.65.Final;classifier=osx-x86_64"
+ private val nettyTcNativeBoringSslOsXArm = ivy"io.netty:netty-tcnative-boringssl-static:2.0.65.Final;classifier=osx-aarch_64"
+ private val nettyTcNativeBoringSslWindows = ivy"io.netty:netty-tcnative-boringssl-static:2.0.65.Final;classifier=windows-x86_64"
+ private val brotli4j = ivy"com.aayushatharva.brotli4j:brotli4j:1.16.0"
+ private val brotli4jLinuxX86 = ivy"com.aayushatharva.brotli4j:native-linux-x86_64:1.16.0"
+ private val brotli4jLinuxArm = ivy"com.aayushatharva.brotli4j:native-linux-aarch64:1.16.0"
+ private val brotli4cOsXX86 = ivy"com.aayushatharva.brotli4j:native-osx-x86_64:1.16.0"
+ private val brotli4cOsXArm = ivy"com.aayushatharva.brotli4j:native-osx-aarch64:1.16.0"
+ private val brotli4jWindows = ivy"com.aayushatharva.brotli4j:native-windows-x86_64:1.16.0"
+ private val config = ivy"com.typesafe:config:1.4.3"
+ private val saxon = ivy"net.sf.saxon:Saxon-HE:10.6"
+ private val slf4jApi = ivy"org.slf4j:slf4j-api:2.0.16"
+ private val cfor = ivy"io.github.metarank::cfor:0.3"
+ private val scopt = ivy"com.github.scopt::scopt:3.7.1"
+ private val scalaLogging = ivy"com.typesafe.scala-logging::scala-logging:3.9.5"
+ private val jackson = ivy"com.fasterxml.jackson.core:jackson-databind:2.17.2"
+ private val sfm = ivy"org.simpleflatmapper:lightning-csv:8.2.3"
+ .exclude(("org.simpleflatmapper", "ow2-asm"))
+ private val lagarto = ivy"org.jodd:jodd-lagarto:6.0.6"
+ private val joddUtil = ivy"org.jodd:jodd-util:6.2.2"
+ private val jmespath = ivy"io.burt:jmespath-jackson:0.6.0"
+ private val boopickle = ivy"io.suzaku::boopickle:1.4.0"
+ private val redisClient = ivy"net.debasishg::redisclient:3.42"
+ private val testInterface = ivy"org.scala-sbt:test-interface:1.0"
+ private val jmsApi = ivy"javax.jms:javax.jms-api:2.0.1"
+ private val logback = ivy"ch.qos.logback:logback-classic:1.5.7"
+ private val tdigest = ivy"com.tdunning:t-digest:3.1"
+ private val hdrHistogram = ivy"org.hdrhistogram:HdrHistogram:2.2.1"
+ private val caffeine = ivy"com.github.ben-manes.caffeine:caffeine:3.1.8"
+ private val bouncyCastle = ivy"io.gatling:gatling-recorder-bc-shaded:1.78.1"
+ private val fastUuid = ivy"com.eatthepath:fast-uuid:0.2.0"
+ private val pebble = ivy"io.pebbletemplates:pebble:3.2.2"
+ private val spotbugs = ivy"com.github.spotbugs:spotbugs-annotations:4.8.6"
+ private val typetools = ivy"net.jodah:typetools:0.6.3"
+
+ // Test dependencies
+ private val scalaTest = ivy"org.scalatest::scalatest:3.2.19"
+ private val scalaTestScalacheck = ivy"org.scalatestplus::scalacheck-1-16:3.2.14.0"
+ private val scalaTestMockito = ivy"org.scalatestplus::mockito-3-4:3.2.10.0"
+ private val scalaCheck = ivy"org.scalacheck::scalacheck:1.18.0"
+ private val mockitoCore = ivy"org.mockito:mockito-core:4.11.0"
+ private val activemqBroker = ivy"org.apache.activemq:activemq-broker:5.18.5"
+ .exclude(("org.apache.geronimo.specs", "geronimo-jms_1.1_spec"))
+ private val h2 = ivy"com.h2database:h2:2.3.232"
+ private val jmh = ivy"org.openjdk.jmh:jmh-core:1.27"
+
+ private val junit = ivy"org.junit.jupiter:junit-jupiter-api:5.11.0"
+ private val junitEngine = ivy"org.junit.jupiter:junit-jupiter-engine:5.11.0"
+ private val jupiterInterface = ivy"net.aichler:jupiter-interface:0.11.1"
+
+ private val jetty = ivy"org.eclipse.jetty:jetty-server:9.4.55.v20240627"
+ private val jettyProxy = ivy"org.eclipse.jetty:jetty-proxy:9.4.55.v20240627"
+
+ // Docs dependencies
+ private val commonsLang = ivy"org.apache.commons:commons-lang3:3.16.0"
+ private val commonsCodec = ivy"commons-codec:commons-codec:1.17.1"
+ private val awsSecretsManager = ivy"software.amazon.awssdk:secretsmanager:2.27.7"
+
+ // format: ON
+ private val loggingDeps = Seq(slf4jApi, scalaLogging, logback)
+ val testDeps = Seq(
+ scalaTest,
+ scalaTestScalacheck,
+ scalaTestMockito,
+ scalaCheck,
+ mockitoCore
+ )
+ private val parserDeps = Seq(jackson, saxon, lagarto, joddUtil, jmespath)
+
+ // Dependencies by module
+ private val gatlingGrpcVersion = "3.11.5"
+ private val gatlingMqttVersion = "3.11.5"
+
+ val nettyUtilDependencies =
+ Seq(
+ gatlingSharedUtil,
+ nettyBuffer,
+ nettyEpollLinuxX86,
+ nettyEpollLinuxArm,
+ nettyIoUringLinuxX86,
+ nettyIoUringLinuxArm,
+ junit,
+ junitEngine,
+ jupiterInterface
+ )
+
+ val sharedModelDependencies =
+ Seq(gatlingSharedUtil, boopickle) ++ testDeps
+
+ val commonsSharedUnstableDependencies = testDeps
+
+ val commonsDependencies =
+ Seq(gatlingSharedUtil, config, cfor) ++ loggingDeps ++ testDeps
+
+ val jsonpathDependencies =
+ Seq(gatlingSharedUtil, scalaParserCombinators, jackson) ++ testDeps
+
+ def quicklensDependencies(scalaVersion: String) =
+ Seq(scalaReflect(scalaVersion))
+
+ val coreDependencies =
+ Seq(
+ gatlingSharedModel,
+ gatlingSharedCli,
+ sfm,
+ caffeine,
+ pebble,
+ scalaParserCombinators,
+ scopt,
+ nettyHandler,
+ nettyTcNative,
+ nettyTcNativeBoringSsl,
+ nettyTcNativeBoringSslLinuxX86,
+ nettyTcNativeBoringSslLinuxArm,
+ nettyTcNativeBoringSslOsXX86,
+ nettyTcNativeBoringSslOsXArm,
+ nettyTcNativeBoringSslWindows
+ ) ++
+ parserDeps ++ testDeps
+
+ val defaultJavaDependencies =
+ Seq(spotbugs, junit, junitEngine, jupiterInterface) ++ testDeps
+
+ val coreJavaDependencies =
+ Seq(typetools) ++ defaultJavaDependencies
+
+ val redisDependencies = redisClient +: testDeps
+
+ val httpClientDependencies = Seq(
+ gatlingSharedUtil,
+ netty,
+ nettyBuffer,
+ nettyHandler,
+ nettyProxy,
+ nettyDns,
+ nettyEpollLinuxX86,
+ nettyEpollLinuxArm,
+ nettyHttp2,
+ nettyResolverNativeOsXX86,
+ nettyResolverNativeOsXArm,
+ nettyTcNative,
+ nettyTcNativeBoringSsl,
+ nettyTcNativeBoringSslLinuxX86,
+ nettyTcNativeBoringSslLinuxArm,
+ nettyTcNativeBoringSslOsXX86,
+ nettyTcNativeBoringSslOsXArm,
+ nettyTcNativeBoringSslWindows,
+ brotli4j,
+ brotli4jLinuxX86,
+ brotli4jLinuxArm,
+ brotli4cOsXX86,
+ brotli4cOsXArm,
+ brotli4jWindows,
+ junit,
+ junitEngine,
+ jupiterInterface,
+ jetty,
+ jettyProxy
+ ) ++ loggingDeps
+
+ val httpDependencies = Seq(saxon) ++ testDeps
+
+ val jmsDependencies = Seq(jmsApi, fastUuid, activemqBroker) ++ testDeps
+
+ val jdbcDependencies = h2 +: testDeps
+
+ val chartsDependencies = tdigest +: testDeps
+
+ val benchmarkDependencies = Seq(jmh)
+
+ val recorderDependencies = Seq(gatlingSharedCli, scalaSwing, jackson, bouncyCastle, netty) ++ testDeps
+
+ val testFrameworkDependencies = Seq(gatlingSharedCli, testInterface)
+
+ val docSamplesDependencies =
+ Seq(
+ commonsLang,
+ commonsCodec,
+ awsSecretsManager,
+ activemqBroker,
+ ivy"io.gatling:gatling-grpc:$gatlingGrpcVersion",
+ ivy"io.gatling:gatling-grpc-java:$gatlingGrpcVersion",
+ ivy"io.gatling:gatling-mqtt:$gatlingMqttVersion",
+ ivy"io.gatling:gatling-mqtt-java:$gatlingMqttVersion"
+ )
+}
+
+trait GatlingModule extends SbtModule{
+
+ def scalaVersion = "2.13.14"
+ def testModuleDeps: Seq[JavaModule] = Nil
+ object test extends SbtTests with TestModule.ScalaTest{
+ def moduleDeps = super.moduleDeps ++ testModuleDeps
+ def ivyDeps = Agg.from(Dependencies.testDeps)
+ }
+}
+
+object `gatling-app` extends GatlingModule{
+ def moduleDeps = Seq(
+ `gatling-core`,
+ `gatling-core-java`,
+ `gatling-http`,
+ `gatling-http-java`,
+ `gatling-jms`,
+ `gatling-jms-java`,
+ `gatling-jdbc`,
+ `gatling-jdbc-java`,
+ `gatling-redis`,
+ `gatling-redis-java`,
+ `gatling-charts`
+ )
+ def ivyDeps = Agg[Dep]()
+}
+object `gatling-benchmarks` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`, `gatling-http`)
+ def ivyDeps = Agg.from(Dependencies.benchmarkDependencies)
+}
+object `gatling-charts` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`)
+ def testModuleDeps = Seq(`gatling-core`.test)
+ def ivyDeps = Agg.from(Dependencies.chartsDependencies)
+}
+object `gatling-commons` extends GatlingModule{
+ def moduleDeps = Seq()
+ def ivyDeps = Agg.from(Dependencies.commonsDependencies)
+}
+object `gatling-core` extends GatlingModule{
+ def moduleDeps = Seq(
+ `gatling-netty-util`,
+ `gatling-quicklens`,
+ `gatling-commons`,
+ `gatling-jsonpath`
+ )
+ def testModuleDeps = Seq(
+ `gatling-commons`.test,
+ `gatling-jsonpath`.test
+ )
+ def ivyDeps = Agg.from(Dependencies.coreDependencies)
+}
+object `gatling-core-java` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`)
+ def testModuleDeps = Seq(`gatling-core`.test)
+ def ivyDeps = Agg.from(Dependencies.coreJavaDependencies)
+}
+object `gatling-http` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`, `gatling-http-client`)
+ def testModuleDeps = Seq(`gatling-core`.test, `gatling-http-client`.test)
+ def ivyDeps = Agg.from(Dependencies.httpDependencies)
+}
+object `gatling-http-client` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-netty-util`)
+ def testModuleDeps = Seq(`gatling-netty-util`.test)
+ def ivyDeps = Agg.from(Dependencies.httpClientDependencies)
+}
+object `gatling-http-java` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core-java`, `gatling-http`)
+ def testModuleDeps = Seq(`gatling-http`.test)
+ def ivyDeps = Agg.from(Dependencies.defaultJavaDependencies)
+}
+object `gatling-jdbc` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`)
+ def testModuleDeps = Seq(`gatling-core`.test)
+ def ivyDeps = Agg.from(Dependencies.jdbcDependencies)
+}
+object `gatling-jdbc-java` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core-java`, `gatling-jdbc`)
+ def testModuleDeps = Seq(`gatling-jdbc`.test)
+ def ivyDeps = Agg.from(Dependencies.defaultJavaDependencies)
+}
+object `gatling-jms` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`)
+ def testModuleDeps = Seq(`gatling-core`.test)
+ def ivyDeps = Agg.from(Dependencies.jmsDependencies)
+}
+object `gatling-jms-java` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core-java`, `gatling-jms`)
+ def testModuleDeps = Seq(`gatling-jms`.test)
+ def ivyDeps = Agg.from(Dependencies.defaultJavaDependencies)
+}
+object `gatling-jsonpath` extends GatlingModule{
+ def moduleDeps = Seq()
+ def ivyDeps = Agg.from(Dependencies.jsonpathDependencies)
+}
+object `gatling-netty-util` extends GatlingModule{
+ def moduleDeps = Seq()
+ def ivyDeps = Agg.from(Dependencies.nettyUtilDependencies)
+}
+object `gatling-quicklens` extends GatlingModule{
+ def moduleDeps = Seq()
+ def ivyDeps = Agg.from(Dependencies.quicklensDependencies(scalaVersion()))
+}
+object `gatling-recorder` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`, `gatling-http`)
+ def testModuleDeps = Seq(`gatling-core`.test)
+ def ivyDeps = Agg.from(Dependencies.recorderDependencies)
+}
+object `gatling-redis` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core`)
+ def testModuleDeps = Seq(`gatling-core`.test)
+ def ivyDeps = Agg.from(Dependencies.redisDependencies)
+}
+object `gatling-redis-java` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-core-java`, `gatling-redis`)
+ def testModuleDeps = Seq(`gatling-redis`.test)
+ def ivyDeps = Agg.from(Dependencies.defaultJavaDependencies)
+}
+object `gatling-samples` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-app`)
+ def ivyDeps = Agg[Dep]()
+}
+object `gatling-test-framework` extends GatlingModule{
+ def moduleDeps = Seq(`gatling-app`)
+ def ivyDeps = Agg.from(Dependencies.testFrameworkDependencies)
+}
+
+/** Usage
+
+> sed -i.bak 's/1.seconds/10.seconds/g' gatling-core/src/test/scala/io/gatling/core/actor/ActorSpec.scala
+
+> sed -i.bak 's/is.toString(charset)/is.toString()/g' gatling-benchmarks/src/main/scala/io/gatling/Utils.scala
+
+> ./mill __.test
+ConsoleTemplateSpec:
+console template
+- should format the request counters properly
+- should format the grouped counts properly
+RampConcurrentUsersInjection
+- should return the correct number of users target
+- should inject once a full user is reached
+composite.injectionSteps
+- should produce the expected injection profile with starting users and with ramps
+- should produce the expected injection profile without starting users and without ramps
+- should produce the expected injection profile with starting users and without ramps
+- should produce the expected injection profile without starting users and with ramps
+JmsSimpleCheckSpec:
+simple check
+- should return success if condition is true
+- should return failure if condition is false
+- should return failure if message is not TextMessage
+JmsJsonPathCheckSpec:
+jsonPath.find for TextMessage
+- should support long values
+jsonPath.find.exists for TextMessage
+- should find single result into JSON serialized form
+- should find single result into Map object form
+- should find a null attribute value when expected type is String
+- should find a null attribute value when expected type is Any
+- should find a null attribute value when expected type is Int
+- should find a null attribute value when expected type is Seq
+- should find a null attribute value when expected type is Map
+- should succeed when expecting a null value and getting a null one
+- should fail when expecting a null value and getting a non-null one
+- should succeed when expecting a non-null value and getting a non-null one
+- should fail when expecting a non-null value and getting a null one
+- should not fail on empty array
+...
+
+*/
\ No newline at end of file
diff --git a/mill b/mill
index d3055fe23ef..d03a045cb77 100755
--- a/mill
+++ b/mill
@@ -7,7 +7,7 @@
set -e
if [ -z "${DEFAULT_MILL_VERSION}" ] ; then
- DEFAULT_MILL_VERSION=0.11.6
+ DEFAULT_MILL_VERSION=0.11.12
fi
if [ -z "$MILL_VERSION" ] ; then