blob: 5f68a70889528ee376ec093cbfbaa89f007641b6 [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."""
phoglund@webrtc.org943770b2013-01-02 15:46:43 +000061 optional_last_letters = obj.group(3) or ''
62 return obj.group(1) + '_' + obj.group(2).lower() + optional_last_letters
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +000063
64
65def DeCamelCase(text):
66 """De-camelize variable names."""
phoglund@webrtc.org943770b2013-01-02 15:46:43 +000067 pattern = re.compile(r'(?<=[ _*\(\&\!])([a-z]+)(?<!k)([A-Z]+)([a-z])?')
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +000068 while re.search(pattern, text):
69 text = re.sub(pattern, LowerWord, text)
70 return text
71
72
73def TrimLineEndings(text):
74 """Removes trailing white spaces."""
75 pattern = re.compile(r'[ ]+(\n)')
76 return re.sub(pattern, r'\1', text)
77
78
79def MoveUnderScore(text):
80 """Moves the underscore from beginning of variable name to the end."""
81 # TODO(mflodman) Replace \1 with ?-expression.
82 # We don't want to change macros and #defines though, so don't do anything
83 # if the first character is uppercase (normal variables shouldn't have that).
phoglund@webrtc.org943770b2013-01-02 15:46:43 +000084 pattern = r'([ \*\!\&\(\[\]])_(?!_)(?![A-Z])(\w+)'
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +000085 return re.sub(pattern, r'\1\2_', text)
86
87
phoglund@webrtc.org943770b2013-01-02 15:46:43 +000088def PostfixToPrefixInForLoops(text):
89 """Converts x++ to ++x in the increment part of a for loop."""
90 pattern = r'(for \(.*;.*;) (\w+)\+\+\)'
91 return re.sub(pattern, r'\1++\2)', text)
92
93
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +000094def RemoveMultipleEmptyLines(text):
95 """Remove all multiple blank lines."""
96 pattern = r'[\n]{3,}'
97 return re.sub(pattern, '\n\n', text)
98
99
100def CPPComments(text):
101 """Remove all C-comments and replace with C++ comments."""
102
103 # Keep the copyright header style.
104 line_list = text.splitlines(True)
105 copyright_list = line_list[0:10]
106 code_list = line_list[10:]
107 copy_text = ''.join(copyright_list)
108 code_text = ''.join(code_list)
109
110 # Remove */ for C-comments, don't care about trailing blanks.
111 comment_end = re.compile(r'\n[ ]*\*/[ ]*')
112 code_text = re.sub(comment_end, '', code_text)
113 comment_end = re.compile(r'\*/')
114 code_text = re.sub(comment_end, '', code_text)
115 # Remove comment lines in the middle of comments, replace with C++ comments.
phoglund@webrtc.org78bec2d2012-12-03 08:48:07 +0000116 comment_star = re.compile(r'(?<=\n)[ ]*(?!\*\w)\*[ ]*')
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000117 code_text = re.sub(comment_star, r'// ', code_text)
118 # Remove start of C comment and replace with C++ comment.
119 comment_start = re.compile(r'/\*[ ]*\n')
120 code_text = re.sub(comment_start, '', code_text)
121 comment_start = re.compile(r'/\*[ ]*(.)')
122 code_text = re.sub(comment_start, r'// \1', code_text)
123
124 # Add copyright info.
125 return copy_text + code_text
126
127
128def SortIncludeHeaders(text, filename):
129 """Sorts all include headers in alphabetic order.
130
131 The file's own header goes first, followed by system headers and then
132 project headers. This function will exit if we detect any fancy #ifdef logic
133 among the includes - that's a lot harder to sort.
134
135 Args:
136 text: The file text.
137 filename: The file we are reformatting.
138
139 Returns:
140 The text with includes sorted.
141 """
142 # Get all includes in file.
143 include_pattern = re.compile('#include.+\n')
144 includes = re.findall(include_pattern, text)
145
146 # Sort system headers and project headers separately.
147 sys_includes = []
148 project_includes = []
149 self_include = ''
150 sys_pattern = re.compile('#include <')
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000151 h_filename, _ = os.path.splitext(os.path.basename(filename))
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000152
153 for item in includes:
154 if re.search(h_filename, item):
155 self_include = item
156 elif re.search(sys_pattern, item):
157 sys_includes.append(item)
158 else:
159 project_includes.append(item)
160
161 sys_includes = sorted(sys_includes)
162 project_includes = sorted(project_includes)
163 headers = (self_include + '\n' + ''.join(sys_includes) + '\n' +
164 ''.join(project_includes))
165
166 # Replace existing headers with the sorted string.
167 text_no_hdrs = re.sub(include_pattern, r'???', text)
168
phoglund@webrtc.org943770b2013-01-02 15:46:43 +0000169 # Insert sorted headers unless we detect #ifdefs right next to the headers.
170 if re.search(r'(#ifdef|#ifndef|#if).*\s*\?{3,}\s*#endif', text_no_hdrs):
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000171 print 'WARNING: Include headers not sorted in ' + filename
172 return text
173
174 return_text = re.sub(r'\?{3,}', headers, text_no_hdrs, 1)
175 if re.search(r'\?{3,}', text_no_hdrs):
176 # Remove possible remaining ???.
177 return_text = re.sub(r'\?{3,}', r'', return_text)
178
179 return return_text
180
181
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000182def AddPath(match):
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000183 """Helper for adding file path for WebRTC header files, ignoring other."""
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000184 file_to_examine = match.group(1) + '.h'
185 # TODO(mflodman) Use current directory and find webrtc/.
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000186 for path, _, files in os.walk('./webrtc'):
187 for filename in files:
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000188 if fnmatch.fnmatch(filename, file_to_examine):
189 path_name = os.path.join(path, filename).replace('./', '')
190 return '#include "%s"\n' % path_name
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000191
192 # No path found, return original string.
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000193 return '#include "'+ file_to_examine + '"\n'
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000194
195
196def AddHeaderPath(text):
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000197 """Add path to all included header files that have no path yet."""
198 headers = re.compile('#include "(.+).h"\n')
199 return re.sub(headers, AddPath, text)
200
201
202def AddWebrtcToOldSrcRelativePath(match):
203 file_to_examine = match.group(1) + '.h'
204 path, filename = os.path.split(file_to_examine)
205 dirs_in_webrtc = [name for name in os.listdir('./webrtc')
206 if os.path.isdir(os.path.join('./webrtc', name))]
207 for dir_in_webrtc in dirs_in_webrtc:
208 if path.startswith(dir_in_webrtc):
209 return '#include "%s"\n' % os.path.join('webrtc', path, filename)
210 return '#include "%s"\n' % file_to_examine
211
212def AddWebrtcPrefixToOldSrcRelativePaths(text):
213 """For all paths starting with for instance video_engine, add webrtc/."""
214 headers = re.compile('#include "(.+).h"\n')
215 return re.sub(headers, AddWebrtcToOldSrcRelativePath, text)
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000216
217
218def IndentLabels(text):
219 """Indent public, protected and private one step (astyle doesn't)."""
220 pattern = re.compile('(?<=\n)(public:|protected:|private:)')
221 return re.sub(pattern, r' \1', text)
222
223
224def FixIncludeGuards(text, file_name):
225 """Change include guard according to the stantard."""
226 # Remove a possible webrtc/ from the path.
227 file_name = re.sub(r'(webrtc\/)(.+)', r'\2', file_name)
228 new_guard = 'WEBRTC_' + file_name
229 new_guard = new_guard.upper()
230 new_guard = re.sub(r'([/\.])', r'_', new_guard)
231 new_guard += '_'
232
233 text = re.sub(r'#ifndef WEBRTC_.+\n', r'#ifndef ' + new_guard + '\n', text, 1)
234 text = re.sub(r'#define WEBRTC_.+\n', r'#define ' + new_guard + '\n', text, 1)
235 text = re.sub(r'#endif *\/\/ *WEBRTC_.+\n', r'#endif // ' + new_guard + '\n',
236 text, 1)
237
238 return text
239
240
241def SaveFile(filename, text):
242 os.remove(filename)
243 f = open(filename, 'w')
244 f.write(text)
245 f.close()
246
247
248def main():
249 args = sys.argv[1:]
250 if not args:
251 print 'Usage: %s <filename>' % sys.argv[0]
252 sys.exit(1)
253
254 for filename in args:
255 f = open(filename)
256 text = f.read()
257 f.close()
258
259 text = DeCamelCase(text)
260 text = MoveUnderScore(text)
phoglund@webrtc.org943770b2013-01-02 15:46:43 +0000261 text = PostfixToPrefixInForLoops(text)
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000262 text = CPPComments(text)
263 text = AddHeaderPath(text)
phoglund@webrtc.orge3b2bc62012-11-23 09:09:59 +0000264 text = AddWebrtcPrefixToOldSrcRelativePaths(text)
phoglund@webrtc.org3ec52c02012-11-21 13:28:52 +0000265 text = SortIncludeHeaders(text, filename)
266 text = RemoveMultipleEmptyLines(text)
267 text = TrimLineEndings(text)
268
269 # Remove the original file and re-create it with the reformatted content.
270 SaveFile(filename, text)
271
272 # Fix tabs, indentation and '{' using astyle.
273 astyle_cmd = 'astyle'
274 if sys.platform == 'win32':
275 astyle_cmd += '.exe'
276 subprocess.call([astyle_cmd, '-n', '-q', filename])
277
278 if filename.endswith('.h'):
279 f = open(filename)
280 text = f.read()
281 f.close()
282 text = IndentLabels(text)
283 text = FixIncludeGuards(text, filename)
284 SaveFile(filename, text)
285
286 print filename + ' done.'
287
288
289if __name__ == '__main__':
290 main()