nixpkgs-suyu/nixos/modules/installer/tools/nixos-option.sh
2014-12-20 19:16:43 +01:00

301 lines
6.2 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
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="--";;
-*) 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
;;
esac
argfun=""
fi
done
if $verbose; then
set -x
else
set +x
fi
#############################
# Process the configuration #
#############################
evalNix(){
result=$(nix-instantiate - --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; }' <<EOF
$result
EOF
return 1;
fi
}
header="
let
nixos = import <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(){
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
}
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