summary refs log tree commit diff
path: root/nixos/maintainers/option-usages.nix
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/option-usages.nix
parent2c9c135ee23f831960a5ae234196aa8c5332c75e (diff)
downloadnixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.tar
nixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.tar.gz
nixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.tar.bz2
nixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.tar.lz
nixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.tar.xz
nixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.tar.zst
nixpkgs-c47e89623b168bc4058e10f08bf8ebd71cab366e.zip
Update option-usages.nix expression to work with newer version of the module system.
Diffstat (limited to 'nixos/maintainers/option-usages.nix')
-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 7413b9e18ce..854ecf7eac5 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;
   };
 }