···77* `runtimeDeps` is used to wrap libraries into `LD_LIBRARY_PATH`. This is how dotnet usually handles runtime dependencies.
78* `buildType` is used to change the type of build. Possible values are `Release`, `Debug`, etc. By default, this is set to `Release`.
79* `dotnet-sdk` is useful in cases where you need to change what dotnet SDK is being used.
80-* `dotnet-runtime` is useful in cases where you need to change what dotnet runtime is being used.
00081* `dotnetRestoreFlags` can be used to pass flags to `dotnet restore`.
82* `dotnetBuildFlags` can be used to pass flags to `dotnet build`.
083* `dotnetInstallFlags` can be used to pass flags to `dotnet install`.
84* `dotnetFlags` can be used to pass flags to all of the above phases.
85
···77* `runtimeDeps` is used to wrap libraries into `LD_LIBRARY_PATH`. This is how dotnet usually handles runtime dependencies.
78* `buildType` is used to change the type of build. Possible values are `Release`, `Debug`, etc. By default, this is set to `Release`.
79* `dotnet-sdk` is useful in cases where you need to change what dotnet SDK is being used.
80+* `dotnet-runtime` is useful in cases where you need to change what dotnet runtime is being used. This can be either a regular dotnet runtime, or an aspnetcore.
81+* `dotnet-test-sdk` is useful in cases where unit tests expect a different dotnet SDK. By default, this is set to the `dotnet-sdk` attribute.
82+* `testProjectFile` is useful in cases where the regular project file does not contain the unit tests. By default, this is set to the `projectFile` attribute.
83+* `disabledTests` is used to disable running specific unit tests. This gets passed as: `dotnet test --filter "FullyQualifiedName!={}"`, to ensure compatibility with all unit test frameworks.
84* `dotnetRestoreFlags` can be used to pass flags to `dotnet restore`.
85* `dotnetBuildFlags` can be used to pass flags to `dotnet build`.
86+* `dotnetTestFlags` can be used to pass flags to `dotnet test`.
87* `dotnetInstallFlags` can be used to pass flags to `dotnet install`.
88* `dotnetFlags` can be used to pass flags to all of the above phases.
89
+53-9
lib/modules.nix
···5253rec {
5455- /* Evaluate a set of modules. The result is a set of two
56- attributes: ‘options’: the nested set of all option declarations,
57- and ‘config’: the nested set of all option values.
0000000000000000000000058 !!! Please think twice before adding to this argument list! The more
59 that is specified here instead of in the modules themselves the harder
60 it is to transparently move a set of modules to be a submodule of another
61 config (as the proper arguments need to be replicated at each call to
62 evalModules) and the less declarative the module set is. */
63- evalModules = { modules
064 , prefix ? []
65 , # This should only be used for special arguments that need to be evaluated
66 # when resolving module structure (like in imports). For everything else,
···120 };
121122 config = {
123- _module.args = args;
00124 };
125 };
126···183 else throw baseMsg
184 else null;
185186- result = builtins.seq checkUnmatched {
187- inherit options;
188- config = removeAttrs config [ "_module" ];
189- inherit (config) _module;
000000000000000000190 };
191 in result;
192
···5253rec {
5455+ /*
56+ Evaluate a set of modules. The result is a set with the attributes:
57+58+ ‘options’: The nested set of all option declarations,
59+60+ ‘config’: The nested set of all option values.
61+62+ ‘type’: A module system type representing the module set as a submodule,
63+ to be extended by configuration from the containing module set.
64+65+ ‘extendModules’: A function similar to ‘evalModules’ but building on top
66+ of the module set. Its arguments, ‘modules’ and ‘specialArgs’ are
67+ added to the existing values.
68+69+ Using ‘extendModules’ a few times has no performance impact as long
70+ as you only reference the final ‘options’ and ‘config’.
71+ If you do reference multiple ‘config’ (or ‘options’) from before and
72+ after ‘extendModules’, performance is the same as with multiple
73+ ‘evalModules’ invocations, because the new modules' ability to
74+ override existing configuration fundamentally requires a new
75+ fixpoint to be constructed.
76+77+ ‘_module’: A portion of the configuration tree which is elided from
78+ ‘config’. It contains some values that are mostly internal to the
79+ module system implementation.
80+81 !!! Please think twice before adding to this argument list! The more
82 that is specified here instead of in the modules themselves the harder
83 it is to transparently move a set of modules to be a submodule of another
84 config (as the proper arguments need to be replicated at each call to
85 evalModules) and the less declarative the module set is. */
86+ evalModules = evalModulesArgs@
87+ { modules
88 , prefix ? []
89 , # This should only be used for special arguments that need to be evaluated
90 # when resolving module structure (like in imports). For everything else,
···144 };
145146 config = {
147+ _module.args = {
148+ inherit extendModules;
149+ } // args;
150 };
151 };
152···209 else throw baseMsg
210 else null;
211212+ checked = builtins.seq checkUnmatched;
213+214+ extendModules = extendArgs@{
215+ modules ? [],
216+ specialArgs ? {},
217+ prefix ? [],
218+ }:
219+ evalModules (evalModulesArgs // {
220+ modules = evalModulesArgs.modules ++ modules;
221+ specialArgs = evalModulesArgs.specialArgs or {} // specialArgs;
222+ prefix = extendArgs.prefix or evalModulesArgs.prefix;
223+ });
224+225+ type = lib.types.submoduleWith {
226+ inherit modules specialArgs;
227+ };
228+229+ result = {
230+ options = checked options;
231+ config = checked (removeAttrs config [ "_module" ]);
232+ _module = checked (config._module);
233+ inherit extendModules type;
234 };
235 in result;
236
+7-3
lib/options.nix
···74 apply ? null,
75 # Whether the option is for NixOS developers only.
76 internal ? null,
77- # Whether the option shows up in the manual.
78 visible ? null,
79 # Whether the option can be set only once
80 readOnly ? null,
···180 description = opt.description or (lib.warn "Option `${name}' has no description." "This option has no description.");
181 declarations = filter (x: x != unknownModule) opt.declarations;
182 internal = opt.internal or false;
183- visible = opt.visible or true;
000184 readOnly = opt.readOnly or false;
185 type = opt.type.description or null;
186 }
···192 subOptions =
193 let ss = opt.type.getSubOptions opt.loc;
194 in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
0195 in
196- [ docOption ] ++ optionals docOption.visible subOptions) (collect isOption options);
197198199 /* This function recursively removes all derivation attributes from
···74 apply ? null,
75 # Whether the option is for NixOS developers only.
76 internal ? null,
77+ # Whether the option shows up in the manual. Default: true. Use false to hide the option and any sub-options from submodules. Use "shallow" to hide only sub-options.
78 visible ? null,
79 # Whether the option can be set only once
80 readOnly ? null,
···180 description = opt.description or (lib.warn "Option `${name}' has no description." "This option has no description.");
181 declarations = filter (x: x != unknownModule) opt.declarations;
182 internal = opt.internal or false;
183+ visible =
184+ if (opt?visible && opt.visible == "shallow")
185+ then true
186+ else opt.visible or true;
187 readOnly = opt.readOnly or false;
188 type = opt.type.description or null;
189 }
···195 subOptions =
196 let ss = opt.type.getSubOptions opt.loc;
197 in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
198+ subOptionsVisible = docOption.visible && opt.visible or null != "shallow";
199 in
200+ [ docOption ] ++ optionals subOptionsVisible subOptions) (collect isOption options);
201202203 /* This function recursively removes all derivation attributes from
+7
lib/tests/modules.sh
···179# which evaluates all the modules defined by the type)
180checkConfigOutput "submodule" options.submodule.type.description ./declare-submoduleWith-modules.nix
1810000000182## Paths should be allowed as values and work as expected
183checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix
184
···179# which evaluates all the modules defined by the type)
180checkConfigOutput "submodule" options.submodule.type.description ./declare-submoduleWith-modules.nix
181182+## submodules can be declared using (evalModules {...}).type
183+checkConfigOutput "true" config.submodule.inner ./declare-submodule-via-evalModules.nix
184+checkConfigOutput "true" config.submodule.outer ./declare-submodule-via-evalModules.nix
185+# Should also be able to evaluate the type name (which evaluates freeformType,
186+# which evaluates all the modules defined by the type)
187+checkConfigOutput "submodule" options.submodule.type.description ./declare-submodule-via-evalModules.nix
188+189## Paths should be allowed as values and work as expected
190checkConfigOutput "true" config.submodule.enable ./declare-submoduleWith-path.nix
191
···505 then setFunctionArgs (args: unify (value args)) (functionArgs value)
506 else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);
507508- allModules = defs: modules ++ imap1 (n: { value, file }:
509 if isAttrs value || isFunction value then
510 # Annotate the value with the location of its definition for better error messages
511 coerce (lib.modules.unifyModuleSyntax file "${toString file}-${toString n}") value
512 else value
513 ) defs;
514515- freeformType = (evalModules {
516- inherit modules specialArgs;
517- args.name = "‹name›";
518- })._module.freeformType;
0000000000000000000519520 in
521 mkOptionType rec {
···523 description = freeformType.description or name;
524 check = x: isAttrs x || isFunction x || path.check x;
525 merge = loc: defs:
526- (evalModules {
527- modules = allModules defs;
528- inherit specialArgs;
529- args.name = last loc;
530 prefix = loc;
531 }).config;
532 emptyValue = { value = {}; };
533- getSubOptions = prefix: (evalModules
534- { inherit modules prefix specialArgs;
535- # This is a work-around due to the fact that some sub-modules,
536- # such as the one included in an attribute set, expects a "args"
537- # attribute to be given to the sub-module. As the option
538- # evaluation does not have any specific attribute name, we
539- # provide a default one for the documentation.
540- #
541- # This is mandatory as some option declaration might use the
542- # "name" attribute given as argument of the submodule and use it
543- # as the default of option declarations.
544- #
545- # Using lookalike unicode single angle quotation marks because
546- # of the docbook transformation the options receive. In all uses
547- # > and < wouldn't be encoded correctly so the encoded values
548- # would be used, and use of `<` and `>` would break the XML document.
549- # It shouldn't cause an issue since this is cosmetic for the manual.
550- args.name = "‹name›";
551- }).options // optionalAttrs (freeformType != null) {
552 # Expose the sub options of the freeform type. Note that the option
553 # discovery doesn't care about the attribute name used here, so this
554 # is just to avoid conflicts with potential options from the submodule
···505 then setFunctionArgs (args: unify (value args)) (functionArgs value)
506 else unify (if shorthandOnlyDefinesConfig then { config = value; } else value);
507508+ allModules = defs: imap1 (n: { value, file }:
509 if isAttrs value || isFunction value then
510 # Annotate the value with the location of its definition for better error messages
511 coerce (lib.modules.unifyModuleSyntax file "${toString file}-${toString n}") value
512 else value
513 ) defs;
514515+ base = evalModules {
516+ inherit specialArgs;
517+ modules = [{
518+ # This is a work-around for the fact that some sub-modules,
519+ # such as the one included in an attribute set, expects an "args"
520+ # attribute to be given to the sub-module. As the option
521+ # evaluation does not have any specific attribute name yet, we
522+ # provide a default for the documentation and the freeform type.
523+ #
524+ # This is necessary as some option declaration might use the
525+ # "name" attribute given as argument of the submodule and use it
526+ # as the default of option declarations.
527+ #
528+ # We use lookalike unicode single angle quotation marks because
529+ # of the docbook transformation the options receive. In all uses
530+ # > and < wouldn't be encoded correctly so the encoded values
531+ # would be used, and use of `<` and `>` would break the XML document.
532+ # It shouldn't cause an issue since this is cosmetic for the manual.
533+ _module.args.name = lib.mkOptionDefault "‹name›";
534+ }] ++ modules;
535+ };
536+537+ freeformType = base._module.freeformType;
538539 in
540 mkOptionType rec {
···542 description = freeformType.description or name;
543 check = x: isAttrs x || isFunction x || path.check x;
544 merge = loc: defs:
545+ (base.extendModules {
546+ modules = [ { _module.args.name = last loc; } ] ++ allModules defs;
00547 prefix = loc;
548 }).config;
549 emptyValue = { value = {}; };
550+ getSubOptions = prefix: (base.extendModules
551+ { inherit prefix; }).options // optionalAttrs (freeformType != null) {
00000000000000000552 # Expose the sub options of the freeform type. Note that the option
553 # discovery doesn't care about the attribute name used here, so this
554 # is just to avoid conflicts with potential options from the submodule
+1-1
nixos/lib/eval-config.nix
···61 args = extraArgs;
62 specialArgs =
63 { modulesPath = builtins.toString ../modules; } // specialArgs;
64- }) config options _module;
6566 # These are the extra arguments passed to every module. In
67 # particular, Nixpkgs is passed through the "pkgs" argument.
···61 args = extraArgs;
62 specialArgs =
63 { modulesPath = builtins.toString ../modules; } // specialArgs;
64+ }) config options _module type;
6566 # These are the extra arguments passed to every module. In
67 # particular, Nixpkgs is passed through the "pkgs" argument.
···23{ name ? "${args.pname}-${args.version}"
4, enableParallelBuilding ? true
05# Flags to pass to `makeWrapper`. This is done to avoid double wrapping.
6, makeWrapperArgs ? []
7···9, dotnetRestoreFlags ? []
10# Flags to pass to `dotnet build`.
11, dotnetBuildFlags ? []
0012# Flags to pass to `dotnet install`.
13, dotnetInstallFlags ? []
14# Flags to pass to dotnet in all phases.
···27# These get wrapped into `LD_LIBRARY_PATH`.
28, runtimeDeps ? []
2900000030# The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc.
31, buildType ? "Release"
32# The dotnet SDK to use.
33, dotnet-sdk ? dotnetCorePackages.sdk_5_0
34# The dotnet runtime to use.
35, dotnet-runtime ? dotnetCorePackages.runtime_5_0
0036, ... } @ args:
3738assert projectFile == null -> throw "Defining the `projectFile` attribute is required. This is usually an `.csproj`, or `.sln` file.";
···117 "''${dotnetFlags[@]}"
118119 runHook postBuild
00000000000000000120 '';
121122 installPhase = args.installPhase or ''
···23{ name ? "${args.pname}-${args.version}"
4, enableParallelBuilding ? true
5+, doCheck ? false
6# Flags to pass to `makeWrapper`. This is done to avoid double wrapping.
7, makeWrapperArgs ? []
8···10, dotnetRestoreFlags ? []
11# Flags to pass to `dotnet build`.
12, dotnetBuildFlags ? []
13+# Flags to pass to `dotnet test`, if running tests is enabled.
14+, dotnetTestFlags ? []
15# Flags to pass to `dotnet install`.
16, dotnetInstallFlags ? []
17# Flags to pass to dotnet in all phases.
···30# These get wrapped into `LD_LIBRARY_PATH`.
31, runtimeDeps ? []
3233+# Tests to disable. This gets passed to `dotnet test --filter "FullyQualifiedName!={}"`, to ensure compatibility with all frameworks.
34+# See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-test#filter-option-details for more details.
35+, disabledTests ? []
36+# The project file to run unit tests against. This is usually the regular project file, but sometimes it needs to be manually set.
37+, testProjectFile ? projectFile
38+39# The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc.
40, buildType ? "Release"
41# The dotnet SDK to use.
42, dotnet-sdk ? dotnetCorePackages.sdk_5_0
43# The dotnet runtime to use.
44, dotnet-runtime ? dotnetCorePackages.runtime_5_0
45+# The dotnet SDK to run tests against. This can differentiate from the SDK compiled against.
46+, dotnet-test-sdk ? dotnet-sdk
47, ... } @ args:
4849assert projectFile == null -> throw "Defining the `projectFile` attribute is required. This is usually an `.csproj`, or `.sln` file.";
···128 "''${dotnetFlags[@]}"
129130 runHook postBuild
131+ '';
132+133+ checkPhase = args.checkPhase or ''
134+ runHook preCheck
135+136+ ${lib.getBin dotnet-test-sdk}/bin/dotnet test "$testProjectFile" \
137+ -maxcpucount:${if enableParallelBuilding then "$NIX_BUILD_CORES" else "1"} \
138+ -p:ContinuousIntegrationBuild=true \
139+ -p:Deterministic=true \
140+ --configuration "$buildType" \
141+ --no-build \
142+ --logger "console;verbosity=normal" \
143+ ${lib.optionalString (disabledTests != []) "--filter \"FullyQualifiedName!=${lib.concatStringsSep "|FullyQualifiedName!=" disabledTests}\""} \
144+ "''${dotnetTestFlags[@]}" \
145+ "''${dotnetFlags[@]}"
146+147+ runHook postCheck
148 '';
149150 installPhase = args.installPhase or ''