summaryrefslogtreecommitdiffstats
path: root/pkgs/rust/lib/crate2nix-package.nix
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/rust/lib/crate2nix-package.nix')
-rw-r--r--pkgs/rust/lib/crate2nix-package.nix34
1 files changed, 31 insertions, 3 deletions
diff --git a/pkgs/rust/lib/crate2nix-package.nix b/pkgs/rust/lib/crate2nix-package.nix
index ee75096..9b16b15 100644
--- a/pkgs/rust/lib/crate2nix-package.nix
+++ b/pkgs/rust/lib/crate2nix-package.nix
@@ -24,12 +24,15 @@
# attrs or (args -> attrs), for every workspace member
, workspaceOverride ? {}
-# IFD only; Cargo.json has its features baked in
+# Root features. A JSON plan has its features baked in; cargoToml checks them.
, features ? [ "default" ]
# Cargo.lock the JSON plan must match; null: no check
, cargoLock ? null
+# Cargo.toml to check the plan's root features; null: no check
+, cargoToml ? null
+
, resolvedJson ? null
}:
@@ -71,6 +74,31 @@ let
inherit buildRustCrateForPkgs;
};
+ plan = builtins.fromJSON (builtins.readFile resolvedJson');
+
+ # Fails evaluation if the plan's root crate wasn't resolved with features:
+ # its declared features must be exactly those reachable from features
+ # through Cargo.toml's [features], and features not declared there (implicit
+ # features of optional dependencies) must be present.
+ checkFeatures = result:
+ let
+ declared = (builtins.fromTOML (builtins.readFile cargoToml)).features or { };
+ resolved = plan.crates.${plan.root}.resolvedDefaultFeatures or [ ];
+ # default is always valid, even when Cargo.toml doesn't declare it.
+ requested = builtins.filter (f: f != "default" || declared ? default) features;
+ expected = map (x: x.key) (builtins.genericClosure {
+ startSet = map (key: { inherit key; }) (builtins.filter (f: declared ? ${f}) requested);
+ operator = x: map (key: { inherit key; }) (builtins.filter (f: declared ? ${f}) declared.${x.key});
+ });
+ actual = builtins.filter (f: declared ? ${f}) resolved;
+ extra = lib.subtractLists expected actual;
+ missing = lib.unique (lib.subtractLists actual expected
+ ++ builtins.filter (f: !(declared ? ${f}) && !(builtins.elem f resolved)) requested);
+ show = l: "[ ${lib.concatStringsSep " " l} ]";
+ in
+ if cargoToml == null || plan.root or null == null || extra == [ ] && missing == [ ] then result
+ else throw "crate2nix-package: ${toString resolvedJson'} was generated with other features than ${show features}${lib.optionalString (missing != [ ]) "; missing ${show missing}"}${lib.optionalString (extra != [ ]) "; extra ${show extra}"}";
+
# 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
@@ -84,13 +112,13 @@ let
(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);
+ plan.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;
+ cargoNix = if useJson then checkFeatures (checkLock fromJson) else fromLock;
in
cargoNix // {
build =