Skip to content

Commit

Permalink
Prevent infinite feature expansion cycles
Browse files Browse the repository at this point in the history
Fixes #209. Hopefully.

I'm not sure how to properly update all the tests and stuff. For now I
manually re-generated the same set of files that was re-generated in
some other, recent commit.
  • Loading branch information
Fuuzetsu authored and kolloch committed Oct 14, 2023
1 parent ef4711e commit f581532
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 30 deletions.
24 changes: 18 additions & 6 deletions crate2nix/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2916,12 +2916,24 @@ rec {
assert (builtins.isAttrs featureMap);
assert (builtins.isList inputFeatures);
let
expandFeature = feature:
assert (builtins.isString feature);
[ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
outFeatures = lib.concatMap expandFeature inputFeatures;
in
sortedUnique outFeatures;
expandFeaturesNoCycle = oldSeen: inputFeatures:
if inputFeatures != []
then
let
# The feature we're currently expanding.
feature = builtins.head inputFeatures;
# All the features we've seen/expanded so far, including the one
# we're currently processing.
seen = oldSeen // { ${feature} = 1; };
# Expand the feature but be careful to not re-introduce a feature
# that we've already seen: this can easily cause a cycle, see issue
# #209.
enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or []);
in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
# No more features left, nothing to expand to.
else [];
outFeatures = expandFeaturesNoCycle { } inputFeatures;
in sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
Expand Down
24 changes: 18 additions & 6 deletions crate2nix/templates/nix/crate2nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -583,12 +583,24 @@ rec {
assert (builtins.isAttrs featureMap);
assert (builtins.isList inputFeatures);
let
expandFeature = feature:
assert (builtins.isString feature);
[ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
outFeatures = lib.concatMap expandFeature inputFeatures;
in
sortedUnique outFeatures;
expandFeaturesNoCycle = oldSeen: inputFeatures:
if inputFeatures != []
then
let
# The feature we're currently expanding.
feature = builtins.head inputFeatures;
# All the features we've seen/expanded so far, including the one
# we're currently processing.
seen = oldSeen // { ${feature} = 1; };
# Expand the feature but be careful to not re-introduce a feature
# that we've already seen: this can easily cause a cycle, see issue
# #209.
enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or []);
in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
# No more features left, nothing to expand to.
else [];
outFeatures = expandFeaturesNoCycle { } inputFeatures;
in sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
Expand Down
24 changes: 18 additions & 6 deletions sample_projects/bin_with_git_submodule_dep/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1931,12 +1931,24 @@ rec {
assert (builtins.isAttrs featureMap);
assert (builtins.isList inputFeatures);
let
expandFeature = feature:
assert (builtins.isString feature);
[ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
outFeatures = lib.concatMap expandFeature inputFeatures;
in
sortedUnique outFeatures;
expandFeaturesNoCycle = oldSeen: inputFeatures:
if inputFeatures != []
then
let
# The feature we're currently expanding.
feature = builtins.head inputFeatures;
# All the features we've seen/expanded so far, including the one
# we're currently processing.
seen = oldSeen // { ${feature} = 1; };
# Expand the feature but be careful to not re-introduce a feature
# that we've already seen: this can easily cause a cycle, see issue
# #209.
enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or []);
in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
# No more features left, nothing to expand to.
else [];
outFeatures = expandFeaturesNoCycle { } inputFeatures;
in sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
Expand Down
24 changes: 18 additions & 6 deletions sample_projects/codegen/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1063,12 +1063,24 @@ rec {
assert (builtins.isAttrs featureMap);
assert (builtins.isList inputFeatures);
let
expandFeature = feature:
assert (builtins.isString feature);
[ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
outFeatures = lib.concatMap expandFeature inputFeatures;
in
sortedUnique outFeatures;
expandFeaturesNoCycle = oldSeen: inputFeatures:
if inputFeatures != []
then
let
# The feature we're currently expanding.
feature = builtins.head inputFeatures;
# All the features we've seen/expanded so far, including the one
# we're currently processing.
seen = oldSeen // { ${feature} = 1; };
# Expand the feature but be careful to not re-introduce a feature
# that we've already seen: this can easily cause a cycle, see issue
# #209.
enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or []);
in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
# No more features left, nothing to expand to.
else [];
outFeatures = expandFeaturesNoCycle { } inputFeatures;
in sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
Expand Down
24 changes: 18 additions & 6 deletions sample_projects/sub_dir_crates/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -715,12 +715,24 @@ rec {
assert (builtins.isAttrs featureMap);
assert (builtins.isList inputFeatures);
let
expandFeature = feature:
assert (builtins.isString feature);
[ feature ] ++ (expandFeatures featureMap (featureMap."${feature}" or [ ]));
outFeatures = lib.concatMap expandFeature inputFeatures;
in
sortedUnique outFeatures;
expandFeaturesNoCycle = oldSeen: inputFeatures:
if inputFeatures != []
then
let
# The feature we're currently expanding.
feature = builtins.head inputFeatures;
# All the features we've seen/expanded so far, including the one
# we're currently processing.
seen = oldSeen // { ${feature} = 1; };
# Expand the feature but be careful to not re-introduce a feature
# that we've already seen: this can easily cause a cycle, see issue
# #209.
enables = builtins.filter (f: !(seen ? "${f}")) (featureMap."${feature}" or []);
in [ feature ] ++ (expandFeaturesNoCycle seen (builtins.tail inputFeatures ++ enables))
# No more features left, nothing to expand to.
else [];
outFeatures = expandFeaturesNoCycle { } inputFeatures;
in sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
Expand Down

0 comments on commit f581532

Please sign in to comment.