about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/installer/tools/nixos-option.sh
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/installer/tools/nixos-option.sh')
-rw-r--r--nixpkgs/nixos/modules/installer/tools/nixos-option.sh327
1 files changed, 327 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/installer/tools/nixos-option.sh b/nixpkgs/nixos/modules/installer/tools/nixos-option.sh
new file mode 100644
index 000000000000..4560e9c7403a
--- /dev/null
+++ b/nixpkgs/nixos/modules/installer/tools/nixos-option.sh
@@ -0,0 +1,327 @@
+#! @shell@ -e
+
+# FIXME: rewrite this in a more suitable language.
+
+usage () {
+    exec man nixos-option
+    exit 1
+}
+
+#####################
+# Process Arguments #
+#####################
+
+xml=false
+verbose=false
+nixPath=""
+
+option=""
+exit_code=0
+
+argfun=""
+for arg; do
+  if test -z "$argfun"; then
+    case $arg in
+      -*)
+        sarg="$arg"
+        longarg=""
+        while test "$sarg" != "-"; do
+          case $sarg in
+            --*) longarg=$arg; sarg="--";;
+            -I) argfun="include_nixpath";;
+            -*) usage;;
+          esac
+          # remove the first letter option
+          sarg="-${sarg#??}"
+        done
+        ;;
+      *) longarg=$arg;;
+    esac
+    for larg in $longarg; do
+      case $larg in
+        --xml) xml=true;;
+        --verbose) verbose=true;;
+        --help) usage;;
+        -*) usage;;
+        *) if test -z "$option"; then
+             option="$larg"
+           else
+             usage
+           fi;;
+      esac
+    done
+  else
+    case $argfun in
+      set_*)
+        var=$(echo $argfun | sed 's,^set_,,')
+        eval $var=$arg
+        ;;
+      include_nixpath)
+        nixPath="-I $arg $nixPath"
+        ;;
+    esac
+    argfun=""
+  fi
+done
+
+if $verbose; then
+  set -x
+else
+  set +x
+fi
+
+#############################
+# Process the configuration #
+#############################
+
+evalNix(){
+  # disable `-e` flag, it's possible that the evaluation of `nix-instantiate` fails (e.g. due to broken pkgs)
+  set +e
+  result=$(nix-instantiate ${nixPath:+$nixPath} - --eval-only "$@" 2>&1)
+  exit_code=$?
+  set -e
+
+  if test $exit_code -eq 0; then
+      sed '/^warning: Nix search path/d' <<EOF
+$result
+EOF
+      return 0;
+  else
+      sed -n '
+  /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; };
+  /^warning: Nix search path/ { p; };
+' >&2 <<EOF
+$result
+EOF
+    exit_code=1
+  fi
+}
+
+header="let
+  nixos = import <nixpkgs/nixos> {};
+  nixpkgs = import <nixpkgs> {};
+in with nixpkgs.lib;
+"
+
+# This function is used for converting the option definition path given by
+# the user into accessors for reaching the definition and the declaration
+# corresponding to this option.
+generateAccessors(){
+  if result=$(evalNix --strict --show-trace <<EOF
+$header
+
+let
+  path = "${option:+$option}";
+  pathList = splitString "." path;
+
+  walkOptions = attrsNames: result:
+    if attrsNames == [] then
+      result
+    else
+      let name = head attrsNames; rest = tail attrsNames; in
+      if isOption result.options then
+        walkOptions rest {
+          options = result.options.type.getSubOptions "";
+          opt = ''(\${result.opt}.type.getSubOptions "")'';
+          cfg = ''\${result.cfg}."\${name}"'';
+        }
+      else
+        walkOptions rest {
+          options = result.options.\${name};
+          opt = ''\${result.opt}."\${name}"'';
+          cfg = ''\${result.cfg}."\${name}"'';
+        }
+    ;
+
+  walkResult = (if path == "" then x: x else walkOptions pathList) {
+    options = nixos.options;
+    opt = ''nixos.options'';
+    cfg = ''nixos.config'';
+  };
+
+in
+  ''let option = \${walkResult.opt}; config = \${walkResult.cfg}; in''
+EOF
+)
+  then
+      echo $result
+  else
+      # In case of error we want to ignore the error message roduced by the
+      # script above, as it is iterating over each attribute, which does not
+      # produce a nice error message.  The following code is a fallback
+      # solution which is cause a nicer error message in the next
+      # evaluation.
+      echo "\"let option = nixos.options${option:+.$option}; config = nixos.config${option:+.$option}; in\""
+  fi
+}
+
+header="$header
+$(eval echo $(generateAccessors))
+"
+
+evalAttr(){
+  local prefix="$1"
+  local strict="$2"
+  local suffix="$3"
+
+  # If strict is set, then set it to "true".
+  test -n "$strict" && strict=true
+
+  evalNix ${strict:+--strict} <<EOF
+$header
+
+let
+  value = $prefix${suffix:+.$suffix};
+  strict = ${strict:-false};
+  cleanOutput = x: with nixpkgs.lib;
+    if isDerivation x then x.outPath
+    else if isFunction x then "<CODE>"
+    else if strict then
+      if isAttrs x then mapAttrs (n: cleanOutput) x
+      else if isList x then map cleanOutput x
+      else x
+    else x;
+in
+  cleanOutput value
+EOF
+}
+
+evalOpt(){
+  evalAttr "option" "" "$@"
+}
+
+evalCfg(){
+  local strict="$1"
+  evalAttr "config" "$strict"
+}
+
+findSources(){
+  local suffix=$1
+  evalNix --strict <<EOF
+$header
+
+option.$suffix
+EOF
+}
+
+# Given a result from nix-instantiate, recover the list of attributes it
+# contains.
+attrNames() {
+  local attributeset=$1
+  # sed is used to replace un-printable subset by 0s, and to remove most of
+  # the inner-attribute set, which reduce the likelyhood to encounter badly
+  # pre-processed input.
+  echo "builtins.attrNames $attributeset" | \
+    sed 's,<[A-Z]*>,0,g; :inner; s/{[^\{\}]*};/0;/g; t inner;' | \
+    evalNix --strict
+}
+
+# map a simple list which contains strings or paths.
+nixMap() {
+  local fun="$1"
+  local list="$2"
+  local elem
+  for elem in $list; do
+    test $elem = '[' -o $elem = ']' && continue;
+    $fun $elem
+  done
+}
+
+# This duplicates the work made below, but it is useful for processing
+# the output of nixos-option with other tools such as nixos-gui.
+if $xml; then
+  evalNix --xml --no-location <<EOF
+$header
+
+let
+  sources = builtins.map (f: f.source);
+  opt = option;
+  cfg = config;
+in
+
+with nixpkgs.lib;
+
+let
+  optStrict = v:
+    let
+      traverse = x :
+        if isAttrs x then
+          if x ? outPath then true
+          else all id (mapAttrsFlatten (n: traverseNoAttrs) x)
+        else traverseNoAttrs x;
+      traverseNoAttrs = x:
+        # do not continue in attribute sets
+        if isAttrs x then true
+        else if isList x then all id (map traverse x)
+        else true;
+    in assert traverse v; v;
+in
+
+if isOption opt then
+  optStrict ({}
+  // optionalAttrs (opt ? default) { inherit (opt) default; }
+  // optionalAttrs (opt ? example) { inherit (opt) example; }
+  // optionalAttrs (opt ? description) { inherit (opt) description; }
+  // optionalAttrs (opt ? type) { typename = opt.type.description; }
+  // optionalAttrs (opt ? options) { inherit (opt) options; }
+  // {
+    # to disambiguate the xml output.
+    _isOption = true;
+    declarations = sources opt.declarations;
+    definitions = sources opt.definitions;
+    value = cfg;
+  })
+else
+  opt
+EOF
+  exit $?
+fi
+
+if test "$(evalOpt "_type" 2> /dev/null)" = '"option"'; then
+  echo "Value:"
+  evalCfg 1
+
+  echo
+
+  echo "Default:"
+  if default=$(evalOpt "default" - 2> /dev/null); then
+    echo "$default"
+  else
+    echo "<None>"
+  fi
+  echo
+  if example=$(evalOpt "example" - 2> /dev/null); then
+    echo "Example:"
+    echo "$example"
+    echo
+  fi
+  echo "Description:"
+  echo
+  echo $(evalOpt "description")
+
+  echo $desc;
+
+  printPath () { echo "  $1"; }
+
+  echo "Declared by:"
+  nixMap printPath "$(findSources "declarations")"
+  echo
+  echo "Defined by:"
+  nixMap printPath "$(findSources "files")"
+  echo
+
+else
+  # echo 1>&2 "Warning: This value is not an option."
+
+  result=$(evalCfg "")
+  if [ ! -z "$result" ]; then
+    names=$(attrNames "$result" 2> /dev/null)
+    echo 1>&2 "This attribute set contains:"
+    escapeQuotes () { eval echo "$1"; }
+    nixMap escapeQuotes "$names"
+  else
+    echo 1>&2 "An error occurred while looking for attribute names. Are you sure that '$option' exists?"
+  fi
+fi
+
+exit $exit_code