Mill Build Tool

repository·main·Indexed 25 days ago

https://github.com/com-lihaoyi/mill

A build tool for Scala and Java projects featuring remote caching (1.2.0+), declarative module definitions via YAML (1.1.0+), and support for single-file scripts. It includes a playlib plugin for Play Framework applications and provides extensive examples for cross-building, web development, and complex multi-module Scala project structures.

Tokens
74.9K
Snippets
206
Records
496
Agent score
83%

What's inside Mill

  1. Overview of Mill build tool

    main
    Mill is a build tool designed for the JVM ecosystem (Java, Scala, and Kotlin) that focuses on simplicity, programmability, and performance. It aims to provide a better developer experience compared to Maven and Gradle by using declarative configuration and aggressive caching/parallelism to achieve faster build times.
  2. Understand Mill's Direct-Style build design

    main

    Mill uses a "direct-style" design inspired by React.js. Instead of registering callbacks that mutate the filesystem (the traditional approach in tools like Gradle or Maven), you write normal functions that return the desired metadata or files.

    Mill automatically handles the complex build logic—such as caching, parallelization, invalidation, and dependency tracking—based on the function calls and data flow you define. This allows you to write "naive" code that looks like a simple script, while Mill ensures it runs efficiently as a high-performance build pipeline.

  3. Why Mill runs on the JVM

    main

    Mill runs on the Java Virtual Machine (JVM) to take advantage of several key capabilities:

    • Dynamic Classloading: Mill uses the JVM's ability to dynamically build, load, and run code. This enables features like running dynamic JVM code, meta-builds, and importing Maven plugins without needing a hard-coded plugin architecture.
    • Extensive Tooling Ecosystem: Mill benefits from the massive JVM ecosystem, including:
      • IDEs: Deep support in IntelliJ and VSCode.
      • Profiling/Debugging: Access to tools like jstack, JProfiler, and YourKit to diagnose stuck or slow builds.
      • Library Access: The ability to seamlessly import any JVM artifact from Maven Central or other repositories.
    • Robust Publishing Infrastructure: Mill leverages the established JVM publishing standards (like Maven Central), providing a predictable and well-designed experience for distributing plugins and artifacts.
  4. Understand Mill's bytecode callgraph analysis for task invalidation

    main

    Mill uses JVM bytecode callgraph analysis to perform fine-grained task invalidation. Instead of aggressively invalidating all caches on every change, Mill analyzes the .class files generated by the Scala compiler (from build.mill or package.mill) to determine the impact of a change.

    How it works

    1. Post-processing: Mill runs a post-processing step on the .class files.
    2. Hash Computation: It computes a hash for every method, representing its own implementation and that of transitively called methods.
    3. Signature Storage: These hashes are saved to a methodCodeHashSignatures.json file.
    4. Runtime Integration: The Mill evaluator includes these signatures in Task cache keys at runtime to decide if a task needs to be re-run.

    Key Characteristics

    • Conservative Correctness: The analysis is designed to be conservatively correct. If it is imprecise, it may cause a task to re-evaluate spuriously (a
  5. Relevant Mill Modules for Android Kotlin Projects

    main

    When building Android applications with Mill, the following modules are the primary building blocks:

    • mill.androidlib.AndroidSdkModule: Manages the Android SDK and essential build tools (e.g., aapt, d8, zipalign, apksigner).
    • mill.kotlinlib.android.AndroidAppKotlinModule: Provides the framework and workflow for building Android applications.
    • mill.kotlinlib.KotlinModule: Provides general Kotlin build tasks such as compiling Kotlin code and creating JAR files.
  6. Explore Java Web Project Examples with Mill

    main

    Mill supports building various Java web applications and backend servers. You can use Mill to manage dependencies and build processes for several popular frameworks, including:

    • Simple Web Example: A basic web setup.
    • Spring Boot: Integration with the Spring Boot framework.
    • Jetty: Running a 'Hello World' application using the Jetty server.
    • Micronaut: Includes 'Hello World', 'TodoMvc', and 'Native (GraalVM)' compilation examples.
    • Quarkus: Includes 'Getting Started' and 'TodoMVC' examples.
  7. Understand Selective Testing approaches

    main

    Selective testing is a technique used in large codebases or monorepos to run only the tests necessary to validate a change, rather than the entire suite. The article outlines four main approaches:

    1. No Selective Testing: Running every test on every pull-request. This scales linearly $O(n)$ with codebase size and becomes a bottleneck as projects grow.
    2. Folder-based Selective Testing: Running tests only for folders containing changed files. This is simple but fails to catch breakages in downstream modules that depend on the changed code.
    3. Dependency-based Selective Testing: Running tests for the changed module and all its downstream dependencies. This is more robust but can be limited by module granularity and redundant testing of shared code.
    4. Heuristic-based Selective Testing: Using ad-hoc rules to optimize test runs, such as:
      • Limiting Dependency Depth: Only running tests for a fixed number of downstream levels ($N$).
      • Hard-coding Dependency Relationships: Manually defining specific test paths to bypass coarse-grained modules.
      • Machine-Learning-based Selection: Using models trained on historical commit data to predict affected tests.
  8. Understand Mill filesystem sandboxing

    main

    Mill uses rudimentary filesystem sandboxing to prevent different tasks and modules from interfering with each other. It attempts to ensure tasks only read and write from their designated .dest/ folders, which are unique to each task to prevent collisions during parallel execution.

    Important Notes:

    • Sandboxing is intended as a set of guardrails to encourage best practices, not as a fully hermetic environment.
    • It is not designed to block intentional misbehavior; tasks can still traverse the filesystem.
    • Mill's redirection of os.pwd does not apply to java.io or java.nio APIs, as these are outside of Mill's control.
  9. Understand the purpose of mill.Module

    main

    In Mill, mill.Module serves two primary roles:

    1. As objects: They act as namespaces to group related Tasks together for organization.
    2. As traits: They act as reusable templates to replicate groups of related Tasks and sub-Modules while allowing for customization.

    Mill provides built-in modules like mill.scalalib.ScalaModule and mill.scalalib.CrossSbtModule, but you can define your own to extend Mill's functionality.