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