blob: 9cd49f2d18ee0dae83163b7a28e685db2e1c361a [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 <')
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000144 h_filename, _ = os.path.splitext(os.path.basename(filename))
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000145
146 for item in includes:
147 if re.search(h_filename, item):
148 self_include = item
149 elif re.search(sys_pattern, item):
150 sys_includes.append(item)
151 else:
152 project_includes.append(item)
153
154 sys_includes = sorted(sys_includes)
155 project_includes = sorted(project_includes)
156 headers = (self_include + '\n' + ''.join(sys_includes) + '\n' +
157 ''.join(project_includes))
158
159 # Replace existing headers with the sorted string.
160 text_no_hdrs = re.sub(include_pattern, r'???', text)
161
162 # Insert sorted headers unless we detect #ifdefs.
163 if re.search(r'(#ifdef|#ifndef|#if).*\?{3,}.*#endif',
164 text_no_hdrs, re.DOTALL):
165 print 'WARNING: Include headers not sorted in ' + filename
166 return text
167
168 return_text = re.sub(r'\?{3,}', headers, text_no_hdrs, 1)
169 if re.search(r'\?{3,}', text_no_hdrs):
170 # Remove possible remaining ???.
171 return_text = re.sub(r'\?{3,}', r'', return_text)
172
173 return return_text
174
175
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000176def AddPath(match):
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000177 """Helper for adding file path for WebRTC header files, ignoring other."""
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000178 file_to_examine = match.group(1) + '.h'
179 # TODO(mflodman) Use current directory and find webrtc/.
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000180 for path, _, files in os.walk('./webrtc'):
181 for filename in files:
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000182 if fnmatch.fnmatch(filename, file_to_examine):
183 path_name = os.path.join(path, filename).replace('./', '')
184 return '#include "%s"\n' % path_name
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000185
186 # No path found, return original string.
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000187 return '#include "'+ file_to_examine + '"\n'
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000188
189
190def AddHeaderPath(text):
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000191 """Add path to all included header files that have no path yet."""
192 headers = re.compile('#include "(.+).h"\n')
193 return re.sub(headers, AddPath, text)
194
195
196def AddWebrtcToOldSrcRelativePath(match):
197 file_to_examine = match.group(1) + '.h'
198 path, filename = os.path.split(file_to_examine)
199 dirs_in_webrtc = [name for name in os.listdir('./webrtc')
200 if os.path.isdir(os.path.join('./webrtc', name))]
201 for dir_in_webrtc in dirs_in_webrtc:
202 if path.startswith(dir_in_webrtc):
203 return '#include "%s"\n' % os.path.join('webrtc', path, filename)
204 return '#include "%s"\n' % file_to_examine
205
206def AddWebrtcPrefixToOldSrcRelativePaths(text):
207 """For all paths starting with for instance video_engine, add webrtc/."""
208 headers = re.compile('#include "(.+).h"\n')
209 return re.sub(headers, AddWebrtcToOldSrcRelativePath, text)
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000210
211
212def IndentLabels(text):
213 """Indent public, protected and private one step (astyle doesn't)."""
214 pattern = re.compile('(?<=\n)(public:|protected:|private:)')
215 return re.sub(pattern, r' \1', text)
216
217
218def FixIncludeGuards(text, file_name):
219 """Change include guard according to the stantard."""
220 # Remove a possible webrtc/ from the path.
221 file_name = re.sub(r'(webrtc\/)(.+)', r'\2', file_name)
222 new_guard = 'WEBRTC_' + file_name
223 new_guard = new_guard.upper()
224 new_guard = re.sub(r'([/\.])', r'_', new_guard)
225 new_guard += '_'
226
227 text = re.sub(r'#ifndef WEBRTC_.+\n', r'#ifndef ' + new_guard + '\n', text, 1)
228 text = re.sub(r'#define WEBRTC_.+\n', r'#define ' + new_guard + '\n', text, 1)
229 text = re.sub(r'#endif *\/\/ *WEBRTC_.+\n', r'#endif // ' + new_guard + '\n',
230 text, 1)
231
232 return text
233
234
235def SaveFile(filename, text):
236 os.remove(filename)
237 f = open(filename, 'w')
238 f.write(text)
239 f.close()
240
241
242def main():
243 args = sys.argv[1:]
244 if not args:
245 print 'Usage: %s <filename>' % sys.argv[0]
246 sys.exit(1)
247
248 for filename in args:
249 f = open(filename)
250 text = f.read()
251 f.close()
252
253 text = DeCamelCase(text)
254 text = MoveUnderScore(text)
255 text = CPPComments(text)
256 text = AddHeaderPath(text)
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000257 text = AddWebrtcPrefixToOldSrcRelativePaths(text)
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000258 text = SortIncludeHeaders(text, filename)
259 text = RemoveMultipleEmptyLines(text)
260 text = TrimLineEndings(text)
261
262 # Remove the original file and re-create it with the reformatted content.
263 SaveFile(filename, text)
264
265 # Fix tabs, indentation and '{' using astyle.
266 astyle_cmd = 'astyle'
267 if sys.platform == 'win32':
268 astyle_cmd += '.exe'
269 subprocess.call([astyle_cmd, '-n', '-q', filename])
270
271 if filename.endswith('.h'):
272 f = open(filename)
273 text = f.read()
274 f.close()
275 text = IndentLabels(text)
276 text = FixIncludeGuards(text, filename)
277 SaveFile(filename, text)
278
279 print filename + ' done.'
280
281
282if __name__ == '__main__':
283 main()