at 17.09-beta 7.0 kB view raw
1#! @shell@ -e 2 3# FIXME: rewrite this in a more suitable language. 4 5usage () { 6 exec man nixos-option 7 exit 1 8} 9 10##################### 11# Process Arguments # 12##################### 13 14xml=false 15verbose=false 16nixPath="" 17 18option="" 19 20argfun="" 21for arg; do 22 if test -z "$argfun"; then 23 case $arg in 24 -*) 25 sarg="$arg" 26 longarg="" 27 while test "$sarg" != "-"; do 28 case $sarg in 29 --*) longarg=$arg; sarg="--";; 30 -I) argfun="include_nixpath";; 31 -*) usage;; 32 esac 33 # remove the first letter option 34 sarg="-${sarg#??}" 35 done 36 ;; 37 *) longarg=$arg;; 38 esac 39 for larg in $longarg; do 40 case $larg in 41 --xml) xml=true;; 42 --verbose) verbose=true;; 43 --help) usage;; 44 -*) usage;; 45 *) if test -z "$option"; then 46 option="$larg" 47 else 48 usage 49 fi;; 50 esac 51 done 52 else 53 case $argfun in 54 set_*) 55 var=$(echo $argfun | sed 's,^set_,,') 56 eval $var=$arg 57 ;; 58 include_nixpath) 59 nixPath="-I $arg $nixPath" 60 ;; 61 esac 62 argfun="" 63 fi 64done 65 66if $verbose; then 67 set -x 68else 69 set +x 70fi 71 72############################# 73# Process the configuration # 74############################# 75 76evalNix(){ 77 result=$(nix-instantiate ${nixPath:+$nixPath} - --eval-only "$@" 2>&1) 78 if test $? -eq 0; then 79 cat <<EOF 80$result 81EOF 82 return 0; 83 else 84 sed -n ' 85 /^error/ { s/, at (string):[0-9]*:[0-9]*//; p; }; 86 /^warning: Nix search path/ { p; }; 87' <<EOF 88$result 89EOF 90 return 1; 91 fi 92} 93 94header="let 95 nixos = import <nixpkgs/nixos> {}; 96 nixpkgs = import <nixpkgs> {}; 97in with nixpkgs.lib; 98" 99 100# This function is used for converting the option definition path given by 101# the user into accessors for reaching the definition and the declaration 102# corresponding to this option. 103generateAccessors(){ 104 if result=$(evalNix --strict --show-trace <<EOF 105$header 106 107let 108 path = "${option:+$option}"; 109 pathList = splitString "." path; 110 111 walkOptions = attrsNames: result: 112 if attrsNames == [] then 113 result 114 else 115 let name = head attrsNames; rest = tail attrsNames; in 116 if isOption result.options then 117 walkOptions rest { 118 options = result.options.type.getSubOptions ""; 119 opt = ''(\${result.opt}.type.getSubOptions "")''; 120 cfg = ''\${result.cfg}."\${name}"''; 121 } 122 else 123 walkOptions rest { 124 options = result.options.\${name}; 125 opt = ''\${result.opt}."\${name}"''; 126 cfg = ''\${result.cfg}."\${name}"''; 127 } 128 ; 129 130 walkResult = (if path == "" then x: x else walkOptions pathList) { 131 options = nixos.options; 132 opt = ''nixos.options''; 133 cfg = ''nixos.config''; 134 }; 135 136in 137 ''let option = \${walkResult.opt}; config = \${walkResult.cfg}; in'' 138EOF 139) 140 then 141 echo $result 142 else 143 # In case of error we want to ignore the error message roduced by the 144 # script above, as it is iterating over each attribute, which does not 145 # produce a nice error message. The following code is a fallback 146 # solution which is cause a nicer error message in the next 147 # evaluation. 148 echo "\"let option = nixos.options${option:+.$option}; config = nixos.config${option:+.$option}; in\"" 149 fi 150} 151 152header="$header 153$(eval echo $(generateAccessors)) 154" 155 156evalAttr(){ 157 local prefix="$1" 158 local strict="$2" 159 local suffix="$3" 160 161 # If strict is set, then set it to "true". 162 test -n "$strict" && strict=true 163 164 evalNix ${strict:+--strict} <<EOF 165$header 166 167let 168 value = $prefix${suffix:+.$suffix}; 169 strict = ${strict:-false}; 170 cleanOutput = x: with nixpkgs.lib; 171 if isDerivation x then x.outPath 172 else if isFunction x then "<CODE>" 173 else if strict then 174 if isAttrs x then mapAttrs (n: cleanOutput) x 175 else if isList x then map cleanOutput x 176 else x 177 else x; 178in 179 cleanOutput value 180EOF 181} 182 183evalOpt(){ 184 evalAttr "option" "" "$@" 185} 186 187evalCfg(){ 188 local strict="$1" 189 evalAttr "config" "$strict" 190} 191 192findSources(){ 193 local suffix=$1 194 evalNix --strict <<EOF 195$header 196 197option.$suffix 198EOF 199} 200 201# Given a result from nix-instantiate, recover the list of attributes it 202# contains. 203attrNames() { 204 local attributeset=$1 205 # sed is used to replace un-printable subset by 0s, and to remove most of 206 # the inner-attribute set, which reduce the likelyhood to encounter badly 207 # pre-processed input. 208 echo "builtins.attrNames $attributeset" | \ 209 sed 's,<[A-Z]*>,0,g; :inner; s/{[^\{\}]*};/0;/g; t inner;' | \ 210 evalNix --strict 211} 212 213# map a simple list which contains strings or paths. 214nixMap() { 215 local fun="$1" 216 local list="$2" 217 local elem 218 for elem in $list; do 219 test $elem = '[' -o $elem = ']' && continue; 220 $fun $elem 221 done 222} 223 224# This duplicates the work made below, but it is useful for processing 225# the output of nixos-option with other tools such as nixos-gui. 226if $xml; then 227 evalNix --xml --no-location <<EOF 228$header 229 230let 231 sources = builtins.map (f: f.source); 232 opt = option; 233 cfg = config; 234in 235 236with nixpkgs.lib; 237 238let 239 optStrict = v: 240 let 241 traverse = x : 242 if isAttrs x then 243 if x ? outPath then true 244 else all id (mapAttrsFlatten (n: traverseNoAttrs) x) 245 else traverseNoAttrs x; 246 traverseNoAttrs = x: 247 # do not continue in attribute sets 248 if isAttrs x then true 249 else if isList x then all id (map traverse x) 250 else true; 251 in assert traverse v; v; 252in 253 254if isOption opt then 255 optStrict ({} 256 // optionalAttrs (opt ? default) { inherit (opt) default; } 257 // optionalAttrs (opt ? example) { inherit (opt) example; } 258 // optionalAttrs (opt ? description) { inherit (opt) description; } 259 // optionalAttrs (opt ? type) { typename = opt.type.description; } 260 // optionalAttrs (opt ? options) { inherit (opt) options; } 261 // { 262 # to disambiguate the xml output. 263 _isOption = true; 264 declarations = sources opt.declarations; 265 definitions = sources opt.definitions; 266 value = cfg; 267 }) 268else 269 opt 270EOF 271 exit $? 272fi 273 274if test "$(evalOpt "_type" 2> /dev/null)" = '"option"'; then 275 echo "Value:" 276 evalCfg 1 277 278 echo 279 280 echo "Default:" 281 if default=$(evalOpt "default" - 2> /dev/null); then 282 echo "$default" 283 else 284 echo "<None>" 285 fi 286 echo 287 if example=$(evalOpt "example" - 2> /dev/null); then 288 echo "Example:" 289 echo "$example" 290 echo 291 fi 292 echo "Description:" 293 echo 294 eval printf $(evalOpt "description") 295 296 echo $desc; 297 298 printPath () { echo " $1"; } 299 300 echo "Declared by:" 301 nixMap printPath "$(findSources "declarations")" 302 echo 303 echo "Defined by:" 304 nixMap printPath "$(findSources "files")" 305 echo 306 307else 308 # echo 1>&2 "Warning: This value is not an option." 309 310 result=$(evalCfg "") 311 if names=$(attrNames "$result" 2> /dev/null); then 312 echo 1>&2 "This attribute set contains:" 313 escapeQuotes () { eval echo "$1"; } 314 nixMap escapeQuotes "$names" 315 else 316 echo 1>&2 "An error occurred while looking for attribute names." 317 echo $result 318 fi 319fi