summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pkgs/rust/default.nix17
-rw-r--r--pkgs/rust/lib/crate2nix-package.nix34
2 files changed, 42 insertions, 9 deletions
diff --git a/pkgs/rust/default.nix b/pkgs/rust/default.nix
index f6c7eb7..c26cf9d 100644
--- a/pkgs/rust/default.nix
+++ b/pkgs/rust/default.nix
@@ -31,10 +31,15 @@ let
resolvedJson = path + "/Cargo.json";
};
- # The checkout's Cargo.lock, to check the JSON plan against.
- localLock = path:
- lib.optionalAttrs (builtins.pathExists (path + "/Cargo.lock")) {
- cargoLock = path + "/Cargo.lock";
+ # Checks the JSON plan against the checkout's Cargo.lock and Cargo.toml.
+ # These come from dir, not src, because cleanCargoSource drops Cargo.lock
+ # next to a Cargo.json.
+ planChecks = dir:
+ lib.optionalAttrs (builtins.pathExists (dir + "/Cargo.lock")) {
+ cargoLock = dir + "/Cargo.lock";
+ }
+ // lib.optionalAttrs (builtins.pathExists (dir + "/Cargo.toml")) {
+ cargoToml = dir + "/Cargo.toml";
};
in
@@ -78,10 +83,10 @@ in
in
if explicitPackages ? ${pname} then
assert mylib.debug "pkgs/rust: overriding ${pname}.src with ${mylib.pretty dir}";
- hrmPlan explicitPackages.${pname} // callArgs explicitPackages.${pname} // { inherit src; } // localLock dir
+ hrmPlan explicitPackages.${pname} // callArgs explicitPackages.${pname} // { inherit src; } // planChecks dir
else
assert mylib.debug "pkgs/rust: automatic package ${pname} from ${mylib.pretty dir}";
- { name = pname; inherit src; } // localLock dir
+ { name = pname; inherit src; } // planChecks dir
)
automaticPackages;
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 =