summary refs log tree commit diff
path: root/lib/options.nix
diff options
context:
space:
mode:
Diffstat (limited to 'lib/options.nix')
-rw-r--r--lib/options.nix120
1 files changed, 120 insertions, 0 deletions
diff --git a/lib/options.nix b/lib/options.nix
new file mode 100644
index 000000000000..63798c4faa3b
--- /dev/null
+++ b/lib/options.nix
@@ -0,0 +1,120 @@
+# Nixpkgs/NixOS option handling.
+
+let lib = import ./default.nix; in
+
+with import ./trivial.nix;
+with import ./lists.nix;
+with import ./misc.nix;
+with import ./attrsets.nix;
+with import ./strings.nix;
+
+rec {
+
+  isOption = lib.isType "option";
+  mkOption =
+    { default ? null # Default value used when no definition is given in the configuration.
+    , defaultText ? null # Textual representation of the default, for in the manual.
+    , example ? null # Example value used in the manual.
+    , description ? null # String describing the option.
+    , type ? null # Option type, providing type-checking and value merging.
+    , apply ? null # Function that converts the option value to something else.
+    , internal ? null # Whether the option is for NixOS developers only.
+    , visible ? null # Whether the option shows up in the manual.
+    , options ? null # Obsolete, used by types.optionSet.
+    } @ attrs:
+    attrs // { _type = "option"; };
+
+  mkEnableOption = name: mkOption {
+    default = false;
+    example = true;
+    description = "Whether to enable ${name}.";
+    type = lib.types.bool;
+  };
+
+  mergeDefaultOption = loc: defs:
+    let list = getValues defs; in
+    if length list == 1 then head list
+    else if all builtins.isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
+    else if all isList list then concatLists list
+    else if all isAttrs list then fold lib.mergeAttrs {} list
+    else if all builtins.isBool list then fold lib.or false list
+    else if all builtins.isString list then lib.concatStrings list
+    else if all builtins.isInt list && all (x: x == head list) list then head list
+    else throw "Cannot merge definitions of `${showOption loc}' given in ${showFiles (getFiles defs)}.";
+
+  /* Obsolete, will remove soon.  Specify an option type or apply
+     function instead.  */
+  mergeTypedOption = typeName: predicate: merge: loc: list:
+    let list' = map (x: x.value) list; in
+    if all predicate list then merge list'
+    else throw "Expected a ${typeName}.";
+
+  mergeEnableOption = mergeTypedOption "boolean"
+    (x: true == x || false == x) (fold lib.or false);
+
+  mergeListOption = mergeTypedOption "list" isList concatLists;
+
+  mergeStringOption = mergeTypedOption "string" builtins.isString lib.concatStrings;
+
+  mergeOneOption = loc: defs:
+    if defs == [] then abort "This case should never happen."
+    else if length defs != 1 then
+      throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
+    else (head defs).value;
+
+  getValues = map (x: x.value);
+  getFiles = map (x: x.file);
+
+
+  # Generate documentation template from the list of option declaration like
+  # the set generated with filterOptionSets.
+  optionAttrSetToDocList = optionAttrSetToDocList' [];
+
+  optionAttrSetToDocList' = prefix: options:
+    fold (opt: rest:
+      let
+        docOption = rec {
+          name = showOption opt.loc;
+          description = opt.description or (throw "Option `${name}' has no description.");
+          declarations = filter (x: x != unknownModule) opt.declarations;
+          internal = opt.internal or false;
+          visible = opt.visible or true;
+        }
+        // optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
+        // optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
+        // optionalAttrs (opt ? defaultText) { default = opt.defaultText; };
+
+        subOptions =
+          let ss = opt.type.getSubOptions opt.loc;
+          in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
+      in
+        # FIXME: expensive, O(n^2)
+        [ docOption ] ++ subOptions ++ rest) [] (collect isOption options);
+
+
+  /* This function recursively removes all derivation attributes from
+     `x' except for the `name' attribute.  This is to make the
+     generation of `options.xml' much more efficient: the XML
+     representation of derivations is very large (on the order of
+     megabytes) and is not actually used by the manual generator. */
+  scrubOptionValue = x:
+    if isDerivation x then
+      { type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
+    else if isList x then map scrubOptionValue x
+    else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
+    else x;
+
+
+  /* For use in the ‘example’ option attribute.  It causes the given
+     text to be included verbatim in documentation.  This is necessary
+     for example values that are not simple values, e.g.,
+     functions. */
+  literalExample = text: { _type = "literalExample"; inherit text; };
+
+
+  /* Helper functions. */
+  showOption = concatStringsSep ".";
+  showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
+  unknownModule = "<unknown-file>";
+
+}