From 94dfc0f3435241021eb5f6471025389fee92e218 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Mon, 31 Aug 2020 17:07:28 -0400 Subject: codeconverter: script for automating QOM code cleanups This started as a simple script that scanned for regular expressions, but became more and more complex when exceptions to the rules were found. I don't know if this should be maintained in the QEMU source tree long term (maybe it can be reused for other code transformations that Coccinelle can't handle). In either case, this is included as part of the patch series to document how exactly the automated code transformations in the next patches were done. Signed-off-by: Eduardo Habkost Message-Id: <20200831210740.126168-7-ehabkost@redhat.com> Signed-off-by: Eduardo Habkost --- scripts/codeconverter/converter.py | 123 +++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100755 scripts/codeconverter/converter.py (limited to 'scripts/codeconverter/converter.py') diff --git a/scripts/codeconverter/converter.py b/scripts/codeconverter/converter.py new file mode 100755 index 0000000000..ebaf9b57ce --- /dev/null +++ b/scripts/codeconverter/converter.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +# QEMU library +# +# Copyright (C) 2020 Red Hat Inc. +# +# Authors: +# Eduardo Habkost +# +# This work is licensed under the terms of the GNU GPL, version 2. See +# the COPYING file in the top-level directory. +# +import sys +import argparse +import os +import os.path +import re +from typing import * + +from codeconverter.patching import FileInfo, match_class_dict, FileList +import codeconverter.qom_macros +from codeconverter.qom_type_info import TI_FIELDS, type_infos, TypeInfoVar + +import logging +logger = logging.getLogger(__name__) +DBG = logger.debug +INFO = logger.info +WARN = logger.warning + +def process_all_files(parser: argparse.ArgumentParser, args: argparse.Namespace) -> None: + DBG("filenames: %r", args.filenames) + + files = FileList() + files.extend(FileInfo(files, fn, args.force) for fn in args.filenames) + for f in files: + DBG('opening %s', f.filename) + f.load() + + if args.table: + fields = ['filename', 'variable_name'] + TI_FIELDS + print('\t'.join(fields)) + for f in files: + for t in f.matches_of_type(TypeInfoVar): + assert isinstance(t, TypeInfoVar) + values = [f.filename, t.name] + \ + [t.get_initializer_value(f).raw + for f in TI_FIELDS] + DBG('values: %r', values) + assert all('\t' not in v for v in values) + values = [v.replace('\n', ' ').replace('"', '') for v in values] + print('\t'.join(values)) + return + + match_classes = match_class_dict() + if not args.patterns: + parser.error("--pattern is required") + + classes = [p for arg in args.patterns + for p in re.split(r'[\s,]', arg)] + for c in classes: + if c not in match_classes: + print("Invalid pattern name: %s" % (c), file=sys.stderr) + print("Valid patterns:", file=sys.stderr) + print(PATTERN_HELP, file=sys.stderr) + sys.exit(1) + + DBG("classes: %r", classes) + for f in files: + DBG("patching contents of %s", f.filename) + f.patch_content(max_passes=args.passes, class_names=classes) + + for f in files: + #alltypes.extend(f.type_infos) + #full_types.extend(f.full_types()) + + if not args.dry_run: + if args.inplace: + f.patch_inplace() + if args.diff: + f.show_diff() + if not args.diff and not args.inplace: + f.write_to_file(sys.stdout) + sys.stdout.flush() + + +PATTERN_HELP = ('\n'.join(" %s: %s" % (n, str(c.__doc__).strip()) + for (n,c) in sorted(match_class_dict().items()) + if c.has_replacement_rule())) + +def main() -> None: + p = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) + p.add_argument('filenames', nargs='+') + p.add_argument('--passes', type=int, default=1, + help="Number of passes (0 means unlimited)") + p.add_argument('--pattern', required=True, action='append', + default=[], dest='patterns', + help="Pattern to scan for") + p.add_argument('--inplace', '-i', action='store_true', + help="Patch file in place") + p.add_argument('--dry-run', action='store_true', + help="Don't patch files or print patching results") + p.add_argument('--force', '-f', action='store_true', + help="Perform changes even if not completely safe") + p.add_argument('--diff', action='store_true', + help="Print diff output on stdout") + p.add_argument('--debug', '-d', action='store_true', + help="Enable debugging") + p.add_argument('--verbose', '-v', action='store_true', + help="Verbose logging on stderr") + p.add_argument('--table', action='store_true', + help="Print CSV table of type information") + p.add_argument_group("Valid pattern names", + PATTERN_HELP) + args = p.parse_args() + + loglevel = (logging.DEBUG if args.debug + else logging.INFO if args.verbose + else logging.WARN) + logging.basicConfig(format='%(levelname)s: %(message)s', level=loglevel) + DBG("args: %r", args) + process_all_files(p, args) + +if __name__ == '__main__': + main() \ No newline at end of file -- cgit 1.4.1