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