1{ config, options, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.nixpkgs;
7 opt = options.nixpkgs;
8
9 isConfig = x:
10 builtins.isAttrs x || lib.isFunction x;
11
12 optCall = f: x:
13 if lib.isFunction f
14 then f x
15 else f;
16
17 mergeConfig = lhs_: rhs_:
18 let
19 lhs = optCall lhs_ { inherit pkgs; };
20 rhs = optCall rhs_ { inherit pkgs; };
21 in
22 recursiveUpdate lhs rhs //
23 optionalAttrs (lhs ? packageOverrides) {
24 packageOverrides = pkgs:
25 optCall lhs.packageOverrides pkgs //
26 optCall (attrByPath [ "packageOverrides" ] { } rhs) pkgs;
27 } //
28 optionalAttrs (lhs ? perlPackageOverrides) {
29 perlPackageOverrides = pkgs:
30 optCall lhs.perlPackageOverrides pkgs //
31 optCall (attrByPath [ "perlPackageOverrides" ] { } rhs) pkgs;
32 };
33
34 configType = mkOptionType {
35 name = "nixpkgs-config";
36 description = "nixpkgs config";
37 check = x:
38 let traceXIfNot = c:
39 if c x then true
40 else lib.traceSeqN 1 x false;
41 in traceXIfNot isConfig;
42 merge = args: foldr (def: mergeConfig def.value) {};
43 };
44
45 overlayType = mkOptionType {
46 name = "nixpkgs-overlay";
47 description = "nixpkgs overlay";
48 check = lib.isFunction;
49 merge = lib.mergeOneOption;
50 };
51
52 pkgsType = types.pkgs // {
53 # This type is only used by itself, so let's elaborate the description a bit
54 # for the purpose of documentation.
55 description = "An evaluation of Nixpkgs; the top level attribute set of packages";
56 };
57
58 hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority;
59 hasHostPlatform = opt.hostPlatform.isDefined;
60 hasPlatform = hasHostPlatform || hasBuildPlatform;
61
62 # Context for messages
63 hostPlatformLine = optionalString hasHostPlatform "${showOptionWithDefLocs opt.hostPlatform}";
64 buildPlatformLine = optionalString hasBuildPlatform "${showOptionWithDefLocs opt.buildPlatform}";
65
66 legacyOptionsDefined =
67 optional (opt.localSystem.highestPrio < (mkDefault {}).priority) opt.system
68 ++ optional (opt.localSystem.highestPrio < (mkOptionDefault {}).priority) opt.localSystem
69 ++ optional (opt.crossSystem.highestPrio < (mkOptionDefault {}).priority) opt.crossSystem
70 ;
71
72 defaultPkgs =
73 if opt.hostPlatform.isDefined
74 then
75 let isCross = cfg.buildPlatform != cfg.hostPlatform;
76 systemArgs =
77 if isCross
78 then {
79 localSystem = cfg.buildPlatform;
80 crossSystem = cfg.hostPlatform;
81 }
82 else {
83 localSystem = cfg.hostPlatform;
84 };
85 in
86 import ../../.. ({
87 inherit (cfg) config overlays;
88 } // systemArgs)
89 else
90 import ../../.. {
91 inherit (cfg) config overlays localSystem crossSystem;
92 };
93
94 finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else defaultPkgs;
95
96in
97
98{
99 imports = [
100 ./assertions.nix
101 ./meta.nix
102 (mkRemovedOptionModule [ "nixpkgs" "initialSystem" ] "The NixOS options `nesting.clone` and `nesting.children` have been deleted, and replaced with named specialisation. Therefore `nixpgks.initialSystem` has no effect anymore.")
103 ];
104
105 options.nixpkgs = {
106
107 pkgs = mkOption {
108 defaultText = literalExpression ''
109 import "''${nixos}/.." {
110 inherit (cfg) config overlays localSystem crossSystem;
111 }
112 '';
113 type = pkgsType;
114 example = literalExpression "import <nixpkgs> {}";
115 description = lib.mdDoc ''
116 If set, the pkgs argument to all NixOS modules is the value of
117 this option, extended with `nixpkgs.overlays`, if
118 that is also set. Either `nixpkgs.crossSystem` or
119 `nixpkgs.localSystem` will be used in an assertion
120 to check that the NixOS and Nixpkgs architectures match. Any
121 other options in `nixpkgs.*`, notably `config`,
122 will be ignored.
123
124 If unset, the pkgs argument to all NixOS modules is determined
125 as shown in the default value for this option.
126
127 The default value imports the Nixpkgs source files
128 relative to the location of this NixOS module, because
129 NixOS and Nixpkgs are distributed together for consistency,
130 so the `nixos` in the default value is in fact a
131 relative path. The `config`, `overlays`,
132 `localSystem`, and `crossSystem` come
133 from this option's siblings.
134
135 This option can be used by applications like NixOps to increase
136 the performance of evaluation, or to create packages that depend
137 on a container that should be built with the exact same evaluation
138 of Nixpkgs, for example. Applications like this should set
139 their default value using `lib.mkDefault`, so
140 user-provided configuration can override it without using
141 `lib`.
142
143 Note that using a distinct version of Nixpkgs with NixOS may
144 be an unexpected source of problems. Use this option with care.
145 '';
146 };
147
148 config = mkOption {
149 default = {};
150 example = literalExpression
151 ''
152 { allowBroken = true; allowUnfree = true; }
153 '';
154 type = configType;
155 description = lib.mdDoc ''
156 The configuration of the Nix Packages collection. (For
157 details, see the Nixpkgs documentation.) It allows you to set
158 package configuration options.
159
160 Ignored when `nixpkgs.pkgs` is set.
161 '';
162 };
163
164 overlays = mkOption {
165 default = [];
166 example = literalExpression
167 ''
168 [
169 (self: super: {
170 openssh = super.openssh.override {
171 hpnSupport = true;
172 kerberos = self.libkrb5;
173 };
174 })
175 ]
176 '';
177 type = types.listOf overlayType;
178 description = lib.mdDoc ''
179 List of overlays to apply to Nixpkgs.
180 This option allows modifying the Nixpkgs package set accessed through the `pkgs` module argument.
181
182 For details, see the [Overlays chapter in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-overlays).
183
184 If the {option}`nixpkgs.pkgs` option is set, overlays specified using `nixpkgs.overlays` will be applied after the overlays that were already included in `nixpkgs.pkgs`.
185 '';
186 };
187
188 hostPlatform = mkOption {
189 type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
190 example = { system = "aarch64-linux"; };
191 # Make sure that the final value has all fields for sake of other modules
192 # referring to this. TODO make `lib.systems` itself use the module system.
193 apply = lib.systems.elaborate;
194 defaultText = literalExpression
195 ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
196 description = lib.mdDoc ''
197 Specifies the platform where the NixOS configuration will run.
198
199 To cross-compile, set also `nixpkgs.buildPlatform`.
200
201 Ignored when `nixpkgs.pkgs` is set.
202 '';
203 };
204
205 buildPlatform = mkOption {
206 type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
207 default = cfg.hostPlatform;
208 example = { system = "x86_64-linux"; };
209 # Make sure that the final value has all fields for sake of other modules
210 # referring to this.
211 apply = lib.systems.elaborate;
212 defaultText = literalExpression
213 ''config.nixpkgs.hostPlatform'';
214 description = lib.mdDoc ''
215 Specifies the platform on which NixOS should be built.
216 By default, NixOS is built on the system where it runs, but you can
217 change where it's built. Setting this option will cause NixOS to be
218 cross-compiled.
219
220 For instance, if you're doing distributed multi-platform deployment,
221 or if you're building machines, you can set this to match your
222 development system and/or build farm.
223
224 Ignored when `nixpkgs.pkgs` is set.
225 '';
226 };
227
228 localSystem = mkOption {
229 type = types.attrs; # TODO utilize lib.systems.parsedPlatform
230 default = { inherit (cfg) system; };
231 example = { system = "aarch64-linux"; };
232 # Make sure that the final value has all fields for sake of other modules
233 # referring to this. TODO make `lib.systems` itself use the module system.
234 apply = lib.systems.elaborate;
235 defaultText = literalExpression
236 ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
237 description = lib.mdDoc ''
238 Systems with a recently generated `hardware-configuration.nix`
239 do not need to specify this option, unless cross-compiling, in which case
240 you should set *only* {option}`nixpkgs.buildPlatform`.
241
242 If this is somehow not feasible, you may fall back to removing the
243 {option}`nixpkgs.hostPlatform` line from the generated config and
244 use the old options.
245
246 Specifies the platform on which NixOS should be built. When
247 `nixpkgs.crossSystem` is unset, it also specifies
248 the platform *for* which NixOS should be
249 built. If this option is unset, it defaults to the platform
250 type of the machine where evaluation happens. Specifying this
251 option is useful when doing distributed multi-platform
252 deployment, or when building virtual machines. See its
253 description in the Nixpkgs manual for more details.
254
255 Ignored when `nixpkgs.pkgs` or `hostPlatform` is set.
256 '';
257 };
258
259 # TODO deprecate. "crossSystem" is a nonsense identifier, because "cross"
260 # is a relation between at least 2 systems in the context of a
261 # specific build step, not a single system.
262 crossSystem = mkOption {
263 type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
264 default = null;
265 example = { system = "aarch64-linux"; };
266 description = lib.mdDoc ''
267 Systems with a recently generated `hardware-configuration.nix`
268 may instead specify *only* {option}`nixpkgs.buildPlatform`,
269 or fall back to removing the {option}`nixpkgs.hostPlatform` line from the generated config.
270
271 Specifies the platform for which NixOS should be
272 built. Specify this only if it is different from
273 `nixpkgs.localSystem`, the platform
274 *on* which NixOS should be built. In other
275 words, specify this to cross-compile NixOS. Otherwise it
276 should be set as null, the default. See its description in the
277 Nixpkgs manual for more details.
278
279 Ignored when `nixpkgs.pkgs` or `hostPlatform` is set.
280 '';
281 };
282
283 system = mkOption {
284 type = types.str;
285 example = "i686-linux";
286 default =
287 if opt.hostPlatform.isDefined
288 then
289 throw ''
290 Neither ${opt.system} nor any other option in nixpkgs.* is meant
291 to be read by modules and configurations.
292 Use pkgs.stdenv.hostPlatform instead.
293 ''
294 else
295 throw ''
296 Neither ${opt.hostPlatform} nor the legacy option ${opt.system} has been set.
297 You can set ${opt.hostPlatform} in hardware-configuration.nix by re-running
298 a recent version of nixos-generate-config.
299 The option ${opt.system} is still fully supported for NixOS 22.05 interoperability,
300 but will be deprecated in the future, so we recommend to set ${opt.hostPlatform}.
301 '';
302 defaultText = lib.literalMD ''
303 Traditionally `builtins.currentSystem`, but unset when invoking NixOS through `lib.nixosSystem`.
304 '';
305 description = lib.mdDoc ''
306 This option does not need to be specified for NixOS configurations
307 with a recently generated `hardware-configuration.nix`.
308
309 Specifies the Nix platform type on which NixOS should be built.
310 It is better to specify `nixpkgs.localSystem` instead.
311 ```
312 {
313 nixpkgs.system = ..;
314 }
315 ```
316 is the same as
317 ```
318 {
319 nixpkgs.localSystem.system = ..;
320 }
321 ```
322 See `nixpkgs.localSystem` for more information.
323
324 Ignored when `nixpkgs.pkgs`, `nixpkgs.localSystem` or `nixpkgs.hostPlatform` is set.
325 '';
326 };
327 };
328
329 config = {
330 _module.args = {
331 pkgs =
332 # We explicitly set the default override priority, so that we do not need
333 # to evaluate finalPkgs in case an override is placed on `_module.args.pkgs`.
334 # After all, to determine a definition priority, we need to evaluate `._type`,
335 # which is somewhat costly for Nixpkgs. With an explicit priority, we only
336 # evaluate the wrapper to find out that the priority is lower, and then we
337 # don't need to evaluate `finalPkgs`.
338 lib.mkOverride lib.modules.defaultOverridePriority
339 finalPkgs.__splicedPackages;
340 };
341
342 assertions = let
343 # Whether `pkgs` was constructed by this module. This is false when any of
344 # nixpkgs.pkgs or _module.args.pkgs is set.
345 constructedByMe =
346 # We set it with default priority and it can not be merged, so if the
347 # pkgs module argument has that priority, it's from us.
348 (lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio
349 == lib.modules.defaultOverridePriority
350 # Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it.
351 && !opt.pkgs.isDefined;
352 in [
353 (
354 let
355 nixosExpectedSystem =
356 if config.nixpkgs.crossSystem != null
357 then config.nixpkgs.crossSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.crossSystem.config))
358 else config.nixpkgs.localSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.localSystem.config));
359 nixosOption =
360 if config.nixpkgs.crossSystem != null
361 then "nixpkgs.crossSystem"
362 else "nixpkgs.localSystem";
363 pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
364 in {
365 assertion = constructedByMe -> !hasPlatform -> nixosExpectedSystem == pkgsSystem;
366 message = "The NixOS nixpkgs.pkgs option was set to a Nixpkgs invocation that compiles to target system ${pkgsSystem} but NixOS was configured for system ${nixosExpectedSystem} via NixOS option ${nixosOption}. The NixOS system settings must match the Nixpkgs target system.";
367 }
368 )
369 {
370 assertion = constructedByMe -> hasPlatform -> legacyOptionsDefined == [];
371 message = ''
372 Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
373 ${hostPlatformLine
374 }${buildPlatformLine
375 }
376 However, it also defines the legacy options:
377 ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
378 For a future proof system configuration, we recommend to remove
379 the legacy definitions.
380 '';
381 }
382 {
383 assertion = opt.pkgs.isDefined -> cfg.config == {};
384 message = ''
385 Your system configures nixpkgs with an externally created instance.
386 `nixpkgs.config` options should be passed when creating the instance instead.
387
388 Current value:
389 ${lib.generators.toPretty { multiline = true; } opt.config}
390 '';
391 }
392 ];
393 };
394
395 # needs a full nixpkgs path to import nixpkgs
396 meta.buildDocsInSandbox = false;
397}