| #!/usr/bin/env python3 | |
| # Copyright 2020 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| # After turning on -Wconversion globally, run a fuchsia build, but save | |
| # the output. | |
| # | |
| # $ fx build > /tmp/output 2>&1 # Can also use `ninja` | |
| # $ ./scripts/wconversion/wconversion-checks.py /tmp/output -i=-Wsign-conversion | |
| # 2815 errors in unique locations | |
| # source files: 2073 | |
| # header files: 741 | |
| # other: 1 | |
| # third party: 180 | |
| # Counter({'-Wshorten-64-to-32': 1144, '-Wimplicit-int-conversion': 1038, ...}) | |
| # No. of failed targets: 679 | |
| import os.path | |
| import re | |
| import sys | |
| WERROR_PATTERN = re.compile("\[-Werror,(.+)\]") | |
| TARGET_PATTERN = re.compile("\[(\d+)/\d+\]") | |
| def parse_args(): | |
| from argparse import ArgumentParser, FileType | |
| parser = ArgumentParser(description="Script for grabbing statistics on -Wconversion errors from an `fx build` or `ninja` output") | |
| parser.add_argument("infile", | |
| type=FileType('r'), | |
| help="File containing ninja logs") | |
| # Ignore the `-W` because python will interpret it as an argument, even if it | |
| # is wrapped in single quotes in bash. | |
| parser.add_argument("-i", "--ignore", | |
| action="append", | |
| default=[], | |
| help="Set of warnings to ignore (\"-Wshorten-64-to-32\", \"-Wsign-conversion\", etc.)") | |
| return parser.parse_args() | |
| def main(): | |
| args = parse_args() | |
| ignored = set(args.ignore) | |
| failed_targets = set() | |
| errors = set() | |
| third_party = 0 | |
| source_files = 0 | |
| header_files = 0 | |
| other = 0 | |
| cur_target = None | |
| for line in args.infile: | |
| if TARGET_PATTERN.search(line): | |
| step, action, target = line.split(maxsplit=2) | |
| cur_target = target.strip() | |
| match = WERROR_PATTERN.search(line) | |
| if not match: | |
| continue | |
| err = match.group(1) | |
| if err in ignored: | |
| continue | |
| error_loc = line.find(": error: ") | |
| assert error_loc >= 0, "Could not find error line" | |
| path = line[:error_loc] | |
| path, line, col = path.split(":") | |
| line = int(line) | |
| col = int(col) | |
| errors.add((path, line, col, err)) | |
| assert cur_target is not None, "Expected a target" | |
| if cur_target not in failed_targets: | |
| failed_targets.add(cur_target) | |
| for path, line, col, err in errors: | |
| if path.startswith("../../third_party/"): | |
| third_party += 1 | |
| if path.endswith(".c") or path.endswith(".cc") or path.endswith(".cpp"): | |
| source_files += 1 | |
| elif path.endswith(".h") or path.endswith(".hpp"): | |
| header_files += 1 | |
| else: | |
| other += 1 | |
| print(len(errors), "errors in unique locations") | |
| print("source files:", source_files) | |
| print("header files:", header_files) | |
| print("other:", other) | |
| print("third party:", third_party) | |
| # Distribution of errors | |
| from collections import Counter | |
| print(Counter((x[3] for x in errors))) | |
| print("No. of failed targets:", len(failed_targets)) | |
| return 0 | |
| if __name__ == "__main__": | |
| sys.exit(main()) |