newsfilter.io

Build Systems with Andrey Mokhov

  • Core Definition and Evolution

    • Build systems automate tedious, ordered developer tasks including compilation, testing, and documentation updates.
    • Prior to 1976, developers relied on manual scripts to manage build tasks.
    • The introduction of Make was a breakthrough due to its simple, text-based rule format defining inputs, outputs, and commands.
    • Make remains the most popular build system globally, found in the majority of open-source projects.
  • Key Services Provided by Make

    • Incrementality: Re-runs only tasks whose inputs have changed (based on timestamps), avoiding full rebuilds.
    • Correctness: Ensures tasks execute in the correct dependency order, preventing errors from skipped steps.
    • Parallelism: Enables concurrent execution of independent tasks, a feat difficult to manage manually.
  • Limitations of Make

    • Overly Conservative Timestamp Heuristic: Recomputes downstream tasks even if the source file change (e.g., a comment) does not alter the output, wasting compute resources.
    • Under-Conservative Timing: Fails to rebuild when external factors (e.g., backup software) alter file modification times incorrectly.
    • Lack of Scalability: Cannot share build artifacts across different machines; every developer builds from scratch.
    • Poor Composability: Uses a single global namespace with mutable variables, making it difficult to reason about build rules locally.
    • Macros and Generativity: Complex logic requires string-splicing macros, which are hard to debug and often generate more macros (meta-programming), leading to "impenetrable" build files.
    • Static Rule Requirement: Requires all build tasks to be defined upfront; cannot easily handle scenarios where new rules are generated dynamically during the build process.
  • Case Study: GHC and Hadrian

    • The Glasgow Haskell Compiler (GHC) contains approximately 1 million lines of code and relied on a fragmented system of 20–30 large Make files.
    • Migration to Hadrian, a new build system for GHC, evolved from a 90% code rewrite into an "archaeology expedition" to extract 25 years of undocumented build knowledge.
    • Hadrian is functional and in use, but the migration remains ongoing due to the complexity of preserving legacy, conditional logic.
  • Theoretical Framework: "Build Systems a la Carte"

    • Core Abstraction: All build systems can be modeled as a combination of two components: a Scheduler (determines execution order) and a Rebuilder (handles task execution, caching, and cloud interaction).
    • Taxonomy: The analysis identified four distinct schedulers and three distinct rebuilder strategies, allowing for 12+ combinations, some of which were previously unknown.
    • Innovation Example: The paper proposed Cloud Shake, combining the Shake scheduler with Bazel's "constructive traces" rebuilder.
      • Uses a "suspending scheduler" to handle dynamic dependencies.
      • Uses task hashes to query cloud storage for pre-computed results, avoiding redundant local computation.
  • Fragmentation and the Language Problem

    • The ecosystem is fractured because build rules are typically written in the language of the target community (e.g., Java for Bazel, OCaml for Dune, Haskell for Shake).
    • While this allows for better tooling integration and community iteration, it prevents cross-language monorepos (e.g., mixing Rust, Haskell, and Java) without complex glue code.
    • Generative Build Systems: A proposed but unrealized architecture involves a language-agnostic engine that accepts initial rules, iterates with the user, and generates new rules based on feedback until a fixed point is reached.
  • Jane Street's Build Systems: Jenga and Dune

    • Jenga:
      • Designed specifically for Jane Street's 15–20 million line monorepo.
      • Uses a two-tier language model: a constrained, data-oriented frontend for developers and a full OCaml backend for execution.
      • Currently lacks native cloud build support; the build team is working to add this feature.
    • Dune:
      • An OCaml build system used by the broader OCaml community.
      • Features a versioned build language, allowing independent projects to evolve their build rules without breaking compatibility.
      • Initially faster than Jenga for "from scratch" builds; now incorporates incrementality and rudimentary cloud features.
    • Migration Strategy: Jane Street plans to migrate from Jenga to Dune but faces the "dual-work" challenge of maintaining features in both systems until Dune matches Jenga's scale.
    • Future Architecture: Plans to decouple Dune into a backend (incremental engine) and frontend (rule definitions) to allow Jenga to adopt the Dune frontend while retaining its current backend.
  • Integration and Granularity Trends

    • IDE Integration: Modern build systems are integrating directly with editors to enable "continuous compilation" and instant error reporting, reducing iteration cycles to milliseconds.
    • Compiler Coupling: Building systems are beginning to automate compiler tasks (type checking, analysis) rather than just file compilation.
    • Fine-Grained Incrementality: Systems like Facebook's "Hack" demonstrate millisecond updates by tracking dependencies at the function level, requiring persistent, stateful servers to avoid serialization overhead.
    • Technical Barrier: As granularity decreases (line-level vs. file-level), the cost of serializing state may outweigh the benefits of computation, necessitating persistent build servers or shared memory models.
  • Broader Scope: Incremental Computation

    • Excel Analogy: Spreadsheets function as incremental computation engines for non-file data, using fixed-point iteration to resolve circular dependencies.
    • Scientific Workflows: Scientific computing pipelines (e.g., bioinformatics) share build system goals (caching, parallelism) but differ by requiring distributed orchestration across massive datasets and long-running jobs (weeks/months).
    • Convergence: Modern build systems and incremental data structures (e.g., incremental maps, sorting) are converging as systems move toward persistent, stateful execution environments.