100 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
		
			2.4 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ 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"
 | 
						|
  ]
 | 
						|
}:
 | 
						|
 | 
						|
# This file is used to generate a dot graph which contains all options and
 | 
						|
# there dependencies to track problems and their sources.
 | 
						|
 | 
						|
let
 | 
						|
 | 
						|
  evalFun = {
 | 
						|
    extraArgs ? {}
 | 
						|
  }: import ../lib/eval-config.nix {
 | 
						|
       modules = [ configuration ];
 | 
						|
       inherit extraArgs;
 | 
						|
     };
 | 
						|
 | 
						|
  eval = evalFun {};
 | 
						|
  inherit (eval) pkgs;
 | 
						|
 | 
						|
  reportNewFailures = old: new: with pkgs.lib;
 | 
						|
    let
 | 
						|
      filterChanges =
 | 
						|
        filter ({fst, snd}:
 | 
						|
          !(fst.config.success -> snd.config.success)
 | 
						|
        );
 | 
						|
 | 
						|
      keepNames =
 | 
						|
        map ({fst, snd}:
 | 
						|
          assert fst.name == snd.name; snd.name
 | 
						|
        );
 | 
						|
     in
 | 
						|
       keepNames (
 | 
						|
         filterChanges (
 | 
						|
           zipLists (collect isOption old) (collect isOption new)
 | 
						|
         )
 | 
						|
       );
 | 
						|
 | 
						|
 | 
						|
  # Create a list of modules where each module contains only one failling
 | 
						|
  # options.
 | 
						|
  introspectionModules = with pkgs.lib;
 | 
						|
    let
 | 
						|
      setIntrospection = opt: rec {
 | 
						|
        name = opt.name;
 | 
						|
        path = splitString "." opt.name;
 | 
						|
        config = setAttrByPath path
 | 
						|
          (throw "Usage introspection of '${name}' by forced failure.");
 | 
						|
      };
 | 
						|
    in
 | 
						|
      map setIntrospection (collect isOption eval.options);
 | 
						|
 | 
						|
  overrideConfig = thrower:
 | 
						|
    pkgs.lib.recursiveUpdateUntil (path: old: new:
 | 
						|
      path == thrower.path
 | 
						|
    ) eval.config thrower.config;
 | 
						|
 | 
						|
 | 
						|
  graph = with pkgs.lib;
 | 
						|
    map (thrower: {
 | 
						|
      option = thrower.name;
 | 
						|
      usedBy = reportNewFailures eval.options (evalFun {
 | 
						|
        extraArgs = {
 | 
						|
          config = overrideConfig thrower;
 | 
						|
        };
 | 
						|
      }).options;
 | 
						|
    }) introspectionModules;
 | 
						|
 | 
						|
  graphToDot = graph: with pkgs.lib; ''
 | 
						|
    digraph "Option Usages" {
 | 
						|
      ${concatMapStrings ({option, usedBy}:
 | 
						|
          assert __trace option true;
 | 
						|
          if displayOptions == [] || elem option displayOptions then
 | 
						|
            concatMapStrings (user: ''
 | 
						|
              "${option}" -> "${user}"''
 | 
						|
            ) usedBy
 | 
						|
          else ""
 | 
						|
        ) graph}
 | 
						|
    }
 | 
						|
  '';
 | 
						|
 | 
						|
in
 | 
						|
 | 
						|
pkgs.texFunctions.dot2pdf {
 | 
						|
  dotGraph = pkgs.writeTextFile {
 | 
						|
    name = "option_usages.dot";
 | 
						|
    text = graphToDot graph;
 | 
						|
  };
 | 
						|
}
 |