at 15.09-beta 5.3 kB view raw
1# Nixpkgs/NixOS option handling. 2 3let lib = import ./default.nix; in 4 5with import ./trivial.nix; 6with import ./lists.nix; 7with import ./attrsets.nix; 8with import ./strings.nix; 9 10rec { 11 12 isOption = lib.isType "option"; 13 mkOption = 14 { default ? null # Default value used when no definition is given in the configuration. 15 , defaultText ? null # Textual representation of the default, for in the manual. 16 , example ? null # Example value used in the manual. 17 , description ? null # String describing the option. 18 , type ? null # Option type, providing type-checking and value merging. 19 , apply ? null # Function that converts the option value to something else. 20 , internal ? null # Whether the option is for NixOS developers only. 21 , visible ? null # Whether the option shows up in the manual. 22 , readOnly ? null # Whether the option can be set only once 23 , options ? null # Obsolete, used by types.optionSet. 24 } @ attrs: 25 attrs // { _type = "option"; }; 26 27 mkEnableOption = name: mkOption { 28 default = false; 29 example = true; 30 description = "Whether to enable ${name}."; 31 type = lib.types.bool; 32 }; 33 34 # This option accept anything, but it does not produce any result. This 35 # is useful for sharing a module across different module sets without 36 # having to implement similar features as long as the value of the options 37 # are not expected. 38 mkSinkUndeclaredOptions = attrs: mkOption ({ 39 internal = true; 40 visible = false; 41 default = false; 42 description = "Sink for option definitions."; 43 type = mkOptionType { 44 name = "sink"; 45 check = x: true; 46 merge = loc: defs: false; 47 }; 48 apply = x: throw "Option value is not readable because the option is not declared."; 49 } // attrs); 50 51 mergeDefaultOption = loc: defs: 52 let list = getValues defs; in 53 if length list == 1 then head list 54 else if all isFunction list then x: mergeDefaultOption loc (map (f: f x) list) 55 else if all isList list then concatLists list 56 else if all isAttrs list then foldl' lib.mergeAttrs {} list 57 else if all isBool list then foldl' lib.or false list 58 else if all isString list then lib.concatStrings list 59 else if all isInt list && all (x: x == head list) list then head list 60 else throw "Cannot merge definitions of `${showOption loc}' given in ${showFiles (getFiles defs)}."; 61 62 mergeOneOption = loc: defs: 63 if defs == [] then abort "This case should never happen." 64 else if length defs != 1 then 65 throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}." 66 else (head defs).value; 67 68 /* "Merge" option definitions by checking that they all have the same value. */ 69 mergeEqualOption = loc: defs: 70 if defs == [] then abort "This case should never happen." 71 else foldl' (val: def: 72 if def.value != val then 73 throw "The option `${showOption loc}' has conflicting definitions, in ${showFiles (getFiles defs)}." 74 else 75 val) (head defs).value defs; 76 77 getValues = map (x: x.value); 78 getFiles = map (x: x.file); 79 80 81 # Generate documentation template from the list of option declaration like 82 # the set generated with filterOptionSets. 83 optionAttrSetToDocList = optionAttrSetToDocList' []; 84 85 optionAttrSetToDocList' = prefix: options: 86 concatMap (opt: 87 let 88 docOption = rec { 89 name = showOption opt.loc; 90 description = opt.description or (throw "Option `${name}' has no description."); 91 declarations = filter (x: x != unknownModule) opt.declarations; 92 internal = opt.internal or false; 93 visible = opt.visible or true; 94 readOnly = opt.readOnly or false; 95 type = opt.type.name or null; 96 } 97 // (if opt ? example then { example = scrubOptionValue opt.example; } else {}) 98 // (if opt ? default then { default = scrubOptionValue opt.default; } else {}) 99 // (if opt ? defaultText then { default = opt.defaultText; } else {}); 100 101 subOptions = 102 let ss = opt.type.getSubOptions opt.loc; 103 in if ss != {} then optionAttrSetToDocList' opt.loc ss else []; 104 in 105 [ docOption ] ++ subOptions) (collect isOption options); 106 107 108 /* This function recursively removes all derivation attributes from 109 `x' except for the `name' attribute. This is to make the 110 generation of `options.xml' much more efficient: the XML 111 representation of derivations is very large (on the order of 112 megabytes) and is not actually used by the manual generator. */ 113 scrubOptionValue = x: 114 if isDerivation x then 115 { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; } 116 else if isList x then map scrubOptionValue x 117 else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"]) 118 else x; 119 120 121 /* For use in the example option attribute. It causes the given 122 text to be included verbatim in documentation. This is necessary 123 for example values that are not simple values, e.g., 124 functions. */ 125 literalExample = text: { _type = "literalExample"; inherit text; }; 126 127 128 /* Helper functions. */ 129 showOption = concatStringsSep "."; 130 showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files); 131 unknownModule = "<unknown-file>"; 132 133}