1import logging 2import sys 3 4from c_common.scriptutil import ( 5 CLIArgSpec as Arg, 6 add_verbosity_cli, 7 add_traceback_cli, 8 add_kind_filtering_cli, 9 add_files_cli, 10 add_failure_filtering_cli, 11 add_commands_cli, 12 process_args_by_key, 13 configure_logger, 14 get_prog, 15 main_for_filenames, 16) 17from . import ( 18 errors as _errors, 19 get_preprocessor as _get_preprocessor, 20) 21 22 23FAIL = { 24 'err': _errors.ErrorDirectiveError, 25 'deps': _errors.MissingDependenciesError, 26 'os': _errors.OSMismatchError, 27} 28FAIL_DEFAULT = tuple(v for v in FAIL if v != 'os') 29 30 31logger = logging.getLogger(__name__) 32 33 34################################## 35# CLI helpers 36 37def add_common_cli(parser, *, get_preprocessor=_get_preprocessor): 38 parser.add_argument('--macros', action='append') 39 parser.add_argument('--incldirs', action='append') 40 parser.add_argument('--same', action='append') 41 process_fail_arg = add_failure_filtering_cli(parser, FAIL) 42 43 def process_args(args, *, argv): 44 ns = vars(args) 45 46 process_fail_arg(args, argv) 47 ignore_exc = ns.pop('ignore_exc') 48 # We later pass ignore_exc to _get_preprocessor(). 49 50 args.get_file_preprocessor = get_preprocessor( 51 file_macros=ns.pop('macros'), 52 file_incldirs=ns.pop('incldirs'), 53 file_same=ns.pop('same'), 54 ignore_exc=ignore_exc, 55 log_err=print, 56 ) 57 return process_args 58 59 60def _iter_preprocessed(filename, *, 61 get_preprocessor, 62 match_kind=None, 63 pure=False, 64 ): 65 preprocess = get_preprocessor(filename) 66 for line in preprocess(tool=not pure) or (): 67 if match_kind is not None and not match_kind(line.kind): 68 continue 69 yield line 70 71 72####################################### 73# the commands 74 75def _cli_preprocess(parser, excluded=None, **prepr_kwargs): 76 parser.add_argument('--pure', action='store_true') 77 parser.add_argument('--no-pure', dest='pure', action='store_const', const=False) 78 process_kinds = add_kind_filtering_cli(parser) 79 process_common = add_common_cli(parser, **prepr_kwargs) 80 parser.add_argument('--raw', action='store_true') 81 process_files = add_files_cli(parser, excluded=excluded) 82 83 return [ 84 process_kinds, 85 process_common, 86 process_files, 87 ] 88 89 90def cmd_preprocess(filenames, *, 91 raw=False, 92 iter_filenames=None, 93 **kwargs 94 ): 95 if 'get_file_preprocessor' not in kwargs: 96 kwargs['get_file_preprocessor'] = _get_preprocessor() 97 if raw: 98 def show_file(filename, lines): 99 for line in lines: 100 print(line) 101 #print(line.raw) 102 else: 103 def show_file(filename, lines): 104 for line in lines: 105 linefile = '' 106 if line.filename != filename: 107 linefile = f' ({line.filename})' 108 text = line.data 109 if line.kind == 'comment': 110 text = '/* ' + line.data.splitlines()[0] 111 text += ' */' if '\n' in line.data else r'\n... */' 112 print(f' {line.lno:>4} {line.kind:10} | {text}') 113 114 filenames = main_for_filenames(filenames, iter_filenames) 115 for filename in filenames: 116 lines = _iter_preprocessed(filename, **kwargs) 117 show_file(filename, lines) 118 119 120def _cli_data(parser): 121 ... 122 123 return None 124 125 126def cmd_data(filenames, 127 **kwargs 128 ): 129 # XXX 130 raise NotImplementedError 131 132 133COMMANDS = { 134 'preprocess': ( 135 'preprocess the given C source & header files', 136 [_cli_preprocess], 137 cmd_preprocess, 138 ), 139 'data': ( 140 'check/manage local data (e.g. excludes, macros)', 141 [_cli_data], 142 cmd_data, 143 ), 144} 145 146 147####################################### 148# the script 149 150def parse_args(argv=sys.argv[1:], prog=sys.argv[0], *, 151 subset='preprocess', 152 excluded=None, 153 **prepr_kwargs 154 ): 155 import argparse 156 parser = argparse.ArgumentParser( 157 prog=prog or get_prog(), 158 ) 159 160 processors = add_commands_cli( 161 parser, 162 commands={k: v[1] for k, v in COMMANDS.items()}, 163 commonspecs=[ 164 add_verbosity_cli, 165 add_traceback_cli, 166 ], 167 subset=subset, 168 ) 169 170 args = parser.parse_args(argv) 171 ns = vars(args) 172 173 cmd = ns.pop('cmd') 174 175 verbosity, traceback_cm = process_args_by_key( 176 args, 177 argv, 178 processors[cmd], 179 ['verbosity', 'traceback_cm'], 180 ) 181 182 return cmd, ns, verbosity, traceback_cm 183 184 185def main(cmd, cmd_kwargs): 186 try: 187 run_cmd = COMMANDS[cmd][0] 188 except KeyError: 189 raise ValueError(f'unsupported cmd {cmd!r}') 190 run_cmd(**cmd_kwargs) 191 192 193if __name__ == '__main__': 194 cmd, cmd_kwargs, verbosity, traceback_cm = parse_args() 195 configure_logger(verbosity) 196 with traceback_cm: 197 main(cmd, cmd_kwargs) 198