diff --git a/doc/functions/library/attrsets.xml b/doc/functions/library/attrsets.xml
index 3c5823c2589..7ef0d16624c 100644
--- a/doc/functions/library/attrsets.xml
+++ b/doc/functions/library/attrsets.xml
@@ -1711,4 +1711,43 @@ recursiveUpdate
+
+ lib.attrsets.cartesianProductOfSets
+
+ cartesianProductOfSets :: AttrSet -> [ AttrSet ]
+
+
+
+
+
+ Return the cartesian product of attribute set value combinations.
+
+
+
+
+
+ set
+
+
+
+ An attribute set with attributes that carry lists of values.
+
+
+
+
+
+
+ Creating the cartesian product of a list of attribute values
+ [
+ { a = 1; b = 10; }
+ { a = 1; b = 20; }
+ { a = 2; b = 10; }
+ { a = 2; b = 20; }
+ ]
+]]>
+
+
+
diff --git a/lib/attrsets.nix b/lib/attrsets.nix
index d91d7a0cd47..0ce3aaeca45 100644
--- a/lib/attrsets.nix
+++ b/lib/attrsets.nix
@@ -183,6 +183,24 @@ rec {
else
[];
+ /* Return the cartesian product of attribute set value combinations.
+
+ Example:
+ cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
+ => [
+ { a = 1; b = 10; }
+ { a = 1; b = 20; }
+ { a = 2; b = 10; }
+ { a = 2; b = 20; }
+ ]
+ */
+ cartesianProductOfSets = attrsOfLists:
+ lib.foldl' (listOfAttrs: attrName:
+ concatMap (attrs:
+ map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
+ ) listOfAttrs
+ ) [{}] (attrNames attrsOfLists);
+
/* Utility function that creates a {name, value} pair as expected by
builtins.listToAttrs.
@@ -493,5 +511,4 @@ rec {
zipWithNames = zipAttrsWithNames;
zip = builtins.trace
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
-
}
diff --git a/lib/default.nix b/lib/default.nix
index 803f1f76564..50320669e28 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -78,7 +78,7 @@ let
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
recursiveUpdate matchAttrs overrideExisting getOutput getBin
getLib getDev getMan chooseDevOutputs zipWithNames zip
- recurseIntoAttrs dontRecurseIntoAttrs;
+ recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets;
inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
concatMap flatten remove findSingle findFirst any all count
optional optionals toList range partition zipListsWith zipLists
diff --git a/lib/lists.nix b/lib/lists.nix
index 06cee2eb112..56af4d9daa1 100644
--- a/lib/lists.nix
+++ b/lib/lists.nix
@@ -629,7 +629,9 @@ rec {
crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
=> [ "13" "14" "23" "24" ]
*/
- crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
+ crossLists = builtins.trace
+ "lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
+ (f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
/* Remove duplicate elements from the list. O(n^2) complexity.
diff --git a/lib/tests/misc.nix b/lib/tests/misc.nix
index 35a5801c724..0d249968402 100644
--- a/lib/tests/misc.nix
+++ b/lib/tests/misc.nix
@@ -660,4 +660,71 @@ runTests {
expected = [ [ "foo" ] [ "foo" "" "bar" ] [ "foo" "bar" ] ];
};
+ testCartesianProductOfEmptySet = {
+ expr = cartesianProductOfSets {};
+ expected = [ {} ];
+ };
+
+ testCartesianProductOfOneSet = {
+ expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
+ expected = [ { a = 1; } { a = 2; } { a = 3; } ];
+ };
+
+ testCartesianProductOfTwoSets = {
+ expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
+ expected = [
+ { a = 1; b = 10; }
+ { a = 1; b = 20; }
+ ];
+ };
+
+ testCartesianProductOfTwoSetsWithOneEmpty = {
+ expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
+ expected = [ ];
+ };
+
+ testCartesianProductOfThreeSets = {
+ expr = cartesianProductOfSets {
+ a = [ 1 2 3 ];
+ b = [ 10 20 30 ];
+ c = [ 100 200 300 ];
+ };
+ expected = [
+ { a = 1; b = 10; c = 100; }
+ { a = 1; b = 10; c = 200; }
+ { a = 1; b = 10; c = 300; }
+
+ { a = 1; b = 20; c = 100; }
+ { a = 1; b = 20; c = 200; }
+ { a = 1; b = 20; c = 300; }
+
+ { a = 1; b = 30; c = 100; }
+ { a = 1; b = 30; c = 200; }
+ { a = 1; b = 30; c = 300; }
+
+ { a = 2; b = 10; c = 100; }
+ { a = 2; b = 10; c = 200; }
+ { a = 2; b = 10; c = 300; }
+
+ { a = 2; b = 20; c = 100; }
+ { a = 2; b = 20; c = 200; }
+ { a = 2; b = 20; c = 300; }
+
+ { a = 2; b = 30; c = 100; }
+ { a = 2; b = 30; c = 200; }
+ { a = 2; b = 30; c = 300; }
+
+ { a = 3; b = 10; c = 100; }
+ { a = 3; b = 10; c = 200; }
+ { a = 3; b = 10; c = 300; }
+
+ { a = 3; b = 20; c = 100; }
+ { a = 3; b = 20; c = 200; }
+ { a = 3; b = 20; c = 300; }
+
+ { a = 3; b = 30; c = 100; }
+ { a = 3; b = 30; c = 200; }
+ { a = 3; b = 30; c = 300; }
+ ];
+ };
}
diff --git a/nixos/modules/services/x11/display-managers/default.nix b/nixos/modules/services/x11/display-managers/default.nix
index 6945a241f92..9fdbe753dad 100644
--- a/nixos/modules/services/x11/display-managers/default.nix
+++ b/nixos/modules/services/x11/display-managers/default.nix
@@ -444,8 +444,8 @@ in
in
# We will generate every possible pair of WM and DM.
concatLists (
- crossLists
- (dm: wm: let
+ builtins.map
+ ({dm, wm}: let
sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}";
script = xsession dm wm;
desktopNames = if dm ? desktopNames
@@ -472,7 +472,7 @@ in
providedSessions = [ sessionName ];
})
)
- [dms wms]
+ (cartesianProductOfSets { dm = dms; wm = wms; })
);
# Make xsessions and wayland sessions available in XDG_DATA_DIRS
diff --git a/nixos/tests/predictable-interface-names.nix b/nixos/tests/predictable-interface-names.nix
index bab091d57ac..c0b472638a1 100644
--- a/nixos/tests/predictable-interface-names.nix
+++ b/nixos/tests/predictable-interface-names.nix
@@ -5,7 +5,11 @@
let
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
-in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
+ testCombinations = pkgs.lib.cartesianProductOfSets {
+ predictable = [true false];
+ withNetworkd = [true false];
+ };
+in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd }: {
name = pkgs.lib.optionalString (!predictable) "un" + "predictable"
+ pkgs.lib.optionalString withNetworkd "Networkd";
value = makeTest {
@@ -30,4 +34,4 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
machine.${if predictable then "fail" else "succeed"}("ip link show eth0")
'';
};
-}) [[true false] [true false]])
+}) testCombinations)
diff --git a/pkgs/os-specific/solo5/default.nix b/pkgs/os-specific/solo5/default.nix
index 2dbeca98051..19d1aa3b5eb 100644
--- a/pkgs/os-specific/solo5/default.nix
+++ b/pkgs/os-specific/solo5/default.nix
@@ -50,10 +50,11 @@ in stdenv.mkDerivation {
homepage = "https://github.com/solo5/solo5";
license = licenses.isc;
maintainers = [ maintainers.ehmry ];
- platforms = lib.crossLists (arch: os: "${arch}-${os}") [
- [ "aarch64" "x86_64" ]
- [ "freebsd" "genode" "linux" "openbsd" ]
- ];
+ platforms = builtins.map ({arch, os}: "${arch}-${os}")
+ (cartesianProductOfSets {
+ arch = [ "aarch64" "x86_64" ];
+ os = [ "freebsd" "genode" "linux" "openbsd" ];
+ });
};
}