| 
									
										
										
										
											2020-08-31 17:07:28 -04:00
										 |  |  | #!/usr/bin/env python3 | 
					
						
							|  |  |  | # QEMU library | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Copyright (C) 2020 Red Hat Inc. | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Authors: | 
					
						
							|  |  |  | #  Eduardo Habkost <ehabkost@redhat.com> | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # 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] + \ | 
					
						
							| 
									
										
										
										
											2020-09-16 14:25:15 -04:00
										 |  |  |                          [t.get_raw_initializer_value(f) | 
					
						
							| 
									
										
										
										
											2020-08-31 17:07:28 -04:00
										 |  |  |                           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 | 
					
						
							| 
									
										
										
										
											2020-09-16 14:25:15 -04:00
										 |  |  |                for p in re.split(r'[\s,]', arg) | 
					
						
							|  |  |  |                if p.strip()] | 
					
						
							| 
									
										
										
										
											2020-08-31 17:07:28 -04:00
										 |  |  |     for c in classes: | 
					
						
							| 
									
										
										
										
											2020-09-16 14:25:15 -04:00
										 |  |  |         if c not in match_classes \ | 
					
						
							|  |  |  |            or not match_classes[c].regexp: | 
					
						
							| 
									
										
										
										
											2020-08-31 17:07:28 -04:00
										 |  |  |             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) | 
					
						
							| 
									
										
										
										
											2020-09-16 14:25:15 -04:00
										 |  |  |     files.patch_content(max_passes=args.passes, class_names=classes) | 
					
						
							| 
									
										
										
										
											2020-08-31 17:07:28 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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() |