nixos/tests/forgejo: fix after git v2.47 bump
[NixPkgs.git] / lib / derivations.nix
blob9c3c4639a26892dd92683a9c4cae4aa38a8f3b15
1 { lib }:
3 let
4   inherit (lib)
5     genAttrs
6     isString
7     throwIfNot
8     ;
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}";
14   showMaybePackagePosPre = prefix: pkg:
15     if pkg?meta.position && isString pkg.meta.position
16     then "${prefix}${pkg.meta.position}"
17     else "";
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.
25     This is useful in situations where a derivation might only be used for its
26     passthru attributes, improving evaluation performance.
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.
32     For illustration and/or testing, we define derivation such that its
33     evaluation is very noticeable.
35         let derivation = throw "This won't be evaluated.";
37     In the following expressions, `derivation` will _not_ be evaluated:
39         (lazyDerivation { inherit derivation; }).type
41         attrNames (lazyDerivation { inherit derivation; })
43         (lazyDerivation { inherit derivation; } // { foo = true; }).foo
45         (lazyDerivation { inherit derivation; meta.foo = true; }).meta
47     In these expressions, `derivation` _will_ be evaluated:
49         "${lazyDerivation { inherit derivation }}"
51         (lazyDerivation { inherit derivation }).outPath
53         (lazyDerivation { inherit derivation }).meta
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:
59         (lazyDerivation { inherit derivation }).buildInputs
61         (lazyDerivation { inherit derivation }).passthru
63         (lazyDerivation { inherit derivation }).pythonPath
65     # Inputs
67     Takes an attribute set with the following attributes
69     `derivation`
70     : The derivation to be wrapped.
72     `meta`
73     : Optional meta attribute.
75       While this function is primarily about derivations, it can improve
76       the `meta` package attribute, which is usually specified through
77       `mkDerivation`.
79     `passthru`
80     : Optional extra values to add to the returned attrset.
82       This can be used for adding package attributes, such as `tests`.
84     `outputs`
85     : Optional list of assumed outputs. Default: ["out"]
87       This must match the set of outputs that the returned derivation has.
88       You must use this when the derivation has multiple outputs.
89   */
90   lazyDerivation =
91     args@{
92       derivation,
93       meta ? null,
94       passthru ? { },
95       outputs ? [ "out" ]
96     }:
97     let
98       # These checks are strict in `drv` and some `drv` attributes, but the
99       # attrset spine returned by lazyDerivation does not depend on it.
100       # Instead, the individual derivation attributes do depend on it.
101       checked =
102         throwIfNot (derivation.type or null == "derivation")
103           "lazyDerivation: input must be a derivation."
104           throwIfNot
105           # NOTE: Technically we could require our outputs to be a subset of the
106           # actual ones, or even leave them unchecked and fail on a lazy basis.
107           # However, consider the case where an output is added in the underlying
108           # derivation, such as dev. lazyDerivation would remove it and cause it
109           # to fail as a buildInputs item, without any indication as to what
110           # happened. Hence the more stringent condition. We could consider
111           # adding a flag to control this behavior if there's a valid case for it,
112           # but the documentation must have a note like this.
113           (derivation.outputs == outputs)
114           ''
115             lib.lazyDerivation: The derivation ${derivation.name or "<unknown>"} has outputs that don't match the assumed outputs.
117             Assumed outputs passed to lazyDerivation${showMaybeAttrPosPre ",\n    at " "outputs" args}:
118                 ${lib.generators.toPretty { multiline = false; } outputs};
120             Actual outputs of the derivation${showMaybePackagePosPre ",\n    defined at " derivation}:
121                 ${lib.generators.toPretty { multiline = false; } derivation.outputs}
123             If the outputs are known ahead of evaluating the derivation,
124             then update the lazyDerivation call to match the actual outputs, in the same order.
125             If lazyDerivation is passed a literal value, just change it to the actual outputs.
126             As a result it will work as before / as intended.
128             Otherwise, when the outputs are dynamic and can't be known ahead of time, it won't
129             be possible to add laziness, but lib.lazyDerivation may still be useful for trimming
130             the attributes.
131             If you want to keep trimming the attributes, make sure that the package is in a
132             variable (don't evaluate it twice!) and pass the variable and its outputs attribute
133             to lib.lazyDerivation. This largely defeats laziness, but keeps the trimming.
134             If none of the above works for you, replace the lib.lazyDerivation call by the
135             expression in the derivation argument.
136           ''
137           derivation;
138     in
139     {
140       # Hardcoded `type`
141       #
142       # `lazyDerivation` requires its `derivation` argument to be a derivation,
143       # so if it is not, that is a programming error by the caller and not
144       # something that `lazyDerivation` consumers should be able to correct
145       # for after the fact.
146       # So, to improve laziness, we assume correctness here and check it only
147       # when actual derivation values are accessed later.
148       type = "derivation";
150       # A fixed set of derivation values, so that `lazyDerivation` can return
151       # its attrset before evaluating `derivation`.
152       # This must only list attributes that are available on _all_ derivations.
153       inherit (checked) outPath outputName drvPath name system;
154       inherit outputs;
156       # The meta attribute can either be taken from the derivation, or if the
157       # `lazyDerivation` caller knew a shortcut, be taken from there.
158       meta = args.meta or checked.meta;
159     }
160     // genAttrs outputs (outputName: checked.${outputName})
161     // passthru;
163   /**
164     Conditionally set a derivation attribute.
166     Because `mkDerivation` sets `__ignoreNulls = true`, a derivation
167     attribute set to `null` will not impact the derivation output hash.
168     Thus, this function passes through its `value` argument if the `cond`
169     is `true`, but returns `null` if not.
172     # Inputs
174     `cond`
176     : Condition
178     `value`
180     : Attribute value
182     # Type
184     ```
185     optionalDrvAttr :: Bool -> a -> a | Null
186     ```
188     # Examples
189     :::{.example}
190     ## `lib.derivations.optionalDrvAttr` usage example
192     ```nix
193     (stdenv.mkDerivation {
194       name = "foo";
195       x = optionalDrvAttr true 1;
196       y = optionalDrvAttr false 1;
197     }).drvPath == (stdenv.mkDerivation {
198       name = "foo";
199       x = 1;
200     }).drvPath
201     => true
202     ```
204     :::
205   */
206   optionalDrvAttr =
207     cond:
208     value: if cond then value else null;