| 
									
										
										
										
											2012-03-02 12:38:22 +00:00
										 |  |  | { configuration ? import ../lib/from-env.nix "NIXOS_CONFIG" <nixos-config> | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  | # provide an option name, as a string literal. | 
					
						
							|  |  |  | , testOption ? null | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # provide a list of option names, as string literals. | 
					
						
							|  |  |  | , testOptions ? [ ] | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  | }: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  | # 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; | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | let | 
					
						
							| 
									
										
										
										
											2011-09-14 18:20:50 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |   evalFun = { | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |     specialArgs ? {} | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |   }: import ../lib/eval-config.nix { | 
					
						
							|  |  |  |        modules = [ configuration ]; | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |        inherit specialArgs; | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |      }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   eval = evalFun {}; | 
					
						
							|  |  |  |   inherit (eval) pkgs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |   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" | 
					
						
							|  |  |  |     "kde.extraPackages" | 
					
						
							|  |  |  |   ]; | 
					
						
							|  |  |  |   excludeOptions = list: | 
					
						
							|  |  |  |     filter (opt: !(elem (showOption opt.loc) excludedOptions)) list; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   reportNewFailures = old: new: | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     let | 
					
						
							|  |  |  |       filterChanges = | 
					
						
							|  |  |  |         filter ({fst, snd}: | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |           !(fst.success -> snd.success) | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       keepNames = | 
					
						
							|  |  |  |         map ({fst, snd}: | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |           /* assert fst.name == snd.name; */ snd.name | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |       # 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)); | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |      in | 
					
						
							|  |  |  |        keepNames ( | 
					
						
							|  |  |  |          filterChanges ( | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |            zipLists (tryCollectOptions old) (tryCollectOptions new) | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |          ) | 
					
						
							|  |  |  |        ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Create a list of modules where each module contains only one failling | 
					
						
							|  |  |  |   # options. | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |   introspectionModules = | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     let | 
					
						
							| 
									
										
										
										
											2009-09-29 16:42:22 +00:00
										 |  |  |       setIntrospection = opt: rec { | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |         name = showOption opt.loc; | 
					
						
							|  |  |  |         path = opt.loc; | 
					
						
							| 
									
										
										
										
											2009-09-29 16:42:22 +00:00
										 |  |  |         config = setAttrByPath path | 
					
						
							|  |  |  |           (throw "Usage introspection of '${name}' by forced failure."); | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     in | 
					
						
							| 
									
										
										
										
											2009-09-29 16:42:22 +00:00
										 |  |  |       map setIntrospection (collect isOption eval.options); | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-29 16:42:22 +00:00
										 |  |  |   overrideConfig = thrower: | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |     recursiveUpdateUntil (path: old: new: | 
					
						
							| 
									
										
										
										
											2009-09-29 16:42:22 +00:00
										 |  |  |       path == thrower.path | 
					
						
							|  |  |  |     ) eval.config thrower.config; | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |   graph = | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     map (thrower: { | 
					
						
							| 
									
										
										
										
											2009-09-29 16:42:22 +00:00
										 |  |  |       option = thrower.name; | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |       usedBy = assert __trace "Investigate ${thrower.name}" true; | 
					
						
							|  |  |  |         reportNewFailures eval.options (evalFun { | 
					
						
							|  |  |  |           specialArgs = { | 
					
						
							|  |  |  |             config = overrideConfig thrower; | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         }).options; | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     }) introspectionModules; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |   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: ''
 | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     digraph "Option Usages" { | 
					
						
							|  |  |  |       ${concatMapStrings ({option, usedBy}: | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |           concatMapStrings (user: ''
 | 
					
						
							|  |  |  |             "${option}" -> "${user}"''
 | 
					
						
							|  |  |  |           ) usedBy | 
					
						
							|  |  |  |         ) displayOptionsGraph} | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     } | 
					
						
							|  |  |  |   '';
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |   graphToText = graph: | 
					
						
							|  |  |  |     concatMapStrings ({option, usedBy}: | 
					
						
							|  |  |  |         concatMapStrings (user: ''
 | 
					
						
							|  |  |  |           ${user} | 
					
						
							|  |  |  |         '') usedBy
 | 
					
						
							|  |  |  |       ) displayOptionsGraph; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  | in | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  | rec { | 
					
						
							|  |  |  |   dotContent = graphToDot graph; | 
					
						
							|  |  |  |   dot = pkgs.writeTextFile { | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |     name = "option_usages.dot"; | 
					
						
							| 
									
										
										
										
											2015-08-09 14:40:01 +02:00
										 |  |  |     text = dotContent; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   pdf = pkgs.texFunctions.dot2pdf { | 
					
						
							|  |  |  |     dotGraph = dot; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   txtContent = graphToText graph; | 
					
						
							|  |  |  |   txt = pkgs.writeTextFile { | 
					
						
							|  |  |  |     name = "option_usages.txt"; | 
					
						
							|  |  |  |     text = txtContent; | 
					
						
							| 
									
										
										
										
											2009-09-28 18:26:07 +00:00
										 |  |  |   }; | 
					
						
							|  |  |  | } |