at master 8.2 kB view raw
1{ lib }: 2 3let 4 inherit (lib) 5 genAttrs 6 isString 7 mapAttrs 8 removeAttrs 9 throwIfNot 10 ; 11 12 showMaybeAttrPosPre = 13 prefix: attrName: v: 14 let 15 pos = builtins.unsafeGetAttrPos attrName v; 16 in 17 if pos == null then "" else "${prefix}${pos.file}:${toString pos.line}:${toString pos.column}"; 18 19 showMaybePackagePosPre = 20 prefix: pkg: 21 if pkg ? meta.position && isString pkg.meta.position then "${prefix}${pkg.meta.position}" else ""; 22in 23{ 24 /** 25 Restrict a derivation to a predictable set of attribute names, so 26 that the returned attrset is not strict in the actual derivation, 27 saving a lot of computation when the derivation is non-trivial. 28 29 This is useful in situations where a derivation might only be used for its 30 passthru attributes, improving evaluation performance. 31 32 The returned attribute set is lazy in `derivation`. Specifically, this 33 means that the derivation will not be evaluated in at least the 34 situations below. 35 36 For illustration and/or testing, we define derivation such that its 37 evaluation is very noticeable. 38 39 let derivation = throw "This won't be evaluated."; 40 41 In the following expressions, `derivation` will _not_ be evaluated: 42 43 (lazyDerivation { inherit derivation; }).type 44 45 attrNames (lazyDerivation { inherit derivation; }) 46 47 (lazyDerivation { inherit derivation; } // { foo = true; }).foo 48 49 (lazyDerivation { inherit derivation; meta.foo = true; }).meta 50 51 In these expressions, `derivation` _will_ be evaluated: 52 53 "${lazyDerivation { inherit derivation }}" 54 55 (lazyDerivation { inherit derivation }).outPath 56 57 (lazyDerivation { inherit derivation }).meta 58 59 And the following expressions are not valid, because the refer to 60 implementation details and/or attributes that may not be present on 61 some derivations: 62 63 (lazyDerivation { inherit derivation }).buildInputs 64 65 (lazyDerivation { inherit derivation }).passthru 66 67 (lazyDerivation { inherit derivation }).pythonPath 68 69 # Inputs 70 71 Takes an attribute set with the following attributes 72 73 `derivation` 74 : The derivation to be wrapped. 75 76 `meta` 77 : Optional meta attribute. 78 79 While this function is primarily about derivations, it can improve 80 the `meta` package attribute, which is usually specified through 81 `mkDerivation`. 82 83 `passthru` 84 : Optional extra values to add to the returned attrset. 85 86 This can be used for adding package attributes, such as `tests`. 87 88 `outputs` 89 : Optional list of assumed outputs. Default: ["out"] 90 91 This must match the set of outputs that the returned derivation has. 92 You must use this when the derivation has multiple outputs. 93 */ 94 lazyDerivation = 95 args@{ 96 derivation, 97 meta ? null, 98 passthru ? { }, 99 outputs ? [ "out" ], 100 }: 101 let 102 # These checks are strict in `drv` and some `drv` attributes, but the 103 # attrset spine returned by lazyDerivation does not depend on it. 104 # Instead, the individual derivation attributes do depend on it. 105 checked = 106 throwIfNot (derivation.type or null == "derivation") "lazyDerivation: input must be a derivation." 107 throwIfNot 108 # NOTE: Technically we could require our outputs to be a subset of the 109 # actual ones, or even leave them unchecked and fail on a lazy basis. 110 # However, consider the case where an output is added in the underlying 111 # derivation, such as dev. lazyDerivation would remove it and cause it 112 # to fail as a buildInputs item, without any indication as to what 113 # happened. Hence the more stringent condition. We could consider 114 # adding a flag to control this behavior if there's a valid case for it, 115 # but the documentation must have a note like this. 116 (derivation.outputs == outputs) 117 '' 118 lib.lazyDerivation: The derivation ${derivation.name or "<unknown>"} has outputs that don't match the assumed outputs. 119 120 Assumed outputs passed to lazyDerivation${showMaybeAttrPosPre ",\n at " "outputs" args}: 121 ${lib.generators.toPretty { multiline = false; } outputs}; 122 123 Actual outputs of the derivation${showMaybePackagePosPre ",\n defined at " derivation}: 124 ${lib.generators.toPretty { multiline = false; } derivation.outputs} 125 126 If the outputs are known ahead of evaluating the derivation, 127 then update the lazyDerivation call to match the actual outputs, in the same order. 128 If lazyDerivation is passed a literal value, just change it to the actual outputs. 129 As a result it will work as before / as intended. 130 131 Otherwise, when the outputs are dynamic and can't be known ahead of time, it won't 132 be possible to add laziness, but lib.lazyDerivation may still be useful for trimming 133 the attributes. 134 If you want to keep trimming the attributes, make sure that the package is in a 135 variable (don't evaluate it twice!) and pass the variable and its outputs attribute 136 to lib.lazyDerivation. This largely defeats laziness, but keeps the trimming. 137 If none of the above works for you, replace the lib.lazyDerivation call by the 138 expression in the derivation argument. 139 '' 140 derivation; 141 in 142 { 143 # Hardcoded `type` 144 # 145 # `lazyDerivation` requires its `derivation` argument to be a derivation, 146 # so if it is not, that is a programming error by the caller and not 147 # something that `lazyDerivation` consumers should be able to correct 148 # for after the fact. 149 # So, to improve laziness, we assume correctness here and check it only 150 # when actual derivation values are accessed later. 151 type = "derivation"; 152 153 # A fixed set of derivation values, so that `lazyDerivation` can return 154 # its attrset before evaluating `derivation`. 155 # This must only list attributes that are available on _all_ derivations. 156 inherit (checked) 157 outPath 158 outputName 159 drvPath 160 name 161 system 162 ; 163 inherit outputs; 164 165 # The meta attribute can either be taken from the derivation, or if the 166 # `lazyDerivation` caller knew a shortcut, be taken from there. 167 meta = args.meta or checked.meta; 168 } 169 // genAttrs outputs (outputName: checked.${outputName}) 170 // passthru; 171 172 /** 173 Conditionally set a derivation attribute. 174 175 Because `mkDerivation` sets `__ignoreNulls = true`, a derivation 176 attribute set to `null` will not impact the derivation output hash. 177 Thus, this function passes through its `value` argument if the `cond` 178 is `true`, but returns `null` if not. 179 180 # Inputs 181 182 `cond` 183 184 : Condition 185 186 `value` 187 188 : Attribute value 189 190 # Type 191 192 ``` 193 optionalDrvAttr :: Bool -> a -> a | Null 194 ``` 195 196 # Examples 197 :::{.example} 198 ## `lib.derivations.optionalDrvAttr` usage example 199 200 ```nix 201 (stdenv.mkDerivation { 202 name = "foo"; 203 x = optionalDrvAttr true 1; 204 y = optionalDrvAttr false 1; 205 }).drvPath == (stdenv.mkDerivation { 206 name = "foo"; 207 x = 1; 208 }).drvPath 209 => true 210 ``` 211 212 ::: 213 */ 214 optionalDrvAttr = cond: value: if cond then value else null; 215 216 /** 217 Wrap a derivation such that instantiating it produces a warning. 218 219 All attributes will be wrapped with `lib.warn` except from `.meta`, `.name`, 220 and `.type` which are used by `nix search`, and `.outputName` which avoids 221 double warnings with `nix-instantiate` and `nix-build`. 222 223 # Inputs 224 225 `msg` 226 : The warning message to emit (via `lib.warn`). 227 228 `drv` 229 : The derivation to wrap. 230 231 # Examples 232 :::{.example} 233 ## `lib.derivations.warnOnInstantiate` usage example 234 235 ```nix 236 { 237 myPackage = warnOnInstantiate "myPackage has been renamed to my-package" my-package; 238 } 239 ``` 240 241 ::: 242 */ 243 warnOnInstantiate = 244 msg: drv: 245 let 246 drvToWrap = removeAttrs drv [ 247 "meta" 248 "name" 249 "type" 250 "outputName" 251 ]; 252 in 253 drv // mapAttrs (_: lib.warn msg) drvToWrap; 254}