320 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
#! @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=""
 | 
						|
 | 
						|
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(){
 | 
						|
  result=$(nix-instantiate ${nixPath:+$nixPath} - --eval-only "$@" 2>&1)
 | 
						|
  if test $? -eq 0; then
 | 
						|
      cat <<EOF
 | 
						|
$result
 | 
						|
EOF
 | 
						|
      return 0;
 | 
						|
  else
 | 
						|
      sed -n '
 | 
						|
  /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; };
 | 
						|
  /^warning: Nix search path/ { p; };
 | 
						|
' <<EOF
 | 
						|
$result
 | 
						|
EOF
 | 
						|
      return 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.name; }
 | 
						|
  // 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
 | 
						|
  eval printf $(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 names=$(attrNames "$result" 2> /dev/null); then
 | 
						|
    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."
 | 
						|
    echo $result
 | 
						|
  fi
 | 
						|
fi
 |