summaryrefslogtreecommitdiffstats
path: root/nixos/maintainers
diff options
context:
space:
mode:
authorNicolas B. Pierron <nicolas.b.pierron@gmail.com>2015-08-09 14:40:01 +0200
committerNicolas B. Pierron <nicolas.b.pierron@gmail.com>2015-08-09 17:52:34 +0200
commitc47e89623b168bc4058e10f08bf8ebd71cab366e (patch)
tree8f828cb4ea82a89a4cfe303a08396ff6bf100325 /nixos/maintainers
parent2c9c135ee23f831960a5ae234196aa8c5332c75e (diff)
Update option-usages.nix expression to work with newer version of the module system.
Diffstat (limited to 'nixos/maintainers')
-rw-r--r--nixos/maintainers/option-usages.nix174
1 files changed, 134 insertions, 40 deletions
diff --git a/nixos/maintainers/option-usages.nix b/nixos/maintainers/option-usages.nix
index 7413b9e18cec..854ecf7eac51 100644
--- a/nixos/maintainers/option-usages.nix
+++ b/nixos/maintainers/option-usages.nix
@@ -1,59 +1,125 @@
{ configuration ? import ../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>
-# []: display all options
-# [<option names>]: display the selected options
-, displayOptions ? [
- "hardware.pcmcia.enable"
- "environment.systemPackages"
- "boot.kernelModules"
- "services.udev.packages"
- "jobs"
- "environment.etc"
- "system.activationScripts"
- ]
+# provide an option name, as a string literal.
+, testOption ? null
+
+# provide a list of option names, as string literals.
+, testOptions ? [ ]
}:
-# This file is used to generate a dot graph which contains all options and
-# there dependencies to track problems and their sources.
+# This file is made to be used as follow:
+#
+# $ nix-instantiate ./option-usage.nix --argstr testOption service.xserver.enable -A txtContent --eval
+#
+# or
+#
+# $ nix-build ./option-usage.nix --argstr testOption service.xserver.enable -A txt -o service.xserver.enable._txt
+#
+# otther target exists such as, `dotContent`, `dot`, and `pdf`. If you are
+# looking for the option usage of multiple options, you can provide a list
+# as argument.
+#
+# $ nix-build ./option-usage.nix --arg testOptions \
+# '["boot.loader.gummiboot.enable" "boot.loader.gummiboot.timeout"]' \
+# -A txt -o gummiboot.list
+#
+# Note, this script is slow as it has to evaluate all options of the system
+# once per queried option.
+#
+# This nix expression works by doing a first evaluation, which evaluates the
+# result of every option.
+#
+# Then, for each queried option, we evaluate the NixOS modules a second
+# time, except that we replace the `config` argument of all the modules with
+# the result of the original evaluation, except for the tested option which
+# value is replaced by a `throw` statement which is caught by the `tryEval`
+# evaluation of each option value.
+#
+# We then compare the result of the evluation of the original module, with
+# the result of the second evaluation, and consider that the new failures are
+# caused by our mutation of the `config` argument.
+#
+# Doing so returns all option results which are directly using the
+# tested option result.
+
+with import ../../lib;
let
evalFun = {
- extraArgs ? {}
+ specialArgs ? {}
}: import ../lib/eval-config.nix {
modules = [ configuration ];
- inherit extraArgs;
+ inherit specialArgs;
};
eval = evalFun {};
inherit (eval) pkgs;
- reportNewFailures = old: new: with pkgs.lib;
+ excludedTestOptions = [
+ # We cannot evluate _module.args, as it is used during the computation
+ # of the modules list.
+ "_module.args"
+
+ # For some reasons which we yet have to investigate, some options cannot
+ # be replaced by a throw without cuasing a non-catchable failure.
+ "networking.bonds"
+ "networking.bridges"
+ "networking.interfaces"
+ "networking.macvlans"
+ "networking.sits"
+ "networking.vlans"
+ "services.openssh.startWhenNeeded"
+ ];
+
+ # for some reasons which we yet have to investigate, some options are
+ # time-consuming to compute, thus we filter them out at the moment.
+ excludedOptions = [
+ "boot.systemd.services"
+ "systemd.services"
+ "environment.gnome3.packageSet"
+ "kde.extraPackages"
+ ];
+ excludeOptions = list:
+ filter (opt: !(elem (showOption opt.loc) excludedOptions)) list;
+
+
+ reportNewFailures = old: new:
let
filterChanges =
filter ({fst, snd}:
- !(fst.config.success -> snd.config.success)
+ !(fst.success -> snd.success)
);
keepNames =
map ({fst, snd}:
- assert fst.name == snd.name; snd.name
+ /* assert fst.name == snd.name; */ snd.name
);
+
+ # Use tryEval (strict ...) to know if there is any failure while
+ # evaluating the option value.
+ #
+ # Note, the `strict` function is not strict enough, but using toXML
+ # builtins multiply by 4 the memory usage and the time used to compute
+ # each options.
+ tryCollectOptions = moduleResult:
+ flip map (excludeOptions (collect isOption moduleResult)) (opt:
+ { name = showOption opt.loc; } // builtins.tryEval (strict opt.value));
in
keepNames (
filterChanges (
- zipLists (collect isOption old) (collect isOption new)
+ zipLists (tryCollectOptions old) (tryCollectOptions new)
)
);
# Create a list of modules where each module contains only one failling
# options.
- introspectionModules = with pkgs.lib;
+ introspectionModules =
let
setIntrospection = opt: rec {
- name = opt.name;
- path = splitString "." opt.name;
+ name = showOption opt.loc;
+ path = opt.loc;
config = setAttrByPath path
(throw "Usage introspection of '${name}' by forced failure.");
};
@@ -61,39 +127,67 @@ let
map setIntrospection (collect isOption eval.options);
overrideConfig = thrower:
- pkgs.lib.recursiveUpdateUntil (path: old: new:
+ recursiveUpdateUntil (path: old: new:
path == thrower.path
) eval.config thrower.config;
- graph = with pkgs.lib;
+ graph =
map (thrower: {
option = thrower.name;
- usedBy = reportNewFailures eval.options (evalFun {
- extraArgs = {
- config = overrideConfig thrower;
- };
- }).options;
+ usedBy = assert __trace "Investigate ${thrower.name}" true;
+ reportNewFailures eval.options (evalFun {
+ specialArgs = {
+ config = overrideConfig thrower;
+ };
+ }).options;
}) introspectionModules;
- graphToDot = graph: with pkgs.lib; ''
+ displayOptionsGraph =
+ let
+ checkList =
+ if !(isNull testOption) then [ testOption ]
+ else testOptions;
+ checkAll = checkList == [];
+ in
+ flip filter graph ({option, usedBy}:
+ (checkAll || elem option checkList)
+ && !(elem option excludedTestOptions)
+ );
+
+ graphToDot = graph: ''
digraph "Option Usages" {
${concatMapStrings ({option, usedBy}:
- assert __trace option true;
- if displayOptions == [] || elem option displayOptions then
- concatMapStrings (user: ''
- "${option}" -> "${user}"''
- ) usedBy
- else ""
- ) graph}
+ concatMapStrings (user: ''
+ "${option}" -> "${user}"''
+ ) usedBy
+ ) displayOptionsGraph}
}
'';
+ graphToText = graph:
+ concatMapStrings ({option, usedBy}:
+ concatMapStrings (user: ''
+ ${user}
+ '') usedBy
+ ) displayOptionsGraph;
+
in
-pkgs.texFunctions.dot2pdf {
- dotGraph = pkgs.writeTextFile {
+rec {
+ dotContent = graphToDot graph;
+ dot = pkgs.writeTextFile {
name = "option_usages.dot";
- text = graphToDot graph;
+ text = dotContent;
+ };
+
+ pdf = pkgs.texFunctions.dot2pdf {
+ dotGraph = dot;
+ };
+
+ txtContent = graphToText graph;
+ txt = pkgs.writeTextFile {
+ name = "option_usages.txt";
+ text = txtContent;
};
}