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