1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
# Per-crate Rust builds with crate2nix.
#
# resolvedJson selects how the build plan is obtained:
# null (default) <src>/Cargo.json if present, else IFD from <src>/Cargo.lock
# a path that file (crate2nix generate --format json)
# false always IFD from <src>/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.<name> 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;
}
|