diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml
index 4ed34462d82..7541421ec64 100644
--- a/.github/workflows/run-tests.yml
+++ b/.github/workflows/run-tests.yml
@@ -59,39 +59,57 @@ jobs:
# We also try to group tests together to manually balance out the runtimes of each jobs
- java-version: 17
millargs: "'{main,scalalib,testrunner,bsp,testkit}.__.testCached'"
+ install-android-sdk: false
+
- java-version: '11'
millargs: "'{scalajslib,scalanativelib,kotlinlib}.__.testCached'"
+ install-android-sdk: false
+
- java-version: 17
millargs: "contrib.__.testCached"
+ install-android-sdk: false
- java-version: 17
- install-android-sdk: true
millargs: "'example.javalib.__.local.testCached'"
+ install-android-sdk: true
+
- java-version: 17
millargs: "'example.scalalib.__.local.testCached'"
+ install-android-sdk: false
+
- java-version: 17
- install-android-sdk: true
millargs: "'example.kotlinlib.__.local.testCached'"
+ install-android-sdk: true
+
- java-version: '11'
millargs: "'example.thirdparty[{mockito,acyclic,commons-io}].local.testCached'"
+ install-android-sdk: false
+
- java-version: 17
millargs: "'example.thirdparty[{fansi,jimfs,netty,gatling}].local.testCached'"
+ install-android-sdk: false
+
- java-version: '17'
millargs: "'example.thirdparty[arrow].local.testCached'"
+ install-android-sdk: false
+
- java-version: '11'
millargs: "'example.{depth,extending}.__.local.testCached'"
-
+ install-android-sdk: false
# Most of these integration tests should not depend on which mode they
# are run in, so just run them in `local`
- java-version: '17'
millargs: "'integration.{failure,feature,ide}.__.local.testCached'"
-
+ install-android-sdk: false
# These invalidation tests need to be exercised in both execution modes
# to make sure they work with and without -i/--no-server being passed
- java-version: 17
millargs: "'integration.invalidation.__.fork.testCached'"
+ install-android-sdk: false
+
- java-version: 17
millargs: "'integration.invalidation.__.server.testCached'"
+ install-android-sdk: false
uses: ./.github/workflows/run-mill-action.yml
with:
diff --git a/docs/modules/ROOT/nav.adoc b/docs/modules/ROOT/nav.adoc
index 3387a77c07a..1e05a12a7a7 100644
--- a/docs/modules/ROOT/nav.adoc
+++ b/docs/modules/ROOT/nav.adoc
@@ -83,7 +83,8 @@
** xref:extending/running-jvm-code.adoc[]
** xref:extending/writing-plugins.adoc[]
** xref:extending/meta-build.adoc[]
-** xref:extending/new-language.adoc[]
+** xref:extending/example-typescript-support.adoc[]
+** xref:extending/example-python-support.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
diff --git a/docs/modules/ROOT/pages/extending/example-python-support.adoc b/docs/modules/ROOT/pages/extending/example-python-support.adoc
new file mode 100644
index 00000000000..319929a5df4
--- /dev/null
+++ b/docs/modules/ROOT/pages/extending/example-python-support.adoc
@@ -0,0 +1,32 @@
+= Example: Python Support
+
+include::partial$gtag-config.adoc[]
+
+This section demonstrates how to integrate `Python` support into `Mill`.
+We will define a simple `PythonModule` trait that can resolve dependencies,
+perform type checking on local code, and bundle an executable.
+
+NOTE: This integration is for educational purposes only, showcasing common technique
+used in building language toolchains, and is not intended for production use.
+
+== Basic Python Build Pipeline
+
+include::partial$example/extending/python/1-hello-python.adoc[]
+
+== Re-usable PythonModule
+
+include::partial$example/extending/python/2-python-modules.adoc[]
+
+== PythonModule `moduleDeps`
+
+include::partial$example/extending/python/3-python-module-deps.adoc[]
+
+== PIP dependencies and bundling
+
+include::partial$example/extending/python/4-python-libs-bundle.adoc[]
+
+
+
+As mentioned, The `PythonModule` examples here demonstrate
+how to add support for a new language toolchain in Mill.
+A production-ready version would require more work to enhance features and performance.
\ No newline at end of file
diff --git a/docs/modules/ROOT/pages/extending/new-language.adoc b/docs/modules/ROOT/pages/extending/example-typescript-support.adoc
similarity index 55%
rename from docs/modules/ROOT/pages/extending/new-language.adoc
rename to docs/modules/ROOT/pages/extending/example-typescript-support.adoc
index aabe3b7a8fe..af3b7c8544f 100644
--- a/docs/modules/ROOT/pages/extending/new-language.adoc
+++ b/docs/modules/ROOT/pages/extending/example-typescript-support.adoc
@@ -1,4 +1,4 @@
-= Support for New Languages
+= Example: Typescript Support
include::partial$gtag-config.adoc[]
@@ -11,21 +11,21 @@ The TypeScript integration here is not intended for production usage, but is
instead meant for illustration purposes of the techniques typically used in
implementing language toolchains.
-== Basic TypeScript Pipeline
+== Basic TypeScript Build Pipeline
-include::partial$example/extending/newlang/1-hello-typescript.adoc[]
+include::partial$example/extending/typescript/1-hello-typescript.adoc[]
== Re-usable TypeScriptModule
-include::partial$example/extending/newlang/2-typescript-modules.adoc[]
+include::partial$example/extending/typescript/2-typescript-modules.adoc[]
== TypeScriptModule `moduleDeps`
-include::partial$example/extending/newlang/3-module-deps.adoc[]
+include::partial$example/extending/typescript/3-module-deps.adoc[]
== NPM dependencies and bundling
-include::partial$example/extending/newlang/4-npm-deps-bundle.adoc[]
+include::partial$example/extending/typescript/4-npm-deps-bundle.adoc[]
@@ -39,25 +39,6 @@ to any language you need: whether it's TypeScript or some other language, most p
language toolchains have similar concepts of `compile`, `run`, `bundle`, etc.
-== Python Support
-
-This section demonstrates how to integrate `Python` support into `Mill`.
-We will define a simple `PythonModule` trait that can resolve dependencies,
-perform type checking on local code, and optimize the final bundle.
-
-`Note`: This integration is for `educational purposes only`, showcasing common technique
-used in building language toolchains, and is not intended for production use.
-
-include::partial$example/extending/newlang/5-hello-python.adoc[]
-
-include::partial$example/extending/newlang/6-python-modules.adoc[]
-
-include::partial$example/extending/newlang/7-python-module-deps.adoc[]
-
-include::partial$example/extending/newlang/8-python-libs-bundle.adoc[]
-
-
-
-As mentioned, The `PythonModule` examples here demonstrate
+As mentioned, The `PythonModule` examples here demonstrate
how to add support for a new language toolchain in Mill.
A production-ready version would require more work to enhance features and performance.
\ No newline at end of file
diff --git a/docs/modules/ROOT/pages/index.adoc b/docs/modules/ROOT/pages/index.adoc
index 14db2302367..e91f1bab844 100644
--- a/docs/modules/ROOT/pages/index.adoc
+++ b/docs/modules/ROOT/pages/index.adoc
@@ -2,33 +2,6 @@
include::partial$gtag-config.adoc[]
-```graphviz
-digraph G {
- rankdir=LR
- node [shape=box width=0 height=0 style=filled fillcolor=white]
- bgcolor=transparent
- newrank=true;
- subgraph cluster_0 {
- style=dashed
- node [shape=box width=0 height=0 style=filled fillcolor=white]
- label = "foo";
-
- "foo.sources" -> "foo.compile" -> "foo.classPath" -> "foo.assembly"
- "foo.resources" -> "foo.assembly"
- "foo.classPath"
- }
- subgraph cluster_1 {
- style=dashed
- node [shape=box width=0 height=0 style=filled fillcolor=white]
- label = "bar";
-
- "foo.classPath" -> "bar.compile" [constraint=false]
- "foo.classPath" -> "bar.classPath"
- "bar.sources" -> "bar.compile" -> "bar.classPath" -> "bar.assembly"
- "bar.resources" -> "bar.assembly"
- }
-}
-```
Mill is a fast, scalable, multi-language build tool that supports Java, Scala,
and Kotlin:
@@ -39,8 +12,8 @@ or xref:comparisons/gradle.adoc[2-4x faster than Gradle]
* Mill's typed config language and immutable xref:depth/design-principles.adoc[task graph]
helps keep builds clean and understandable
-* Mill scales well from small single-module projects
-to xref:depth/large-builds.adoc[large monorepos] with hundreds of modules
+* Mill is an alternative to https://bazel.build/[Bazel]
+for xref:depth/large-builds.adoc[large monorepos] with hundreds of modules in multiple languages
Although the Java compiler is very fast and the Java language is easy to learn,
JVM build tools have a reputation for being sluggish and confusing. Mill tries to
@@ -51,10 +24,10 @@ Java platform's performance and usability:
xref:depth/evaluation-model.adoc#_caching_at_each_layer_of_the_evaluation_model[caches]
and xref:cli/flags.adoc#_jobs_j[parallelizes] build
tasks, keeping your workflows fast and responsive. Mill adds minimal overhead over
-the logic necessary to build your project, while providing tools to let you identify
-and resolve bottlenecks in your build
+the logic necessary to build your project, and avoids the long configuration or initialization
+times often necessary in other build tools like Gradle or SBT
-* *Maintainability*: Mill goes beyond YAML and Bash, with config and custom logic written in
+* *Maintainability*: Mill's config and custom logic is written in
xref:javalib/intro.adoc#_custom_build_logic[concise type-checked code],
and an immutable xref:depth/design-principles.adoc[module tree and task graph]. This
catches config issues early, and helps IDEs
@@ -64,7 +37,7 @@ understand your Mill build better than any other build system
* *Flexibility*: Mill's tasks and modules allow anything from adding
xref:fundamentals/tasks.adoc#primitive-tasks[simple build steps], up to
-entire xref:extending/new-language.adoc[language toolchains].
+entire xref:extending/example-python-support.adoc[language toolchains].
You can xref:extending/import-ivy-plugins.adoc[import any JVM library] in 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
diff --git a/docs/modules/ROOT/pages/kotlinlib/intro.adoc b/docs/modules/ROOT/pages/kotlinlib/intro.adoc
index 2f846920c29..8af2a8b9f21 100644
--- a/docs/modules/ROOT/pages/kotlinlib/intro.adoc
+++ b/docs/modules/ROOT/pages/kotlinlib/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
-= (Experimental) Kotlin with Mill
+= Kotlin with Mill
:page-aliases: Kotlin_Intro_to_Mill.adoc
include::partial$gtag-config.adoc[]
@@ -22,10 +22,9 @@ include::partial$gtag-config.adoc[]
include::partial$Intro_Header.adoc[]
-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
+NOTE: Mill Kotlin support is currently focused on the JVM, with a lot of APIs
+still under active development. It is expected to continue evolving over time
+as Android and Multiplatform support is fleshed out. Try it out, but please be aware of its
limitations!
== Simple Kotlin Module
diff --git a/docs/modules/ROOT/pages/why-mill.adoc b/docs/modules/ROOT/pages/why-mill.adoc
index 9701714d7a5..da4e4bb2ef7 100644
--- a/docs/modules/ROOT/pages/why-mill.adoc
+++ b/docs/modules/ROOT/pages/why-mill.adoc
@@ -239,7 +239,7 @@ can trivially load it into their Chrome browser to analyze and figure out what.
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
+`./mill visualize __.compile` (double underscore means wildcard) to
show the dependency graph between the modules of the Netty build below:
image::comparisons/NettyCompileGraph.svg[]
@@ -319,7 +319,7 @@ 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
+polished experience to your build system - traditionally something that has been opaque
and hard to understand - and does so in a way that no other build tool does.
@@ -385,6 +385,11 @@ show its value via the Mill command line to force it to evaluate:
17
```
+Note that as `lineCount` is a `Task`, we get automatic caching, invalidation, and
+parallelization: these are things that every `Task` gets for free, without the task
+author to do anything. And although we wrote the `lineCount` logic in the main
+`build.mill` file for this example, if it grows complex enough to get messy it is
+easy to move it to your own xref:extending/writing-plugins.adoc[custom plugins]
### Overriding Tasks
diff --git a/docs/supplemental-ui/partials/nav-tree.hbs b/docs/supplemental-ui/partials/nav-tree.hbs
index 1d93e35d18f..53a8d6a810e 100644
--- a/docs/supplemental-ui/partials/nav-tree.hbs
+++ b/docs/supplemental-ui/partials/nav-tree.hbs
@@ -12,7 +12,7 @@ show all of them all the time
{{#each navigation}}
-
+
{{#if ./content}}
{{#if ./items.length}}
diff --git a/example/extending/newlang/5-hello-python/build.mill b/example/extending/newlang/5-hello-python/build.mill
deleted file mode 100644
index 9065ab5d606..00000000000
--- a/example/extending/newlang/5-hello-python/build.mill
+++ /dev/null
@@ -1,114 +0,0 @@
-// == Python Integration in Mill
-
-// This Example shows how to integrate https://www.python.org[Python] into a Mill build, enabling Python script compilation.
-// Mill does not come bundled with Python support, so we will set up integration using `python3`, which is generally pre-installed.
-
-// === Python initialization Setup
-//
-// First, The `setup` task creates a Python virtual environment and installs `mypy` for type-checking.
-
-// `mypy` verifies type correctness using Python's `type hints`, helping to catch errors in development.
-
-// Guides:
-// https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments[Python virtual environment]
-// & https://mypy.readthedocs.io/en/stable[mypy]
-
-package build
-import mill._
-
-def setup: T[PathRef] = Task {
- val pythonVenv = Task.dest / "venv" / "bin" / "python3"
-
- os.call(("python3", "-m", "venv", Task.dest / "venv"))
- os.call((pythonVenv, "-m", "pip", "install", "mypy"))
-
- PathRef(pythonVenv)
-}
-
-// === Defining our Sources
-
-// The `sources` task specifies the directory for Python source files (`src` folder).
-// This setup facilitates organizing and accessing Python files needed for building, testing, or analysis.
-
-def sources: T[PathRef] = Task.Source(millSourcePath / "src")
-
-// === Type Checking
-
-// The `typeCheck` task verifies that the code in the main Python file passes type checks.
-// It checks for errors before execution to ensure a reliable setup for running Python scripts.
-
-def typeCheck: T[PathRef] = Task {
- val pythonVenv = setup().path
-
- os.call(
- (pythonVenv, "-m", "mypy", "--strict", sources().path / mainFileName()),
- stdout = os.Inherit
- )
-
- PathRef(pythonVenv)
-}
-
-// At this point, we have a minimal working build, with a build graph that looks like this:
-//
-// ```graphviz
-// digraph G {
-// rankdir=LR
-// node [shape=box width=0 height=0 style=filled fillcolor=white]
-// setup -> typeCheck
-// sources -> typeCheck
-// mainFileName -> typeCheck
-// }
-// ```
-
-// Here is the `main.py` file
-/** See Also: src/main.py */
-
-// === Running
-
-// The `mainFileName` task defines the name of the main Python script (`main.py`).
-
-// The `run` function runs the main file with user-provided command-line arguments.
-// It uses the virtual environment's Python interpreter to execute the script, with output displayed in the console.
-
-def mainFileName: T[String] = Task { "main.py" }
-def run(args: mill.define.Args) = Task.Command {
- val pythonVenv = typeCheck().path
-
- os.call(
- (pythonVenv, sources().path / mainFileName(), args.value),
- stdout = os.Inherit
- )
-}
-
-// Note that we use `stdout = os.Inherit` since we want to display any output to the user,
-// rather than capturing it for use in our command.
-
-// ```graphviz
-// digraph G {
-// rankdir=LR
-// node [shape=box width=0 height=0 style=filled fillcolor=white]
-// setup -> typeCheck -> run
-// sources -> typeCheck
-// mainFileName -> typeCheck
-// sources -> run
-// mainFileName -> run
-// mainFileName [color=green, penwidth=3]
-// run [color=green, penwidth=3]
-// }
-// ```
-
-// Running run command will return the result to the console.
-
-/** Usage
-
-> ./mill typeCheck
-Success: no issues found in 1 source file
-
-> ./mill run Mill Python
-Hello, Mill Python!
-15
-
-*/
-
-// This completes a basic `Python integration in Mill`.
-// Next steps could involve transforming this into a `reusable PythonModule`
diff --git a/example/extending/newlang/6-python-modules/build.mill b/example/extending/newlang/6-python-modules/build.mill
deleted file mode 100644
index be910b4f6ae..00000000000
--- a/example/extending/newlang/6-python-modules/build.mill
+++ /dev/null
@@ -1,123 +0,0 @@
-// == Re-usable PythonModule
-// This example illustrates the `PythonModule` trait for managing Python scripts within multiple Mill objects.
-
-// ==== Using Same PythonModule for Multiple Objects
-
-// `PythonModule` automates essential tasks in Python project management within Mill,
-// such as setting up virtual environments, managing source files, performing type checks, and running scripts.
-
-package build
-import mill._
-
-/** `PythonModule`: Trait for basic Python support in Mill */
-trait PythonModule extends Module {
-
- def sources: T[PathRef] = Task.Source(millSourcePath / "src")
- def mainFileName: T[String] = Task { "main.py" }
-
- def setup: T[PathRef] = Task {
- val pythonVenv = Task.dest / "venv" / "bin" / "python3"
-
- os.call(("python3", "-m", "venv", Task.dest / "venv"))
- os.call((pythonVenv, "-m", "pip", "install", "mypy"))
-
- PathRef(pythonVenv)
- }
-
- def typeCheck: T[PathRef] = Task {
- val pythonVenv = setup().path
-
- os.call(
- (pythonVenv, "-m", "mypy", "--strict", sources().path / mainFileName()),
- stdout = os.Inherit
- )
-
- PathRef(pythonVenv)
- }
-
- def run(args: mill.define.Args) = Task.Command {
- val pythonVenv = typeCheck().path
-
- os.call(
- (pythonVenv, sources().path / mainFileName(), args.value),
- stdout = os.Inherit
- )
- }
-
-}
-
-// ==== Example
-
-// Below are three objects extending `PythonModule`, each representing a unique configuration.
-
-object foo extends PythonModule {
- override def mainFileName: T[String] = Task { "foo.py" }
- object bar extends PythonModule {
- override def mainFileName: T[String] = Task { "bar.py" }
- // Inherits PythonModule features
- }
-}
-
-object qux extends PythonModule {
- override def mainFileName: T[String] = Task { "qux.py" }
- // Independent, but uses PythonModule methods
-}
-
-// We have used three different Python Scripts `foo/src/foo.py`, `foo/bar/src/bar.py`, `qux/src/qux.py`
-
-/** See Also: foo/src/foo.py */
-/** See Also: foo/bar/src/bar.py */
-/** See Also: qux/src/qux.py */
-
-// Use the following commands to run each module, displaying unique outputs per configuration:
-
-/** Usage
-
-> ./mill foo.run Mill
-Hello, Mill Foo!
-
-> ./mill foo.bar.run Mill
-Hello, Mill Foo Bar!
-
-> ./mill qux.run Mill
-Hello, Mill Qux!
-
-*/
-
-// The Final working build, with a build graph that looks like this:
-
-// ```graphviz
-// digraph G {
-// rankdir=LR
-// node [shape=box width=0 height=0 style=filled fillcolor=white]
-// subgraph cluster_3 {
-// style=dashed
-// label=qux
-// "qux.setup" -> "qux.typeCheck" -> "qux.run"
-// "qux.sources" -> "qux.typeCheck"
-// "qux.mainFileName" -> "qux.typeCheck"
-// "qux.sources" -> "qux.run"
-// "qux.mainFileName" -> "qux.run"
-// }
-// subgraph cluster_1 {
-// subgraph cluster_2 {
-// style=dashed
-// label=bar
-// "bar.setup" -> "bar.typeCheck" -> "bar.run"
-// "bar.sources" -> "bar.typeCheck"
-// "bar.mainFileName" -> "bar.typeCheck"
-// "bar.sources" -> "bar.run"
-// "bar.mainFileName" -> "bar.run"
-// }
-// style=dashed
-// label=foo
-// "foo.setup" -> "foo.typeCheck" -> "foo.run"
-// "foo.sources" -> "foo.typeCheck"
-// "foo.mainFileName" -> "foo.typeCheck"
-// "foo.sources" -> "foo.run"
-// "foo.mainFileName" -> "foo.run"
-// }
-// }
-// ```
-
-// Next, we will look at how to Manage `Module Dependencies` for `Python` in `Mill`.
diff --git a/example/extending/newlang/7-python-module-deps/build.mill b/example/extending/newlang/7-python-module-deps/build.mill
deleted file mode 100644
index c5016d3b411..00000000000
--- a/example/extending/newlang/7-python-module-deps/build.mill
+++ /dev/null
@@ -1,138 +0,0 @@
-// == PythonModule moduleDeps
-// This example shows how to add module dependency handling to `PythonModule`, making it ideal
-// for complex projects that need dynamic dependencies and isolated environments for each module.
-
-// ==== Extending `PythonModule` Trait
-
-// Now, The `PythonModule` trait’s `moduleDeps` allows dependencies between modules.
-// Using `Task.traverse`, it gathers and verifies `upstream dependencies` in `typeCheck`
-// and prepares their `source` files for execution in the `run` task.
-
-package build
-import mill._
-
-/** Extended `PythonModule` for dependent modules. */
-trait PythonModule extends Module {
-
- // List of module dependencies required by this module.
- def moduleDeps: Seq[PythonModule] = Nil
-
- def sources: T[PathRef] = Task.Source(millSourcePath / "src")
- def mainFileName: T[String] = Task { "main.py" }
-
- def setup: T[PathRef] = Task {
- val pythonVenv = Task.dest / "venv" / "bin" / "python3"
-
- os.call(("python3", "-m", "venv", Task.dest / "venv"))
- os.call((pythonVenv, "-m", "pip", "install", "mypy"))
-
- PathRef(pythonVenv)
- }
-
- def typeCheck: T[PathRef] = Task {
- // Checks upstream modules for error free code
- val upstreamTypeCheck = Task.traverse(moduleDeps)(_.typeCheck)()
- val pythonVenv = setup().path
-
- os.call(
- (pythonVenv, "-m", "mypy", "--strict", sources().path / mainFileName()),
- stdout = os.Inherit
- )
-
- PathRef(pythonVenv)
- }
-
- def run(args: mill.define.Args) = Task.Command {
- // Gets upstream modules Source Files
- val upstream = Task.traverse(moduleDeps)(_.sources)().zip(moduleDeps)
-
- for (((sourcesFolder), mod) <- upstream) {
- val destinationPath = Task.dest / mod.millSourcePath.subRelativeTo(build.millSourcePath)
- os.copy.over(sourcesFolder.path / os.up, destinationPath)
- }
-
- val pythonVenv = typeCheck().path
-
- os.call(
- (pythonVenv, sources().path / mainFileName(), args.value),
- env = Map("PYTHONPATH" -> Task.dest.toString),
- stdout = os.Inherit
- )
- }
-
-}
-
-// ==== Example setup with dependencies
-//
-// `qux` depends on `foo` and `foo.bar`, which export their APIs for use in `qux`.
-
-object foo extends PythonModule {
- override def mainFileName: T[String] = Task { "foo.py" }
- object bar extends PythonModule {
- override def mainFileName: T[String] = Task { "bar.py" }
- }
-}
-
-object qux extends PythonModule {
- override def mainFileName: T[String] = Task { "qux.py" }
- // Defines dependencies on foo and foo.bar modules for qux.
- def moduleDeps: Seq[PythonModule] = Seq[PythonModule](foo, foo.bar)
-}
-// Below are the Files from Source Folders
-
-// See, How we have imported the other files(foo.py & bar.py) functions in `qux.py` file
-
-/** See Also: foo/src/foo.py */
-/** See Also: foo/bar/src/bar.py */
-/** See Also: qux/src/qux.py */
-
-// To run this build script, use the following command.
-
-/** Usage
-
-> ./mill qux.run 10 20
-Add: 10 + 20 = 30 | Multiply: 10 * 20 = 200 | Divide: 10 / 20 = 0.5
-
-*/
-
-// Task dependency graph, showing `foo` and `bar` tasks feeding into `qux`:
-//
-// ```graphviz
-// digraph G {
-// rankdir=LR
-// node [shape=box width=0 height=0 style=filled fillcolor=white]
-// subgraph cluster_3 {
-// style=dashed
-// label=qux
-// "qux.setup" -> "qux.typeCheck" -> "qux.run"
-// "qux.sources" -> "qux.typeCheck"
-// "qux.mainFileName" -> "qux.typeCheck"
-// "qux.sources" -> "qux.run"
-// "qux.mainFileName" -> "qux.run"
-// }
-// subgraph cluster_1 {
-// subgraph cluster_2 {
-// style=dashed
-// label=bar
-// "bar.setup" -> "bar.typeCheck" -> "bar.run"
-// "bar.sources" -> "bar.typeCheck"
-// "bar.mainFileName" -> "bar.typeCheck"
-// "bar.sources" -> "bar.run"
-// "bar.mainFileName" -> "bar.run"
-// }
-// style=dashed
-// label=foo
-// "foo.setup" -> "foo.typeCheck" -> "foo.run"
-// "foo.sources" -> "foo.typeCheck"
-// "foo.mainFileName" -> "foo.typeCheck"
-// "foo.sources" -> "foo.run"
-// "foo.mainFileName" -> "foo.run"
-// }
-// "bar.typeCheck" -> "qux.typeCheck" [color=green, penwidth=3]
-// "bar.sources" -> "qux.run" [color=green, penwidth=3]
-// "foo.typeCheck" -> "qux.typeCheck" [color=green, penwidth=3]
-// "foo.sources" -> "qux.run" [color=green, penwidth=3]
-// }
-// ```
-
-// Now Next, We will add External Library and Bundling Support
diff --git a/example/extending/newlang/8-python-libs-bundle/build.mill b/example/extending/newlang/8-python-libs-bundle/build.mill
deleted file mode 100644
index 5dbaac2bd5b..00000000000
--- a/example/extending/newlang/8-python-libs-bundle/build.mill
+++ /dev/null
@@ -1,186 +0,0 @@
-// == Python Library Dependencies & Bundling
-
-// This example shows how to handle external dependencies and
-// create PEX (Python EXecutable) bundles using the `PythonModule` trait.
-
-// PEX simplifies Python app distribution by packaging
-// code and dependencies into a single executable file.
-
-// Guide: https://docs.pex-tool.org[Pex Documentation]
-
-// ==== Extending PythonModule For Pex Support
-
-// This implementation extends `PythonModule` with these key Tasks:
-
-// - def `finalDependencies`: Merges and deduplicates dependencies from `requirements.txt` and the `dependencies` task.
-// - def `bundle`: Packages the module and dependencies into a standalone `result.pex` file, making deployment easier.
-
-package build
-import mill._
-
-/** extends for efficient library dependency management and executable bundling. */
-trait PythonModule extends Module {
- def moduleDeps: Seq[PythonModule] = Nil
- def dependencies: T[Seq[String]] = Task { Seq.empty[String] }
- def mainFileName: T[String] = Task { "main.py" }
- def sources: T[PathRef] = Task.Source(millSourcePath / "src")
- def requirementsPath: T[PathRef] = Task.Source(millSourcePath / "requirements.txt")
-
- def requirementsDependencies: T[Seq[String]] = Task {
- val reqFile = requirementsPath().path
- if (os.exists(reqFile)) {
- os.read.lines(reqFile).map(_.trim).filter(_.nonEmpty)
- } else {
- Seq.empty[String]
- }
- }
-
- def finalDependencies: T[Seq[String]] = Task {
- // Collects the Upstream dependencies(external Libs)
- val upstreamDependencies = Task.traverse(moduleDeps)(_.finalDependencies)()
-
- (
- dependencies()
- ++ requirementsDependencies()
- ++ upstreamDependencies.flatten
- ).distinct
-
- }
-
- def setup: T[PathRef] = Task {
- val pythonVenv = Task.dest / "venv" / "bin" / "python3"
-
- // installs mypy and pex
- os.call(("python3", "-m", "venv", Task.dest / "venv"))
- os.call((pythonVenv, "-m", "pip", "install", "mypy", "pex"))
-
- // installs library dependencies
- if (finalDependencies().nonEmpty) {
- os.call((pythonVenv, "-m", "pip", "install", finalDependencies()))
- }
-
- PathRef(pythonVenv)
- }
-
- def typeCheck: T[PathRef] = Task {
- val upstreamTypeCheck = Task.traverse(moduleDeps)(_.typeCheck)()
- val pythonVenv = setup().path
-
- os.call(
- (pythonVenv, "-m", "mypy", "--strict", sources().path / mainFileName()),
- stdout = os.Inherit
- )
-
- PathRef(pythonVenv)
- }
-
- def run(args: mill.define.Args) = Task.Command {
- val upstream = Task.traverse(moduleDeps)(_.sources)().zip(moduleDeps)
-
- for (((sourcesFolder), mod) <- upstream) {
- val destinationPath = Task.dest / mod.millSourcePath.subRelativeTo(build.millSourcePath)
- os.copy.over(sourcesFolder.path / os.up, destinationPath)
- }
-
- val pythonVenv = typeCheck().path
-
- os.call(
- (pythonVenv, sources().path / mainFileName(), args.value),
- env = Map("PYTHONPATH" -> Task.dest.toString),
- stdout = os.Inherit
- )
- }
-
- /** Bundles the project into a single PEX executable(result.pex). */
- def bundle(args: mill.define.Args) = Task.Command {
- val pythonVenv = setup().path
- val pexFile = Task.dest / "result.pex"
- val upstream = Task.traverse(moduleDeps)(_.sources)().zip(moduleDeps)
-
- for (((sourcesFolder), mod) <- upstream) {
- val destinationPath = Task.dest / mod.millSourcePath.subRelativeTo(build.millSourcePath)
- os.copy.over(sourcesFolder.path / os.up, destinationPath)
- }
-
- os.call(
- (
- pythonVenv,
- "-m",
- "pex",
- finalDependencies(),
- "-c",
- sources().path / mainFileName(),
- "-o",
- pexFile,
- args.value
- ),
- env = Map("PYTHONPATH" -> Task.dest.toString),
- stdout = os.Inherit
- )
-
- PathRef(pexFile)
- }
-
-}
-
-// Note the use of `Task.traverse(moduleDeps)` in order to aggregate the upstream modules
-// library dependencies and the `typeCheck` outputs, which is necessary before running any
-// code, finally it gathers upstream modules `sources` for further processing in `run` Task
-
-// ==== Example
-
-// The `pex` object demonstrates `PythonModule` with inline and file-based dependencies.
-
-// Running `pex.bundle` creates `result.pex`, a standalone executable for easy distribution.
-
-object pex extends PythonModule {
- // For Education Purpose both way of giving dependencies are discussed here
- // one dependency is mentioned in requirements.txt and one inline(below)
- override def dependencies: T[Seq[String]] = Task { Seq("pandas") }
-
-}
-
-// Source Files For this Example:
-/** See Also: pex/src/main.py */
-/** See Also: pex/requirements.txt */
-
-// To run the project and create a `.pex` executable, use the following commands:
-
-/** Usage
-
-> ./mill pex.run
-Numpy : Sum: 150 | Pandas: Mean: 30.0, Max: 50
-
-> ./mill show pex.bundle
-".../out/pex/bundle.dest/result.pex"
-
-*/
-
-// This generates the `result.pex` file, which packages all dependencies
-// and can be executed as a standalone application.
-
-// To Run the `result.pex` file, First Provide the executable permission(+x)
-// to result.pex and then run using `./result.pex` command.
-
-// The final module tree and task graph is now as follows, with the additional dependencies
-// tasks with upstream and the bundle tasks downstream:
-
-// ```graphviz
-// digraph G {
-// rankdir=LR;
-// node [shape=box, width=0, height=0, style=filled, fillcolor=white];
-// "pex.setup" -> "pex.typeCheck" -> "pex.run";
-// "pex.typeCheck" -> "pex.bundle";
-// "pex.finalDependencies" -> "pex.setup";
-// "pex.finalDependencies" -> "pex.bundle";
-// "pex.sources" -> "pex.typeCheck";
-// "pex.mainFileName" -> "pex.typeCheck";
-// "pex.sources" -> "pex.run";
-// "pex.mainFileName" -> "pex.run";
-// "pex.sources" -> "pex.bundle";
-// "pex.mainFileName" -> "pex.bundle";
-// "pex.mainFileName" [color=green, penwidth=3];
-// "pex.run" [color=green, penwidth=3];
-// "pex.bundle" [color=green, penwidth=3];
-// }
-// ```
diff --git a/example/extending/newlang/8-python-libs-bundle/pex/requirements.txt b/example/extending/newlang/8-python-libs-bundle/pex/requirements.txt
deleted file mode 100644
index 296d654528b..00000000000
--- a/example/extending/newlang/8-python-libs-bundle/pex/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-numpy
\ No newline at end of file
diff --git a/example/extending/python/1-hello-python/build.mill b/example/extending/python/1-hello-python/build.mill
new file mode 100644
index 00000000000..ec96d797839
--- /dev/null
+++ b/example/extending/python/1-hello-python/build.mill
@@ -0,0 +1,105 @@
+// First, we define a `pythonExe` task to create a
+// https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments[Python virtual environment] and installs
+// https://mypy.readthedocs.io/en/stable[mypy] for type-checking. `mypy` verifies type correctness using Python's
+// type hints, helping to catch errors in development.
+
+package build
+import mill._
+
+def pythonExe: T[PathRef] = Task {
+
+ os.call(("python3", "-m", "venv", Task.dest / "venv"))
+ val python = Task.dest / "venv" / "bin" / "python3"
+ os.call((python, "-m", "pip", "install", "mypy==1.13.0"))
+
+ PathRef(python)
+}
+
+// === Defining our Sources
+
+// The `sources` task specifies the directory for Python source files (`src` folder).
+
+def sources: T[PathRef] = Task.Source(millSourcePath / "src")
+
+// === Type Checking
+
+// The `typeCheck` task verifies that the code in the main Python file passes type checks.
+
+def typeCheck: T[Unit] = Task {
+ os.call(
+ (pythonExe().path, "-m", "mypy", "--strict", sources().path),
+ stdout = os.Inherit
+ )
+}
+
+// At this point, we have a minimal working build, with a build graph that looks like this:
+//
+// ```graphviz
+// digraph G {
+// rankdir=LR
+// node [shape=box width=0 height=0 style=filled fillcolor=white]
+// pythonExe -> typeCheck
+// sources -> typeCheck
+// }
+// ```
+
+// Here is the `main.py` file
+/** See Also: src/main.py */
+
+// === Running
+
+// The `mainFileName` task defines the name of the main Python script (in this case `main.py`).
+// The `run` task runs the main file with user-provided command-line arguments.
+// It uses the virtual environment's Python interpreter to execute the script,
+// with output displayed in the console.
+
+def mainFileName: T[String] = Task { "main.py" }
+def run(args: mill.define.Args) = Task.Command {
+ os.call(
+ (pythonExe().path, sources().path / mainFileName(), args.value),
+ stdout = os.Inherit
+ )
+}
+
+// Note that we use `stdout = os.Inherit` since we want to display any output to the user,
+// rather than capturing it for use in our command.
+
+// ```graphviz
+// digraph G {
+// rankdir=LR
+// node [shape=box width=0 height=0 style=filled fillcolor=white]
+// pythonExe -> typeCheck
+// pythonExe -> run
+// sources -> typeCheck
+// sources -> run
+// mainFileName -> run
+// mainFileName [color=green, penwidth=3]
+// run [color=green, penwidth=3]
+// }
+// ```
+//
+// Note that like many optionally-typed languages, The `run` and `typeCheck` tasks are
+// independent: you can run a Python program without needing to typecheck it first. This is
+// different from compiled languages like Java, which require typechecking before execution
+//
+// Below are commands that demonstrate the typechecking and running functionality
+// of our pipeline:
+
+/** Usage
+
+> ./mill typeCheck
+Success: no issues found in 1 source file
+
+> ./mill run Mill Python
+Hello, Mill Python!
+15
+
+> sed -i.bak 's/print(add(5, 10))/print(addd(5, 10))/g' src/main.py
+
+> ./mill typeCheck # if we make a typo in a method name, mypy flags it
+error: ...Name "addd" is not defined...
+
+*/
+
+// We have now completed a basic Python integration in Mill, as a pipeline of inter-related tasks.
+// Next steps is to turn this one-off pipeline into a reusable PythonModule
diff --git a/example/extending/newlang/5-hello-python/src/main.py b/example/extending/python/1-hello-python/src/main.py
similarity index 100%
rename from example/extending/newlang/5-hello-python/src/main.py
rename to example/extending/python/1-hello-python/src/main.py
diff --git a/example/extending/python/2-python-modules/build.mill b/example/extending/python/2-python-modules/build.mill
new file mode 100644
index 00000000000..46e8ed1f28b
--- /dev/null
+++ b/example/extending/python/2-python-modules/build.mill
@@ -0,0 +1,108 @@
+// This example shows how to define a `PythonModule` trait for managing Python scripts
+// within multiple Mill objects. `PythonModule` takes the one-off pipeline we defined
+// earlier with `sources`, `pythonExe`, `typeCheck`, etc. and wraps it in a `trait`
+// that can be re-used.
+
+package build
+import mill._
+
+trait PythonModule extends Module {
+
+ def sources: T[PathRef] = Task.Source(millSourcePath / "src")
+ def mainFileName: T[String] = Task { "main.py" }
+
+ def pythonExe: T[PathRef] = Task {
+
+ os.call(("python3", "-m", "venv", Task.dest / "venv"))
+ val python = Task.dest / "venv" / "bin" / "python3"
+ os.call((python, "-m", "pip", "install", "mypy==1.13.0"))
+
+ PathRef(python)
+ }
+
+ def typeCheck: T[Unit] = Task {
+ os.call(
+ (pythonExe().path, "-m", "mypy", "--strict", sources().path),
+ stdout = os.Inherit
+ )
+
+ }
+
+ def run(args: mill.define.Args) = Task.Command {
+ os.call(
+ (pythonExe().path, sources().path / mainFileName(), args.value),
+ stdout = os.Inherit
+ )
+ }
+
+}
+
+// Once the `trait PythonModule`, has been defined, we can re-use it in
+// three seperate objects below
+
+object foo extends PythonModule {
+ def mainFileName = "foo.py"
+ object bar extends PythonModule {
+ def mainFileName = "bar.py"
+ }
+}
+
+object qux extends PythonModule {
+ def mainFileName = "qux.py"
+}
+
+// For this example, we have three different Python Scripts
+// `foo/src/foo.py`, `foo/bar/src/bar.py`, `qux/src/qux.py`, one in each `PythonModule`.
+// The following commands run each module and display their output:
+
+/** Usage
+
+> ./mill foo.run Mill
+Hello, Mill Foo!
+
+> ./mill foo.bar.run Mill
+Hello, Mill Foo Bar!
+
+> ./mill qux.run Mill
+Hello, Mill Qux!
+
+*/
+
+// After this step, we have a build graph that looks like this:
+
+// ```graphviz
+// digraph G {
+// rankdir=LR
+// node [shape=box width=0 height=0 style=filled fillcolor=white]
+// subgraph cluster_3 {
+// style=dashed
+// label=qux
+// "qux.pythonExe" -> "qux.typeCheck"
+// "qux.pythonExe" -> "qux.run"
+// "qux.sources" -> "qux.typeCheck"
+// "qux.sources" -> "qux.run"
+// "qux.mainFileName" -> "qux.run"
+// }
+// subgraph cluster_1 {
+// subgraph cluster_2 {
+// style=dashed
+// label=bar
+// "bar.pythonExe" -> "bar.typeCheck"
+// "bar.pythonExe" -> "bar.run"
+// "bar.sources" -> "bar.typeCheck"
+// "bar.sources" -> "bar.run"
+// "bar.mainFileName" -> "bar.run"
+// }
+// style=dashed
+// label=foo
+// "foo.pythonExe" -> "foo.typeCheck"
+// "foo.pythonExe" -> "foo.run"
+// "foo.sources" -> "foo.typeCheck"
+// "foo.sources" -> "foo.run"
+// "foo.mainFileName" -> "foo.run"
+// }
+// }
+// ```
+
+// Right now, the three ``PythonModule``s are independent. Next we will look into
+// how to allow them to depend on each other using `moduleDeps`.
diff --git a/example/extending/newlang/6-python-modules/foo/bar/src/bar.py b/example/extending/python/2-python-modules/foo/bar/src/bar.py
similarity index 100%
rename from example/extending/newlang/6-python-modules/foo/bar/src/bar.py
rename to example/extending/python/2-python-modules/foo/bar/src/bar.py
diff --git a/example/extending/newlang/6-python-modules/foo/src/foo.py b/example/extending/python/2-python-modules/foo/src/foo.py
similarity index 100%
rename from example/extending/newlang/6-python-modules/foo/src/foo.py
rename to example/extending/python/2-python-modules/foo/src/foo.py
diff --git a/example/extending/newlang/6-python-modules/qux/src/qux.py b/example/extending/python/2-python-modules/qux/src/qux.py
similarity index 100%
rename from example/extending/newlang/6-python-modules/qux/src/qux.py
rename to example/extending/python/2-python-modules/qux/src/qux.py
diff --git a/example/extending/python/3-python-module-deps/build.mill b/example/extending/python/3-python-module-deps/build.mill
new file mode 100644
index 00000000000..09f1416ac31
--- /dev/null
+++ b/example/extending/python/3-python-module-deps/build.mill
@@ -0,0 +1,128 @@
+// This example shows how to add module dependencies to `PythonModule`, allowing them
+// to depend on one another.
+//
+// The main change is the addition of `def moduleDeps` to specify the inter-module dependencies.
+// We then use `Task.traverse` to aggregate the `sources` of the upstream modules and
+// make them available during `typeCheck` and `run`:
+
+package build
+import mill._
+
+trait PythonModule extends Module {
+
+ // List of module dependencies required by this module.
+ def moduleDeps: Seq[PythonModule] = Nil
+
+ def sources: T[PathRef] = Task.Source(millSourcePath / "src")
+ def mainFileName: T[String] = Task { "main.py" }
+
+ def pythonExe: T[PathRef] = Task {
+
+ os.call(("python3", "-m", "venv", Task.dest / "venv"))
+ val python = Task.dest / "venv" / "bin" / "python3"
+ os.call((python, "-m", "pip", "install", "mypy==1.13.0"))
+
+ PathRef(python)
+ }
+
+ def typeCheck: T[Unit] = Task {
+ val upstreamTypeCheck = Task.traverse(moduleDeps)(_.typeCheck)()
+ val pythonVenv = pythonExe().path
+
+ os.call(
+ (pythonVenv, "-m", "mypy", "--strict", sources().path),
+ stdout = os.Inherit
+ )
+
+ }
+
+ def gatherScripts(upstream: Seq[(PathRef, PythonModule)]) = {
+ for ((sourcesFolder, mod) <- upstream) {
+ val destinationPath = os.pwd / mod.millSourcePath.subRelativeTo(build.millSourcePath)
+ os.copy.over(sourcesFolder.path / os.up, destinationPath)
+ }
+ }
+
+ def run(args: mill.define.Args) = Task.Command {
+ gatherScripts(Task.traverse(moduleDeps)(_.sources)().zip(moduleDeps))
+
+ os.call(
+ (pythonExe().path, sources().path / mainFileName(), args.value),
+ env = Map("PYTHONPATH" -> Task.dest.toString),
+ stdout = os.Inherit
+ )
+ }
+
+}
+
+// Now we can take the three modules defined earlier and wire them up:
+// `qux` depends on `foo` and `foo.bar`, which export their APIs for use in `qux`.
+
+object foo extends PythonModule {
+ def mainFileName = "foo.py"
+ object bar extends PythonModule {
+ def mainFileName = "bar.py"
+ }
+}
+
+object qux extends PythonModule {
+ def mainFileName = "qux.py"
+ def moduleDeps = Seq(foo, foo.bar)
+}
+
+// For this example, we define the following three files, one in each module, that
+// depend on one another:
+
+/** See Also: foo/src/foo.py */
+/** See Also: foo/bar/src/bar.py */
+/** See Also: qux/src/qux.py */
+
+/** Usage
+
+> ./mill qux.run 10 20
+Add: 10 + 20 = 30 | Multiply: 10 * 20 = 200 | Divide: 10 / 20 = 0.5
+
+*/
+
+// Task dependency graph, showing `foo` and `bar` tasks feeding into `qux`:
+//
+// ```graphviz
+// digraph G {
+// rankdir=LR
+// node [shape=box width=0 height=0 style=filled fillcolor=white]
+// subgraph cluster_1 {
+// subgraph cluster_2 {
+// style=dashed
+// label=bar
+// "bar.pythonExe" -> "bar.typeCheck"
+// "bar.pythonExe" -> "bar.run"
+// "bar.sources" -> "bar.typeCheck"
+// "bar.sources" -> "bar.run"
+// "bar.mainFileName" -> "bar.run"
+// }
+// style=dashed
+// label=foo
+// "foo.pythonExe" -> "foo.typeCheck"
+// "foo.pythonExe" -> "foo.run"
+// "foo.sources" -> "foo.typeCheck"
+// "foo.sources" -> "foo.run"
+// "foo.mainFileName" -> "foo.run"
+// }
+// subgraph cluster_3 {
+// style=dashed
+// label=qux
+// "qux.pythonExe" -> "qux.typeCheck"
+// "qux.pythonExe" -> "qux.run"
+// "qux.sources" -> "qux.typeCheck"
+// "qux.sources" -> "qux.run"
+// "qux.mainFileName" -> "qux.run"
+// }
+// "bar.typeCheck" -> "qux.typeCheck" [color=green, penwidth=3]
+// "bar.sources" -> "qux.run" [color=green, penwidth=3]
+// "foo.typeCheck" -> "qux.typeCheck" [color=green, penwidth=3]
+// "foo.sources" -> "qux.run" [color=green, penwidth=3]
+// }
+// ```
+
+// Next, we will add support for depending on external Python libraries from
+// https://pypi.org/[PyPI], and bundling via https://pypi.org/project/pex/[PEX]
diff --git a/example/extending/newlang/7-python-module-deps/foo/bar/src/bar.py b/example/extending/python/3-python-module-deps/foo/bar/src/bar.py
similarity index 100%
rename from example/extending/newlang/7-python-module-deps/foo/bar/src/bar.py
rename to example/extending/python/3-python-module-deps/foo/bar/src/bar.py
diff --git a/example/extending/newlang/7-python-module-deps/foo/src/foo.py b/example/extending/python/3-python-module-deps/foo/src/foo.py
similarity index 100%
rename from example/extending/newlang/7-python-module-deps/foo/src/foo.py
rename to example/extending/python/3-python-module-deps/foo/src/foo.py
diff --git a/example/extending/newlang/7-python-module-deps/qux/src/qux.py b/example/extending/python/3-python-module-deps/qux/src/qux.py
similarity index 100%
rename from example/extending/newlang/7-python-module-deps/qux/src/qux.py
rename to example/extending/python/3-python-module-deps/qux/src/qux.py
diff --git a/example/extending/python/4-python-libs-bundle/build.mill b/example/extending/python/4-python-libs-bundle/build.mill
new file mode 100644
index 00000000000..27d1745f0c9
--- /dev/null
+++ b/example/extending/python/4-python-libs-bundle/build.mill
@@ -0,0 +1,179 @@
+// This implementation extends `PythonModule` with these key Tasks:
+//
+// - `pythonDeps`: allows the user to define python dependencies that will be `pip installed`.
+// These are aggregated into `transitivePythonDeps`
+// - `bundle`: Packages the module and dependencies into a standalone `bundle.pex` file,
+// making deployment easier.
+
+package build
+import mill._
+
+trait PythonModule extends Module {
+ def moduleDeps: Seq[PythonModule] = Nil
+ def mainFileName: T[String] = Task { "main.py" }
+ def sources: T[PathRef] = Task.Source(millSourcePath / "src")
+
+ def pythonDeps: T[Seq[String]] = Task { Seq.empty[String] }
+
+ def transitivePythonDeps: T[Seq[String]] = Task {
+ val upstreamDependencies = Task.traverse(moduleDeps)(_.transitivePythonDeps)().flatten
+ pythonDeps() ++ upstreamDependencies
+ }
+
+ def pythonExe: T[PathRef] = Task {
+ os.call(("python3", "-m", "venv", Task.dest / "venv"))
+ val python = Task.dest / "venv" / "bin" / "python3"
+ os.call((python, "-m", "pip", "install", "mypy==1.13.0", "pex==2.24.1", transitivePythonDeps()))
+
+ PathRef(python)
+ }
+
+ def typeCheck: T[Unit] = Task {
+ val upstreamTypeCheck = Task.traverse(moduleDeps)(_.typeCheck)()
+
+ os.call(
+ (pythonExe().path, "-m", "mypy", "--strict", sources().path),
+ stdout = os.Inherit,
+ cwd = T.workspace
+ )
+ }
+
+ def gatherScripts(upstream: Seq[(PathRef, PythonModule)]) = {
+ for ((sourcesFolder, mod) <- upstream) {
+ val destinationPath = os.pwd / mod.millSourcePath.subRelativeTo(build.millSourcePath)
+ os.copy.over(sourcesFolder.path / os.up, destinationPath)
+ }
+ }
+
+ def run(args: mill.define.Args) = Task.Command {
+ gatherScripts(Task.traverse(moduleDeps)(_.sources)().zip(moduleDeps))
+
+ os.call(
+ (pythonExe().path, sources().path / mainFileName(), args.value),
+ env = Map("PYTHONPATH" -> Task.dest.toString),
+ stdout = os.Inherit
+ )
+ }
+
+ /** Bundles the project into a single PEX executable(bundle.pex). */
+ def bundle = Task {
+ gatherScripts(Task.traverse(moduleDeps)(_.sources)().zip(moduleDeps))
+
+ val pexFile = Task.dest / "bundle.pex"
+ os.call(
+ (
+ pythonExe().path,
+ "-m",
+ "pex",
+ transitivePythonDeps(),
+ "-D",
+ Task.dest,
+ "-c",
+ sources().path / mainFileName(),
+ "-o",
+ pexFile,
+ "--scie",
+ "eager"
+ ),
+ env = Map("PYTHONPATH" -> Task.dest.toString),
+ stdout = os.Inherit
+ )
+
+ PathRef(pexFile)
+ }
+
+}
+
+// Note the use of `Task.traverse(moduleDeps)` in order to aggregate the upstream modules
+// library dependencies and the `typeCheck` outputs.
+//
+// Now, our three modules can define `pythonDeps` to be used at runtime:
+
+object foo extends PythonModule {
+ object bar extends PythonModule {
+ def pythonDeps = Seq("pandas==2.2.3", "numpy==2.1.3")
+ }
+ def pythonDeps = Seq("numpy==2.1.3")
+}
+
+object qux extends PythonModule {
+ def moduleDeps = Seq(foo, foo.bar)
+}
+
+// To run the project and create a `.pex` executable, use the following commands:
+
+/** Usage
+
+> ./mill qux.run
+Numpy : Sum: 150 | Pandas: Mean: 30.0, Max: 50
+
+> ./mill show qux.bundle
+".../out/qux/bundle.dest/bundle.pex"
+
+> out/qux/bundle.dest/bundle.pex # running the PEX binary outside of Mill
+Numpy : Sum: 150 | Pandas: Mean: 30.0, Max: 50
+
+*/
+
+// This generates the `bundle.pex` file, which packages all dependencies
+// and can be executed as a standalone application.
+//
+// To Run the `bundle.pex` file, First Provide the executable permission(+x)
+// to bundle.pex and then run using `./bundle.pex` command.
+//
+// The final module tree and task graph is now as follows, with the additional dependencies
+// tasks with upstream and the bundle tasks downstream:
+//
+// ```graphviz
+// digraph G {
+// rankdir=LR
+// node [shape=box width=0 height=0 style=filled fillcolor=white]
+// subgraph cluster_3 {
+// style=dashed
+// label=qux
+// "qux.pythonDeps" -> "qux.pythonExe" [color=green, penwidth=3]
+// "qux.pythonExe" -> "qux.typeCheck"
+// "qux.pythonExe" -> "qux.run"
+// "qux.sources" -> "qux.typeCheck"
+// "qux.sources" -> "qux.run"
+// "qux.sources" -> "qux.bundle" [color=green, penwidth=3]
+// "qux.bundle" [color=green, penwidth=3]
+// "qux.mainFileName" -> "qux.run"
+// "qux.pythonDeps" [color=green, penwidth=3]
+// }
+// subgraph cluster_1 {
+// subgraph cluster_2 {
+// style=dashed
+// label=bar
+// "bar.pythonDeps" -> "bar.pythonExe" [color=green, penwidth=3]
+// "bar.pythonExe" -> "bar.typeCheck"
+// "bar.pythonExe" -> "bar.run"
+// "bar.sources" -> "bar.typeCheck"
+// "bar.sources" -> "bar.run"
+// "bar.sources" -> "bar.bundle" [color=green, penwidth=3]
+// "bar.bundle" [color=green, penwidth=3]
+// "bar.mainFileName" -> "bar.run"
+// "bar.pythonDeps" [color=green, penwidth=3]
+// }
+// style=dashed
+// label=foo
+// "foo.pythonDeps" -> "foo.pythonExe" [color=green, penwidth=3]
+// "foo.pythonExe" -> "foo.typeCheck"
+// "foo.pythonExe" -> "foo.run"
+// "foo.sources" -> "foo.typeCheck"
+// "foo.sources" -> "foo.run"
+// "foo.sources" -> "foo.bundle" [color=green, penwidth=3]
+// "foo.bundle" [color=green, penwidth=3]
+// "foo.mainFileName" -> "foo.run"
+// "foo.pythonDeps" [color=green, penwidth=3]
+// }
+// "bar.pythonDeps" -> "qux.pythonDeps" [color=green, penwidth=3]
+// "foo.pythonDeps" -> "qux.pythonDeps" [color=green, penwidth=3]
+// "bar.typeCheck" -> "qux.typeCheck"
+// "bar.sources" -> "qux.run"
+// "bar.sources" -> "qux.bundle" [color=green, penwidth=3]
+// "foo.typeCheck" -> "qux.typeCheck"
+// "foo.sources" -> "qux.run"
+// "foo.sources" -> "qux.bundle" [color=green, penwidth=3]
+// }
+// ```
diff --git a/example/extending/python/4-python-libs-bundle/foo/bar/src/bar.py b/example/extending/python/4-python-libs-bundle/foo/bar/src/bar.py
new file mode 100644
index 00000000000..636f68d0328
--- /dev/null
+++ b/example/extending/python/4-python-libs-bundle/foo/bar/src/bar.py
@@ -0,0 +1,5 @@
+import pandas as pd # type: ignore
+import numpy as np
+
+data = np.array([10, 20, 30, 40, 50])
+df = pd.DataFrame({"Values": data})
\ No newline at end of file
diff --git a/example/extending/python/4-python-libs-bundle/foo/src/foo.py b/example/extending/python/4-python-libs-bundle/foo/src/foo.py
new file mode 100644
index 00000000000..e7de58403f8
--- /dev/null
+++ b/example/extending/python/4-python-libs-bundle/foo/src/foo.py
@@ -0,0 +1,2 @@
+import numpy as np
+data = np.array([10, 20, 30, 40, 50])
\ No newline at end of file
diff --git a/example/extending/newlang/8-python-libs-bundle/pex/src/main.py b/example/extending/python/4-python-libs-bundle/qux/src/main.py
old mode 100644
new mode 100755
similarity index 61%
rename from example/extending/newlang/8-python-libs-bundle/pex/src/main.py
rename to example/extending/python/4-python-libs-bundle/qux/src/main.py
index 9f629726cb9..9a9acb0b35d
--- a/example/extending/newlang/8-python-libs-bundle/pex/src/main.py
+++ b/example/extending/python/4-python-libs-bundle/qux/src/main.py
@@ -1,8 +1,11 @@
+#!/usr/bin/python3
import numpy as np
-import pandas as pd # type: ignore
+
+from foo.src.foo import data
+from foo.bar.src.bar import df
+
def main() -> None:
- data = np.array([10, 20, 30, 40, 50])
- df = pd.DataFrame({"Values": data})
print(f"Numpy : Sum: {np.sum(data)} | Pandas: Mean: {df['Values'].mean()}, Max: {df['Values'].max()}")
+
if __name__ == "__main__":
main()
\ No newline at end of file
diff --git a/example/extending/newlang/1-hello-typescript/build.mill b/example/extending/typescript/1-hello-typescript/build.mill
similarity index 100%
rename from example/extending/newlang/1-hello-typescript/build.mill
rename to example/extending/typescript/1-hello-typescript/build.mill
diff --git a/example/extending/newlang/1-hello-typescript/src/hello.ts b/example/extending/typescript/1-hello-typescript/src/hello.ts
similarity index 100%
rename from example/extending/newlang/1-hello-typescript/src/hello.ts
rename to example/extending/typescript/1-hello-typescript/src/hello.ts
diff --git a/example/extending/newlang/2-typescript-modules/build.mill b/example/extending/typescript/2-typescript-modules/build.mill
similarity index 100%
rename from example/extending/newlang/2-typescript-modules/build.mill
rename to example/extending/typescript/2-typescript-modules/build.mill
diff --git a/example/extending/newlang/2-typescript-modules/foo/bar/src/bar.ts b/example/extending/typescript/2-typescript-modules/foo/bar/src/bar.ts
similarity index 100%
rename from example/extending/newlang/2-typescript-modules/foo/bar/src/bar.ts
rename to example/extending/typescript/2-typescript-modules/foo/bar/src/bar.ts
diff --git a/example/extending/newlang/2-typescript-modules/foo/src/foo.ts b/example/extending/typescript/2-typescript-modules/foo/src/foo.ts
similarity index 100%
rename from example/extending/newlang/2-typescript-modules/foo/src/foo.ts
rename to example/extending/typescript/2-typescript-modules/foo/src/foo.ts
diff --git a/example/extending/newlang/2-typescript-modules/qux/src/qux.ts b/example/extending/typescript/2-typescript-modules/qux/src/qux.ts
similarity index 100%
rename from example/extending/newlang/2-typescript-modules/qux/src/qux.ts
rename to example/extending/typescript/2-typescript-modules/qux/src/qux.ts
diff --git a/example/extending/newlang/3-module-deps/build.mill b/example/extending/typescript/3-module-deps/build.mill
similarity index 100%
rename from example/extending/newlang/3-module-deps/build.mill
rename to example/extending/typescript/3-module-deps/build.mill
diff --git a/example/extending/newlang/3-module-deps/foo/bar/src/bar.js b/example/extending/typescript/3-module-deps/foo/bar/src/bar.js
similarity index 100%
rename from example/extending/newlang/3-module-deps/foo/bar/src/bar.js
rename to example/extending/typescript/3-module-deps/foo/bar/src/bar.js
diff --git a/example/extending/newlang/3-module-deps/foo/bar/src/bar.ts b/example/extending/typescript/3-module-deps/foo/bar/src/bar.ts
similarity index 100%
rename from example/extending/newlang/3-module-deps/foo/bar/src/bar.ts
rename to example/extending/typescript/3-module-deps/foo/bar/src/bar.ts
diff --git a/example/extending/newlang/3-module-deps/foo/src/foo.js b/example/extending/typescript/3-module-deps/foo/src/foo.js
similarity index 100%
rename from example/extending/newlang/3-module-deps/foo/src/foo.js
rename to example/extending/typescript/3-module-deps/foo/src/foo.js
diff --git a/example/extending/newlang/3-module-deps/foo/src/foo.ts b/example/extending/typescript/3-module-deps/foo/src/foo.ts
similarity index 100%
rename from example/extending/newlang/3-module-deps/foo/src/foo.ts
rename to example/extending/typescript/3-module-deps/foo/src/foo.ts
diff --git a/example/extending/newlang/3-module-deps/qux/src/qux.ts b/example/extending/typescript/3-module-deps/qux/src/qux.ts
similarity index 100%
rename from example/extending/newlang/3-module-deps/qux/src/qux.ts
rename to example/extending/typescript/3-module-deps/qux/src/qux.ts
diff --git a/example/extending/newlang/4-npm-deps-bundle/build.mill b/example/extending/typescript/4-npm-deps-bundle/build.mill
similarity index 100%
rename from example/extending/newlang/4-npm-deps-bundle/build.mill
rename to example/extending/typescript/4-npm-deps-bundle/build.mill
diff --git a/example/extending/newlang/4-npm-deps-bundle/foo/bar/src/bar.js b/example/extending/typescript/4-npm-deps-bundle/foo/bar/src/bar.js
similarity index 100%
rename from example/extending/newlang/4-npm-deps-bundle/foo/bar/src/bar.js
rename to example/extending/typescript/4-npm-deps-bundle/foo/bar/src/bar.js
diff --git a/example/extending/newlang/4-npm-deps-bundle/foo/bar/src/bar.ts b/example/extending/typescript/4-npm-deps-bundle/foo/bar/src/bar.ts
similarity index 100%
rename from example/extending/newlang/4-npm-deps-bundle/foo/bar/src/bar.ts
rename to example/extending/typescript/4-npm-deps-bundle/foo/bar/src/bar.ts
diff --git a/example/extending/newlang/4-npm-deps-bundle/foo/src/foo.js b/example/extending/typescript/4-npm-deps-bundle/foo/src/foo.js
similarity index 100%
rename from example/extending/newlang/4-npm-deps-bundle/foo/src/foo.js
rename to example/extending/typescript/4-npm-deps-bundle/foo/src/foo.js
diff --git a/example/extending/newlang/4-npm-deps-bundle/foo/src/foo.ts b/example/extending/typescript/4-npm-deps-bundle/foo/src/foo.ts
similarity index 100%
rename from example/extending/newlang/4-npm-deps-bundle/foo/src/foo.ts
rename to example/extending/typescript/4-npm-deps-bundle/foo/src/foo.ts
diff --git a/example/extending/newlang/4-npm-deps-bundle/qux/src/qux.ts b/example/extending/typescript/4-npm-deps-bundle/qux/src/qux.ts
similarity index 100%
rename from example/extending/newlang/4-npm-deps-bundle/qux/src/qux.ts
rename to example/extending/typescript/4-npm-deps-bundle/qux/src/qux.ts
diff --git a/example/package.mill b/example/package.mill
index ddf3e856637..fedb790a290 100644
--- a/example/package.mill
+++ b/example/package.mill
@@ -80,7 +80,8 @@ object `package` extends RootModule with Module {
object metabuild extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "metabuild"))
object plugins extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "plugins"))
object jvmcode extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "jvmcode"))
- object newlang extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "newlang"))
+ object python extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "python"))
+ object typescript extends Cross[ExampleCrossModule](build.listIn(millSourcePath / "typescript"))
}
trait ExampleCrossModuleKotlin extends ExampleCrossModuleJava {