at 23.05-pre 12 kB view raw
1{ lib }: 2let 3 inherit (builtins) head tail isList isAttrs isInt attrNames; 4 5in 6 7with lib.lists; 8with lib.attrsets; 9with lib.strings; 10 11rec { 12 13 # returns default if env var is not set 14 maybeEnv = name: default: 15 let value = builtins.getEnv name; in 16 if value == "" then default else value; 17 18 defaultMergeArg = x : y: if builtins.isAttrs y then 19 y 20 else 21 (y x); 22 defaultMerge = x: y: x // (defaultMergeArg x y); 23 foldArgs = merger: f: init: x: 24 let arg = (merger init (defaultMergeArg init x)); 25 # now add the function with composed args already applied to the final attrs 26 base = (setAttrMerge "passthru" {} (f arg) 27 ( z: z // { 28 function = foldArgs merger f arg; 29 args = (lib.attrByPath ["passthru" "args"] {} z) // x; 30 } )); 31 withStdOverrides = base // { 32 override = base.passthru.function; 33 }; 34 in 35 withStdOverrides; 36 37 38 # shortcut for attrByPath ["name"] default attrs 39 maybeAttrNullable = maybeAttr; 40 41 # shortcut for attrByPath ["name"] default attrs 42 maybeAttr = name: default: attrs: attrs.${name} or default; 43 44 45 # Return the second argument if the first one is true or the empty version 46 # of the second argument. 47 ifEnable = cond: val: 48 if cond then val 49 else if builtins.isList val then [] 50 else if builtins.isAttrs val then {} 51 # else if builtins.isString val then "" 52 else if val == true || val == false then false 53 else null; 54 55 56 # Return true only if there is an attribute and it is true. 57 checkFlag = attrSet: name: 58 if name == "true" then true else 59 if name == "false" then false else 60 if (elem name (attrByPath ["flags"] [] attrSet)) then true else 61 attrByPath [name] false attrSet ; 62 63 64 # Input : attrSet, [ [name default] ... ], name 65 # Output : its value or default. 66 getValue = attrSet: argList: name: 67 ( attrByPath [name] (if checkFlag attrSet name then true else 68 if argList == [] then null else 69 let x = builtins.head argList; in 70 if (head x) == name then 71 (head (tail x)) 72 else (getValue attrSet 73 (tail argList) name)) attrSet ); 74 75 76 # Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ] 77 # Output : are reqs satisfied? It's asserted. 78 checkReqs = attrSet: argList: condList: 79 ( 80 foldr lib.and true 81 (map (x: let name = (head x); in 82 83 ((checkFlag attrSet name) -> 84 (foldr lib.and true 85 (map (y: let val=(getValue attrSet argList y); in 86 (val!=null) && (val!=false)) 87 (tail x))))) condList)); 88 89 90 # This function has O(n^2) performance. 91 uniqList = { inputList, acc ? [] }: 92 let go = xs: acc: 93 if xs == [] 94 then [] 95 else let x = head xs; 96 y = if elem x acc then [] else [x]; 97 in y ++ go (tail xs) (y ++ acc); 98 in go inputList acc; 99 100 uniqListExt = { inputList, 101 outputList ? [], 102 getter ? (x: x), 103 compare ? (x: y: x==y) }: 104 if inputList == [] then outputList else 105 let x = head inputList; 106 isX = y: (compare (getter y) (getter x)); 107 newOutputList = outputList ++ 108 (if any isX outputList then [] else [x]); 109 in uniqListExt { outputList = newOutputList; 110 inputList = (tail inputList); 111 inherit getter compare; 112 }; 113 114 condConcat = name: list: checker: 115 if list == [] then name else 116 if checker (head list) then 117 condConcat 118 (name + (head (tail list))) 119 (tail (tail list)) 120 checker 121 else condConcat 122 name (tail (tail list)) checker; 123 124 lazyGenericClosure = {startSet, operator}: 125 let 126 work = list: doneKeys: result: 127 if list == [] then 128 result 129 else 130 let x = head list; key = x.key; in 131 if elem key doneKeys then 132 work (tail list) doneKeys result 133 else 134 work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result); 135 in 136 work startSet [] []; 137 138 innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else 139 innerModifySumArgs f x (a // b); 140 modifySumArgs = f: x: innerModifySumArgs f x {}; 141 142 143 innerClosePropagation = acc: xs: 144 if xs == [] 145 then acc 146 else let y = head xs; 147 ys = tail xs; 148 in if ! isAttrs y 149 then innerClosePropagation acc ys 150 else let acc' = [y] ++ acc; 151 in innerClosePropagation 152 acc' 153 (uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y) 154 ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y) 155 ++ ys; 156 acc = acc'; 157 } 158 ); 159 160 closePropagationSlow = list: (uniqList {inputList = (innerClosePropagation [] list);}); 161 162 # This is an optimisation of lib.closePropagation which avoids the O(n^2) behavior 163 # Using a list of derivations, it generates the full closure of the propagatedXXXBuildInputs 164 # The ordering / sorting / comparison is done based on the `outPath` 165 # attribute of each derivation. 166 # On some benchmarks, it performs up to 15 times faster than lib.closePropagation. 167 # See https://github.com/NixOS/nixpkgs/pull/194391 for details. 168 closePropagationFast = list: 169 builtins.map (x: x.val) (builtins.genericClosure { 170 startSet = builtins.map (x: { 171 key = x.outPath; 172 val = x; 173 }) (builtins.filter (x: x != null) list); 174 operator = item: 175 if !builtins.isAttrs item.val then 176 [ ] 177 else 178 builtins.concatMap (x: 179 if x != null then [{ 180 key = x.outPath; 181 val = x; 182 }] else 183 [ ]) ((item.val.propagatedBuildInputs or [ ]) 184 ++ (item.val.propagatedNativeBuildInputs or [ ])); 185 }); 186 187 closePropagation = if builtins ? genericClosure 188 then closePropagationFast 189 else closePropagationSlow; 190 191 # calls a function (f attr value ) for each record item. returns a list 192 mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r); 193 194 # attribute set containing one attribute 195 nvs = name: value: listToAttrs [ (nameValuePair name value) ]; 196 # adds / replaces an attribute of an attribute set 197 setAttr = set: name: v: set // (nvs name v); 198 199 # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name) 200 # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; } 201 # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; } 202 setAttrMerge = name: default: attrs: f: 203 setAttr attrs name (f (maybeAttr name default attrs)); 204 205 # Using f = a: b = b the result is similar to // 206 # merge attributes with custom function handling the case that the attribute 207 # exists in both sets 208 mergeAttrsWithFunc = f: set1: set2: 209 foldr (n: set: if set ? ${n} 210 then setAttr set n (f set.${n} set2.${n}) 211 else set ) 212 (set2 // set1) (attrNames set2); 213 214 # merging two attribute set concatenating the values of same attribute names 215 # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; } 216 mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) ); 217 218 # merges attributes using //, if a name exists in both attributes 219 # an error will be triggered unless its listed in mergeLists 220 # so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get 221 # { buildInputs = [a b]; } 222 # merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs? 223 # in these cases the first buildPhase will override the second one 224 # ! deprecated, use mergeAttrByFunc instead 225 mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"], 226 overrideSnd ? [ "buildPhase" ] 227 }: attrs1: attrs2: 228 foldr (n: set: 229 setAttr set n ( if set ? ${n} 230 then # merge 231 if elem n mergeLists # attribute contains list, merge them by concatenating 232 then attrs2.${n} ++ attrs1.${n} 233 else if elem n overrideSnd 234 then attrs1.${n} 235 else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined" 236 else attrs2.${n} # add attribute not existing in attr1 237 )) attrs1 (attrNames attrs2); 238 239 240 # example usage: 241 # mergeAttrByFunc { 242 # inherit mergeAttrBy; # defined below 243 # buildInputs = [ a b ]; 244 # } { 245 # buildInputs = [ c d ]; 246 # }; 247 # will result in 248 # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; } 249 # is used by defaultOverridableDelayableArgs and can be used when composing using 250 # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix 251 mergeAttrByFunc = x: y: 252 let 253 mergeAttrBy2 = { mergeAttrBy = lib.mergeAttrs; } 254 // (maybeAttr "mergeAttrBy" {} x) 255 // (maybeAttr "mergeAttrBy" {} y); in 256 foldr lib.mergeAttrs {} [ 257 x y 258 (mapAttrs ( a: v: # merge special names using given functions 259 if x ? ${a} 260 then if y ? ${a} 261 then v x.${a} y.${a} # both have attr, use merge func 262 else x.${a} # only x has attr 263 else y.${a} # only y has attr) 264 ) (removeAttrs mergeAttrBy2 265 # don't merge attrs which are neither in x nor y 266 (filter (a: ! x ? ${a} && ! y ? ${a}) 267 (attrNames mergeAttrBy2)) 268 ) 269 ) 270 ]; 271 mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; }; 272 mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"]; 273 274 # sane defaults (same name as attr name so that inherit can be used) 275 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; } 276 listToAttrs (map (n: nameValuePair n lib.concat) 277 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ]) 278 // listToAttrs (map (n: nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ]) 279 // listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ]) 280 ; 281 282 nixType = x: 283 if isAttrs x then 284 if x ? outPath then "derivation" 285 else "attrs" 286 else if lib.isFunction x then "function" 287 else if isList x then "list" 288 else if x == true then "bool" 289 else if x == false then "bool" 290 else if x == null then "null" 291 else if isInt x then "int" 292 else "string"; 293 294 /* deprecated: 295 296 For historical reasons, imap has an index starting at 1. 297 298 But for consistency with the rest of the library we want an index 299 starting at zero. 300 */ 301 imap = imap1; 302 303 # Fake hashes. Can be used as hash placeholders, when computing hash ahead isn't trivial 304 fakeHash = "sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="; 305 fakeSha256 = "0000000000000000000000000000000000000000000000000000000000000000"; 306 fakeSha512 = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; 307}