
Initial conversion performed using '2to3 -f print .'. Imports added and duplicate parentheses removed manually. Manually converted files, comments and inline code that 2to3 missed. Afterwards ran "git cl format --python" and cherry-picked the formatting changes. There are no intended behavioural changes. Bug: 941669 Change-Id: I4a423a71375fd4d8cfc8283dd2703855781ece6b Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1818519 Commit-Queue: Nico Weber <thakis@chromium.org> Reviewed-by: Brian White <bcwhite@chromium.org> Reviewed-by: Nico Weber <thakis@chromium.org> Auto-Submit: Raul Tambre <raul@tambre.ee> Cr-Commit-Position: refs/heads/master@{#699709}
127 lines
4.7 KiB
Python
Executable File
127 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python
|
|
# Copyright 2016 The Chromium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
"""This script will search through the target folder specified and try to find
|
|
duplicate includes from h and cc files, and remove them from the cc files. The
|
|
current/working directory needs to be chromium_checkout/src/ when this tool is
|
|
run.
|
|
|
|
Usage: remove_duplicate_includes.py --dry-run components/foo components/bar
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
|
|
import argparse
|
|
import collections
|
|
import logging
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
# This could be generalized if desired, and moved to command line arguments.
|
|
H_FILE_SUFFIX = '.h'
|
|
CC_FILE_SUFFIX = '.cc'
|
|
|
|
# The \s should allow us to ignore any whitespace and only focus on the group
|
|
# captured when comparing between files.
|
|
INCLUDE_REGEX = re.compile('^\s*(#include\s+[\"<](.*?)[\">])\s*$')
|
|
|
|
def HasSuffix(file_name, suffix):
|
|
return os.path.splitext(file_name)[1] == suffix
|
|
|
|
def IsEmpty(line):
|
|
return not line.strip()
|
|
|
|
def FindIncludeSet(input_lines, h_path_to_include_set, cc_file_name):
|
|
"""Finds and returns the corresponding include set for the given .cc file.
|
|
|
|
This is done by finding the first include in the file and then trying to look
|
|
up an .h file in the passed in map. If not present, then None is returned
|
|
immediately.
|
|
"""
|
|
for line in input_lines:
|
|
match = INCLUDE_REGEX.search(line)
|
|
# The first include match should be the corresponding .h file, else skip.
|
|
if match:
|
|
h_file_path = os.path.join(os.getcwd(), match.group(2))
|
|
if h_file_path not in h_path_to_include_set:
|
|
print('First include did not match to a known .h file, skipping ' + \
|
|
cc_file_name + ', line: ' + match.group(1))
|
|
return None
|
|
return h_path_to_include_set[h_file_path]
|
|
|
|
def WithoutDuplicates(input_lines, include_set, cc_file_name):
|
|
"""Checks every input line and sees if we can remove it based on the contents
|
|
of the given include set.
|
|
|
|
Returns what the new contents of the file should be.
|
|
"""
|
|
output_lines = []
|
|
# When a section of includes are completely removed, we want to remove the
|
|
# trailing empty as well.
|
|
lastCopiedLineWasEmpty = False
|
|
lastLineWasOmitted = False
|
|
for line in input_lines:
|
|
match = INCLUDE_REGEX.search(line)
|
|
if match and match.group(2) in include_set:
|
|
print('Removed ' + match.group(1) + ' from ' + cc_file_name)
|
|
lastLineWasOmitted = True
|
|
elif lastCopiedLineWasEmpty and lastLineWasOmitted and IsEmpty(line):
|
|
print('Removed empty line from ' + cc_file_name)
|
|
lastLineWasOmitted = True
|
|
else:
|
|
lastCopiedLineWasEmpty = IsEmpty(line)
|
|
lastLineWasOmitted = False
|
|
output_lines.append(line)
|
|
return output_lines
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--dry-run', action='store_true',
|
|
help='Does not actually remove lines when specified.')
|
|
parser.add_argument('targets', nargs='+',
|
|
help='Relative path to folders to search for duplicate includes in.')
|
|
args = parser.parse_args()
|
|
|
|
# A map of header file paths to the includes they contain.
|
|
h_path_to_include_set = {}
|
|
|
|
# Simply collects the path of all cc files present.
|
|
cc_file_path_set = set()
|
|
|
|
for relative_root in args.targets:
|
|
absolute_root = os.path.join(os.getcwd(), relative_root)
|
|
for dir_path, dir_name_list, file_name_list in os.walk(absolute_root):
|
|
for file_name in file_name_list:
|
|
file_path = os.path.join(dir_path, file_name)
|
|
if HasSuffix(file_name, H_FILE_SUFFIX):
|
|
# By manually adding the set instead of using defaultdict we can avoid
|
|
# warning about missing .h files when the .h file has no includes.
|
|
h_path_to_include_set[file_path] = set()
|
|
with open(file_path) as fh:
|
|
for line in fh:
|
|
match = INCLUDE_REGEX.search(line)
|
|
if match:
|
|
h_path_to_include_set[file_path].add(match.group(2))
|
|
elif HasSuffix(file_name, CC_FILE_SUFFIX):
|
|
cc_file_path_set.add(file_path)
|
|
|
|
for cc_file_path in cc_file_path_set:
|
|
cc_file_name = os.path.basename(cc_file_path)
|
|
with open(cc_file_path, 'r' if args.dry_run else 'r+') as fh:
|
|
# Read out all lines and reset file position to allow overwriting.
|
|
input_lines = fh.readlines()
|
|
fh.seek(0)
|
|
include_set = FindIncludeSet(input_lines, h_path_to_include_set,
|
|
cc_file_name)
|
|
if include_set:
|
|
output_lines = WithoutDuplicates(input_lines, include_set, cc_file_name)
|
|
if not args.dry_run:
|
|
fh.writelines(output_lines)
|
|
fh.truncate()
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|