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