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 = ''
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 = ''
156 Global configuration for Nixpkgs.
157 The complete list of [Nixpkgs configuration options](https://nixos.org/manual/nixpkgs/unstable/#sec-config-options-reference) is in the [Nixpkgs manual section on global configuration](https://nixos.org/manual/nixpkgs/unstable/#chap-packageconfig).
158
159 Ignored when {option}`nixpkgs.pkgs` is set.
160 '';
161 };
162
163 overlays = mkOption {
164 default = [];
165 example = literalExpression
166 ''
167 [
168 (self: super: {
169 openssh = super.openssh.override {
170 hpnSupport = true;
171 kerberos = self.libkrb5;
172 };
173 })
174 ]
175 '';
176 type = types.listOf overlayType;
177 description = ''
178 List of overlays to apply to Nixpkgs.
179 This option allows modifying the Nixpkgs package set accessed through the `pkgs` module argument.
180
181 For details, see the [Overlays chapter in the Nixpkgs manual](https://nixos.org/manual/nixpkgs/stable/#chap-overlays).
182
183 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`.
184 '';
185 };
186
187 hostPlatform = mkOption {
188 type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
189 example = { system = "aarch64-linux"; };
190 # Make sure that the final value has all fields for sake of other modules
191 # referring to this. TODO make `lib.systems` itself use the module system.
192 apply = lib.systems.elaborate;
193 defaultText = literalExpression
194 ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
195 description = ''
196 Specifies the platform where the NixOS configuration will run.
197
198 To cross-compile, set also `nixpkgs.buildPlatform`.
199
200 Ignored when `nixpkgs.pkgs` is set.
201 '';
202 };
203
204 buildPlatform = mkOption {
205 type = types.either types.str types.attrs; # TODO utilize lib.systems.parsedPlatform
206 default = cfg.hostPlatform;
207 example = { system = "x86_64-linux"; };
208 # Make sure that the final value has all fields for sake of other modules
209 # referring to this.
210 apply = inputBuildPlatform:
211 let elaborated = lib.systems.elaborate inputBuildPlatform;
212 in if lib.systems.equals elaborated cfg.hostPlatform
213 then cfg.hostPlatform # make identical, so that `==` equality works; see https://github.com/NixOS/nixpkgs/issues/278001
214 else elaborated;
215 defaultText = literalExpression
216 ''config.nixpkgs.hostPlatform'';
217 description = ''
218 Specifies the platform on which NixOS should be built.
219 By default, NixOS is built on the system where it runs, but you can
220 change where it's built. Setting this option will cause NixOS to be
221 cross-compiled.
222
223 For instance, if you're doing distributed multi-platform deployment,
224 or if you're building machines, you can set this to match your
225 development system and/or build farm.
226
227 Ignored when `nixpkgs.pkgs` is set.
228 '';
229 };
230
231 localSystem = mkOption {
232 type = types.attrs; # TODO utilize lib.systems.parsedPlatform
233 default = { inherit (cfg) system; };
234 example = { system = "aarch64-linux"; };
235 # Make sure that the final value has all fields for sake of other modules
236 # referring to this. TODO make `lib.systems` itself use the module system.
237 apply = lib.systems.elaborate;
238 defaultText = literalExpression
239 ''(import "''${nixos}/../lib").lib.systems.examples.aarch64-multiplatform'';
240 description = ''
241 Systems with a recently generated `hardware-configuration.nix`
242 do not need to specify this option, unless cross-compiling, in which case
243 you should set *only* {option}`nixpkgs.buildPlatform`.
244
245 If this is somehow not feasible, you may fall back to removing the
246 {option}`nixpkgs.hostPlatform` line from the generated config and
247 use the old options.
248
249 Specifies the platform on which NixOS should be built. When
250 `nixpkgs.crossSystem` is unset, it also specifies
251 the platform *for* which NixOS should be
252 built. If this option is unset, it defaults to the platform
253 type of the machine where evaluation happens. Specifying this
254 option is useful when doing distributed multi-platform
255 deployment, or when building virtual machines. See its
256 description in the Nixpkgs manual for more details.
257
258 Ignored when `nixpkgs.pkgs` or `hostPlatform` is set.
259 '';
260 };
261
262 # TODO deprecate. "crossSystem" is a nonsense identifier, because "cross"
263 # is a relation between at least 2 systems in the context of a
264 # specific build step, not a single system.
265 crossSystem = mkOption {
266 type = types.nullOr types.attrs; # TODO utilize lib.systems.parsedPlatform
267 default = null;
268 example = { system = "aarch64-linux"; };
269 description = ''
270 Systems with a recently generated `hardware-configuration.nix`
271 may instead specify *only* {option}`nixpkgs.buildPlatform`,
272 or fall back to removing the {option}`nixpkgs.hostPlatform` line from the generated config.
273
274 Specifies the platform for which NixOS should be
275 built. Specify this only if it is different from
276 `nixpkgs.localSystem`, the platform
277 *on* which NixOS should be built. In other
278 words, specify this to cross-compile NixOS. Otherwise it
279 should be set as null, the default. See its description in the
280 Nixpkgs manual for more details.
281
282 Ignored when `nixpkgs.pkgs` or `hostPlatform` is set.
283 '';
284 };
285
286 system = mkOption {
287 type = types.str;
288 example = "i686-linux";
289 default =
290 if opt.hostPlatform.isDefined
291 then
292 throw ''
293 Neither ${opt.system} nor any other option in nixpkgs.* is meant
294 to be read by modules and configurations.
295 Use pkgs.stdenv.hostPlatform instead.
296 ''
297 else
298 throw ''
299 Neither ${opt.hostPlatform} nor the legacy option ${opt.system} has been set.
300 You can set ${opt.hostPlatform} in hardware-configuration.nix by re-running
301 a recent version of nixos-generate-config.
302 The option ${opt.system} is still fully supported for NixOS 22.05 interoperability,
303 but will be deprecated in the future, so we recommend to set ${opt.hostPlatform}.
304 '';
305 defaultText = lib.literalMD ''
306 Traditionally `builtins.currentSystem`, but unset when invoking NixOS through `lib.nixosSystem`.
307 '';
308 description = ''
309 This option does not need to be specified for NixOS configurations
310 with a recently generated `hardware-configuration.nix`.
311
312 Specifies the Nix platform type on which NixOS should be built.
313 It is better to specify `nixpkgs.localSystem` instead.
314 ```
315 {
316 nixpkgs.system = ..;
317 }
318 ```
319 is the same as
320 ```
321 {
322 nixpkgs.localSystem.system = ..;
323 }
324 ```
325 See `nixpkgs.localSystem` for more information.
326
327 Ignored when `nixpkgs.pkgs`, `nixpkgs.localSystem` or `nixpkgs.hostPlatform` is set.
328 '';
329 };
330 };
331
332 config = {
333 _module.args = {
334 pkgs =
335 # We explicitly set the default override priority, so that we do not need
336 # to evaluate finalPkgs in case an override is placed on `_module.args.pkgs`.
337 # After all, to determine a definition priority, we need to evaluate `._type`,
338 # which is somewhat costly for Nixpkgs. With an explicit priority, we only
339 # evaluate the wrapper to find out that the priority is lower, and then we
340 # don't need to evaluate `finalPkgs`.
341 lib.mkOverride lib.modules.defaultOverridePriority
342 finalPkgs.__splicedPackages;
343 };
344
345 assertions = let
346 # Whether `pkgs` was constructed by this module. This is false when any of
347 # nixpkgs.pkgs or _module.args.pkgs is set.
348 constructedByMe =
349 # We set it with default priority and it can not be merged, so if the
350 # pkgs module argument has that priority, it's from us.
351 (lib.modules.mergeAttrDefinitionsWithPrio options._module.args).pkgs.highestPrio
352 == lib.modules.defaultOverridePriority
353 # Although, if nixpkgs.pkgs is set, we did forward it, but we did not construct it.
354 && !opt.pkgs.isDefined;
355 in [
356 (
357 let
358 nixosExpectedSystem =
359 if config.nixpkgs.crossSystem != null
360 then config.nixpkgs.crossSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.crossSystem.config))
361 else config.nixpkgs.localSystem.system or (lib.systems.parse.doubleFromSystem (lib.systems.parse.mkSystemFromString config.nixpkgs.localSystem.config));
362 nixosOption =
363 if config.nixpkgs.crossSystem != null
364 then "nixpkgs.crossSystem"
365 else "nixpkgs.localSystem";
366 pkgsSystem = finalPkgs.stdenv.targetPlatform.system;
367 in {
368 assertion = constructedByMe -> !hasPlatform -> nixosExpectedSystem == pkgsSystem;
369 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.";
370 }
371 )
372 {
373 assertion = constructedByMe -> hasPlatform -> legacyOptionsDefined == [];
374 message = ''
375 Your system configures nixpkgs with the platform parameter${optionalString hasBuildPlatform "s"}:
376 ${hostPlatformLine
377 }${buildPlatformLine
378 }
379 However, it also defines the legacy options:
380 ${concatMapStrings showOptionWithDefLocs legacyOptionsDefined}
381 For a future proof system configuration, we recommend to remove
382 the legacy definitions.
383 '';
384 }
385 {
386 assertion = opt.pkgs.isDefined -> cfg.config == {};
387 message = ''
388 Your system configures nixpkgs with an externally created instance.
389 `nixpkgs.config` options should be passed when creating the instance instead.
390
391 Current value:
392 ${lib.generators.toPretty { multiline = true; } opt.config}
393 '';
394 }
395 ];
396 };
397
398 # needs a full nixpkgs path to import nixpkgs
399 meta.buildDocsInSandbox = false;
400}