diff --git a/lib/systems/default.nix b/lib/systems/default.nix index f4784c61c675..3558ce32fc8c 100644 --- a/lib/systems/default.nix +++ b/lib/systems/default.nix @@ -9,6 +9,39 @@ rec { examples = import ./examples.nix { inherit lib; }; architectures = import ./architectures.nix { inherit lib; }; + /* + Elaborated systems contain functions, which means that they don't satisfy + `==` for a lack of reflexivity. + + They might *appear* to satisfy `==` reflexivity when the same exact value is + compared to itself, because object identity is used as an "optimization"; + compare the value with a reconstruction of itself, e.g. with `f == a: f a`, + or perhaps calling `elaborate` twice, and one will see reflexivity fail as described. + + Hence a custom equality test. + + Note that this does not canonicalize the systems, so you'll want to make sure + both arguments have been `elaborate`-d. + */ + equals = + let removeFunctions = a: lib.filterAttrs (_: v: !builtins.isFunction v) a; + in a: b: removeFunctions a == removeFunctions b; + + /* + Try to convert an elaborated system back to a simple string. If not possible, + return null. So we have the property: + + sys: _valid_ sys -> + sys == elaborate (toLosslessStringMaybe sys) + + NOTE: This property is not guaranteed when `sys` was elaborated by a different + version of Nixpkgs. + */ + toLosslessStringMaybe = sys: + if lib.isString sys then sys + else if equals sys (elaborate sys.system) then sys.system + else null; + /* List of all Nix system doubles the nixpkgs flake will expose the package set for. All systems listed here must be supported by nixpkgs as `localSystem`. diff --git a/lib/tests/release.nix b/lib/tests/release.nix index c3bf58db241f..5bade7112f19 100644 --- a/lib/tests/release.nix +++ b/lib/tests/release.nix @@ -53,6 +53,9 @@ let echo "Running lib/tests/sources.sh" TEST_LIB=$PWD/lib bash lib/tests/sources.sh + echo "Running lib/tests/systems.nix" + [[ $(nix-instantiate --eval --strict lib/tests/systems.nix | tee /dev/stderr) == '[ ]' ]]; + mkdir $out echo success > $out/${nix.version} ''; diff --git a/lib/tests/systems.nix b/lib/tests/systems.nix index 2afe128c4208..862496313e90 100644 --- a/lib/tests/systems.nix +++ b/lib/tests/systems.nix @@ -1,10 +1,8 @@ -# We assert that the new algorithmic way of generating these lists matches the -# way they were hard-coded before. +# Run: +# [nixpkgs]$ nix-instantiate --eval --strict lib/tests/systems.nix +# Expected output: [], or the failed cases # -# One might think "if we exhaustively test, what's the point of procedurally -# calculating the lists anyway?". The answer is one can mindlessly update these -# tests as new platforms become supported, and then just give the diff a quick -# sanity check before committing :). +# OfBorg runs (approximately) nix-build lib/tests/release.nix let lib = import ../default.nix; mseteq = x: y: { @@ -12,7 +10,16 @@ let expected = lib.sort lib.lessThan y; }; in -with lib.systems.doubles; lib.runTests { +lib.runTests ( +# We assert that the new algorithmic way of generating these lists matches the +# way they were hard-coded before. +# +# One might think "if we exhaustively test, what's the point of procedurally +# calculating the lists anyway?". The answer is one can mindlessly update these +# tests as new platforms become supported, and then just give the diff a quick +# sanity check before committing :). + +(with lib.systems.doubles; { testall = mseteq all (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ wasi ++ windows ++ embedded ++ mmix ++ js ++ genode ++ redox); testarm = mseteq arm [ "armv5tel-linux" "armv6l-linux" "armv6l-netbsd" "armv6l-none" "armv7a-linux" "armv7a-netbsd" "armv7l-linux" "armv7l-netbsd" "arm-none" "armv7a-darwin" ]; @@ -39,4 +46,44 @@ with lib.systems.doubles; lib.runTests { testopenbsd = mseteq openbsd [ "i686-openbsd" "x86_64-openbsd" ]; testwindows = mseteq windows [ "i686-cygwin" "x86_64-cygwin" "i686-windows" "x86_64-windows" ]; testunix = mseteq unix (linux ++ darwin ++ freebsd ++ openbsd ++ netbsd ++ illumos ++ cygwin ++ redox); +}) + +// { + test_equals_example_x86_64-linux = { + expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") (lib.systems.elaborate "x86_64-linux"); + expected = true; + }; + + test_toLosslessStringMaybe_example_x86_64-linux = { + expr = lib.systems.toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux"); + expected = "x86_64-linux"; + }; + test_toLosslessStringMaybe_fail = { + expr = lib.systems.toLosslessStringMaybe (lib.systems.elaborate "x86_64-linux" // { something = "extra"; }); + expected = null; + }; } + +# Generate test cases to assert that a change in any non-function attribute makes a platform unequal +// lib.concatMapAttrs (platformAttrName: origValue: { + + ${"test_equals_unequal_${platformAttrName}"} = + let modified = + assert origValue != arbitraryValue; + lib.systems.elaborate "x86_64-linux" // { ${platformAttrName} = arbitraryValue; }; + arbitraryValue = x: "<>"; + in { + expr = lib.systems.equals (lib.systems.elaborate "x86_64-linux") modified; + expected = { + # Changes in these attrs are not detectable because they're function. + # The functions should be derived from the data, so this is not a problem. + canExecute = null; + emulator = null; + emulatorAvailable = null; + isCompatible = null; + }?${platformAttrName}; + }; + +}) (lib.systems.elaborate "x86_64-linux" /* arbitrary choice, just to get all the elaborated attrNames */) + +)