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