Skip to content

Commit

Permalink
Adds optional dependencies to features when they are enabled indirectly.
Browse files Browse the repository at this point in the history
When Cargo sees a dependency feature `opt_crate/feat` for an optional dependency `opt_crate`,
it implicitly adds `opt_crate` to the feature set for the current crate.
For example, `num-rational` crate relies on this behavior for importing `num-bigint`, which is optional.
This patch lets crate2nix mimic this behavior.

Closes nix-community#146 and nix-community#182.
  • Loading branch information
pandaman64 committed Apr 22, 2021
1 parent d8ddf41 commit 94968de
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 20 deletions.
29 changes: 26 additions & 3 deletions crate2nix/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2596,10 +2596,11 @@ rec {
let
crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
expandedFeatures = expandFeatures (crateConfig.features or { }) features;
enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
depWithResolvedFeatures = dependency:
let
packageId = dependency.packageId;
features = dependencyFeatures expandedFeatures dependency;
features = dependencyFeatures enabledFeatures dependency;
in
{ inherit packageId features; };
resolveDependencies = cache: path: dependencies:
Expand All @@ -2608,7 +2609,7 @@ rec {
let
enabledDependencies = filterEnabledDependencies {
inherit dependencies target;
features = expandedFeatures;
features = enabledFeatures;
};
directDependencies = map depWithResolvedFeatures enabledDependencies;
foldOverCache = op: lib.foldl op cache directDependencies;
Expand All @@ -2632,7 +2633,7 @@ rec {
cacheWithSelf =
let
cacheFeatures = featuresByPackageId.${packageId} or [ ];
combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures);
combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
in
featuresByPackageId // {
"${packageId}" = combinedFeatures;
Expand Down Expand Up @@ -2700,6 +2701,28 @@ rec {
in
sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
described in a note at:
https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
*/
enableFeatures = dependencies: features:
assert (builtins.isList features);
assert (builtins.isList dependencies);
let
additionalFeatures = lib.concatMap
(
dependency:
assert (builtins.isAttrs dependency);
let
enabled = builtins.any (doesFeatureEnableDependency dependency) features;
in
if (dependency.optional or false) && enabled then [ dependency.name ] else [ ]
)
dependencies;
in
sortedUnique (features ++ additionalFeatures);

/*
Returns the actual features for the given dependency.
Expand Down
29 changes: 26 additions & 3 deletions crate2nix/templates/nix/crate2nix/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -466,10 +466,11 @@ rec {
let
crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
expandedFeatures = expandFeatures (crateConfig.features or { }) features;
enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
depWithResolvedFeatures = dependency:
let
packageId = dependency.packageId;
features = dependencyFeatures expandedFeatures dependency;
features = dependencyFeatures enabledFeatures dependency;
in
{ inherit packageId features; };
resolveDependencies = cache: path: dependencies:
Expand All @@ -478,7 +479,7 @@ rec {
let
enabledDependencies = filterEnabledDependencies {
inherit dependencies target;
features = expandedFeatures;
features = enabledFeatures;
};
directDependencies = map depWithResolvedFeatures enabledDependencies;
foldOverCache = op: lib.foldl op cache directDependencies;
Expand All @@ -502,7 +503,7 @@ rec {
cacheWithSelf =
let
cacheFeatures = featuresByPackageId.${packageId} or [ ];
combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures);
combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
in
featuresByPackageId // {
"${packageId}" = combinedFeatures;
Expand Down Expand Up @@ -570,6 +571,28 @@ rec {
in
sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
described in a note at:
https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
*/
enableFeatures = dependencies: features:
assert (builtins.isList features);
assert (builtins.isList dependencies);
let
additionalFeatures = lib.concatMap
(
dependency:
assert (builtins.isAttrs dependency);
let
enabled = builtins.any (doesFeatureEnableDependency dependency) features;
in
if (dependency.optional or false) && enabled then [ dependency.name ] else [ ]
)
dependencies;
in
sortedUnique (features ++ additionalFeatures);

/*
Returns the actual features for the given dependency.
Expand Down
4 changes: 2 additions & 2 deletions crate2nix/templates/nix/crate2nix/tests/packageFeatures.nix
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ in
testNumDependencies = {
expr = packageFeatures "pkg_num" [ "default" ];
expected = {
"pkg_num" = [ "default" "num-bigint/std" "std" ];
"pkg_num" = [ "default" "num-bigint" "num-bigint/std" "std" ];
"pkg_num_bigint" = [ "std" ];
};
};
Expand All @@ -107,7 +107,7 @@ in
expr = packageFeatures "pkg_numtest" [ "default" ];
expected = {
"pkg_numtest" = [ "default" ];
"pkg_num" = [ "default" "num-bigint/std" "std" ];
"pkg_num" = [ "default" "num-bigint" "num-bigint/std" "std" ];
"pkg_num_bigint" = [ "std" ];
};
};
Expand Down
29 changes: 26 additions & 3 deletions sample_projects/bin_with_git_branch_dep/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -575,10 +575,11 @@ rec {
let
crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
expandedFeatures = expandFeatures (crateConfig.features or { }) features;
enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
depWithResolvedFeatures = dependency:
let
packageId = dependency.packageId;
features = dependencyFeatures expandedFeatures dependency;
features = dependencyFeatures enabledFeatures dependency;
in
{ inherit packageId features; };
resolveDependencies = cache: path: dependencies:
Expand All @@ -587,7 +588,7 @@ rec {
let
enabledDependencies = filterEnabledDependencies {
inherit dependencies target;
features = expandedFeatures;
features = enabledFeatures;
};
directDependencies = map depWithResolvedFeatures enabledDependencies;
foldOverCache = op: lib.foldl op cache directDependencies;
Expand All @@ -611,7 +612,7 @@ rec {
cacheWithSelf =
let
cacheFeatures = featuresByPackageId.${packageId} or [ ];
combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures);
combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
in
featuresByPackageId // {
"${packageId}" = combinedFeatures;
Expand Down Expand Up @@ -679,6 +680,28 @@ rec {
in
sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
described in a note at:
https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
*/
enableFeatures = dependencies: features:
assert (builtins.isList features);
assert (builtins.isList dependencies);
let
additionalFeatures = lib.concatMap
(
dependency:
assert (builtins.isAttrs dependency);
let
enabled = builtins.any (doesFeatureEnableDependency dependency) features;
in
if (dependency.optional or false) && enabled then [ dependency.name ] else [ ]
)
dependencies;
in
sortedUnique (features ++ additionalFeatures);

/*
Returns the actual features for the given dependency.
Expand Down
29 changes: 26 additions & 3 deletions sample_projects/bin_with_git_submodule_dep/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -1748,10 +1748,11 @@ rec {
let
crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
expandedFeatures = expandFeatures (crateConfig.features or { }) features;
enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
depWithResolvedFeatures = dependency:
let
packageId = dependency.packageId;
features = dependencyFeatures expandedFeatures dependency;
features = dependencyFeatures enabledFeatures dependency;
in
{ inherit packageId features; };
resolveDependencies = cache: path: dependencies:
Expand All @@ -1760,7 +1761,7 @@ rec {
let
enabledDependencies = filterEnabledDependencies {
inherit dependencies target;
features = expandedFeatures;
features = enabledFeatures;
};
directDependencies = map depWithResolvedFeatures enabledDependencies;
foldOverCache = op: lib.foldl op cache directDependencies;
Expand All @@ -1784,7 +1785,7 @@ rec {
cacheWithSelf =
let
cacheFeatures = featuresByPackageId.${packageId} or [ ];
combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures);
combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
in
featuresByPackageId // {
"${packageId}" = combinedFeatures;
Expand Down Expand Up @@ -1852,6 +1853,28 @@ rec {
in
sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
described in a note at:
https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
*/
enableFeatures = dependencies: features:
assert (builtins.isList features);
assert (builtins.isList dependencies);
let
additionalFeatures = lib.concatMap
(
dependency:
assert (builtins.isAttrs dependency);
let
enabled = builtins.any (doesFeatureEnableDependency dependency) features;
in
if (dependency.optional or false) && enabled then [ dependency.name ] else [ ]
)
dependencies;
in
sortedUnique (features ++ additionalFeatures);

/*
Returns the actual features for the given dependency.
Expand Down
29 changes: 26 additions & 3 deletions sample_projects/codegen/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,11 @@ rec {
let
crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
expandedFeatures = expandFeatures (crateConfig.features or { }) features;
enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
depWithResolvedFeatures = dependency:
let
packageId = dependency.packageId;
features = dependencyFeatures expandedFeatures dependency;
features = dependencyFeatures enabledFeatures dependency;
in
{ inherit packageId features; };
resolveDependencies = cache: path: dependencies:
Expand All @@ -939,7 +940,7 @@ rec {
let
enabledDependencies = filterEnabledDependencies {
inherit dependencies target;
features = expandedFeatures;
features = enabledFeatures;
};
directDependencies = map depWithResolvedFeatures enabledDependencies;
foldOverCache = op: lib.foldl op cache directDependencies;
Expand All @@ -963,7 +964,7 @@ rec {
cacheWithSelf =
let
cacheFeatures = featuresByPackageId.${packageId} or [ ];
combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures);
combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
in
featuresByPackageId // {
"${packageId}" = combinedFeatures;
Expand Down Expand Up @@ -1031,6 +1032,28 @@ rec {
in
sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
described in a note at:
https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
*/
enableFeatures = dependencies: features:
assert (builtins.isList features);
assert (builtins.isList dependencies);
let
additionalFeatures = lib.concatMap
(
dependency:
assert (builtins.isAttrs dependency);
let
enabled = builtins.any (doesFeatureEnableDependency dependency) features;
in
if (dependency.optional or false) && enabled then [ dependency.name ] else [ ]
)
dependencies;
in
sortedUnique (features ++ additionalFeatures);

/*
Returns the actual features for the given dependency.
Expand Down
29 changes: 26 additions & 3 deletions sample_projects/sub_dir_crates/Cargo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -594,10 +594,11 @@ rec {
let
crateConfig = crateConfigs."${packageId}" or (builtins.throw "Package not found: ${packageId}");
expandedFeatures = expandFeatures (crateConfig.features or { }) features;
enabledFeatures = enableFeatures (crateConfig.dependencies or [ ]) expandedFeatures;
depWithResolvedFeatures = dependency:
let
packageId = dependency.packageId;
features = dependencyFeatures expandedFeatures dependency;
features = dependencyFeatures enabledFeatures dependency;
in
{ inherit packageId features; };
resolveDependencies = cache: path: dependencies:
Expand All @@ -606,7 +607,7 @@ rec {
let
enabledDependencies = filterEnabledDependencies {
inherit dependencies target;
features = expandedFeatures;
features = enabledFeatures;
};
directDependencies = map depWithResolvedFeatures enabledDependencies;
foldOverCache = op: lib.foldl op cache directDependencies;
Expand All @@ -630,7 +631,7 @@ rec {
cacheWithSelf =
let
cacheFeatures = featuresByPackageId.${packageId} or [ ];
combinedFeatures = sortedUnique (cacheFeatures ++ expandedFeatures);
combinedFeatures = sortedUnique (cacheFeatures ++ enabledFeatures);
in
featuresByPackageId // {
"${packageId}" = combinedFeatures;
Expand Down Expand Up @@ -698,6 +699,28 @@ rec {
in
sortedUnique outFeatures;

/* This function adds optional dependencies as features if they are enabled
indirectly by dependency features. This function mimics Cargo's behavior
described in a note at:
https://doc.rust-lang.org/nightly/cargo/reference/features.html#dependency-features
*/
enableFeatures = dependencies: features:
assert (builtins.isList features);
assert (builtins.isList dependencies);
let
additionalFeatures = lib.concatMap
(
dependency:
assert (builtins.isAttrs dependency);
let
enabled = builtins.any (doesFeatureEnableDependency dependency) features;
in
if (dependency.optional or false) && enabled then [ dependency.name ] else [ ]
)
dependencies;
in
sortedUnique (features ++ additionalFeatures);

/*
Returns the actual features for the given dependency.
Expand Down

0 comments on commit 94968de

Please sign in to comment.