Mill: A fast JVM build tool for Java and Scala

(mill-build.org)

54 points | by 0x54MUR41 8 hours ago ago

40 comments

  • lihaoyi an hour ago

    Author here! Hope you take a look at the project and find it cool. There's a lot of interesting stuff here. In particular, the Video linked on the landing page is a great intros from a Java developer point of view, and the following video is a great intro from a Build Tool Architect point of view:

    * https://www.youtube.com/watch?v=UsXgCeU-ovI

    While Mill is focusing on JVM for now, it is very extensible and I have a strawman demo of adding a Javascript toolchain in ~100 lines of code https://mill-build.org/mill/0.12.1/extending/new-language.ht...

    For those of you who want to learn more about the design principles and architecture of Mill, and what makes it unique, you should check out the page on Design Principles which has links to videos and blog posts where I elaborate on what exactly makes Mill so different from Maven, Gradle, SBT, Bazel, and so on:

    * Mill Design Principles https://mill-build.org/mill/0.12.1/depth/design-principles.h...

    I've mentioned this in a few places, but the comparisons with other build tools are best-effort. I have no doubt they can be made more accurate, and welcome feedback so I can go back and refine them. Please take them with a grain of salt

    I'm also trying to get the community involved, so it's not just me writing code and running the show. To that end, I have set up a bounty program, so pay out significant sums of money (500-2000USD a piece) for people who make non-trivial contributions. It's already paid out about 10kUSD and has another 20kUSD on the table, so if anyone wants to get involved and make a little cash, feel free to take a shot at one of the bounties! https://github.com/orgs/com-lihaoyi/discussions/6

    • philipwhiuk 17 minutes ago

      The current version is 0.12.1.

      What's required for v1.0?

      • lihaoyi 15 minutes ago

        Traditionally I've labelled my OSS projects 1.0 when they've stabilized and the rate of change has greatly reduced. Right now Mill is not there yet, but maybe if at end-2025 we realize no breaking changes have been needed since end-2024, we can call it 1.0

  • k3vinw 24 minutes ago

    The thing that’s great about maven is its declarative nature. You can declare goals and profiles for whatever you need the build system to do.

    The main appeal that I can see from mill over maven is the power of dynamic programming over static xml files. Maybe good lsp/ide support will make managing a build system like this bearable?

    • lihaoyi 19 minutes ago

      Yes, IDE support in Mill is key. Without IntelliJ or VSCode, Mill would not be nearly as pleasant to use as it is today.

      Mill and Maven both let you declare goals for what you want to do. One does it in XML and one does it in typechecked code. While XML does work, doing things in code with typechecking and full IDE support turns out to be pretty nice as well!

  • spuz 3 hours ago

    A build tool that is not only fast but configured in a type safe way sounds great. I really like this quote from the "Why use scala" part of the documentation:

    > Most developers using a build tool are not build tool experts, and have no desire to become build tool experts. They will forever be cargo-culting examples they find online, copy-pasting from other parts of the codebase, or blindly fumbling their customizations. It is in this context that Mill’s static typing really shines: what such "perpetual beginners" need most is help understanding/navigating the build logic, and help checking their proposed changes for dumb mistakes. And there will be dumb mistakes, because most people are not and will never be build-tool experts or enthusiasts

  • cryptos 2 hours ago

    The comparision with Gradle is not up to date. There is stated that you would end up in an untyped mess of Groovy build files, but statically typed Kotlin files are the default for quite some time now in Gradle! https://mill-build.org/mill/0.12.1/comparisons/gradle.html

    • lihaoyi an hour ago

      Author here. Unfortunately this is because my own experience with Gradle is not up to date; I've only lived in the Gradle Groovy world! If anyone is interested in helping out, I have a 1500USD bounty on porting a gradle.kts build to Mill, so we can do a fair up-to-date comparison https://github.com/com-lihaoyi/mill/issues/3670

  • iamcalledrob 3 hours ago

    It's not clear to me how this is better than Gradle. And I hate Gradle.

    At first glance, Mill looks like it has many of the pitfalls of Gradle: - Plugins: Creates the temptation to rely on plugins for everything, and suddenly you're in plugin dependency hell with no idea how anything actually works. - Build scripts written in a DSL on top of a new language: Now I have to learn Scala and your DSL. I don't want to do either! - Build scripts written in a language that can be used for code too: Versioning hell when the compiler for the build system needs to be a different version to the compiler for the actual project code. See: Gradle and Kotlin

    • lihaoyi an hour ago

      Author here! The issue here is that builds, and many other "just configuration" scenarios, are fundamentally complex. So many projects that start off as "just XML" or "just YAML" end up implementing their own half-baked programming language interepreter inside of their XML/YAML/JSON/whatever.

      Examples:

      * Github Actions Config Expressions https://docs.github.com/en/actions/writing-workflows/choosin...

      * CloudFormation Functions https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGui...

      * Helm Chart Templates https://helm.sh/docs/chart_best_practices/templates/

      There is a reason why Bazel went with Python/Starlark, why Pulumi and CDK and friends are getting popular. Fundamentally, many of these use cases look surprisingly like programming languages: maybe not immediately, but certainly after you've dug in a bit. And having a properly designed purpose-build programming language (e.g. StarLark) or a flexible general purpose language (e.g. Typescript, Kotlin, Scala) does turn out to be the least-bad option

    • fabioz 2 hours ago

      The first advantage the homepage lists is:

      > Mill can build the same Java codebase 5-10x faster than Maven, or 2-4x faster than Gradle

      Speed per se can be a good selling point (having to wait for slow builds is really annoying).

      I can't really comment on anything else though as I just stumbled upon it here in HN ;)

      • iainmerrick 2 hours ago

        The goal should be more like 50x faster than Gradle. Gradle is ludicrously slow (at least in every single Gradle project I’ve had to work with).

        • kaba0 an hour ago

          First invocation may be. Subsequent builds are very fast, unless someone decided to write random bullshit into the build scripts that execute at config time, making the config process impure.

    • kaba0 an hour ago

      There is basically no DSL. You simply write what a build needs, e.g. you write a function `collectCFiles()` that collects every file with extension `.c`. You then issue a command like `gcc ${collectCFiles()}`. And pretty much that’s it - you can use shell commands, or do anything in scala (or java or whathaveyou). You simply have your functions return either a value (e.g. a checksum) or a location, which is the only mill-specific logic. So your compileC() function will simply invoke your collectCFiles() function, and this invocation implicitly creates a dependency between these tasks. You have written literally the simplest way to describe your build logic. But in the background mill will cache your functions’ inputs outputs and parallelize those that need re-run, which is what a build tool should do.

      The implementation may not be the theoretical best, but I think the idea is pretty much the perfect build system out there.

  • sireat 36 minutes ago

    Just a note that the author Li Haoyi is a fantastic contributor to Scala community.

    He has written multiple useful libraries. Out of many JSON libraries, his one was the most intuitive and practial.

    His book is excellent too. I bought it when it came out. It is worthy of a plug: https://www.handsonscala.com/

    I miss working on Scala projects. Sadly I rarely see new ones these days.

    Does IntelliJ plugin finally work on Scala 3? About 2 years ago it was half broken.

  • potamic 3 hours ago

    What tends to be complex about build requirements that necessitates special purpose tools? Golang seems to be doing fine with just go build and go test. What else are people doing with gradle/maven that requires static typing, DAGs, plugins etc.?

    • lihaoyi an hour ago

      Author here!

      `go build` and `go test` do work, at limited scale and complexity. In Scala there's Scala-CLI which is excellent. If they work for you, you probably aren't the target market for these build tools. Once you start layering on bash scripts, layering on make, layering on Python scripts, layering on manual steps written down in a readme.md somewhere, that's the time when you should consider a proper build tool. And if that has never happened to you in your career, count your blessings :)

      Why not just write some boring, pure code to handle the build? Why not write my build system in vanilla LYAH Haskell? It turns out that builds do have some specific requirements that most programs do not need to care about: caching, parallelization, introspection, and so on. Check out the following blog/talk for more details:

      * Blog Post: Build Tools as Pure Functional Programs https://www.lihaoyi.com/post/BuildToolsasPureFunctionalProgr...

      * Video: Mill: a Build Tool based on Pure Functional Programming https://www.youtube.com/watch?v=j6uThGxx-18&list=PLBqWQH1Miw...

      Thus "naively" building your project "directly with code" ends up not working, so you do need some additional support. While most build tools end up constructing a complete bespoke programming environment from scratch, Mill tries to leverage the Scala language and JVM as much as possible, so you can re-use all your expertise and tooling (e.g. IntelliJ, VSCode, Maven Central, etc.) almost verbatim while getting all the necessary build-tool stuff (parallelism, caching, introspection) for free. Check out those two links if you want to learn more!

    • rykad 2 hours ago

      To be blunt, nothing.

      The issue is that most people in jvm land are on a closed bubble and haven't seen anything else. This is true for build systems as is for non OO design for example. Most simply don't know better and the rest of us are simply stuck.

      Ant and then Maven started simple enough but people always find a way to justify adding more stuff. Gradle already started complex enough and they keep adding more stuff...

      • cryptos 2 hours ago

        You have to keep in mind that Gradle Inc. earns money by providing consulting for complex builds. An easy build tool would destroy this business model ;-)

      • kaba0 44 minutes ago

        To be blunt, if anything it might be you who live in a closed bubble.

        Builds that require ad-hoc functionality is the default. It’s extremely rare that everything fits nicely into “cargo build” or other single language build tools’ model. And while these often have escape hatches, at that point you have to write imperative code with no caching and parallelization that is literally the job of a build tool.

    • dikei an hour ago

      In JVM world, the de factor equivalent to `go build` and `go test` are `mvn compile` and `mvn test`, which works 99% percent of the time.

      Other build tools and plugins just compete/fill in for:

      * improved build speed / test speed: using background daemon to reduce strtup speed, intelligent caching / task reordering to avoid redoing, etc..

      * extra functionalities like code generation, publishing or deployments. As code generation is really big in JVM world, and there are many ways to deploy an application: jar + libs in a zip file, uber jars, container image, etc...

    • iamcalledrob 3 hours ago

      I'm working on a project that encompasses both JVM (Gradle, Kotlin) and Golang.

      My hot take: JVM build tools, especially Gradle, are a soup of unnecessary complexity, and people working in that ecosystem have Stockholm Syndrome.

      In Golang, I spend about 99% of my time dealing with code.

      In JVM land, I'm spending 30% just dealing with the build system. It's actually insane, and the community at large thinks this is normal. The amount of time it takes to publish a multi-platform Kotlin library for the first time can be measured in days. I published my first Golang library in minutes, by comparison.

      • cryptos 2 hours ago

        You speak from my soul! I'm in the Java world for a really long time now and I'm wondering for years why the build tools need to be so complicated an annoying. I know Go, Node.js and bit of Rust and all have more pleasant easier to use build tools! The JVM (or GraalVM) as an ecosystem is just fine and probably one of the best, but build tools might be achille's heel. Maybe it would be a good idea for Oracle to invest into that area ...

        • mike_hearn an hour ago

          My experience of JS projects is that build tools are frequently ad-hoc. That is, there simply isn't a general build tool at all, but just a large pile of scripts calling under-documented libraries. Parallelization, caching and quite often even portability are just missing.

          To justify this statement consider this blog post I wrote a while ago about porting GitHub Desktop (an Electron app) from its prior build/deployment system to Conveyor [1]. Conveyor is a tool for shipping desktop apps and is implemented as a single-purpose build system. The relevant part is this commit:

          https://github.com/hydraulic-software/github-desktop/commit/...

          The amount of code that can be deleted is huge! Some of it is in-process code that isn't needed with Conveyor (setting up Squirrel etc), but a lot is just shell scripts that happen to be written in JS. Replacing that with a real build system not only simplifies the codebase but means the build steps are fully parallelized, fully incremental, easier to debug, portable (the build can run on any platform), progress is reported in a uniform way and so on.

          So whilst the JS ecosystem's approach to build tools may be "simple" in some way, in the sense that there's no dominant build tool like Maven or Gradle, that simplicity does cost you in other ways.

          [1] https://hydraulic.dev/blog/8-packaging-electron-apps.html (Disclosure: Conveyor is a commercial product made by my company)

      • dikei an hour ago

        > The amount of time it takes to publish a multi-platform Kotlin library for the first time can be measured in days. I published my first Golang library in minutes, by comparison.

        It's a bit Apple & Orange comparison: publishing a JVM only Kotlin library is quite easy, it's the multiplatform part that takes time.

      • neeleshs 15 minutes ago

        Interesting. I spend nearly zero time with my maven setup and almost all the time is in coding. I am genuinely curious to know where that 30% time goes? Is it waiting for builds?

      • user1241320 2 hours ago

        One note from having worked with both that I don’t see mentioned: Golang dependencies are sources you basically pull and compile with your own code. In JVM-land dependencies are precompliled packages (jars). This adds one little step.

        • iamcalledrob an hour ago

          ...or a big step, if cross-compiling is required (e.g. Kotlin Multiplatform)

          I'm surprised there is no source-only dependency solution for JVM -- it'd solve this issue. Pull down the source and build on the fly. Perhaps there is and I'm unaware?

      • threeseed 3 hours ago

        > the community at large thinks this is normal

        Half are ignorant. Other half are like me and just stuck with no options.

        But the tooling ecosystem on the JVM truly is horrific.

        • iamcalledrob 2 hours ago

          I think there are a lot of "JVM Lifers" who are so deep in the ecosystem they are unaware how much better things can be.

          Anecdote: I wanted to publish a ~100LoC multiplatform Kotlin library -- just some bindings. I publish these sorts of things for Go with just a "git push".

          Steps were: 1. Spend a few hours trying to understand Maven Central/Sonotype, register and get "verified". They're in the middle of some kind of transition so everything is deprecated or unstable. 2. Figure out signing, because of course published packages must be signed. Now I have a secret to keep track of too, great. 3. Discover that there is no stable Gradle plugin for publishing to the "new" Maven Central, it's coming soon... Choose one of the handful of community plugins with a handful of stars on GitHub. 4. Spend a few hours troubleshooting a "Gradle build failed to end" error, which ended up being due to signing not finding a signing key. 3rd party plugin didn't handle errors properly, and a bug in Gradle meant that my secret wasn't picked up from local.properties. 4. Eventually discover that because Kotlin Multiplatform can't be cross-compiled, there is no way to actually publish a multiplatform library without spinning up a bunch of CI runners. And you can't just publish code -- JVM packages have to contain compiled artifacts. 5. Realise this now involves maintaining GitHub Actions and Gradle, which is an ongoing cost. 6. Give up.

          The harm that this kind of complexity must be causing to the ecosystem is immeasurable.

          • iainmerrick 2 hours ago

            I’d just like to add, NPM gets a lot of flak (mostly deservedly) but it too is still vastly easier than anything in the JVM ecosystem.

            Even with all the headaches around modules versus CJS, and JS versus TypeScript, NPM is a lot easier than Gradle. Notably, you have a choice of alternate tools (eg pnpm, yarn, bun) that interoperate pretty well.

            I guess my point is, Gradle and Maven are specifically and outstandingly bad.

            • lihaoyi 16 minutes ago

              If you think gradle and maven are bad, you should try Mill! There is more to build tooling than gradle or maven, the field has evolved significantly since those tools launched 15-20 years ago, and Mill tries to do things better

          • mike_hearn an hour ago

            Although a lot of it is generic badness, Kotlin Multiplatform isn't the JVM ecosystem. You don't need CI runners to publish a JVM library. The reason it comes up with Multiplatform is because Kotlin defines "Multiplatform" to mean platforms like JavaScript, or their own LLVM based compiler toolchain that bypasses JVMs entirely.

            • iamcalledrob an hour ago

              Very true, although it definitely feels like part of the ecosystem since it uses the same project structure, build tooling etc.

      • philipwhiuk 13 minutes ago

        > I published my first Golang library in minutes, by comparison.

        For what platform(s)?

        Or did you really just push the source code?

    • kaba0 an hour ago

      There are quite a few cases: the moment you touch another language, resources that require a compile-step (e.g. xml schemas to code dtos, protonbuf, all that kind of stuff), sometimes even the code itself requires generation.

    • mike_hearn an hour ago

      Build tools sit in an unhappy corner of the design space where they provide features not found in the core of regular programming languages, and which are so generally useful that there's a temptation to make them very abstract, but then they often lack some of the features that let regular programs scale well.

      The key feature that justifies their existence is parallel and incremental execution of DAGs of world-mutating tasks. This is an awkward fit with most programming languages, hence the prevalence of DSLs. But people don't want their build system to become a general purpose programming language, because they don't want to think about build systems at all and because programmers don't buy programming languages anymore, so, this causes a big design tension between generality (people want to use build systems to automate many things) and deliberately limiting the expressive power to try and constrain the design space and thus tooling investment required.

      Java is in an awkward place because the JDK was born in the 90s on UNIX, by people who thought make is a sufficiently good solution. You still see remnants of this belief in the official Java tutorials, in JEPs, and in the fact that OpenJDK itself is compiled using an autotools based build system! (fortunately it's one of the nice make based build systems out there).

      The problem with make is twofold:

      1. It assumes a CLI that's both powerful and standardized provided by the host OS. Windows violates this assumption but Java is meant to be portable to Windows.

      2. "Plugins" are CLI tools or scripts and so make implicitly assumes that subprocess creation is cheap. But process creation on Windows is expensive, and starting up JVM programs is also expensive due to the JIT compiling.

      Therefore make just doesn't work well in the JVM ecosystem. At the same time, the Java project wasn't providing any competing solution, so the wider open source community was left to fill in the gaps. These days language developers provide build tooling out of the box as part of the base toolset along with the compiler, but Java still doesn't.

      So - you ask, what are people doing with Gradle/Maven that requires all those features. The answer is: everything! Gradle builds frequently orchestrate dozens of different tools as part of a build pipeline, build documentation websites, do upload and deployment, download and manage dependencies, run security scanners and license compliance checkers, analyze dependency graphs, modify compiler behaviors, and so on.

      Additionally Gradle isn't specific to Java, or even JVM apps. It can also be used to compile C/C++ programs, run native code compilers like Kotlin/Native, and it abstracts the underlying platform so Gradle builds aren't tied to UNIX.

      That's why it's so complicated.

      • AtlasBarfed 26 minutes ago

        Build systems exist in two turning complete rabbit holes/ slippery slides:

        Configuration and workflow execution.

  • jiehong an hour ago

    Why not compare it to bazel/pants/buck2 as well?

    Mill seems to have taken some inspiration from those as well.

    • lihaoyi an hour ago

      Author here. It does! I started working on Mill when I started learning Bazel, during my first months at Databricks. There's a lot of cross-pollination of ideas there, from my 7 years adopting and maintaining the Bazel build at Databricks, but I haven't had time to do a proper head-to-head comparison. Hopefully someone else can though!