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 = mkOptionType {
53 name = "nixpkgs";
54 description = "An evaluation of Nixpkgs; the top level attribute set of packages";
55 check = builtins.isAttrs;
56 };
57
58 # Whether `pkgs` was constructed by this module - not if nixpkgs.pkgs or
59 # _module.args.pkgs is set. However, determining whether _module.args.pkgs
60 # is defined elsewhere does not seem feasible.
61 constructedByMe = !opt.pkgs.isDefined;
62
63 hasBuildPlatform = opt.buildPlatform.highestPrio < (mkOptionDefault {}).priority;
64 hasHostPlatform = opt.hostPlatform.isDefined;
65 hasPlatform = hasHostPlatform || hasBuildPlatform;
66
67 # Context for messages
68 hostPlatformLine = optionalString hasHostPlatform "${showOptionWithDefLocs opt.hostPlatform}";
69 buildPlatformLine = optionalString hasBuildPlatform "${showOptionWithDefLocs opt.buildPlatform}";
70 platformLines = optionalString hasPlatform ''
71 Your system configuration configures nixpkgs with platform parameters:
72 ${hostPlatformLine
73 }${buildPlatformLine
74 }'';
75
76 legacyOptionsDefined =
77 optional (opt.localSystem.highestPrio < (mkDefault {}).priority) opt.system
78 ++ optional (opt.localSystem.highestPrio < (mkOptionDefault {}).priority) opt.localSystem
79 ++ optional (opt.crossSystem.highestPrio < (mkOptionDefault {}).priority) opt.crossSystem
80 ;
81
82 defaultPkgs =
83 if opt.hostPlatform.isDefined
84 then
85 let isCross = cfg.buildPlatform != cfg.hostPlatform;
86 systemArgs =
87 if isCross
88 then {
89 localSystem = cfg.buildPlatform;
90 crossSystem = cfg.hostPlatform;
91 }
92 else {
93 localSystem = cfg.hostPlatform;
94 };
95 in
96 import ../../.. ({
97 inherit (cfg) config overlays;
98 } // systemArgs)
99 else
100 import ../../.. {
101 inherit (cfg) config overlays localSystem crossSystem;
102 };
103
104 finalPkgs = if opt.pkgs.isDefined then cfg.pkgs.appendOverlays cfg.overlays else defaultPkgs;
105
106in
107
108{
109 imports = [
110 ./assertions.nix
111 ./meta.nix
112 (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.")
113 ];
114
115 options.nixpkgs = {
116
117 pkgs = mkOption {
118 defaultText = literalExpression ''
119 import "''${nixos}/.." {
120 inherit (cfg) config overlays localSystem crossSystem;
121 }
122 '';
123 type = pkgsType;
124 example = literalExpression "import <nixpkgs> {}";
125 description = lib.mdDoc ''
126 If set, the pkgs argument to all NixOS modules is the value of
127 this option, extended with `nixpkgs.overlays`, if
128 that is also set. Either `nixpkgs.crossSystem` or
129 `nixpkgs.localSystem` will be used in an assertion
130 to check that the NixOS and Nixpkgs architectures match. Any
131 other options in `nixpkgs.*`, notably `config`,
132 will be ignored.
133
134 If unset, the pkgs argument to all NixOS modules is determined
135 as shown in the default value for this option.
136
137 The default value imports the Nixpkgs source files
138 relative to the location of this NixOS module, because
139 NixOS and Nixpkgs are distributed together for consistency,
140 so the `nixos` in the default value is in fact a
141 relative path. The `config`, `overlays`,
142 `localSystem`, and `crossSystem` come
143 from this option's siblings.
144
145 This option can be used by applications like NixOps to increase
146 the performance of evaluation, or to create packages that depend
147 on a container that should be built with the exact same evaluation
148 of Nixpkgs, for example. Applications like this should set
149 their default value using `lib.mkDefault`, so
150 user-provided configuration can override it without using
151 `lib`.
152
153 Note that using a distinct version of Nixpkgs with NixOS may
154 be an unexpected source of problems. Use this option with care.
155 '';
156 };
157
158 config = mkOption {
159 default = {};
160 example = literalExpression
161 ''
162 { allowBroken = true; allowUnfree = true; }
163 '';
164 type = configType;
165 description = lib.mdDoc ''
166 The configuration of the Nix Packages collection. (For
167 details, see the Nixpkgs documentation.) It allows you to set
168 package configuration options.
169
170 Ignored when `nixpkgs.pkgs` is set.
171 '';
172 };
173
174 overlays = mkOption {
175 default = [];
176 example = literalExpression
177 ''
178 [
179 (self: super: {
180 openssh = super.openssh.override {
181 hpnSupport = true;
182 kerberos = self.libkrb5;
183 };
184 })
185 ]
186 '';
187 type = types.listOf overlayType;
188 description = lib.mdDoc ''
189 List of overlays to use with the Nix Packages collection.
190 (For details, see the Nixpkgs documentation.) It allows
191 you to override packages globally. Each function in the list
192 takes as an argument the *original* Nixpkgs.
193 The first argument should be used for finding dependencies, and
194 the second should be used for overriding recipes.
195
196 If `nixpkgs.pkgs` is set, overlays specified here
197 will be applied after the overlays that were already present
198 in `nixpkgs.pkgs`.
199 '';
200 };
201
202 hostPlatform = mkOption {
203 type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
204 example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
205 # Make sure that the final value has all fields for sake of other modules
206 # referring to this. TODO make `lib.systems` itself use the module system.
207 apply = lib.systems.elaborate;
208 defaultText = literalExpression
209 ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
210 description = lib.mdDoc ''
211 Specifies the platform where the NixOS configuration will run.
212
213 To cross-compile, set also `nixpkgs.buildPlatform`.
214
215 Ignored when `nixpkgs.pkgs` is set.
216 '';
217 };
218
219 buildPlatform = mkOption {
220 type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
221 default = cfg.hostPlatform;
222 example = { system = "x86_64-linux"; config = "x86_64-unknown-linux-gnu"; };
223 # Make sure that the final value has all fields for sake of other modules
224 # referring to this.
225 apply = lib.systems.elaborate;
226 defaultText = literalExpression
227 ''config.nixpkgs.hostPlatform'';
228 description = lib.mdDoc ''
229 Specifies the platform on which NixOS should be built.
230 By default, NixOS is built on the system where it runs, but you can
231 change where it's built. Setting this option will cause NixOS to be
232 cross-compiled.
233
234 For instance, if you're doing distributed multi-platform deployment,
235 or if you're building machines, you can set this to match your
236 development system and/or build farm.
237
238 Ignored when `nixpkgs.pkgs` is set.
239 '';
240 };
241
242 localSystem = mkOption {
243 type = types.attrs; # TODO utilize lib.systems.parsedPlatform
244 default = { inherit (cfg) system; };
245 example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
246 # Make sure that the final value has all fields for sake of other modules
247 # referring to this. TODO make `lib.systems` itself use the module system.
248 apply = lib.systems.elaborate;
249 defaultText = literalExpression
250 ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
251 description = lib.mdDoc ''
252 Systems with a recently generated `hardware-configuration.nix`
253 do not need to specify this option, unless cross-compiling, in which case
254 you should set *only* {option}`nixpkgs.buildPlatform`.
255
256 If this is somehow not feasible, you may fall back to removing the
257 {option}`nixpkgs.hostPlatform` line from the generated config and
258 use the old options.
259
260 Specifies the platform on which NixOS should be built. When
261 `nixpkgs.crossSystem` is unset, it also specifies
262 the platform *for* which NixOS should be
263 built. If this option is unset, it defaults to the platform
264 type of the machine where evaluation happens. Specifying this
265 option is useful when doing distributed multi-platform
266 deployment, or when building virtual machines. See its
267 description in the Nixpkgs manual for more details.
268
269 Ignored when `nixpkgs.pkgs` or `hostPlatform` is set.
270 '';
271 };
272
273 # TODO deprecate. "crossSystem" is a nonsense identifier, because "cross"
274 # is a relation between at least 2 systems in the context of a
275 # specific build step, not a single system.
276 crossSystem = mkOption {
277 type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
278 default = null;
279 example = { system = "aarch64-linux"; config = "aarch64-unknown-linux-gnu"; };
280 description = lib.mdDoc ''
281 Systems with a recently generated `hardware-configuration.nix`
282 may instead specify *only* {option}`nixpkgs.buildPlatform`,
283 or fall back to removing the {option}`nixpkgs.hostPlatform` line from the generated config.
284
285 Specifies the platform for which NixOS should be
286 built. Specify this only if it is different from
287 `nixpkgs.localSystem`, the platform
288 *on* which NixOS should be built. In other
289 words, specify this to cross-compile NixOS. Otherwise it
290 should be set as null, the default. See its description in the
291 Nixpkgs manual for more details.
292
293 Ignored when `nixpkgs.pkgs` or `hostPlatform` is set.
294 '';
295 };
296
297 system = mkOption {
298 type = types.str;
299 example = "i686-linux";
300 default =
301 if opt.hostPlatform.isDefined
302 then
303 throw ''
304 Neither ${opt.system} nor any other option in nixpkgs.* is meant
305 to be read by modules and configurations.
306 Use pkgs.stdenv.hostPlatform instead.
307 ''
308 else
309 throw ''
310 Neither ${opt.hostPlatform} nor the legacy option ${opt.system} has been set.
311 You can set ${opt.hostPlatform} in hardware-configuration.nix by re-running
312 a recent version of nixos-generate-config.
313 The option ${opt.system} is still fully supported for NixOS 22.05 interoperability,
314 but will be deprecated in the future, so we recommend to set ${opt.hostPlatform}.
315 '';
316 defaultText = lib.literalMD ''
317 Traditionally `builtins.currentSystem`, but unset when invoking NixOS through `lib.nixosSystem`.
318 '';
319 description = lib.mdDoc ''
320 This option does not need to be specified for NixOS configurations
321 with a recently generated `hardware-configuration.nix`.
322
323 Specifies the Nix platform type on which NixOS should be built.
324 It is better to specify `nixpkgs.localSystem` instead.
325 ```
326 {
327 nixpkgs.system = ..;
328 }
329 ```
330 is the same as
331 ```
332 {
333 nixpkgs.localSystem.system = ..;
334 }
335 ```
336 See `nixpkgs.localSystem` for more information.
337
338 Ignored when `nixpkgs.pkgs`, `nixpkgs.localSystem` or `nixpkgs.hostPlatform` is set.
339 '';
340 };
341 };
342
343 config = {
344 _module.args = {
345 pkgs = finalPkgs.__splicedPackages;
346 };
347
348 assertions = [
349 (
350 let
351 nixosExpectedSystem =
352 if config.nixpkgs.crossSystem != null
353 then config.nixpkgs.crossSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.crossSystem.config))
354 else config.nixpkgs.localSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.localSystem.config));
355 nixosOption =
356 if config.nixpkgs.crossSystem != null
357 then "nixpkgs.crossSystem"
358 else "nixpkgs.localSystem";
359 pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
360 in {
361 assertion = constructedByMe -> !hasPlatform -> nixosExpectedSystem == pkgsSystem;
362 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.";
363 }
364 )
365 {
366 assertion = constructedByMe -> hasPlatform -> legacyOptionsDefined == [];
367 message = ''
368 Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
369 ${hostPlatformLine
370 }${buildPlatformLine
371 }
372 However, it also defines the legacy options:
373 ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
374 For a future proof system configuration, we recommend to remove
375 the legacy definitions.
376 '';
377 }
378 ];
379 };
380
381 # needs a full nixpkgs path to import nixpkgs
382 meta.buildDocsInSandbox = false;
383}