1 #!/usr/bin/env python 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 6 """Module that sanitizes source files with specified modifiers.""" 7 8 9 import commands 10 import os 11 import sys 12 13 14 _FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c', 'gyp', 'gypi'] 15 16 _SUBDIRS_TO_IGNORE = ['.git', '.svn', 'third_party'] 17 18 19 def SanitizeFilesWithModifiers(directory, file_modifiers, line_modifiers): 20 """Sanitizes source files with the specified file and line modifiers. 21 22 Args: 23 directory: string - The directory which will be recursively traversed to 24 find source files to apply modifiers to. 25 file_modifiers: list - file-modification methods which should be applied to 26 the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder). 27 line_modifiers: list - line-modification methods which should be applied to 28 lines in a file (Eg: TabReplacer). 29 """ 30 for item in os.listdir(directory): 31 32 full_item_path = os.path.join(directory, item) 33 34 if os.path.isfile(full_item_path): # Item is a file. 35 36 # Only sanitize files with extensions we care about. 37 if (len(full_item_path.split('.')) > 1 and 38 full_item_path.split('.')[-1] in _FILE_EXTENSIONS_TO_SANITIZE): 39 f = file(full_item_path) 40 try: 41 lines = f.readlines() 42 finally: 43 f.close() 44 45 new_lines = [] # Collect changed lines here. 46 line_number = 0 # Keeps track of line numbers in the source file. 47 write_to_file = False # File is written to only if this flag is set. 48 49 # Run the line modifiers for each line in this file. 50 for line in lines: 51 original_line = line 52 line_number += 1 53 54 for modifier in line_modifiers: 55 line = modifier(line, full_item_path, line_number) 56 if original_line != line: 57 write_to_file = True 58 new_lines.append(line) 59 60 # Run the file modifiers. 61 old_content = ''.join(lines) 62 new_content = ''.join(new_lines) 63 for modifier in file_modifiers: 64 new_content = modifier(new_content, full_item_path) 65 if new_content != old_content: 66 write_to_file = True 67 68 # Write modifications to the file. 69 if write_to_file: 70 f = file(full_item_path, 'w') 71 try: 72 f.write(new_content) 73 finally: 74 f.close() 75 print 'Made changes to %s' % full_item_path 76 77 elif item not in _SUBDIRS_TO_IGNORE: 78 # Item is a directory recursively call the method. 79 SanitizeFilesWithModifiers(full_item_path, file_modifiers, line_modifiers) 80 81 82 ############## Line Modification methods ############## 83 84 85 def TrailingWhitespaceRemover(line, file_path, line_number): 86 """Strips out trailing whitespaces from the specified line.""" 87 stripped_line = line.rstrip() + '\n' 88 if line != stripped_line: 89 print 'Removing trailing whitespace in %s:%s' % (file_path, line_number) 90 return stripped_line 91 92 93 def CrlfReplacer(line, file_path, line_number): 94 """Replaces CRLF with LF.""" 95 if '\r\n' in line: 96 print 'Replacing CRLF with LF in %s:%s' % (file_path, line_number) 97 return line.replace('\r\n', '\n') 98 99 100 def TabReplacer(line, file_path, line_number): 101 """Replaces Tabs with 4 whitespaces.""" 102 if '\t' in line: 103 print 'Replacing Tab with whitespace in %s:%s' % (file_path, line_number) 104 return line.replace('\t', ' ') 105 106 107 ############## File Modification methods ############## 108 109 110 def CopywriteChecker(file_content, unused_file_path): 111 """Ensures that the copywrite information is correct.""" 112 # TODO(rmistry): Figure out the legal implications of changing old copyright 113 # headers. 114 return file_content 115 116 117 def EOFOneAndOnlyOneNewlineAdder(file_content, file_path): 118 """Adds one and only one LF at the end of the file.""" 119 if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'): 120 file_content = file_content.rstrip() 121 file_content += '\n' 122 print 'Added exactly one newline to %s' % file_path 123 return file_content 124 125 126 def SvnEOLChecker(file_content, file_path): 127 """Sets svn:eol-style property to LF.""" 128 output = commands.getoutput( 129 'svn propget svn:eol-style %s' % file_path) 130 if output != 'LF': 131 print 'Setting svn:eol-style property to LF in %s' % file_path 132 os.system('svn ps svn:eol-style LF %s' % file_path) 133 return file_content 134 135 136 ####################################################### 137 138 139 if '__main__' == __name__: 140 sys.exit(SanitizeFilesWithModifiers( 141 os.getcwd(), 142 file_modifiers=[ 143 CopywriteChecker, 144 EOFOneAndOnlyOneNewlineAdder, 145 SvnEOLChecker, 146 ], 147 line_modifiers=[ 148 CrlfReplacer, 149 TabReplacer, 150 TrailingWhitespaceRemover, 151 ], 152 )) 153