123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- #!/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())
|