Ctrl K
ComunidadGuías…

flake.nix

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
{
  description = "Self-hosting Common Lisp compiler targeting bytecode, native code, PHP, and JavaScript";

  inputs = {
    # nixos-unstable, not nixpkgs-unstable: it advances only after the NixOS
    # release tests pass, so it is less likely to land a broken build.
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";

    treefmt-nix = {
      url = "github:numtide/treefmt-nix";
      inputs.nixpkgs.follows = "nixpkgs";
    };
    cl-nix-forge = {
      url = "github:nerima-lisp/cl-nix-forge/v0.7.4";
      inputs.nixpkgs.follows = "nixpkgs";
      inputs.treefmt-nix.follows = "treefmt-nix";
    };

    # Every sibling is pinned to an immutable ref — a release tag where one
    # exists, a commit otherwise. A bare `github:nerima-lisp/cl-weave` follows
    # that repository's default branch, so an upstream push to main would break
    # this repository's CI with no change here and no warning.
    #
    # All of them are taken as PLAIN SOURCE TREES (`flake = false`) and built
    # with cl-cc's own `sbcl.buildASDFSystem` below, exactly the way cl-cc
    # builds its own packages/ subsystems. Two reasons, both still current:
    # several of these flakes declare Linux-only `systems`, so consuming their
    # `packages.<system>` output would break aarch64-darwin here; and evaluating
    # them as flakes would drag in their transitive inputs (paredit-cli and so
    # on) to obtain nothing but their source.
    #
    # PACKAGE_STANDARD.md requires `inputs.nixpkgs.follows` on every input. A
    # `flake = false` input HAS no inputs, and Nix rejects an override for one,
    # so the attribute is omitted here. The rule exists to stop each input from
    # locking its own nixpkgs and inflating flake.lock; taking source trees
    # achieves that outcome more completely, since no second nixpkgs enters the
    # lock at all. treefmt-nix and cl-nix-forge, the real flake inputs, carry `follows`.
    cl-prolog-kit = {
      url = "github:nerima-lisp/cl-prolog-kit/v1.5.0";
      flake = false;
    };
    cl-weave = {
      url = "github:nerima-lisp/cl-weave/32671560d3d15d4ecf33b9d8af862ea60737841b";
      flake = false;
    };
    # cl-parser-kit backs packages/parse's tokenizer/combinator/Pratt layer,
    # cl-dataflow-kit backs the packages/pipeline pass-graph orchestration,
    # cl-boundary-kit provides the testable I/O boundaries used by
    # packages/cli + packages/repl, cl-cli is the declarative argument parser
    # behind packages/cli, and cl-tty-kit provides the ANSI/screen/input
    # primitives for the interactive REPL.
    cl-parser-kit = {
      url = "github:nerima-lisp/cl-parser-kit/6379014e6d85bdaecc57157611baa4ddf4b2b206";
      flake = false;
    };
    cl-dataflow-kit = {
      url = "github:nerima-lisp/cl-dataflow-kit/v1.2.0";
      flake = false;
    };
    cl-boundary-kit = {
      url = "github:nerima-lisp/cl-boundary-kit/62e4a7df3e52061c38b7ff4f596436bdda985f71";
      flake = false;
    };
    # cl-log-kit backs cl-boundary-kit's logging boundary (split out of
    # cl-boundary-kit itself); pulled in the same way as the toolkits above.
    cl-log-kit = {
      url = "github:nerima-lisp/cl-log-kit/580ebe23e4106edb8dcc89c0d4732f585c17e3f9";
      flake = false;
    };
    cl-cli = {
      url = "github:nerima-lisp/cl-cli/v1.0.1";
      flake = false;
    };
    cl-tty-kit = {
      url = "github:nerima-lisp/cl-tty-kit/add24964a286720baa9e08722f4b611fcefdf22f";
      flake = false;
    };
    cl-codec-kit = {
      url = "github:nerima-lisp/cl-codec-kit/5885db990560b1aa3992a88daceea010b9adc1d2";
      flake = false;
    };
    cl-regex-kit = {
      url = "github:nerima-lisp/cl-regex-kit/da83905cad2bcb28518ebb28cac6628c654bb328";
      flake = false;
    };
    cl-host-kit = {
      url = "github:nerima-lisp/cl-host-kit/ec565470b634605e23b0d6eef0645eb15e8e3356";
      flake = false;
    };
    # Additional runtime dependencies, pinned to the revisions in the
    # standalone runtime's lockfile.
    cl-observability-kit = {
      url = "github:nerima-lisp/cl-observability-kit/c7036f43ac8b69457a2979c2cb2c6806452bd728";
      flake = false;
    };
    cl-deflate-kit = {
      url = "github:nerima-lisp/cl-deflate-kit/711eb7540ac43869ae08060b6153e5dc768a8382";
      flake = false;
    };
    cl-crypto-kit = {
      url = "github:nerima-lisp/cl-crypto-kit/41ed7e90f736591aa1961bc6deb0741c73392a60";
      flake = false;
    };
    cl-resilience-kit = {
      url = "github:nerima-lisp/cl-resilience-kit/741d42399ad781244bd36750c6626bd9f4e8b16b";
      flake = false;
    };
    # cl-cc-ast and cl-cc-type are the first subsystems split out of this
    # repository. Injected into the internal system graph under their original
    # names by nix/asdf-systems.nix, so every `deps = [ "cl-cc-ast" ... ]`
    # resolves to the external derivation. cl-cc-type depends on cl-cc-ast.
    #
    # Pinned by commit, not tag: neither repository has cut a release yet.
    # A commit is a stronger pin than a tag (a tag can be moved), so this
    # satisfies the invariant the tag rule exists to protect. Swap in `/v0.1.0`
    # once those releases are tagged.
    cl-cc-ast = {
      url = "github:nerima-lisp/cl-cc-ast/8289d9cec15df0ac73011e3ea872dc3c669f9540";
      flake = false;
    };
    cl-cc-type = {
      url = "github:nerima-lisp/cl-cc-type/6ec18167058005d7e4c71c7b83dc5a473ade8620";
      flake = false;
    };
    cl-cc-binary = {
      url = "github:nerima-lisp/cl-cc-binary/3fa4d4b00f4a6298f4b1e78c11930a74f14a8da2";
      flake = false;
    };
    cl-cc-bootstrap = {
      url = "github:nerima-lisp/cl-cc-bootstrap/837e41fd105c632088033be433ef3f1352eda462";
      flake = false;
    };
    cl-cc-vm = {
      url = "github:nerima-lisp/cl-cc-vm/fdc08220e4a53f096fa903e76dbcbb26ebb13765";
      flake = false;
    };
    cl-cc-mir = {
      url = "github:nerima-lisp/cl-cc-mir/3c3cba4b57ac10d7ad52a7bf6aaf421e598dd198";
      flake = false;
    };
    cl-cc-cps = {
      url = "github:nerima-lisp/cl-cc-cps/ad021a15069d33b6e973c68c291993b5c303997b";
      flake = false;
    };
    cl-cc-expand = {
      url = "github:nerima-lisp/cl-cc-expand/15630d964566eca640c2fa5cecea24c26c79dcce";
      flake = false;
    };
    cl-cc-optimize = {
      url = "github:nerima-lisp/cl-cc-optimize/87c44f3a5ae7075ca1f087e7c9339f70a07bd05a";
      flake = false;
    };
    cl-cc-codegen-native = {
      url = "github:nerima-lisp/cl-cc-codegen-native/3bc0966c82cbd11d83899d1661ea428dc7bb5ee1";
      flake = false;
    };
    cl-cc-parse = {
      url = "github:nerima-lisp/cl-cc-parse/8e2f5f1dd20b056d66718ef305a3a7d79be1e4c1";
      flake = false;
    };
    cl-cc-php = {
      url = "github:nerima-lisp/cl-cc-php/d59cdbf72bffacf77a276dfb3492f2be35e8a363";
      flake = false;
    };
    php-src = {
      url = "github:php/php-src/93e09753772a2a36c7b83c890c023f31656ad457";
      flake = false;
    };
    cl-cc-javascript = {
      url = "github:nerima-lisp/cl-cc-javascript/ea2e920ed36ba203943fc62bcac1d70d79dcfa29";
      flake = false;
    };
    cl-cc-runtime = {
      url = "github:nerima-lisp/cl-cc-runtime/ea817be8dc506b45e8e9b4f14073422108a1e7ec";
      flake = false;
    };
    # Dependencies of the standalone cl-cc-runtime that the in-tree copy did
    # not have. Existing toolkit pins above provide logging, process, and JSON;
    # the additional source pins above provide observability, compression,
    # cryptography, and resilience.
    cl-process-kit = {
      url = "github:nerima-lisp/cl-process-kit/1276a8b8db6120ac15a0911c255a586b3a030ca1";
      flake = false;
    };
    cl-json-kit = {
      url = "github:nerima-lisp/cl-json-kit/dace990f643d1dd07639ce57a2559c5143850cce";
      flake = false;
    };
    cl-date-kit = {
      url = "github:nerima-lisp/cl-date-kit/021c00df3f4dd18cad7206221125d1596db2fae5";
      flake = false;
    };
    cl-concurrent-kit = {
      url = "github:nerima-lisp/cl-concurrent-kit/0ef20d36a0a12800f231172227bd94034611c639";
      flake = false;
    };
  };

  # Plain `outputs` with `forAllSystems`, not flake-parts. The whole flake is
  # visible in one file this way: `nix flake show` output maps directly onto the
  # attribute names written below, with no module system deciding what appears.
  outputs =
    inputs@{
      self,
      nixpkgs,
      treefmt-nix,
      ...
    }:
    let
      # x86_64-linux is verified by CI; aarch64-darwin is verified by the
      # development machine on every local `nix flake check`. aarch64-linux and
      # x86_64-darwin are not declared because nothing runs them (ADR-0078).
      # `ci.yml` deliberately omits --all-systems: the runner is x86_64-linux
      # and would fail evaluating the darwin derivations.
      # Only what is verified, and CI verifies exactly one platform:
      # x86_64-linux. aarch64-darwin was dropped in the 2026-08-01 revision of
      # PACKAGE_STANDARD.md -- its only gate was the maintainer remembering to
      # run `nix flake check` locally, and a gate nobody can observe being
      # skipped is not a gate.
      #
      # Consequence, accepted deliberately: every per-system output --
      # packages, checks, apps AND devShells -- is generated from this one
      # list, so `nix develop` and `nix build` do not work on macOS.
      # Development happens on Linux.
      systems = [
        "x86_64-linux"
      ];
      forAllSystems = nixpkgs.lib.genAttrs systems;

      # Single source of truth for a package's version: the `:version` form in
      # its .asd. Before this, the flake said 0.8.0, cl-cc.asd said 0.1.0 and
      # the newest tag was v0.1.0 — three answers to one question, and nothing
      # to say which was right. A release now edits one line.
      #
      # Nix regexes are whole-string anchored and `.` never spans newlines, so
      # the version is extracted from non-comment lines rather than with a
      # single multi-line match. ASDF metadata may share a line.
      asdVersion =
        asd:
        let
          lines = nixpkgs.lib.splitString "\n" (builtins.readFile asd);
          versionPattern = "[^;]*[[:space:]]*:version \"([^\"]*)\".*";
          versionLine = builtins.head (
            builtins.filter (
              line: builtins.match "[[:space:]]*;.*" line == null && builtins.match versionPattern line != null
            ) lines
          );
        in
        builtins.head (builtins.match versionPattern versionLine);

      version = asdVersion ./cl-cc.asd;

      # The same treatment for every sibling: read the version out of the source
      # tree actually being built, rather than restating it here where it can
      # (and did) fall behind the pinned ref.
      siblingVersion = name: asdVersion "${inputs.${name}}/${name}.asd";

      # treefmt drives `nix fmt` and the `checks.<system>.formatting` gate.
      # Scope is Nix only. Prettier over *.md/*.yml used to be enabled here; it
      # is not any more, because a YAML formatter rewrites the GitHub Actions
      # `on:` key into the boolean `true`, and reformatting Markdown would churn
      # every page under docs/ for no review value.
      treefmtEval = forAllSystems (
        system:
        treefmt-nix.lib.evalModule nixpkgs.legacyPackages.${system} {
          projectRootFile = "flake.nix";
          programs.nixfmt.enable = true;
          settings.global.excludes = [
            "flake.lock"
            "LICENSE*"
          ];
        }
      );

      # Everything system-dependent is assembled once per system here, and the
      # top-level outputs below only project attributes out of it. Assembling it
      # separately inside `packages`, `checks`, `apps` and `devShells` would
      # re-evaluate the entire ASDF system graph four times.
      perSystem = forAllSystems (
        system:
        let
          pkgs = nixpkgs.legacyPackages.${system}.extend (
            final: prev: {
              sbcl = final.wrapLisp {
                pkg = prev.sbcl.overrideAttrs (_: {
                  version = "2.6.9";
                  src = final.fetchurl {
                    name = "sbcl-2.6.9-source.tar.bz2";
                    url = "https://sourceforge.net/projects/sbcl/files/sbcl/2.6.9/sbcl-2.6.9-source.tar.bz2/download";
                    hash = "sha256-xv0dc1Vw60/zTK+WCZiMp37QvRK1XQmk/tB1vokNpRM=";
                  };
                });
                faslExt = "fasl";
                flags = [
                  "--dynamic-space-size"
                  "3000"
                ];
              };
            }
          );
          inherit (pkgs) lib;

          dispatchModule = import ./nix/dispatch-sem-fix.nix { inherit pkgs lib; };
          dispatchSemFix = dispatchModule.dispatchSemFix or null;
          sbclModule = import ./nix/sbcl.nix { inherit pkgs; };
          sbclBase = sbclModule.sbcl;
          buildAsdfSystem =
            args:
            (sbclBase.buildASDFSystem args)
            // {
              asdfMetadata = {
                inherit (args) pname src systems;
                lispLibs = args.lispLibs or [ ];
              };
            };
          sbcl = sbclBase // {
            buildASDFSystem = buildAsdfSystem;
          };
          inherit (sbclModule) sbclBootstrap;

          # Sibling toolkits, built from the pinned source trees. The `version`
          # of each mirrors the tag or commit its input is pinned to.
          clPrologKit = sbcl.buildASDFSystem {
            pname = "cl-prolog-kit";
            version = siblingVersion "cl-prolog-kit";
            src = inputs.cl-prolog-kit;
            systems = [
              "cl-prolog-kit"
              "cl-prolog-kit/callgraph"
            ];
          };
          clWeave = sbcl.buildASDFSystem {
            pname = "cl-weave";
            version = siblingVersion "cl-weave";
            src = inputs.cl-weave;
            systems = [ "cl-weave" ];
          };
          # cl-parser-kit is self-contained. cl-boundary-kit uses cl-host-kit.
          # cl-dataflow-kit and cl-cli depend on the external cl-prolog-kit
          # engine in production. cl-tty-kit instead depends on codec and
          # concurrency libraries in its current main system.
          clParserKit = sbcl.buildASDFSystem {
            pname = "cl-parser-kit";
            version = siblingVersion "cl-parser-kit";
            src = inputs.cl-parser-kit;
            systems = [ "cl-parser-kit" ];
          };
          clDataflowKit = sbcl.buildASDFSystem {
            pname = "cl-dataflow-kit";
            version = siblingVersion "cl-dataflow-kit";
            src = inputs.cl-dataflow-kit;
            systems = [ "cl-dataflow-kit" ];
            lispLibs = [
              clBoundaryKit
              clConcurrentKit
              clDateKit
              clHostKit
              clPrologKit
            ];
          };
          clLogKit = sbcl.buildASDFSystem {
            pname = "cl-log-kit";
            version = siblingVersion "cl-log-kit";
            src = inputs.cl-log-kit;
            systems = [ "cl-log-kit" ];
            lispLibs = [
              clBoundaryKit
              clConcurrentKit
              clDateKit
              clHostKit
            ];
          };
          clBoundaryKit = sbcl.buildASDFSystem {
            pname = "cl-boundary-kit";
            version = siblingVersion "cl-boundary-kit";
            src = inputs.cl-boundary-kit;
            systems = [ "cl-boundary-kit" ];
            lispLibs = [ clHostKit ];
          };
          clCli = sbcl.buildASDFSystem {
            pname = "cl-cli";
            version = siblingVersion "cl-cli";
            src = inputs.cl-cli;
            systems = [ "cl-cli" ];
            lispLibs = [ clPrologKit ];
          };
          clTtyKit = sbcl.buildASDFSystem {
            pname = "cl-tty-kit";
            version = siblingVersion "cl-tty-kit";
            src = inputs.cl-tty-kit;
            systems = [ "cl-tty-kit" ];
            lispLibs = [
              clBoundaryKit
              clCodecKit
              clConcurrentKit
              clDateKit
              clHostKit
            ];
          };
          clRegexKit = sbcl.buildASDFSystem {
            pname = "cl-regex-kit";
            version = siblingVersion "cl-regex-kit";
            src = inputs.cl-regex-kit;
            systems = [ "cl-regex-kit" ];
            lispLibs = [
              clBoundaryKit
              clConcurrentKit
              clDateKit
              clHostKit
              clParserKit
            ];
          };
          clHostKit = sbcl.buildASDFSystem {
            pname = "cl-host-kit";
            version = siblingVersion "cl-host-kit";
            src = inputs.cl-host-kit;
            systems = [ "cl-host-kit" ];
          };
          # Subsystems split out of this repository. clCcAst is a
          # dependency-free leaf; clCcType depends on it.
          #
          # These are now the only definitions of those two systems. packages/
          # {ast,type} used to define them as well, and because ASDF resolved
          # whichever the registry reached first -- always the in-tree copy,
          # since this tree is scanned ahead of the injected derivations -- the
          # repositories were never actually compiled and the two sides drifted.
          # The in-tree copies have been removed; packages/{ast,type}/tests
          # remain and now exercise these derivations.
          clCcAst = sbcl.buildASDFSystem {
            pname = "cl-cc-ast";
            version = siblingVersion "cl-cc-ast";
            src = inputs.cl-cc-ast;
            systems = [ "cl-cc-ast" ];
          };
          clCcType = sbcl.buildASDFSystem {
            pname = "cl-cc-type";
            version = siblingVersion "cl-cc-type";
            src = inputs.cl-cc-type;
            systems = [ "cl-cc-type" ];
            lispLibs = [ clCcAst ];
          };
          clProcessKit = sbcl.buildASDFSystem {
            pname = "cl-process-kit";
            version = siblingVersion "cl-process-kit";
            src = inputs.cl-process-kit;
            systems = [ "cl-process-kit" ];
            lispLibs = [
              clBoundaryKit
              clCodecKit
              clConcurrentKit
              clDateKit
              clHostKit
              clLogKit
            ];
          };
          clJsonKit = sbcl.buildASDFSystem {
            pname = "cl-json-kit";
            version = siblingVersion "cl-json-kit";
            src = inputs.cl-json-kit;
            systems = [ "cl-json-kit" ];
          };
          clObservabilityKit = sbcl.buildASDFSystem {
            pname = "cl-observability-kit";
            version = siblingVersion "cl-observability-kit";
            src = inputs.cl-observability-kit;
            systems = [
              "cl-observability-kit"
              "cl-observability-kit/prometheus"
              "cl-observability-kit/otlp"
            ];
            lispLibs = [
              clBoundaryKit
              clConcurrentKit
            ];
          };
          clDeflateKit = sbcl.buildASDFSystem {
            pname = "cl-deflate-kit";
            version = siblingVersion "cl-deflate-kit";
            src = inputs.cl-deflate-kit;
            systems = [ "cl-deflate-kit" ];
          };
          clCryptoKit = sbcl.buildASDFSystem {
            pname = "cl-crypto-kit";
            version = siblingVersion "cl-crypto-kit";
            src = inputs.cl-crypto-kit;
            systems = [ "cl-crypto-kit" ];
          };
          clResilienceKit = sbcl.buildASDFSystem {
            pname = "cl-resilience-kit";
            version = siblingVersion "cl-resilience-kit";
            src = inputs.cl-resilience-kit;
            systems = [ "cl-resilience-kit" ];
            lispLibs = [
              clBoundaryKit
              clConcurrentKit
              clDateKit
            ];
          };
          clDateKit = sbcl.buildASDFSystem {
            pname = "cl-date-kit";
            version = siblingVersion "cl-date-kit";
            src = inputs.cl-date-kit;
            systems = [ "cl-date-kit" ];
          };
          clConcurrentKit = sbcl.buildASDFSystem {
            pname = "cl-concurrent-kit";
            version = siblingVersion "cl-concurrent-kit";
            src = inputs.cl-concurrent-kit;
            systems = [ "cl-concurrent-kit" ];
            lispLibs = [
              clBoundaryKit
              clDateKit
              clHostKit
            ];
          };
          clCodecKit = sbcl.buildASDFSystem {
            pname = "cl-codec-kit";
            version = siblingVersion "cl-codec-kit";
            src = inputs.cl-codec-kit;
            systems = [ "cl-codec-kit" ];
          };
          clCcRuntime = sbcl.buildASDFSystem {
            pname = "cl-cc-runtime";
            version = siblingVersion "cl-cc-runtime";
            src = inputs.cl-cc-runtime;
            systems = [ "cl-cc-runtime" ];
            lispLibs = [
              clLogKit
              clProcessKit
              clJsonKit
              clObservabilityKit
              clDeflateKit
              clCryptoKit
              clResilienceKit
            ];
          };
          clCcBootstrap = sbcl.buildASDFSystem {
            pname = "cl-cc-bootstrap";
            version = siblingVersion "cl-cc-bootstrap";
            src = inputs.cl-cc-bootstrap;
            systems = [ "cl-cc-bootstrap" ];
          };
          clCcVm = sbcl.buildASDFSystem {
            pname = "cl-cc-vm";
            version = siblingVersion "cl-cc-vm";
            src = inputs.cl-cc-vm;
            systems = [ "cl-cc-vm" ];
            lispLibs = [
              clCcBootstrap
              clCcRuntime
              clRegexKit
              clTtyKit
              clHostKit
            ];
          };
          clCcMir = sbcl.buildASDFSystem {
            pname = "cl-cc-mir";
            version = siblingVersion "cl-cc-mir";
            src = inputs.cl-cc-mir;
            systems = [
              "cl-cc-mir"
              "cl-cc-target"
            ];
          };
          clCcTarget = clCcMir;
          clCcCps = sbcl.buildASDFSystem {
            pname = "cl-cc-cps";
            version = siblingVersion "cl-cc-cps";
            src = inputs.cl-cc-cps;
            systems = [
              "cl-cc-cps"
              "cl-cc-cps/simplify"
              "cl-cc-cps/trampoline"
              "cl-cc-cps/trmc"
              "cl-cc-cps/transform"
            ];
            lispLibs = [
              clCcBootstrap
              clCcAst
            ];
          };
          clCcExpand = sbcl.buildASDFSystem {
            pname = "cl-cc-expand";
            version = siblingVersion "cl-cc-expand";
            src = inputs.cl-cc-expand;
            systems = [ "cl-cc-expand" ];
            lispLibs = [
              clCcBootstrap
              clCcType
              clCcVm
              clHostKit
            ];
          };
          clCcOptimize = sbcl.buildASDFSystem {
            pname = "cl-cc-optimize";
            version = siblingVersion "cl-cc-optimize";
            src = inputs.cl-cc-optimize;
            systems = [ "cl-cc-optimize" ];
            lispLibs = [
              clCcVm
              clCcType
              clCcAst
              clPrologKit
              clParserKit
              clHostKit
            ];
          };
          # One source tree, three systems -- the repository holds them
          # together because the boundaries between them move. Each is built as
          # its own derivation so dependents keep naming the system they use.
          clCcRegalloc = sbcl.buildASDFSystem {
            pname = "cl-cc-regalloc";
            version = siblingVersion "cl-cc-codegen-native";
            src = inputs.cl-cc-codegen-native;
            systems = [ "cl-cc-regalloc" ];
            lispLibs = [
              clCcVm
              clCcMir
              clCcTarget
              clCcOptimize
            ];
          };
          clCcCodegen = sbcl.buildASDFSystem {
            pname = "cl-cc-codegen";
            version = siblingVersion "cl-cc-codegen-native";
            src = inputs.cl-cc-codegen-native;
            systems = [ "cl-cc-codegen" ];
            lispLibs = [
              clCcBootstrap
              clCcVm
              clCcMir
              clCcTarget
              clCcBinary
              clCcOptimize
              clCcRegalloc
              clProcessKit
              clHostKit
            ];
          };
          clCcEmit = sbcl.buildASDFSystem {
            pname = "cl-cc-emit";
            version = siblingVersion "cl-cc-codegen-native";
            src = inputs.cl-cc-codegen-native;
            systems = [ "cl-cc-emit" ];
            lispLibs = [
              clCcVm
              clCcAst
              clCcMir
              clCcOptimize
              clCcCodegen
            ];
          };
          clCcParse = sbcl.buildASDFSystem {
            pname = "cl-cc-parse";
            version = siblingVersion "cl-cc-parse";
            src = inputs.cl-cc-parse;
            systems = [ "cl-cc-parse" ];
            lispLibs = [
              clCcAst
              clCcBootstrap
              clParserKit
            ];
          };
          clCcPhp = sbcl.buildASDFSystem {
            pname = "cl-cc-php";
            version = siblingVersion "cl-cc-php";
            src = inputs.cl-cc-php;
            systems = [ "cl-cc-php" ];
            lispLibs = [
              clCcAst
              clCcBootstrap
              clCcParse
              clCcVm
              clHostKit
              clJsonKit
            ];
          };
          clCcJavascript = sbcl.buildASDFSystem {
            pname = "cl-cc-javascript";
            version = siblingVersion "cl-cc-javascript";
            src = inputs.cl-cc-javascript;
            systems = [ "cl-cc-javascript" ];
            lispLibs = [
              clCcAst
              clCcBootstrap
              clCcParse
              clCcVm
              clBoundaryKit
              clDateKit
              clJsonKit
              clConcurrentKit
              clHostKit
            ];
          };
          clCcBinary = sbcl.buildASDFSystem {
            pname = "cl-cc-binary";
            version = siblingVersion "cl-cc-binary";
            src = inputs.cl-cc-binary;
            systems = [ "cl-cc-binary" ];
            lispLibs = [ clProcessKit ];
          };

          asdf = import ./nix/asdf-systems.nix {
            inherit
              pkgs
              lib
              sbcl
              clPrologKit
              clWeave
              clParserKit
              clDataflowKit
              clBoundaryKit
              clCli
              clTtyKit
              clRegexKit
              clCcAst
              clCcType
              clCcBinary
              clCcRuntime
              clCcBootstrap
              clCcVm
              clCcMir
              clCcTarget
              clCcCps
              clCcExpand
              clCcOptimize
              clCcRegalloc
              clCcCodegen
              clCcEmit
              clCcParse
              clCcPhp
              clCcJavascript
              ;
            buildAsdfSystem = sbcl.buildASDFSystem;
          };
          inherit (asdf)
            productionAsdfSystems
            testAsdfSystems
            sbclWithCLCC
            sbclWithJavascriptTests
            sbclWithJitTests
            sbclWithTests
            cl-cc-prolog-tools
            cl-cc-prolog-tools-test
            ;

          childFlakeSources = [
            {
              name = "bootstrap";
              source = inputs.cl-cc-bootstrap;
            }
            {
              name = "ast";
              source = inputs.cl-cc-ast;
            }
            {
              name = "parse";
              source = inputs.cl-cc-parse;
            }
            {
              name = "expand";
              source = inputs.cl-cc-expand;
            }
            {
              name = "cps";
              source = inputs.cl-cc-cps;
            }
            {
              name = "type";
              source = inputs.cl-cc-type;
            }
            {
              name = "optimize";
              source = inputs.cl-cc-optimize;
            }
            {
              name = "mir";
              source = inputs.cl-cc-mir;
            }
            {
              name = "codegen-native";
              source = inputs.cl-cc-codegen-native;
            }
            {
              name = "binary";
              source = inputs.cl-cc-binary;
            }
            {
              name = "vm";
              source = inputs.cl-cc-vm;
            }
            {
              name = "runtime";
              source = inputs.cl-cc-runtime;
            }
            {
              name = "php";
              source = inputs.cl-cc-php;
            }
            {
              name = "javascript";
              source = inputs.cl-cc-javascript;
            }
          ];

          asdfSystemDerivations = lib.unique (
            builtins.attrValues productionAsdfSystems
            ++ builtins.attrValues testAsdfSystems
            ++ [
              clPrologKit
              clWeave
              clParserKit
              clDataflowKit
              clLogKit
              clBoundaryKit
              clCli
              clTtyKit
              clRegexKit
              clHostKit
              clCcAst
              clCcType
              clProcessKit
              clJsonKit
              clObservabilityKit
              clDeflateKit
              clCryptoKit
              clResilienceKit
              clDateKit
              clConcurrentKit
              clCodecKit
              clCcRuntime
              clCcBootstrap
              clCcVm
              clCcMir
              clCcCps
              clCcExpand
              clCcOptimize
              clCcRegalloc
              clCcCodegen
              clCcEmit
              clCcParse
              clCcPhp
              clCcJavascript
              clCcBinary
              cl-cc-prolog-tools
              cl-cc-prolog-tools-test
            ]
          );

          rootLock = builtins.fromJSON (builtins.readFile ./flake.lock);

          binaryModule = import ./nix/binary.nix {
            inherit
              pkgs
              lib
              sbclWithCLCC
              version
              ;
          };
          testImage = import ./nix/sbcl-image.nix {
            inherit
              pkgs
              lib
              sbclWithTests
              dispatchSemFix
              ;
          };
          phptCheckFor =
            name: split: shardArgs:
            pkgs.stdenvNoCC.mkDerivation {
              pname = "cl-cc-phpt-${lib.toLower name}";
              inherit version;
              src = lib.fileset.toSource {
                root = ./.;
                fileset = lib.fileset.unions [
                  ./packages
                  ./t
                  ./docs
                  ./run-tests.lisp
                  ./cl-cc.asd
                ];
              };
              nativeBuildInputs = [
                sbclWithTests
                pkgs.python3
                pkgs.pcre2.out
              ];
              buildPhase = ''
                runHook preBuild
                export HOME="$TMPDIR/home"
                export XDG_CACHE_HOME="$TMPDIR/cache"
                export PCRE2_LIBRARY="${pkgs.pcre2.out}/lib/libpcre2-8.so.0"
                mkdir -p "$HOME" "$XDG_CACHE_HOME" "$TMPDIR/source"
                cp -R "$src"/. "$TMPDIR/source/"
                chmod -R u+w "$TMPDIR/source"
                cd "$TMPDIR/source"
                PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover \
                  -s t/conformance/phpt -p 'test_*.py'
                python3 t/conformance/phpt/run.py \
                  --php-src "${inputs.php-src}" \
                  --split ${lib.escapeShellArg split} \
                  --jobs 2 \
                  ${lib.escapeShellArgs shardArgs}
                runHook postBuild
              '';
              installPhase = "mkdir -p $out && touch $out/passed";
              meta.description = "cl-cc php-src PHPT ${name} conformance check";
            };
          countPhpt =
            path:
            let
              entries = builtins.readDir path;
            in
            lib.foldl' (
              total: name:
              let
                kind = entries.${name};
              in
              total
              + (
                if kind == "directory" then
                  countPhpt "${path}/${name}"
                else if kind == "regular" && lib.hasSuffix ".phpt" name then
                  1
                else
                  0
              )
            ) 0 (builtins.attrNames entries);
          phptChecksFor =
            name: split:
            let
              total = countPhpt "${inputs.php-src}/${split}";
              shardCount = lib.max 1 (builtins.div (total + 399) 400);
              checkName =
                index:
                "phpt-${name}-${
                  lib.fixedWidthString 2 "0" (builtins.toString index)
                }-of-${builtins.toString shardCount}";
            in
            lib.listToAttrs (
              map (index: {
                name = checkName index;
                value = phptCheckFor name split [
                  "--shard"
                  "${builtins.toString (index + 1)}/${builtins.toString shardCount}"
                ];
              }) (lib.range 0 (shardCount - 1))
            );
          phptChecks =
            phptChecksFor "Zend" "Zend"
            // phptChecksFor "tests" "tests"
            // phptChecksFor "ext" "ext"
            // phptChecksFor "sapi" "sapi";
          appsModule = import ./nix/apps.nix {
            inherit
              pkgs
              lib
              sbclWithCLCC
              sbclWithJavascriptTests
              sbclWithTests
              sbclBootstrap
              dispatchSemFix
              testImage
              ;
          };

          # Rendered documentation site (Material for MkDocs). Builds fully
          # offline: Material bundles its assets, so the Nix sandbox needs no
          # network. --strict promotes a broken link or a page missing from the
          # nav into a build failure.
          docs = pkgs.stdenvNoCC.mkDerivation {
            pname = "cl-cc-docs";
            inherit version;
            src = lib.fileset.toSource {
              root = ./docs;
              fileset = lib.fileset.unions [
                ./docs/mkdocs.yml
                ./docs/src
              ];
            };
            nativeBuildInputs = [ pkgs.python3Packages.mkdocs-material ];
            buildPhase = ''
              runHook preBuild
              mkdocs build --strict --config-file mkdocs.yml --site-dir "$out"
              runHook postBuild
            '';
            dontInstall = true;
            meta = {
              description = "Rendered MkDocs (Material) documentation for cl-cc";
              homepage = "https://github.com/nerima-lisp/cl-cc";
              license = lib.licenses.mit;
            };
          };

          checksModule = import ./nix/checks.nix {
            inherit
              pkgs
              lib
              version
              sbclWithJitTests
              sbclWithTests
              clPrologKit
              clWeave
              clCcAst
              rootLock
              childFlakeSources
              asdfSystemDerivations
              productionAsdfSystems
              ;
            inherit (appsModule) apps;
          };
          devshellModule = import ./nix/devshell.nix { inherit pkgs lib sbclWithCLCC; };
        in
        {
          packages =
            productionAsdfSystems
            // testAsdfSystems
            // {
              inherit (binaryModule) default;
              inherit cl-cc-prolog-tools cl-cc-prolog-tools-test docs;
              test-image = testImage;
            };
          inherit (appsModule) apps;
          inherit (checksModule)
            testSuite
            testSuites
            jitTests
            prologToolsTests
            crossRepoInternalRefs
            contextMap
            toolkitPinConsistency
            lisplibsConsistency
            ;
          inherit phptChecks;
          inherit (devshellModule) devShells;
        }
      );
    in
    {
      packages = forAllSystems (system: perSystem.${system}.packages);

      # Granularity lives HERE, not in extra GitHub Actions jobs: `nix flake
      # check` evaluates each attribute below as its own derivation, in
      # parallel, with build caching. Want the test suite, the formatter and the
      # docs reported separately? That is what these three names are for. Adding
      # a second job to ci.yml instead would re-implement the same scheduling
      # and lose cache sharing between the pieces.
      checks = forAllSystems (
        system:
        {
          # The SBCL test suite, run through ./run-tests.lisp.
          default = perSystem.${system}.testSuite;
          tests-unit = perSystem.${system}.testSuites.unit;
          tests-integration = perSystem.${system}.testSuites.integration;
          tests-e2e = perSystem.${system}.testSuites.e2e;
          tests-conformance = perSystem.${system}.testSuites.conformance;

          # Fails `nix flake check` when any tracked Nix file is unformatted,
          # which is what makes the formatter a gate rather than a suggestion.
          formatting = treefmtEval.${system}.config.build.check self;

          # `packages.docs` runs `mkdocs build --strict`, so a broken link or a
          # page missing from the nav fails right here. Without this the docs are
          # only ever built by docs.yml, which runs after the merge to main —
          # a break would surface as a failed deploy rather than a failed pull
          # request.
          docs = perSystem.${system}.packages.docs;

          # The standalone binary has to keep linking, not just the library.
          build = perSystem.${system}.packages.default;

          # packages/prolog-tools has its own cl-weave suite, driven by
          # `asdf:test-system` rather than by cl-cc's runner.
          cl-cc-prolog-tools-tests = perSystem.${system}.prologToolsTests;

          cross-repo-internal-refs = perSystem.${system}.crossRepoInternalRefs;

          context-map = perSystem.${system}.contextMap;

          # The JIT write-barrier suite is a secondary ASDF system and must be
          # exercised explicitly; building its FASLs alone does not run tests.
          cl-cc-jit-tests = perSystem.${system}.jitTests;

          parity-x86-64 =
            with nixpkgs.legacyPackages.${system};
            runCommand "parity-x86-64"
              {
                nativeBuildInputs = [ python3 ] ++ perSystem.${system}.devShells.default.nativeBuildInputs;
              }
              ''
                export PARITY_OUTPUT="$TMPDIR/parity-matrix.tsv"
                bash ${self}/t/parity/run-parity.sh x86-64
                cp "$PARITY_OUTPUT" "$out"
              '';

          parity-wasm =
            with nixpkgs.legacyPackages.${system};
            runCommand "parity-wasm"
              {
                nativeBuildInputs = [
                  python3
                  wasmtime
                ]
                ++ perSystem.${system}.devShells.default.nativeBuildInputs;
              }
              ''
                export PARITY_OUTPUT="$TMPDIR/parity-matrix.tsv"
                bash ${self}/t/parity/run-parity.sh wasm
                cp "$PARITY_OUTPUT" "$out"
              '';

          parity-aarch64 =
            with nixpkgs.legacyPackages.${system};
            runCommand "parity-aarch64"
              {
                nativeBuildInputs = [
                  python3
                  qemu-user
                ]
                ++ perSystem.${system}.devShells.default.nativeBuildInputs;
              }
              ''
                export PARITY_OUTPUT="$TMPDIR/parity-matrix.tsv"
                bash ${self}/t/parity/run-parity.sh aarch64
                cp "$PARITY_OUTPUT" "$out"
              '';

          parity-riscv64 =
            with nixpkgs.legacyPackages.${system};
            runCommand "parity-riscv64"
              {
                nativeBuildInputs = [
                  python3
                  qemu-user
                ]
                ++ perSystem.${system}.devShells.default.nativeBuildInputs;
              }
              ''
                export PARITY_OUTPUT="$TMPDIR/parity-matrix.tsv"
                bash ${self}/t/parity/run-parity.sh riscv64
                cp "$PARITY_OUTPUT" "$out"
              '';

          toolkit-pin-consistency = perSystem.${system}.toolkitPinConsistency;
          lisplibs-consistency = perSystem.${system}.lisplibsConsistency;
          orphan-tests = inputs.cl-nix-forge.lib.${system}.mkOrphanTestCheck {
            src = ./.;
            testRoots = [
              "packages/binary/tests"
              "packages/cli/tests"
              "packages/compile/tests"
              "packages/debug/tests"
              "packages/docgen/tests"
              "packages/emit/tests"
              "packages/formatter/tests"
              "packages/optimize/tests"
              "packages/pipeline/tests"
              "packages/prolog-tools/tests"
              "packages/testing-framework/tests"
              "packages/tools/tests"
              "packages/vm/tests"
              "src/jit/tests"
              "t"
            ];
            testSystems = [
              "cl-cc/test"
              "cl-cc/test/e2e"
              "cl-cc-debug/tests"
              "cl-cc-docgen/test"
              "cl-cc-jit/tests"
              "cl-cc-prolog-tools/tests"
              "cl-cc-tools/tests"
            ];
            allowlist =
              nixpkgs.lib.filter (path: path != "") (
                nixpkgs.lib.splitString "\n" (builtins.readFile ./t/orphan-allowlist.txt)
              )
              ++ [
                # These files are inputs to standalone Nix checks, not ASDF-registered test files.
                "t/conformance/phpt/driver.lisp"
                "t/selfhost/determinism-input.lisp"
                "t/selfhost/determinism-worker.lisp"
                "t/selfhost/determinism.lisp"
                "t/selfhost/harness-support.lisp"
                "t/selfhost/stage2-fixpoint.lisp"
                # These files are inputs to standalone parity Nix checks, not ASDF-registered test files.
                "t/parity/worker.lisp"
                "t/parity/programs/bignum-add.lisp"
                "t/parity/programs/bignum-multiply.lisp"
                "t/parity/programs/ffi-abs.lisp"
                "t/parity/programs/ffi-strlen.lisp"
                "t/parity/programs/file-stream-position.lisp"
                "t/parity/programs/file-stream-roundtrip.lisp"
                "t/parity/programs/format-a.lisp"
                "t/parity/programs/format-newline.lisp"
                "t/parity/programs/heap-cons.lisp"
                "t/parity/programs/heap-vector.lisp"
                "t/parity/programs/integer-add.lisp"
                "t/parity/programs/integer-subtract.lisp"
                "t/parity/programs/load-continuation.lisp"
                "t/parity/programs/load-value.lisp"
                "t/parity/programs/ratio-add.lisp"
                "t/parity/programs/ratio-divide.lisp"
                "t/parity/programs/string-stream-input.lisp"
                "t/parity/programs/string-stream-output.lisp"
                "t/parity/programs/wasm-atomic-rmw-xchg.lisp"
                "t/parity/programs/wasm-float-add.lisp"
                "t/parity/programs/wasm-float-arithmetic.lisp"
                "t/parity/support/loaded-continuation.lisp"
                "t/parity/support/loaded-value.lisp"
              ];
            name = "orphan-tests";
          };
          determinism =
            let
              pkgs = nixpkgs.legacyPackages.${system};
              lib = nixpkgs.lib;
              selfhostSource = builtins.path {
                name = "cl-cc-selfhost-source";
                path = ./.;
                filter =
                  path: type:
                  let
                    absolutePath = toString path;
                    repositoryRoot = toString ./.;
                    relativePath = lib.removePrefix "${repositoryRoot}/" absolutePath;
                    under = directory: relativePath == directory || lib.hasPrefix "${directory}/" relativePath;
                  in
                  absolutePath == repositoryRoot
                  || relativePath == "cl-cc.asd"
                  || under "packages"
                  || (relativePath == "src" && type == "directory")
                  || relativePath == "src/package.lisp"
                  || (relativePath == "src/ffi" && type == "directory")
                  || relativePath == "src/ffi/dynlib.lisp"
                  || (relativePath == "t" && type == "directory")
                  || under "t/selfhost";
              };
            in
            pkgs.stdenvNoCC.mkDerivation {
              pname = "cl-cc-selfhost-determinism";
              version = "1";
              src = selfhostSource;
              buildPhase = ''
                runHook preBuild
                export HOME="$TMPDIR"
                cat > run-tests.lisp <<'EOF'
                (load "t/selfhost/determinism.lisp")
                EOF
                ${perSystem.${system}.apps.test.program}
                runHook postBuild
              '';
              installPhase = "mkdir -p $out && touch $out/passed";
            };
          stage2-fixpoint =
            let
              pkgs = nixpkgs.legacyPackages.${system};
              lib = nixpkgs.lib;
              selfhostSource = builtins.path {
                name = "cl-cc-selfhost-source";
                path = ./.;
                filter =
                  path: type:
                  let
                    absolutePath = toString path;
                    repositoryRoot = toString ./.;
                    relativePath = lib.removePrefix "${repositoryRoot}/" absolutePath;
                    under = directory: relativePath == directory || lib.hasPrefix "${directory}/" relativePath;
                  in
                  absolutePath == repositoryRoot
                  || relativePath == "cl-cc.asd"
                  || under "packages"
                  || (relativePath == "src" && type == "directory")
                  || relativePath == "src/package.lisp"
                  || (relativePath == "src/ffi" && type == "directory")
                  || relativePath == "src/ffi/dynlib.lisp"
                  || (relativePath == "t" && type == "directory")
                  || under "t/selfhost";
              };
            in
            pkgs.stdenvNoCC.mkDerivation {
              pname = "cl-cc-selfhost-stage2-fixpoint";
              version = "1";
              src = selfhostSource;
              buildPhase = ''
                runHook preBuild
                export HOME="$TMPDIR"
                cat > run-tests.lisp <<'EOF'
                (load "t/selfhost/stage2-fixpoint.lisp")
                EOF
                ${perSystem.${system}.apps.test.program}
                runHook postBuild
              '';
              installPhase = "mkdir -p $out && touch $out/passed";
            };
        }
        // perSystem.${system}.phptChecks
      );

      apps = forAllSystems (system: perSystem.${system}.apps);

      devShells = forAllSystems (system: perSystem.${system}.devShells);

      # `nix fmt` entry point.
      formatter = forAllSystems (system: treefmtEval.${system}.config.build.wrapper);

      overlays.default = final: _prev: { cl-cc = self.packages.${final.system}.default; };
    };
}