blob: c7edaa0bd21cafe8dd7dca800432725d0831a180 [file] [log] [blame]
rmistry@google.comb1faddf2012-08-22 18:10:37 +00001#!/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
9import commands
10import os
11import sys
12
13
14_FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c', 'gyp', 'gypi']
15
16_SUBDIRS_TO_IGNORE = ['.svn', 'third_party']
17
18
19def 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
rmistry@google.com647ca8b2013-01-25 18:26:58 +000026 the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder).
rmistry@google.comb1faddf2012-08-22 18:10:37 +000027 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
85def 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
93def 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
100def 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
110def 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
rmistry@google.com647ca8b2013-01-25 18:26:58 +0000117def 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()
rmistry@google.comb1faddf2012-08-22 18:10:37 +0000121 file_content += '\n'
rmistry@google.com647ca8b2013-01-25 18:26:58 +0000122 print 'Added exactly one newline to %s' % file_path
rmistry@google.comb1faddf2012-08-22 18:10:37 +0000123 return file_content
124
125
126def 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
139if '__main__' == __name__:
140 sys.exit(SanitizeFilesWithModifiers(
141 os.getcwd(),
142 file_modifiers=[
143 CopywriteChecker,
rmistry@google.com647ca8b2013-01-25 18:26:58 +0000144 EOFOneAndOnlyOneNewlineAdder,
rmistry@google.comb1faddf2012-08-22 18:10:37 +0000145 SvnEOLChecker,
146 ],
147 line_modifiers=[
148 CrlfReplacer,
149 TabReplacer,
150 TrailingWhitespaceRemover,
151 ],
152 ))