1/*
2 Nix expression to test for regressions in the Haskell configuration overlays.
3
4 test-configurations.nix determines all attributes touched by given Haskell
5 configuration overlays (i. e. pkgs/development/haskell-modules/configuration-*.nix)
6 and builds all derivations (or at least a reasonable subset) affected by
7 these overrides.
8
9 By default, it checks `configuration-{common,nix,ghc-9.8.x}.nix`. You can
10 invoke it like this:
11
12 nix-build maintainers/scripts/haskell/test-configurations.nix --keep-going
13
14 It is possible to specify other configurations:
15
16 nix-build maintainers/scripts/haskell/test-configurations.nix \
17 --arg files '[ "configuration-ghc-9.0.x.nix" "configuration-ghc-9.2.x.nix" ]' \
18 --keep-going
19
20 You can also just supply a single string:
21
22 nix-build maintainers/scripts/haskell/test-configurations.nix \
23 --argstr files "configuration-arm.nix" --keep-going
24
25 You can even supply full paths which is handy, as it allows for tab-completing
26 the configurations:
27
28 nix-build maintainers/scripts/haskell/test-configurations.nix \
29 --argstr files pkgs/development/haskell-modules/configuration-arm.nix \
30 --keep-going
31
32 By default, derivation that fail to evaluate are skipped, unless they are
33 “just” marked as broken. You can check for other eval errors like this:
34
35 nix-build maintainers/scripts/haskell/test-configurations.nix \
36 --arg skipEvalErrors false --keep-going
37
38 You can also disable checking broken packages by passing a nixpkgs config:
39
40 nix-build maintainers/scripts/haskell/test-configurations.nix \
41 --arg config '{ allowBroken = false; }' --keep-going
42
43 By default the haskell.packages.ghc*Binary sets used for bootstrapping GHC
44 are _not_ tested. You can change this using:
45
46 nix-build maintainers/scripts/haskell/test-configurations.nix \
47 --arg skipBinaryGHCs false --keep-going
48*/
49{
50 files ? [
51 "configuration-common.nix"
52 "configuration-nix.nix"
53 "configuration-ghc-9.8.x.nix"
54 ],
55 nixpkgsPath ? ../../..,
56 config ? {
57 allowBroken = true;
58 },
59 skipEvalErrors ? true,
60 skipBinaryGHCs ? true,
61}:
62
63let
64 pkgs = import nixpkgsPath { inherit config; };
65 inherit (pkgs) lib;
66
67 # see usage explanation for the input format `files` allows
68 files' = builtins.map builtins.baseNameOf (if !builtins.isList files then [ files ] else files);
69
70 packageSetsWithVersionedHead =
71 pkgs.haskell.packages
72 // (
73 let
74 headSet = pkgs.haskell.packages.ghcHEAD;
75 # Determine the next GHC release version following GHC HEAD.
76 # GHC HEAD always has an uneven, tentative version number, e.g. 9.7.
77 # GHC releases always have even numbers, i.e. GHC 9.8 is branched off from
78 # GHC HEAD 9.7. Since we use the to be release number for GHC HEAD's
79 # configuration file, we need to calculate this here.
80 headVersion = lib.pipe headSet.ghc.version [
81 lib.versions.splitVersion
82 (lib.take 2)
83 lib.concatStrings
84 lib.strings.toInt
85 (builtins.add 1)
86 toString
87 ];
88 in
89 {
90 "ghc${headVersion}" = headSet;
91 }
92 );
93
94 setsForFile =
95 fileName:
96 let
97 # extract the unique part of the config's file name
98 configName = builtins.head (builtins.match "configuration-(.+).nix" fileName);
99 # match the major and minor version of the GHC the config is intended for, if any
100 configVersion = lib.concatStrings (builtins.match "ghc-([0-9]+).([0-9]+).x" configName);
101 # return all package sets under haskell.packages matching the version components
102 setsForVersion = builtins.map (name: packageSetsWithVersionedHead.${name}) (
103 builtins.filter (
104 setName:
105 lib.hasPrefix "ghc${configVersion}" setName && (skipBinaryGHCs -> !(lib.hasInfix "Binary" setName))
106 ) (builtins.attrNames packageSetsWithVersionedHead)
107 );
108
109 defaultSets = [ pkgs.haskellPackages ];
110 in
111 {
112 # use plain haskellPackages for the version-agnostic files
113 # TODO(@sternenseemann): also consider currently selected versioned sets
114 "common" = defaultSets;
115 "nix" = defaultSets;
116 "arm" = defaultSets;
117 "darwin" = defaultSets;
118 }
119 .${configName} or setsForVersion;
120
121 # attribute set that has all the attributes of haskellPackages set to null
122 availableHaskellPackages = builtins.listToAttrs (
123 builtins.map (attr: lib.nameValuePair attr null) (builtins.attrNames pkgs.haskellPackages)
124 );
125
126 # evaluate a configuration and only return the attributes changed by it,
127 # pass availableHaskellPackages as super in case intersectAttrs is used
128 overriddenAttrs =
129 fileName:
130 builtins.attrNames (
131 lib.fix (
132 self:
133 import (nixpkgsPath + "/pkgs/development/haskell-modules/${fileName}") {
134 haskellLib = pkgs.haskell.lib.compose;
135 inherit pkgs;
136 } self availableHaskellPackages
137 )
138 );
139
140 # list of derivations that are affected by overrides in the given configuration
141 # overlays. For common, nix, darwin etc. only the derivation from the default
142 # package set will be emitted.
143 packages =
144 builtins.filter
145 (
146 v:
147 lib.warnIf (v.meta.broken or false) "${v.pname} is marked as broken" (
148 v != null && (skipEvalErrors -> (builtins.tryEval (v.outPath or v)).success)
149 )
150 )
151 (
152 lib.concatMap (
153 fileName:
154 let
155 sets = setsForFile fileName;
156 attrs = overriddenAttrs fileName;
157 in
158 lib.concatMap (set: builtins.map (attr: set.${attr}) attrs) sets
159 ) files'
160 );
161in
162
163packages