at 15.09-beta 17 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, outputList ? [], 168 getter ? (x : x), compare ? (x: y: x==y)}: 169 if inputList == [] then outputList else 170 let x=head inputList; 171 isX = y: (compare (getter y) (getter x)); 172 newOutputList = outputList ++ 173 (if any isX outputList then [] else [x]); 174 in uniqListExt {outputList=newOutputList; 175 inputList = (tail inputList); 176 inherit getter compare; 177 }; 178 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 exisits 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 does'nt 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 # merge attrs based on version key into mkDerivation args, see mergeAttrBy to learn about smart merge defaults 313 # 314 # This function is best explained by an example: 315 # 316 # {version ? "2.x"} : 317 # 318 # mkDerivation (mergeAttrsByVersion "package-name" version 319 # { # version specific settings 320 # "git" = { src = ..; preConfigre = "autogen.sh"; buildInputs = [automake autoconf libtool]; }; 321 # "2.x" = { src = ..; }; 322 # } 323 # { // shared settings 324 # buildInputs = [ common build inputs ]; 325 # meta = { .. } 326 # } 327 # ) 328 # 329 # Please note that e.g. Eelco Dolstra usually prefers having one file for 330 # each version. On the other hand there are valuable additional design goals 331 # - readability 332 # - do it once only 333 # - try to avoid duplication 334 # 335 # Marc Weber and Michael Raskin sometimes prefer keeping older 336 # versions around for testing and regression tests - as long as its cheap to 337 # do so. 338 # 339 # Very often it just happens that the "shared" code is the bigger part. 340 # Then using this function might be appropriate. 341 # 342 # Be aware that its easy to cause recompilations in all versions when using 343 # this function - also if derivations get too complex splitting into multiple 344 # files is the way to go. 345 # 346 # See misc.nix -> versionedDerivation 347 # discussion: nixpkgs: pull/310 348 mergeAttrsByVersion = name: version: attrsByVersion: base: 349 mergeAttrsByFuncDefaultsClean [ { name = "${name}-${version}"; } base (maybeAttr version (throw "bad version ${version} for ${name}") attrsByVersion)]; 350 351 # sane defaults (same name as attr name so that inherit can be used) 352 mergeAttrBy = # { buildInputs = concatList; [...]; passthru = mergeAttr; [..]; } 353 listToAttrs (map (n : nameValuePair n lib.concat) 354 [ "nativeBuildInputs" "buildInputs" "propagatedBuildInputs" "configureFlags" "prePhases" "postAll" "patches" ]) 355 // listToAttrs (map (n : nameValuePair n lib.mergeAttrs) [ "passthru" "meta" "cfg" "flags" ]) 356 // listToAttrs (map (n : nameValuePair n (a: b: "${a}\n${b}") ) [ "preConfigure" "postInstall" ]) 357 ; 358 359 # prepareDerivationArgs tries to make writing configurable derivations easier 360 # example: 361 # prepareDerivationArgs { 362 # mergeAttrBy = { 363 # myScript = x : y : x ++ "\n" ++ y; 364 # }; 365 # cfg = { 366 # readlineSupport = true; 367 # }; 368 # flags = { 369 # readline = { 370 # set = { 371 # configureFlags = [ "--with-compiler=${compiler}" ]; 372 # buildInputs = [ compiler ]; 373 # pass = { inherit compiler; READLINE=1; }; 374 # assertion = compiler.dllSupport; 375 # myScript = "foo"; 376 # }; 377 # unset = { configureFlags = ["--without-compiler"]; }; 378 # }; 379 # }; 380 # src = ... 381 # buildPhase = '' ... ''; 382 # name = ... 383 # myScript = "bar"; 384 # }; 385 # if you don't have need for unset you can omit the surrounding set = { .. } attr 386 # all attrs except flags cfg and mergeAttrBy will be merged with the 387 # additional data from flags depending on config settings 388 # It's used in composableDerivation in all-packages.nix. It's also used 389 # heavily in the new python and libs implementation 390 # 391 # should we check for misspelled cfg options? 392 # TODO use args.mergeFun here as well? 393 prepareDerivationArgs = args: 394 let args2 = { cfg = {}; flags = {}; } // args; 395 flagName = name : "${name}Support"; 396 cfgWithDefaults = (listToAttrs (map (n : nameValuePair (flagName n) false) (attrNames args2.flags))) 397 // args2.cfg; 398 opts = attrValues (mapAttrs (a : v : 399 let v2 = if v ? set || v ? unset then v else { set = v; }; 400 n = if cfgWithDefaults.${flagName a} then "set" else "unset"; 401 attr = maybeAttr n {} v2; in 402 if (maybeAttr "assertion" true attr) 403 then attr 404 else throw "assertion of flag ${a} of derivation ${args.name} failed" 405 ) args2.flags ); 406 in removeAttrs 407 (mergeAttrsByFuncDefaults ([args] ++ opts ++ [{ passthru = cfgWithDefaults; }])) 408 ["flags" "cfg" "mergeAttrBy" ]; 409 410 411 nixType = x: 412 if isAttrs x then 413 if x ? outPath then "derivation" 414 else "aattrs" 415 else if isFunction x then "function" 416 else if isList x then "list" 417 else if x == true then "bool" 418 else if x == false then "bool" 419 else if x == null then "null" 420 else if isInt x then "int" 421 else "string"; 422 423}