Eduardo Habkost | 94dfc0f | 2020-08-31 17:07:28 -0400 | [diff] [blame] | 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 | # |
| 12 | import sys |
| 13 | import argparse |
| 14 | import os |
| 15 | import os.path |
| 16 | import re |
| 17 | from typing import * |
| 18 | |
| 19 | from codeconverter.patching import FileInfo, match_class_dict, FileList |
| 20 | import codeconverter.qom_macros |
| 21 | from codeconverter.qom_type_info import TI_FIELDS, type_infos, TypeInfoVar |
| 22 | |
| 23 | import logging |
| 24 | logger = logging.getLogger(__name__) |
| 25 | DBG = logger.debug |
| 26 | INFO = logger.info |
| 27 | WARN = logger.warning |
| 28 | |
| 29 | def 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] + \ |
Eduardo Habkost | 4a15e5b | 2020-09-16 14:25:15 -0400 | [diff] [blame] | 45 | [t.get_raw_initializer_value(f) |
Eduardo Habkost | 94dfc0f | 2020-08-31 17:07:28 -0400 | [diff] [blame] | 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 |
Eduardo Habkost | 4a15e5b | 2020-09-16 14:25:15 -0400 | [diff] [blame] | 58 | for p in re.split(r'[\s,]', arg) |
| 59 | if p.strip()] |
Eduardo Habkost | 94dfc0f | 2020-08-31 17:07:28 -0400 | [diff] [blame] | 60 | for c in classes: |
Eduardo Habkost | 4a15e5b | 2020-09-16 14:25:15 -0400 | [diff] [blame] | 61 | if c not in match_classes \ |
| 62 | or not match_classes[c].regexp: |
Eduardo Habkost | 94dfc0f | 2020-08-31 17:07:28 -0400 | [diff] [blame] | 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) |
Eduardo Habkost | 4a15e5b | 2020-09-16 14:25:15 -0400 | [diff] [blame] | 69 | files.patch_content(max_passes=args.passes, class_names=classes) |
Eduardo Habkost | 94dfc0f | 2020-08-31 17:07:28 -0400 | [diff] [blame] | 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 | |
| 85 | PATTERN_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 | |
| 89 | def 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 | |
| 122 | if __name__ == '__main__': |
| 123 | main() |