···5555 };
5656 };
57575858- closed = closeModules (modules ++ [ internalModule ]) (specialArgs // { inherit config options; lib = import ./.; });
5858+ closed = closeModules (modules ++ [ internalModule ]) ({ inherit config options; lib = import ./.; } // specialArgs);
59596060 # Note: the list of modules is reversed to maintain backward
6161 # compatibility with the old module system. Not sure if this is
+3-1
nixos/lib/eval-config.nix
···1717 baseModules ? import ../modules/module-list.nix
1818, # !!! See comment about args in lib/modules.nix
1919 extraArgs ? {}
2020+, # !!! See comment about args in lib/modules.nix
2121+ specialArgs ? {}
2022, modules
2123, # !!! See comment about check in lib/modules.nix
2224 check ? true
···4749 inherit prefix check;
4850 modules = modules ++ extraModules ++ baseModules ++ [ pkgsModule ];
4951 args = extraArgs;
5050- specialArgs = { modulesPath = ../modules; };
5252+ specialArgs = { modulesPath = ../modules; } // specialArgs;
5153 }) config options;
52545355 # These are the extra arguments passed to every module. In
+134-40
nixos/maintainers/option-usages.nix
···11{ configuration ? import ../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>
2233-# []: display all options
44-# [<option names>]: display the selected options
55-, displayOptions ? [
66- "hardware.pcmcia.enable"
77- "environment.systemPackages"
88- "boot.kernelModules"
99- "services.udev.packages"
1010- "jobs"
1111- "environment.etc"
1212- "system.activationScripts"
1313- ]
33+# provide an option name, as a string literal.
44+, testOption ? null
55+66+# provide a list of option names, as string literals.
77+, testOptions ? [ ]
148}:
1591616-# This file is used to generate a dot graph which contains all options and
1717-# there dependencies to track problems and their sources.
1010+# This file is made to be used as follow:
1111+#
1212+# $ nix-instantiate ./option-usage.nix --argstr testOption service.xserver.enable -A txtContent --eval
1313+#
1414+# or
1515+#
1616+# $ nix-build ./option-usage.nix --argstr testOption service.xserver.enable -A txt -o service.xserver.enable._txt
1717+#
1818+# otther target exists such as, `dotContent`, `dot`, and `pdf`. If you are
1919+# looking for the option usage of multiple options, you can provide a list
2020+# as argument.
2121+#
2222+# $ nix-build ./option-usage.nix --arg testOptions \
2323+# '["boot.loader.gummiboot.enable" "boot.loader.gummiboot.timeout"]' \
2424+# -A txt -o gummiboot.list
2525+#
2626+# Note, this script is slow as it has to evaluate all options of the system
2727+# once per queried option.
2828+#
2929+# This nix expression works by doing a first evaluation, which evaluates the
3030+# result of every option.
3131+#
3232+# Then, for each queried option, we evaluate the NixOS modules a second
3333+# time, except that we replace the `config` argument of all the modules with
3434+# the result of the original evaluation, except for the tested option which
3535+# value is replaced by a `throw` statement which is caught by the `tryEval`
3636+# evaluation of each option value.
3737+#
3838+# We then compare the result of the evluation of the original module, with
3939+# the result of the second evaluation, and consider that the new failures are
4040+# caused by our mutation of the `config` argument.
4141+#
4242+# Doing so returns all option results which are directly using the
4343+# tested option result.
4444+4545+with import ../../lib;
18461947let
20482149 evalFun = {
2222- extraArgs ? {}
5050+ specialArgs ? {}
2351 }: import ../lib/eval-config.nix {
2452 modules = [ configuration ];
2525- inherit extraArgs;
5353+ inherit specialArgs;
2654 };
27552856 eval = evalFun {};
2957 inherit (eval) pkgs;
30583131- reportNewFailures = old: new: with pkgs.lib;
5959+ excludedTestOptions = [
6060+ # We cannot evluate _module.args, as it is used during the computation
6161+ # of the modules list.
6262+ "_module.args"
6363+6464+ # For some reasons which we yet have to investigate, some options cannot
6565+ # be replaced by a throw without cuasing a non-catchable failure.
6666+ "networking.bonds"
6767+ "networking.bridges"
6868+ "networking.interfaces"
6969+ "networking.macvlans"
7070+ "networking.sits"
7171+ "networking.vlans"
7272+ "services.openssh.startWhenNeeded"
7373+ ];
7474+7575+ # for some reasons which we yet have to investigate, some options are
7676+ # time-consuming to compute, thus we filter them out at the moment.
7777+ excludedOptions = [
7878+ "boot.systemd.services"
7979+ "systemd.services"
8080+ "environment.gnome3.packageSet"
8181+ "kde.extraPackages"
8282+ ];
8383+ excludeOptions = list:
8484+ filter (opt: !(elem (showOption opt.loc) excludedOptions)) list;
8585+8686+8787+ reportNewFailures = old: new:
3288 let
3389 filterChanges =
3490 filter ({fst, snd}:
3535- !(fst.config.success -> snd.config.success)
9191+ !(fst.success -> snd.success)
3692 );
37933894 keepNames =
3995 map ({fst, snd}:
4040- assert fst.name == snd.name; snd.name
9696+ /* assert fst.name == snd.name; */ snd.name
4197 );
9898+9999+ # Use tryEval (strict ...) to know if there is any failure while
100100+ # evaluating the option value.
101101+ #
102102+ # Note, the `strict` function is not strict enough, but using toXML
103103+ # builtins multiply by 4 the memory usage and the time used to compute
104104+ # each options.
105105+ tryCollectOptions = moduleResult:
106106+ flip map (excludeOptions (collect isOption moduleResult)) (opt:
107107+ { name = showOption opt.loc; } // builtins.tryEval (strict opt.value));
42108 in
43109 keepNames (
44110 filterChanges (
4545- zipLists (collect isOption old) (collect isOption new)
111111+ zipLists (tryCollectOptions old) (tryCollectOptions new)
46112 )
47113 );
481144911550116 # Create a list of modules where each module contains only one failling
51117 # options.
5252- introspectionModules = with pkgs.lib;
118118+ introspectionModules =
53119 let
54120 setIntrospection = opt: rec {
5555- name = opt.name;
5656- path = splitString "." opt.name;
121121+ name = showOption opt.loc;
122122+ path = opt.loc;
57123 config = setAttrByPath path
58124 (throw "Usage introspection of '${name}' by forced failure.");
59125 };
···61127 map setIntrospection (collect isOption eval.options);
6212863129 overrideConfig = thrower:
6464- pkgs.lib.recursiveUpdateUntil (path: old: new:
130130+ recursiveUpdateUntil (path: old: new:
65131 path == thrower.path
66132 ) eval.config thrower.config;
67133681346969- graph = with pkgs.lib;
135135+ graph =
70136 map (thrower: {
71137 option = thrower.name;
7272- usedBy = reportNewFailures eval.options (evalFun {
7373- extraArgs = {
7474- config = overrideConfig thrower;
7575- };
7676- }).options;
138138+ usedBy = assert __trace "Investigate ${thrower.name}" true;
139139+ reportNewFailures eval.options (evalFun {
140140+ specialArgs = {
141141+ config = overrideConfig thrower;
142142+ };
143143+ }).options;
77144 }) introspectionModules;
781457979- graphToDot = graph: with pkgs.lib; ''
146146+ displayOptionsGraph =
147147+ let
148148+ checkList =
149149+ if !(isNull testOption) then [ testOption ]
150150+ else testOptions;
151151+ checkAll = checkList == [];
152152+ in
153153+ flip filter graph ({option, usedBy}:
154154+ (checkAll || elem option checkList)
155155+ && !(elem option excludedTestOptions)
156156+ );
157157+158158+ graphToDot = graph: ''
80159 digraph "Option Usages" {
81160 ${concatMapStrings ({option, usedBy}:
8282- assert __trace option true;
8383- if displayOptions == [] || elem option displayOptions then
8484- concatMapStrings (user: ''
8585- "${option}" -> "${user}"''
8686- ) usedBy
8787- else ""
8888- ) graph}
161161+ concatMapStrings (user: ''
162162+ "${option}" -> "${user}"''
163163+ ) usedBy
164164+ ) displayOptionsGraph}
89165 }
90166 '';
91167168168+ graphToText = graph:
169169+ concatMapStrings ({option, usedBy}:
170170+ concatMapStrings (user: ''
171171+ ${user}
172172+ '') usedBy
173173+ ) displayOptionsGraph;
174174+92175in
931769494-pkgs.texFunctions.dot2pdf {
9595- dotGraph = pkgs.writeTextFile {
177177+rec {
178178+ dotContent = graphToDot graph;
179179+ dot = pkgs.writeTextFile {
96180 name = "option_usages.dot";
9797- text = graphToDot graph;
181181+ text = dotContent;
182182+ };
183183+184184+ pdf = pkgs.texFunctions.dot2pdf {
185185+ dotGraph = dot;
186186+ };
187187+188188+ txtContent = graphToText graph;
189189+ txt = pkgs.writeTextFile {
190190+ name = "option_usages.txt";
191191+ text = txtContent;
98192 };
99193}