Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
1{ lib }: 2 3rec { 4 5 6 /* `overrideDerivation drv f` takes a derivation (i.e., the result 7 of a call to the builtin function `derivation`) and returns a new 8 derivation in which the attributes of the original are overridden 9 according to the function `f`. The function `f` is called with 10 the original derivation attributes. 11 12 `overrideDerivation` allows certain "ad-hoc" customisation 13 scenarios (e.g. in ~/.config/nixpkgs/config.nix). For instance, 14 if you want to "patch" the derivation returned by a package 15 function in Nixpkgs to build another version than what the 16 function itself provides, you can do something like this: 17 18 mySed = overrideDerivation pkgs.gnused (oldAttrs: { 19 name = "sed-4.2.2-pre"; 20 src = fetchurl { 21 url = ftp://alpha.gnu.org/gnu/sed/sed-4.2.2-pre.tar.bz2; 22 sha256 = "11nq06d131y4wmf3drm0yk502d2xc6n5qy82cg88rb9nqd2lj41k"; 23 }; 24 patches = []; 25 }); 26 27 For another application, see build-support/vm, where this 28 function is used to build arbitrary derivations inside a QEMU 29 virtual machine. 30 31 Note that in order to preserve evaluation errors, the new derivation's 32 outPath depends on the old one's, which means that this function cannot 33 be used in circular situations when the old derivation also depends on the 34 new one. 35 36 You should in general prefer `drv.overrideAttrs` over this function; 37 see the nixpkgs manual for more information on overriding. 38 */ 39 overrideDerivation = drv: f: 40 let 41 newDrv = derivation (drv.drvAttrs // (f drv)); 42 in lib.flip (extendDerivation (builtins.seq drv.drvPath true)) newDrv ( 43 { meta = drv.meta or {}; 44 passthru = if drv ? passthru then drv.passthru else {}; 45 } 46 // 47 (drv.passthru or {}) 48 // 49 # TODO(@Artturin): remove before release 23.05 and only have __spliced. 50 (lib.optionalAttrs (drv ? crossDrv && drv ? nativeDrv) { 51 crossDrv = overrideDerivation drv.crossDrv f; 52 nativeDrv = overrideDerivation drv.nativeDrv f; 53 }) 54 // 55 lib.optionalAttrs (drv ? __spliced) { 56 __spliced = {} // (lib.mapAttrs (_: sDrv: overrideDerivation sDrv f) drv.__spliced); 57 }); 58 59 60 /* `makeOverridable` takes a function from attribute set to attribute set and 61 injects `override` attribute which can be used to override arguments of 62 the function. 63 64 nix-repl> x = {a, b}: { result = a + b; } 65 66 nix-repl> y = lib.makeOverridable x { a = 1; b = 2; } 67 68 nix-repl> y 69 { override = «lambda»; overrideDerivation = «lambda»; result = 3; } 70 71 nix-repl> y.override { a = 10; } 72 { override = «lambda»; overrideDerivation = «lambda»; result = 12; } 73 74 Please refer to "Nixpkgs Contributors Guide" section 75 "<pkg>.overrideDerivation" to learn about `overrideDerivation` and caveats 76 related to its use. 77 */ 78 makeOverridable = f: origArgs: 79 let 80 result = f origArgs; 81 82 # Creates a functor with the same arguments as f 83 copyArgs = g: lib.setFunctionArgs g (lib.functionArgs f); 84 # Changes the original arguments with (potentially a function that returns) a set of new attributes 85 overrideWith = newArgs: origArgs // (if lib.isFunction newArgs then newArgs origArgs else newArgs); 86 87 # Re-call the function but with different arguments 88 overrideArgs = copyArgs (newArgs: makeOverridable f (overrideWith newArgs)); 89 # Change the result of the function call by applying g to it 90 overrideResult = g: makeOverridable (copyArgs (args: g (f args))) origArgs; 91 in 92 if builtins.isAttrs result then 93 result // { 94 override = overrideArgs; 95 overrideDerivation = fdrv: overrideResult (x: overrideDerivation x fdrv); 96 ${if result ? overrideAttrs then "overrideAttrs" else null} = fdrv: 97 overrideResult (x: x.overrideAttrs fdrv); 98 } 99 else if lib.isFunction result then 100 # Transform the result into a functor while propagating its arguments 101 lib.setFunctionArgs result (lib.functionArgs result) // { 102 override = overrideArgs; 103 } 104 else result; 105 106 107 /* Call the package function in the file `fn` with the required 108 arguments automatically. The function is called with the 109 arguments `args`, but any missing arguments are obtained from 110 `autoArgs`. This function is intended to be partially 111 parameterised, e.g., 112 113 callPackage = callPackageWith pkgs; 114 pkgs = { 115 libfoo = callPackage ./foo.nix { }; 116 libbar = callPackage ./bar.nix { }; 117 }; 118 119 If the `libbar` function expects an argument named `libfoo`, it is 120 automatically passed as an argument. Overrides or missing 121 arguments can be supplied in `args`, e.g. 122 123 libbar = callPackage ./bar.nix { 124 libfoo = null; 125 enableX11 = true; 126 }; 127 */ 128 callPackageWith = autoArgs: fn: args: 129 let 130 f = if lib.isFunction fn then fn else import fn; 131 fargs = lib.functionArgs f; 132 133 # All arguments that will be passed to the function 134 # This includes automatic ones and ones passed explicitly 135 allArgs = builtins.intersectAttrs fargs autoArgs // args; 136 137 # A list of argument names that the function requires, but 138 # wouldn't be passed to it 139 missingArgs = lib.attrNames 140 # Filter out arguments that have a default value 141 (lib.filterAttrs (name: value: ! value) 142 # Filter out arguments that would be passed 143 (removeAttrs fargs (lib.attrNames allArgs))); 144 145 # Get a list of suggested argument names for a given missing one 146 getSuggestions = arg: lib.pipe (autoArgs // args) [ 147 lib.attrNames 148 # Only use ones that are at most 2 edits away. While mork would work, 149 # levenshteinAtMost is only fast for 2 or less. 150 (lib.filter (lib.strings.levenshteinAtMost 2 arg)) 151 # Put strings with shorter distance first 152 (lib.sort (x: y: lib.strings.levenshtein x arg < lib.strings.levenshtein y arg)) 153 # Only take the first couple results 154 (lib.take 3) 155 # Quote all entries 156 (map (x: "\"" + x + "\"")) 157 ]; 158 159 prettySuggestions = suggestions: 160 if suggestions == [] then "" 161 else if lib.length suggestions == 1 then ", did you mean ${lib.elemAt suggestions 0}?" 162 else ", did you mean ${lib.concatStringsSep ", " (lib.init suggestions)} or ${lib.last suggestions}?"; 163 164 errorForArg = arg: 165 let 166 loc = builtins.unsafeGetAttrPos arg fargs; 167 # loc' can be removed once lib/minver.nix is >2.3.4, since that includes 168 # https://github.com/NixOS/nix/pull/3468 which makes loc be non-null 169 loc' = if loc != null then loc.file + ":" + toString loc.line 170 else if ! lib.isFunction fn then 171 toString fn + lib.optionalString (lib.sources.pathIsDirectory fn) "/default.nix" 172 else "<unknown location>"; 173 in "Function called without required argument \"${arg}\" at " 174 + "${loc'}${prettySuggestions (getSuggestions arg)}"; 175 176 # Only show the error for the first missing argument 177 error = errorForArg (lib.head missingArgs); 178 179 in if missingArgs == [] then makeOverridable f allArgs else abort error; 180 181 182 /* Like callPackage, but for a function that returns an attribute 183 set of derivations. The override function is added to the 184 individual attributes. */ 185 callPackagesWith = autoArgs: fn: args: 186 let 187 f = if lib.isFunction fn then fn else import fn; 188 auto = builtins.intersectAttrs (lib.functionArgs f) autoArgs; 189 origArgs = auto // args; 190 pkgs = f origArgs; 191 mkAttrOverridable = name: _: makeOverridable (newArgs: (f newArgs).${name}) origArgs; 192 in 193 if lib.isDerivation pkgs then throw 194 ("function `callPackages` was called on a *single* derivation " 195 + ''"${pkgs.name or "<unknown-name>"}";'' 196 + " did you mean to use `callPackage` instead?") 197 else lib.mapAttrs mkAttrOverridable pkgs; 198 199 200 /* Add attributes to each output of a derivation without changing 201 the derivation itself and check a given condition when evaluating. */ 202 extendDerivation = condition: passthru: drv: 203 let 204 outputs = drv.outputs or [ "out" ]; 205 206 commonAttrs = drv // (builtins.listToAttrs outputsList) // 207 ({ all = map (x: x.value) outputsList; }) // passthru; 208 209 outputToAttrListElement = outputName: 210 { name = outputName; 211 value = commonAttrs // { 212 inherit (drv.${outputName}) type outputName; 213 outputSpecified = true; 214 drvPath = assert condition; drv.${outputName}.drvPath; 215 outPath = assert condition; drv.${outputName}.outPath; 216 } // 217 # TODO: give the derivation control over the outputs. 218 # `overrideAttrs` may not be the only attribute that needs 219 # updating when switching outputs. 220 lib.optionalAttrs (passthru?overrideAttrs) { 221 # TODO: also add overrideAttrs when overrideAttrs is not custom, e.g. when not splicing. 222 overrideAttrs = f: (passthru.overrideAttrs f).${outputName}; 223 }; 224 }; 225 226 outputsList = map outputToAttrListElement outputs; 227 in commonAttrs // { 228 drvPath = assert condition; drv.drvPath; 229 outPath = assert condition; drv.outPath; 230 }; 231 232 /* Strip a derivation of all non-essential attributes, returning 233 only those needed by hydra-eval-jobs. Also strictly evaluate the 234 result to ensure that there are no thunks kept alive to prevent 235 garbage collection. */ 236 hydraJob = drv: 237 let 238 outputs = drv.outputs or ["out"]; 239 240 commonAttrs = 241 { inherit (drv) name system meta; inherit outputs; } 242 // lib.optionalAttrs (drv._hydraAggregate or false) { 243 _hydraAggregate = true; 244 constituents = map hydraJob (lib.flatten drv.constituents); 245 } 246 // (lib.listToAttrs outputsList); 247 248 makeOutput = outputName: 249 let output = drv.${outputName}; in 250 { name = outputName; 251 value = commonAttrs // { 252 outPath = output.outPath; 253 drvPath = output.drvPath; 254 type = "derivation"; 255 inherit outputName; 256 }; 257 }; 258 259 outputsList = map makeOutput outputs; 260 261 drv' = (lib.head outputsList).value; 262 in if drv == null then null else 263 lib.deepSeq drv' drv'; 264 265 /* Make a set of packages with a common scope. All packages called 266 with the provided `callPackage` will be evaluated with the same 267 arguments. Any package in the set may depend on any other. The 268 `overrideScope'` function allows subsequent modification of the package 269 set in a consistent way, i.e. all packages in the set will be 270 called with the overridden packages. The package sets may be 271 hierarchical: the packages in the set are called with the scope 272 provided by `newScope` and the set provides a `newScope` attribute 273 which can form the parent scope for later package sets. */ 274 makeScope = newScope: f: 275 let self = f self // { 276 newScope = scope: newScope (self // scope); 277 callPackage = self.newScope {}; 278 overrideScope = g: lib.warn 279 "`overrideScope` (from `lib.makeScope`) is deprecated. Do `overrideScope' (self: super: { })` instead of `overrideScope (super: self: { })`. All other overrides have the parameters in that order, including other definitions of `overrideScope`. This was the only definition violating the pattern." 280 (makeScope newScope (lib.fixedPoints.extends (lib.flip g) f)); 281 overrideScope' = g: makeScope newScope (lib.fixedPoints.extends g f); 282 packages = f; 283 }; 284 in self; 285 286 /* Like the above, but aims to support cross compilation. It's still ugly, but 287 hopefully it helps a little bit. */ 288 makeScopeWithSplicing = splicePackages: newScope: otherSplices: keep: extra: f: 289 let 290 spliced0 = splicePackages { 291 pkgsBuildBuild = otherSplices.selfBuildBuild; 292 pkgsBuildHost = otherSplices.selfBuildHost; 293 pkgsBuildTarget = otherSplices.selfBuildTarget; 294 pkgsHostHost = otherSplices.selfHostHost; 295 pkgsHostTarget = self; # Not `otherSplices.selfHostTarget`; 296 pkgsTargetTarget = otherSplices.selfTargetTarget; 297 }; 298 spliced = extra spliced0 // spliced0 // keep self; 299 self = f self // { 300 newScope = scope: newScope (spliced // scope); 301 callPackage = newScope spliced; # == self.newScope {}; 302 # N.B. the other stages of the package set spliced in are *not* 303 # overridden. 304 overrideScope = g: makeScopeWithSplicing 305 splicePackages 306 newScope 307 otherSplices 308 keep 309 extra 310 (lib.fixedPoints.extends g f); 311 packages = f; 312 }; 313 in self; 314 315}