blob: db0de03766922d96724b8b3602b4a0b1fdbdfc41 [file] [log] [blame]
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +00001#!/usr/bin/python
2# Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3#
4# Use of this source code is governed by a BSD-style license
5# that can be found in the LICENSE file in the root of the source
6# tree. An additional intellectual property rights grant can be found
7# in the file PATENTS. All contributing project authors may
8# be found in the AUTHORS file in the root of the source tree.
9
10"""WebRTC reformat script.
11
12This script is used to reformat WebRTC code from the old code style to Google
13C++ code style.
14
15You need to have astyle (http://astyle.sourceforge.net/) in your path. You also
16need to put the following contents into ~/.astylerc:
17
18# =======================COPY==============================================
19# Google C++ style guide settings.
20indent=spaces=2 # Indentation uses two spaces.
21style=attach # Attach braces.
22
23indent-switches
24indent-preprocessor # Indent preprocessor continuation lines.
25min-conditional-indent=0 # Align conditional continuation with "(".
26 # e.g. if (foo &&
27 # bar
28max-instatement-indent=80 # Try not to mess with current alignment.
29
30pad-oper # Padding around operators.
31pad-header # Padding after if, for etc.
32unpad-paren # No padding around parentheses.
33align-pointer=type # e.g. int* foo
34
35convert-tabs # Convert non-indentation tabs as well.
36
37# The following are available in the unreleased svn repo.
38# Behvaiour isn't quite what we'd like; more testing needed.
39#max-code-length=80
40#break-after-logical
41
42lineend=linux
43# =========================================================================
44"""
45
46# TODO(mflodman)
47# x s/type *var/type* var/g
48# x : list indention -> 4 spaces.
49
50__author__ = 'mflodman@webrtc.org (Magnus Flodman)'
51
52import fnmatch
53import os
54import re
55import subprocess
56import sys
57
58
59def LowerWord(obj):
60 """Helper for DeCamelCase."""
61 return obj.group(1) + '_' + obj.group(2).lower() + obj.group(3)
62
63
64def DeCamelCase(text):
65 """De-camelize variable names."""
66 pattern = re.compile(r'(?<=[ _*\(\&\!])([a-z]+)(?<!k)([A-Z]+)([a-z])')
67 while re.search(pattern, text):
68 text = re.sub(pattern, LowerWord, text)
69 return text
70
71
72def TrimLineEndings(text):
73 """Removes trailing white spaces."""
74 pattern = re.compile(r'[ ]+(\n)')
75 return re.sub(pattern, r'\1', text)
76
77
78def MoveUnderScore(text):
79 """Moves the underscore from beginning of variable name to the end."""
80 # TODO(mflodman) Replace \1 with ?-expression.
81 # We don't want to change macros and #defines though, so don't do anything
82 # if the first character is uppercase (normal variables shouldn't have that).
83 pattern = r'([ \*\!\&\(])_(?!_)(?![A-Z])(\w+)'
84 return re.sub(pattern, r'\1\2_', text)
85
86
87def RemoveMultipleEmptyLines(text):
88 """Remove all multiple blank lines."""
89 pattern = r'[\n]{3,}'
90 return re.sub(pattern, '\n\n', text)
91
92
93def CPPComments(text):
94 """Remove all C-comments and replace with C++ comments."""
95
96 # Keep the copyright header style.
97 line_list = text.splitlines(True)
98 copyright_list = line_list[0:10]
99 code_list = line_list[10:]
100 copy_text = ''.join(copyright_list)
101 code_text = ''.join(code_list)
102
103 # Remove */ for C-comments, don't care about trailing blanks.
104 comment_end = re.compile(r'\n[ ]*\*/[ ]*')
105 code_text = re.sub(comment_end, '', code_text)
106 comment_end = re.compile(r'\*/')
107 code_text = re.sub(comment_end, '', code_text)
108 # Remove comment lines in the middle of comments, replace with C++ comments.
109 comment_star = re.compile(r'(?<=\n)[ ]*\*[ ]*')
110 code_text = re.sub(comment_star, r'// ', code_text)
111 # Remove start of C comment and replace with C++ comment.
112 comment_start = re.compile(r'/\*[ ]*\n')
113 code_text = re.sub(comment_start, '', code_text)
114 comment_start = re.compile(r'/\*[ ]*(.)')
115 code_text = re.sub(comment_start, r'// \1', code_text)
116
117 # Add copyright info.
118 return copy_text + code_text
119
120
121def SortIncludeHeaders(text, filename):
122 """Sorts all include headers in alphabetic order.
123
124 The file's own header goes first, followed by system headers and then
125 project headers. This function will exit if we detect any fancy #ifdef logic
126 among the includes - that's a lot harder to sort.
127
128 Args:
129 text: The file text.
130 filename: The file we are reformatting.
131
132 Returns:
133 The text with includes sorted.
134 """
135 # Get all includes in file.
136 include_pattern = re.compile('#include.+\n')
137 includes = re.findall(include_pattern, text)
138
139 # Sort system headers and project headers separately.
140 sys_includes = []
141 project_includes = []
142 self_include = ''
143 sys_pattern = re.compile('#include <')
144 h_filename = re.sub(r'(\.cc)', r'.h', filename)
145 # Remove a possible webrtc/ from the filename.
146 h_filename = re.sub(r'(webrtc\/)(.+)', r'\2', h_filename)
147
148 for item in includes:
149 if re.search(h_filename, item):
150 self_include = item
151 elif re.search(sys_pattern, item):
152 sys_includes.append(item)
153 else:
154 project_includes.append(item)
155
156 sys_includes = sorted(sys_includes)
157 project_includes = sorted(project_includes)
158 headers = (self_include + '\n' + ''.join(sys_includes) + '\n' +
159 ''.join(project_includes))
160
161 # Replace existing headers with the sorted string.
162 text_no_hdrs = re.sub(include_pattern, r'???', text)
163
164 # Insert sorted headers unless we detect #ifdefs.
165 if re.search(r'(#ifdef|#ifndef|#if).*\?{3,}.*#endif',
166 text_no_hdrs, re.DOTALL):
167 print 'WARNING: Include headers not sorted in ' + filename
168 return text
169
170 return_text = re.sub(r'\?{3,}', headers, text_no_hdrs, 1)
171 if re.search(r'\?{3,}', text_no_hdrs):
172 # Remove possible remaining ???.
173 return_text = re.sub(r'\?{3,}', r'', return_text)
174
175 return return_text
176
177
178def AddPath(obj):
179 """Helper for adding file path for WebRTC header files, ignoring other."""
180 file_name = obj.group(1) + '.h'
181 # TODO(mflodman) Use current directory and find src.
182 for path, _, files in os.walk('./webrtc'):
183 for filename in files:
184 if fnmatch.fnmatch(filename, file_name):
185 # Remove leading '/webrtc'
186 path_name = os.path.join(path, filename)
187 return re.sub('(.+)(?<=webrtc/)(.+)', r'#include "\2"\n', path_name)
188
189 # No path found, return original string.
190 return '#include "'+ file_name + '"\n'
191
192
193def AddHeaderPath(text):
194 """Add path to all included header files."""
195 hdr_name = re.compile('#include "(.+).h"\n')
196 return re.sub(hdr_name, AddPath, text)
197
198
199def IndentLabels(text):
200 """Indent public, protected and private one step (astyle doesn't)."""
201 pattern = re.compile('(?<=\n)(public:|protected:|private:)')
202 return re.sub(pattern, r' \1', text)
203
204
205def FixIncludeGuards(text, file_name):
206 """Change include guard according to the stantard."""
207 # Remove a possible webrtc/ from the path.
208 file_name = re.sub(r'(webrtc\/)(.+)', r'\2', file_name)
209 new_guard = 'WEBRTC_' + file_name
210 new_guard = new_guard.upper()
211 new_guard = re.sub(r'([/\.])', r'_', new_guard)
212 new_guard += '_'
213
214 text = re.sub(r'#ifndef WEBRTC_.+\n', r'#ifndef ' + new_guard + '\n', text, 1)
215 text = re.sub(r'#define WEBRTC_.+\n', r'#define ' + new_guard + '\n', text, 1)
216 text = re.sub(r'#endif *\/\/ *WEBRTC_.+\n', r'#endif // ' + new_guard + '\n',
217 text, 1)
218
219 return text
220
221
222def SaveFile(filename, text):
223 os.remove(filename)
224 f = open(filename, 'w')
225 f.write(text)
226 f.close()
227
228
229def main():
230 args = sys.argv[1:]
231 if not args:
232 print 'Usage: %s <filename>' % sys.argv[0]
233 sys.exit(1)
234
235 for filename in args:
236 f = open(filename)
237 text = f.read()
238 f.close()
239
240 text = DeCamelCase(text)
241 text = MoveUnderScore(text)
242 text = CPPComments(text)
243 text = AddHeaderPath(text)
244 text = SortIncludeHeaders(text, filename)
245 text = RemoveMultipleEmptyLines(text)
246 text = TrimLineEndings(text)
247
248 # Remove the original file and re-create it with the reformatted content.
249 SaveFile(filename, text)
250
251 # Fix tabs, indentation and '{' using astyle.
252 astyle_cmd = 'astyle'
253 if sys.platform == 'win32':
254 astyle_cmd += '.exe'
255 subprocess.call([astyle_cmd, '-n', '-q', filename])
256
257 if filename.endswith('.h'):
258 f = open(filename)
259 text = f.read()
260 f.close()
261 text = IndentLabels(text)
262 text = FixIncludeGuards(text, filename)
263 SaveFile(filename, text)
264
265 print filename + ' done.'
266
267
268if __name__ == '__main__':
269 main()