at 18.03-beta 16 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 // rec { 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 # predecessors: proposed replacement for applyAndFun (which has a bug cause it merges twice) 39 # the naming "overridableDelayableArgs" tries to express that you can 40 # - override attr values which have been supplied earlier 41 # - use attr values before they have been supplied by accessing the fix point 42 # name "fixed" 43 # f: the (delayed overridden) arguments are applied to this 44 # 45 # initial: initial attrs arguments and settings. see defaultOverridableDelayableArgs 46 # 47 # returns: f applied to the arguments // special attributes attrs 48 # a) merge: merge applied args with new args. Wether an argument is overridden depends on the merge settings 49 # b) replace: this let's you replace and remove names no matter which merge function has been set 50 # 51 # examples: see test cases "res" below; 52 overridableDelayableArgs = 53 f: # the function applied to the arguments 54 initial: # you pass attrs, the functions below are passing a function taking the fix argument 55 let 56 takeFixed = if lib.isFunction initial then initial else (fixed : initial); # transform initial to an expression always taking the fixed argument 57 tidy = args: 58 let # apply all functions given in "applyPreTidy" in sequence 59 applyPreTidyFun = fold ( n: a: x: n ( a x ) ) lib.id (maybeAttr "applyPreTidy" [] args); 60 in removeAttrs (applyPreTidyFun args) ( ["applyPreTidy"] ++ (maybeAttr "removeAttrs" [] args) ); # tidy up args before applying them 61 fun = n: x: 62 let newArgs = fixed: 63 let args = takeFixed fixed; 64 mergeFun = args.${n}; 65 in if isAttrs x then (mergeFun args x) 66 else assert lib.isFunction x; 67 mergeFun args (x ( args // { inherit fixed; })); 68 in overridableDelayableArgs f newArgs; 69 in 70 (f (tidy (lib.fix takeFixed))) // { 71 merge = fun "mergeFun"; 72 replace = fun "keepFun"; 73 }; 74 defaultOverridableDelayableArgs = f: 75 let defaults = { 76 mergeFun = mergeAttrByFunc; # default merge function. merge strategie (concatenate lists, strings) is given by mergeAttrBy 77 keepFun = a: b: { inherit (a) removeAttrs mergeFun keepFun mergeAttrBy; } // b; # even when using replace preserve these values 78 applyPreTidy = []; # list of functions applied to args before args are tidied up (usage case : prepareDerivationArgs) 79 mergeAttrBy = mergeAttrBy // { 80 applyPreTidy = a: b: a ++ b; 81 removeAttrs = a: b: a ++ b; 82 }; 83 removeAttrs = ["mergeFun" "keepFun" "mergeAttrBy" "removeAttrs" "fixed" ]; # before applying the arguments to the function make sure these names are gone 84 }; 85 in (overridableDelayableArgs f defaults).merge; 86 87 88 89 # rec { # an example of how composedArgsAndFun can be used 90 # a = composedArgsAndFun (x: x) { a = ["2"]; meta = { d = "bar";}; }; 91 # # meta.d will be lost ! It's your task to preserve it (eg using a merge function) 92 # b = a.passthru.function { a = [ "3" ]; meta = { d2 = "bar2";}; }; 93 # # instead of passing/ overriding values you can use a merge function: 94 # c = b.passthru.function ( x: { a = x.a ++ ["4"]; }); # consider using (maybeAttr "a" [] x) 95 # } 96 # result: 97 # { 98 # a = { a = ["2"]; meta = { d = "bar"; }; passthru = { function = .. }; }; 99 # b = { a = ["3"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; }; 100 # c = { a = ["3" "4"]; meta = { d2 = "bar2"; }; passthru = { function = .. }; }; 101 # # c2 is equal to c 102 # } 103 composedArgsAndFun = f: foldArgs defaultMerge f {}; 104 105 106 # shortcut for attrByPath ["name"] default attrs 107 maybeAttrNullable = maybeAttr; 108 109 # shortcut for attrByPath ["name"] default attrs 110 maybeAttr = name: default: attrs: attrs.${name} or default; 111 112 113 # Return the second argument if the first one is true or the empty version 114 # of the second argument. 115 ifEnable = cond: val: 116 if cond then val 117 else if builtins.isList val then [] 118 else if builtins.isAttrs val then {} 119 # else if builtins.isString val then "" 120 else if val == true || val == false then false 121 else null; 122 123 124 # Return true only if there is an attribute and it is true. 125 checkFlag = attrSet: name: 126 if name == "true" then true else 127 if name == "false" then false else 128 if (elem name (attrByPath ["flags"] [] attrSet)) then true else 129 attrByPath [name] false attrSet ; 130 131 132 # Input : attrSet, [ [name default] ... ], name 133 # Output : its value or default. 134 getValue = attrSet: argList: name: 135 ( attrByPath [name] (if checkFlag attrSet name then true else 136 if argList == [] then null else 137 let x = builtins.head argList; in 138 if (head x) == name then 139 (head (tail x)) 140 else (getValue attrSet 141 (tail argList) name)) attrSet ); 142 143 144 # Input : attrSet, [[name default] ...], [ [flagname reqs..] ... ] 145 # Output : are reqs satisfied? It's asserted. 146 checkReqs = attrSet: argList: condList: 147 ( 148 fold lib.and true 149 (map (x: let name = (head x); in 150 151 ((checkFlag attrSet name) -> 152 (fold lib.and true 153 (map (y: let val=(getValue attrSet argList y); in 154 (val!=null) && (val!=false)) 155 (tail x))))) condList)); 156 157 158 # This function has O(n^2) performance. 159 uniqList = { inputList, acc ? [] }: 160 let go = xs: acc: 161 if xs == [] 162 then [] 163 else let x = head xs; 164 y = if elem x acc then [] else [x]; 165 in y ++ go (tail xs) (y ++ acc); 166 in go inputList acc; 167 168 uniqListExt = { inputList, 169 outputList ? [], 170 getter ? (x: x), 171 compare ? (x: y: x==y) }: 172 if inputList == [] then outputList else 173 let x = head inputList; 174 isX = y: (compare (getter y) (getter x)); 175 newOutputList = outputList ++ 176 (if any isX outputList then [] else [x]); 177 in uniqListExt { outputList = newOutputList; 178 inputList = (tail inputList); 179 inherit getter compare; 180 }; 181 182 condConcat = name: list: checker: 183 if list == [] then name else 184 if checker (head list) then 185 condConcat 186 (name + (head (tail list))) 187 (tail (tail list)) 188 checker 189 else condConcat 190 name (tail (tail list)) checker; 191 192 lazyGenericClosure = {startSet, operator}: 193 let 194 work = list: doneKeys: result: 195 if list == [] then 196 result 197 else 198 let x = head list; key = x.key; in 199 if elem key doneKeys then 200 work (tail list) doneKeys result 201 else 202 work (tail list ++ operator x) ([key] ++ doneKeys) ([x] ++ result); 203 in 204 work startSet [] []; 205 206 innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else 207 innerModifySumArgs f x (a // b); 208 modifySumArgs = f: x: innerModifySumArgs f x {}; 209 210 211 innerClosePropagation = acc: xs: 212 if xs == [] 213 then acc 214 else let y = head xs; 215 ys = tail xs; 216 in if ! isAttrs y 217 then innerClosePropagation acc ys 218 else let acc' = [y] ++ acc; 219 in innerClosePropagation 220 acc' 221 (uniqList { inputList = (maybeAttrNullable "propagatedBuildInputs" [] y) 222 ++ (maybeAttrNullable "propagatedNativeBuildInputs" [] y) 223 ++ ys; 224 acc = acc'; 225 } 226 ); 227 228 closePropagation = list: (uniqList {inputList = (innerClosePropagation [] list);}); 229 230 # calls a function (f attr value ) for each record item. returns a list 231 mapAttrsFlatten = f: r: map (attr: f attr r.${attr}) (attrNames r); 232 233 # attribute set containing one attribute 234 nvs = name: value: listToAttrs [ (nameValuePair name value) ]; 235 # adds / replaces an attribute of an attribute set 236 setAttr = set: name: v: set // (nvs name v); 237 238 # setAttrMerge (similar to mergeAttrsWithFunc but only merges the values of a particular name) 239 # setAttrMerge "a" [] { a = [2];} (x: x ++ [3]) -> { a = [2 3]; } 240 # setAttrMerge "a" [] { } (x: x ++ [3]) -> { a = [ 3]; } 241 setAttrMerge = name: default: attrs: f: 242 setAttr attrs name (f (maybeAttr name default attrs)); 243 244 # Using f = a: b = b the result is similar to // 245 # merge attributes with custom function handling the case that the attribute 246 # exists in both sets 247 mergeAttrsWithFunc = f: set1: set2: 248 fold (n: set: if set ? ${n} 249 then setAttr set n (f set.${n} set2.${n}) 250 else set ) 251 (set2 // set1) (attrNames set2); 252 253 # merging two attribute set concatenating the values of same attribute names 254 # eg { a = 7; } { a = [ 2 3 ]; } becomes { a = [ 7 2 3 ]; } 255 mergeAttrsConcatenateValues = mergeAttrsWithFunc ( a: b: (toList a) ++ (toList b) ); 256 257 # merges attributes using //, if a name exists in both attributes 258 # an error will be triggered unless its listed in mergeLists 259 # so you can mergeAttrsNoOverride { buildInputs = [a]; } { buildInputs = [a]; } {} to get 260 # { buildInputs = [a b]; } 261 # merging buildPhase doesn't really make sense. The cases will be rare where appending /prefixing will fit your needs? 262 # in these cases the first buildPhase will override the second one 263 # ! deprecated, use mergeAttrByFunc instead 264 mergeAttrsNoOverride = { mergeLists ? ["buildInputs" "propagatedBuildInputs"], 265 overrideSnd ? [ "buildPhase" ] 266 }: attrs1: attrs2: 267 fold (n: set: 268 setAttr set n ( if set ? ${n} 269 then # merge 270 if elem n mergeLists # attribute contains list, merge them by concatenating 271 then attrs2.${n} ++ attrs1.${n} 272 else if elem n overrideSnd 273 then attrs1.${n} 274 else throw "error mergeAttrsNoOverride, attribute ${n} given in both attributes - no merge func defined" 275 else attrs2.${n} # add attribute not existing in attr1 276 )) attrs1 (attrNames attrs2); 277 278 279 # example usage: 280 # mergeAttrByFunc { 281 # inherit mergeAttrBy; # defined below 282 # buildInputs = [ a b ]; 283 # } { 284 # buildInputs = [ c d ]; 285 # }; 286 # will result in 287 # { mergeAttrsBy = [...]; buildInputs = [ a b c d ]; } 288 # is used by prepareDerivationArgs, defaultOverridableDelayableArgs and can be used when composing using 289 # foldArgs, composedArgsAndFun or applyAndFun. Example: composableDerivation in all-packages.nix 290 mergeAttrByFunc = x: y: 291 let 292 mergeAttrBy2 = { mergeAttrBy = lib.mergeAttrs; } 293 // (maybeAttr "mergeAttrBy" {} x) 294 // (maybeAttr "mergeAttrBy" {} y); in 295 fold lib.mergeAttrs {} [ 296 x y 297 (mapAttrs ( a: v: # merge special names using given functions 298 if x ? ${a} 299 then if y ? ${a} 300 then v x.${a} y.${a} # both have attr, use merge func 301 else x.${a} # only x has attr 302 else y.${a} # only y has attr) 303 ) (removeAttrs mergeAttrBy2 304 # don't merge attrs which are neither in x nor y 305 (filter (a: ! x ? ${a} && ! y ? ${a}) 306 (attrNames mergeAttrBy2)) 307 ) 308 ) 309 ]; 310 mergeAttrsByFuncDefaults = foldl mergeAttrByFunc { inherit mergeAttrBy; }; 311 mergeAttrsByFuncDefaultsClean = list: removeAttrs (mergeAttrsByFuncDefaults list) ["mergeAttrBy"]; 312 313 # sane defaults (same name as attr name so that inherit can be used) 314 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; } 315 listToAttrs (map (n: nameValuePair n lib.concat) 316 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ]) 317 // listToAttrs (map (n: nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ]) 318 // listToAttrs (map (n: nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ]) 319 ; 320 321 # prepareDerivationArgs tries to make writing configurable derivations easier 322 # example: 323 # prepareDerivationArgs { 324 # mergeAttrBy = { 325 # myScript = x: y: x ++ "\n" ++ y; 326 # }; 327 # cfg = { 328 # readlineSupport = true; 329 # }; 330 # flags = { 331 # readline = { 332 # set = { 333 # configureFlags = [ "--with-compiler=${compiler}" ]; 334 # buildInputs = [ compiler ]; 335 # pass = { inherit compiler; READLINE=1; }; 336 # assertion = compiler.dllSupport; 337 # myScript = "foo"; 338 # }; 339 # unset = { configureFlags = ["--without-compiler"]; }; 340 # }; 341 # }; 342 # src = ... 343 # buildPhase = '' ... ''; 344 # name = ... 345 # myScript = "bar"; 346 # }; 347 # if you don't have need for unset you can omit the surrounding set = { .. } attr 348 # all attrs except flags cfg and mergeAttrBy will be merged with the 349 # additional data from flags depending on config settings 350 # It's used in composableDerivation in all-packages.nix. It's also used 351 # heavily in the new python and libs implementation 352 # 353 # should we check for misspelled cfg options? 354 # TODO use args.mergeFun here as well? 355 prepareDerivationArgs = args: 356 let args2 = { cfg = {}; flags = {}; } // args; 357 flagName = name: "${name}Support"; 358 cfgWithDefaults = (listToAttrs (map (n: nameValuePair (flagName n) false) (attrNames args2.flags))) 359 // args2.cfg; 360 opts = attrValues (mapAttrs (a: v: 361 let v2 = if v ? set || v ? unset then v else { set = v; }; 362 n = if cfgWithDefaults.${flagName a} then "set" else "unset"; 363 attr = maybeAttr n {} v2; in 364 if (maybeAttr "assertion" true attr) 365 then attr 366 else throw "assertion of flag ${a} of derivation ${args.name} failed" 367 ) args2.flags ); 368 in removeAttrs 369 (mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }])) 370 ["flags" "cfg" "mergeAttrBy" ]; 371 372 373 nixType = x: 374 if isAttrs x then 375 if x ? outPath then "derivation" 376 else "attrs" 377 else if lib.isFunction x then "function" 378 else if isList x then "list" 379 else if x == true then "bool" 380 else if x == false then "bool" 381 else if x == null then "null" 382 else if isInt x then "int" 383 else "string"; 384 385 /* deprecated: 386 387 For historical reasons, imap has an index starting at 1. 388 389 But for consistency with the rest of the library we want an index 390 starting at zero. 391 */ 392 imap = imap1; 393}