1#!/usr/bin/env python3 2# QEMU library 3# 4# Copyright (C) 2020 Red Hat Inc. 5# 6# Authors: 7# Eduardo Habkost <ehabkost@redhat.com> 8# 9# This work is licensed under the terms of the GNU GPL, version 2. See 10# the COPYING file in the top-level directory. 11# 12import sys 13import argparse 14import os 15import os.path 16import re 17from typing import * 18 19from codeconverter.patching import FileInfo, match_class_dict, FileList 20import codeconverter.qom_macros 21from codeconverter.qom_type_info import TI_FIELDS, type_infos, TypeInfoVar 22 23import logging 24logger = logging.getLogger(__name__) 25DBG = logger.debug 26INFO = logger.info 27WARN = logger.warning 28 29def process_all_files(parser: argparse.ArgumentParser, args: argparse.Namespace) -> None: 30 DBG("filenames: %r", args.filenames) 31 32 files = FileList() 33 files.extend(FileInfo(files, fn, args.force) for fn in args.filenames) 34 for f in files: 35 DBG('opening %s', f.filename) 36 f.load() 37 38 if args.table: 39 fields = ['filename', 'variable_name'] + TI_FIELDS 40 print('\t'.join(fields)) 41 for f in files: 42 for t in f.matches_of_type(TypeInfoVar): 43 assert isinstance(t, TypeInfoVar) 44 values = [f.filename, t.name] + \ 45 [t.get_raw_initializer_value(f) 46 for f in TI_FIELDS] 47 DBG('values: %r', values) 48 assert all('\t' not in v for v in values) 49 values = [v.replace('\n', ' ').replace('"', '') for v in values] 50 print('\t'.join(values)) 51 return 52 53 match_classes = match_class_dict() 54 if not args.patterns: 55 parser.error("--pattern is required") 56 57 classes = [p for arg in args.patterns 58 for p in re.split(r'[\s,]', arg) 59 if p.strip()] 60 for c in classes: 61 if c not in match_classes \ 62 or not match_classes[c].regexp: 63 print("Invalid pattern name: %s" % (c), file=sys.stderr) 64 print("Valid patterns:", file=sys.stderr) 65 print(PATTERN_HELP, file=sys.stderr) 66 sys.exit(1) 67 68 DBG("classes: %r", classes) 69 files.patch_content(max_passes=args.passes, class_names=classes) 70 71 for f in files: 72 #alltypes.extend(f.type_infos) 73 #full_types.extend(f.full_types()) 74 75 if not args.dry_run: 76 if args.inplace: 77 f.patch_inplace() 78 if args.diff: 79 f.show_diff() 80 if not args.diff and not args.inplace: 81 f.write_to_file(sys.stdout) 82 sys.stdout.flush() 83 84 85PATTERN_HELP = ('\n'.join(" %s: %s" % (n, str(c.__doc__).strip()) 86 for (n,c) in sorted(match_class_dict().items()) 87 if c.has_replacement_rule())) 88 89def main() -> None: 90 p = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) 91 p.add_argument('filenames', nargs='+') 92 p.add_argument('--passes', type=int, default=1, 93 help="Number of passes (0 means unlimited)") 94 p.add_argument('--pattern', required=True, action='append', 95 default=[], dest='patterns', 96 help="Pattern to scan for") 97 p.add_argument('--inplace', '-i', action='store_true', 98 help="Patch file in place") 99 p.add_argument('--dry-run', action='store_true', 100 help="Don't patch files or print patching results") 101 p.add_argument('--force', '-f', action='store_true', 102 help="Perform changes even if not completely safe") 103 p.add_argument('--diff', action='store_true', 104 help="Print diff output on stdout") 105 p.add_argument('--debug', '-d', action='store_true', 106 help="Enable debugging") 107 p.add_argument('--verbose', '-v', action='store_true', 108 help="Verbose logging on stderr") 109 p.add_argument('--table', action='store_true', 110 help="Print CSV table of type information") 111 p.add_argument_group("Valid pattern names", 112 PATTERN_HELP) 113 args = p.parse_args() 114 115 loglevel = (logging.DEBUG if args.debug 116 else logging.INFO if args.verbose 117 else logging.WARN) 118 logging.basicConfig(format='%(levelname)s: %(message)s', level=loglevel) 119 DBG("args: %r", args) 120 process_all_files(p, args) 121 122if __name__ == '__main__': 123 main()