Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

394 строки
16 KiB

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