|  | #!/usr/bin/env python3 | 
|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | # fix_include_guard.py script takes any number of C or C++ header files as | 
|  | # input, and formats their include guards to conform to the style mandated | 
|  | # by Google C++. See | 
|  | # https://google.github.io/styleguide/cppguide.html#The__define_Guard | 
|  | # | 
|  | # clang-format does not, unfortunately, have support for automatically | 
|  | # generating the correct include guards; hence the existence of this script. | 
|  | # | 
|  | # This script will write the names of all affected files to stdout; if no such | 
|  | # output is present, all files have the correct guards. This script is | 
|  | # idempotent. If it cannot fix a file, the script will write the file name to | 
|  | # stderr and will exit at the end with a nonzero status code. | 
|  |  | 
|  | import argparse | 
|  | import sys | 
|  | import re | 
|  |  | 
|  | from pathlib import Path | 
|  |  | 
|  | PROJECT_NAME = "OPENTITAN" | 
|  |  | 
|  | # This file is $REPO_TOP/util/fix_include_guard.py, so it takes two parent() | 
|  | # calls to get back to the top. | 
|  | REPO_TOP = Path(__file__).resolve().parent.parent | 
|  |  | 
|  |  | 
|  | def main(): | 
|  | parser = argparse.ArgumentParser() | 
|  | parser.add_argument( | 
|  | '--dry-run', | 
|  | action='store_true', | 
|  | help='report writes which would have happened') | 
|  | parser.add_argument( | 
|  | 'headers', | 
|  | type=str, | 
|  | nargs='+', | 
|  | help='headers to fix guards for') | 
|  | args = parser.parse_args() | 
|  |  | 
|  | total_unfixable = 0 | 
|  | total_fixable = 0 | 
|  |  | 
|  | for header_path in args.headers: | 
|  | header = Path(header_path).resolve().relative_to(REPO_TOP) | 
|  | if header.suffix != '.h' or 'vendor' in header.parts: | 
|  | continue | 
|  |  | 
|  | uppercase_dir = re.sub(r'[^\w]', '_', str(header.parent)).upper() | 
|  | uppercase_stem = re.sub(r'[^\w]', '_', str(header.stem)).upper() | 
|  | guard = '%s_%s_%s_H_' % (PROJECT_NAME, uppercase_dir, uppercase_stem) | 
|  |  | 
|  | header_text = header.read_text() | 
|  | header_original = header_text | 
|  |  | 
|  | # Find the first non-comment, non-whitespace line in the file | 
|  | first_line_match = re.search(r'^\s*([^/].*)', header_text, re.MULTILINE) | 
|  | if first_line_match is None: | 
|  | total_unfixable += 1 | 
|  | print('No non-comment line found in `{}\'.'.format(header), | 
|  | file=sys.stderr) | 
|  | continue | 
|  |  | 
|  | first_line = first_line_match.group(1).rstrip() | 
|  |  | 
|  | # Find the old guard name, which will be the first #ifndef in the file. | 
|  | # This should be an #ifndef line. If it isn't, we can't fix things | 
|  | ifndef_match = re.match(r'#ifndef\s+(\w+)$', first_line) | 
|  | if ifndef_match is None: | 
|  | total_unfixable += 1 | 
|  | print('Unsupported first non-comment line in `{}\': {!r}.' | 
|  | .format(header, first_line), | 
|  | file=sys.stderr) | 
|  | continue | 
|  |  | 
|  | old_guard = ifndef_match.group(1) | 
|  |  | 
|  | # Fix the guards at the top, which are guaranteed to be there. | 
|  | header_text = re.sub('#(ifndef|define) +%s' % (old_guard, ), | 
|  | r'#\1 %s' % (guard, ), header_text) | 
|  |  | 
|  | # Fix up the endif. Since this is the last thing in the file, and it | 
|  | # might be missing the comment, we just truncate the file, and add on | 
|  | # the required guard end. | 
|  | header_text = header_text[:header_text.rindex('#endif')] | 
|  | header_text += "#endif  // %s\n" % (guard, ) | 
|  |  | 
|  | if header_text != header_original: | 
|  | print('Fixing header: "%s"' % (header, ), file=sys.stdout) | 
|  | total_fixable += 1 | 
|  | if not args.dry_run: | 
|  | header.write_text(header_text) | 
|  |  | 
|  | if total_fixable: | 
|  | verb = 'Would have fixed' if args.dry_run else 'Fixed' | 
|  | print('{} {} files.'.format(verb, total_fixable), file=sys.stderr) | 
|  | if total_unfixable: | 
|  | print('Failed to fix {} files.'.format(total_unfixable), | 
|  | file=sys.stderr) | 
|  |  | 
|  | # Pass if we fixed everything or there was nothing to fix. | 
|  | unfixed = total_unfixable + (total_fixable if args.dry_run else 0) | 
|  | return 1 if unfixed else 0 | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | sys.exit(main()) |