at 25.11-pre 3.6 kB view raw
1import collections 2import json 3import os 4import sys 5from typing import Any, Dict, List 6 7JSON = Dict[str, Any] 8 9class Key: 10 def __init__(self, path: List[str]): 11 self.path = path 12 def __hash__(self): 13 result = 0 14 for id in self.path: 15 result ^= hash(id) 16 return result 17 def __eq__(self, other): 18 return type(self) is type(other) and self.path == other.path 19 20Option = collections.namedtuple('Option', ['name', 'value']) 21 22# pivot a dict of options keyed by their display name to a dict keyed by their path 23def pivot(options: Dict[str, JSON]) -> Dict[Key, Option]: 24 result: Dict[Key, Option] = dict() 25 for (name, opt) in options.items(): 26 result[Key(opt['loc'])] = Option(name, opt) 27 return result 28 29# pivot back to indexed-by-full-name 30# like the docbook build we'll just fail if multiple options with differing locs 31# render to the same option name. 32def unpivot(options: Dict[Key, Option]) -> Dict[str, JSON]: 33 result: Dict[str, Dict] = dict() 34 for (key, opt) in options.items(): 35 if opt.name in result: 36 raise RuntimeError( 37 'multiple options with colliding ids found', 38 opt.name, 39 result[opt.name]['loc'], 40 opt.value['loc'], 41 ) 42 result[opt.name] = opt.value 43 return result 44 45warningsAreErrors = False 46optOffset = 0 47for arg in sys.argv[1:]: 48 if arg == "--warnings-are-errors": 49 optOffset += 1 50 warningsAreErrors = True 51 52options = pivot(json.load(open(sys.argv[1 + optOffset], 'r'))) 53overrides = pivot(json.load(open(sys.argv[2 + optOffset], 'r'))) 54 55# fix up declaration paths in lazy options, since we don't eval them from a full nixpkgs dir 56for (k, v) in options.items(): 57 # The _module options are not declared in nixos/modules 58 if v.value['loc'][0] != "_module": 59 v.value['declarations'] = list(map(lambda s: f'nixos/modules/{s}' if isinstance(s, str) else s, v.value['declarations'])) 60 61# merge both descriptions 62for (k, v) in overrides.items(): 63 cur = options.setdefault(k, v).value 64 for (ok, ov) in v.value.items(): 65 if ok == 'declarations': 66 decls = cur[ok] 67 for d in ov: 68 if d not in decls: 69 decls += [d] 70 elif ok == "type": 71 # ignore types of placeholder options 72 if ov != "_unspecified" or cur[ok] == "_unspecified": 73 cur[ok] = ov 74 elif ov is not None or cur.get(ok, None) is None: 75 cur[ok] = ov 76 77severity = "error" if warningsAreErrors else "warning" 78 79# check that every option has a description 80hasWarnings = False 81hasErrors = False 82for (k, v) in options.items(): 83 if v.value.get('description', None) is None: 84 hasWarnings = True 85 print(f"\x1b[1;31m{severity}: option {v.name} has no description\x1b[0m", file=sys.stderr) 86 v.value['description'] = "This option has no description." 87 if v.value.get('type', "unspecified") == "unspecified": 88 hasWarnings = True 89 print( 90 f"\x1b[1;31m{severity}: option {v.name} has no type. Please specify a valid type, see " + 91 "https://nixos.org/manual/nixos/stable/index.html#sec-option-types\x1b[0m", file=sys.stderr) 92 93if hasErrors: 94 sys.exit(1) 95if hasWarnings and warningsAreErrors: 96 print( 97 "\x1b[1;31m" + 98 "Treating warnings as errors. Set documentation.nixos.options.warningsAreErrors " + 99 "to false to ignore these warnings." + 100 "\x1b[0m", 101 file=sys.stderr) 102 sys.exit(1) 103 104json.dump(unpivot(options), fp=sys.stdout)