| #!/usr/bin/env python | |
| # Copyright (c) 2012 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. | |
| """Module that sanitizes source files with specified modifiers.""" | |
| import commands | |
| import os | |
| import sys | |
| _FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c', 'gyp', 'gypi'] | |
| _SUBDIRS_TO_IGNORE = ['.git', '.svn', 'third_party'] | |
| def SanitizeFilesWithModifiers(directory, file_modifiers, line_modifiers): | |
| """Sanitizes source files with the specified file and line modifiers. | |
| Args: | |
| directory: string - The directory which will be recursively traversed to | |
| find source files to apply modifiers to. | |
| file_modifiers: list - file-modification methods which should be applied to | |
| the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder). | |
| line_modifiers: list - line-modification methods which should be applied to | |
| lines in a file (Eg: TabReplacer). | |
| """ | |
| for item in os.listdir(directory): | |
| full_item_path = os.path.join(directory, item) | |
| if os.path.isfile(full_item_path): # Item is a file. | |
| # Only sanitize files with extensions we care about. | |
| if (len(full_item_path.split('.')) > 1 and | |
| full_item_path.split('.')[-1] in _FILE_EXTENSIONS_TO_SANITIZE): | |
| f = file(full_item_path) | |
| try: | |
| lines = f.readlines() | |
| finally: | |
| f.close() | |
| new_lines = [] # Collect changed lines here. | |
| line_number = 0 # Keeps track of line numbers in the source file. | |
| write_to_file = False # File is written to only if this flag is set. | |
| # Run the line modifiers for each line in this file. | |
| for line in lines: | |
| original_line = line | |
| line_number += 1 | |
| for modifier in line_modifiers: | |
| line = modifier(line, full_item_path, line_number) | |
| if original_line != line: | |
| write_to_file = True | |
| new_lines.append(line) | |
| # Run the file modifiers. | |
| old_content = ''.join(lines) | |
| new_content = ''.join(new_lines) | |
| for modifier in file_modifiers: | |
| new_content = modifier(new_content, full_item_path) | |
| if new_content != old_content: | |
| write_to_file = True | |
| # Write modifications to the file. | |
| if write_to_file: | |
| f = file(full_item_path, 'w') | |
| try: | |
| f.write(new_content) | |
| finally: | |
| f.close() | |
| print 'Made changes to %s' % full_item_path | |
| elif item not in _SUBDIRS_TO_IGNORE: | |
| # Item is a directory recursively call the method. | |
| SanitizeFilesWithModifiers(full_item_path, file_modifiers, line_modifiers) | |
| ############## Line Modification methods ############## | |
| def TrailingWhitespaceRemover(line, file_path, line_number): | |
| """Strips out trailing whitespaces from the specified line.""" | |
| stripped_line = line.rstrip() + '\n' | |
| if line != stripped_line: | |
| print 'Removing trailing whitespace in %s:%s' % (file_path, line_number) | |
| return stripped_line | |
| def CrlfReplacer(line, file_path, line_number): | |
| """Replaces CRLF with LF.""" | |
| if '\r\n' in line: | |
| print 'Replacing CRLF with LF in %s:%s' % (file_path, line_number) | |
| return line.replace('\r\n', '\n') | |
| def TabReplacer(line, file_path, line_number): | |
| """Replaces Tabs with 4 whitespaces.""" | |
| if '\t' in line: | |
| print 'Replacing Tab with whitespace in %s:%s' % (file_path, line_number) | |
| return line.replace('\t', ' ') | |
| ############## File Modification methods ############## | |
| def CopywriteChecker(file_content, unused_file_path): | |
| """Ensures that the copywrite information is correct.""" | |
| # TODO(rmistry): Figure out the legal implications of changing old copyright | |
| # headers. | |
| return file_content | |
| def EOFOneAndOnlyOneNewlineAdder(file_content, file_path): | |
| """Adds one and only one LF at the end of the file.""" | |
| if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'): | |
| file_content = file_content.rstrip() | |
| file_content += '\n' | |
| print 'Added exactly one newline to %s' % file_path | |
| return file_content | |
| def SvnEOLChecker(file_content, file_path): | |
| """Sets svn:eol-style property to LF.""" | |
| output = commands.getoutput( | |
| 'svn propget svn:eol-style %s' % file_path) | |
| if output != 'LF': | |
| print 'Setting svn:eol-style property to LF in %s' % file_path | |
| os.system('svn ps svn:eol-style LF %s' % file_path) | |
| return file_content | |
| ####################################################### | |
| if '__main__' == __name__: | |
| sys.exit(SanitizeFilesWithModifiers( | |
| os.getcwd(), | |
| file_modifiers=[ | |
| CopywriteChecker, | |
| EOFOneAndOnlyOneNewlineAdder, | |
| SvnEOLChecker, | |
| ], | |
| line_modifiers=[ | |
| CrlfReplacer, | |
| TabReplacer, | |
| TrailingWhitespaceRemover, | |
| ], | |
| )) |