# Per-crate Rust builds with crate2nix. # # resolvedJson selects how the build plan is obtained: # null (default) /Cargo.json if present, else IFD from /Cargo.lock # a path that file (crate2nix generate --format json) # false always IFD from /Cargo.lock # # Overrides are attrs, or functions from the crate's buildRustCrate arguments # to attrs, and are merged into those arguments before buildRustCrate sees # them, so they can also set release. For a workspace member, # workspaceOverride applies first and crateOverrides. sees its result. # nixpkgs' defaultCrateOverrides still apply inside buildRustCrate. { pkgs , lib ? pkgs.lib , applyOverride , name , src # crate name -> attrs or (args -> attrs) , crateOverrides ? {} # attrs or (args -> attrs), for every workspace member , workspaceOverride ? {} # IFD only; Cargo.json has its features baked in , features ? [ "default" ] # Cargo.lock the JSON plan must match; null: no check , cargoLock ? null , resolvedJson ? null }: let resolvedJsonFromSrc = src + "/Cargo.json"; useJson = if resolvedJson == null then builtins.pathExists resolvedJsonFromSrc else resolvedJson != false; resolvedJson' = if resolvedJson != null then resolvedJson else resolvedJsonFromSrc; workspaceMembers = builtins.attrNames cargoNix.workspaceMembers; buildRustCrateForPkgs = pkgs: args: let withWorkspace = if builtins.elem args.crateName workspaceMembers then applyOverride workspaceOverride args else args; in pkgs.buildRustCrate (applyOverride (crateOverrides.${args.crateName} or {}) withWorkspace); fromJson = pkgs.crate2nix.build-from-json { inherit buildRustCrateForPkgs pkgs src; resolvedJson = resolvedJson'; }; fromLock = pkgs.callPackage (pkgs.crate2nix.tools.generatedCargoNix { inherit name src; additionalCargoNixArgs = [ "--all-features" ]; }) { rootFeatures = features; inherit buildRustCrateForPkgs; }; # Fails evaluation if the plan uses a crate version the lock doesn't have, # i.e. Cargo.lock changed since Cargo.json was generated. Dependencies added # to the lock aren't detected, since the plan may leave out crates for # features or targets it doesn't use; those fail the build instead. checkLock = result: let id = name: version: "${name} ${version}"; lockPackages = (builtins.fromTOML (builtins.readFile cargoLock)).package; locked = builtins.listToAttrs (map (p: { name = id p.name p.version; value = null; }) (builtins.filter (p: p ? source) lockPackages)); planned = lib.mapAttrsToList (_: c: id c.crateName c.version) (lib.filterAttrs (_: c: (c.source.type or "local") != "local") (builtins.fromJSON (builtins.readFile resolvedJson')).crates); missing = builtins.filter (i: !(locked ? ${i})) planned; in if cargoLock == null || missing == [] then result else throw "crate2nix-package: ${toString resolvedJson'} is stale against ${toString cargoLock}; not in the lock: ${lib.concatStringsSep ", " (lib.take 5 missing)}${lib.optionalString (builtins.length missing > 5) ", ..."}"; cargoNix = if useJson then checkLock fromJson else fromLock; in cargoNix // { build = if cargoNix.rootCrate or null != null then cargoNix.rootCrate.build else cargoNix.allWorkspaceMembers; }