phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame^] | 1 | #!/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 | |
| 12 | This script is used to reformat WebRTC code from the old code style to Google |
| 13 | C++ code style. |
| 14 | |
| 15 | You need to have astyle (http://astyle.sourceforge.net/) in your path. You also |
| 16 | need to put the following contents into ~/.astylerc: |
| 17 | |
| 18 | # =======================COPY============================================== |
| 19 | # Google C++ style guide settings. |
| 20 | indent=spaces=2 # Indentation uses two spaces. |
| 21 | style=attach # Attach braces. |
| 22 | |
| 23 | indent-switches |
| 24 | indent-preprocessor # Indent preprocessor continuation lines. |
| 25 | min-conditional-indent=0 # Align conditional continuation with "(". |
| 26 | # e.g. if (foo && |
| 27 | # bar |
| 28 | max-instatement-indent=80 # Try not to mess with current alignment. |
| 29 | |
| 30 | pad-oper # Padding around operators. |
| 31 | pad-header # Padding after if, for etc. |
| 32 | unpad-paren # No padding around parentheses. |
| 33 | align-pointer=type # e.g. int* foo |
| 34 | |
| 35 | convert-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 | |
| 42 | lineend=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 | |
| 52 | import fnmatch |
| 53 | import os |
| 54 | import re |
| 55 | import subprocess |
| 56 | import sys |
| 57 | |
| 58 | |
| 59 | def LowerWord(obj): |
| 60 | """Helper for DeCamelCase.""" |
| 61 | return obj.group(1) + '_' + obj.group(2).lower() + obj.group(3) |
| 62 | |
| 63 | |
| 64 | def 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 | |
| 72 | def TrimLineEndings(text): |
| 73 | """Removes trailing white spaces.""" |
| 74 | pattern = re.compile(r'[ ]+(\n)') |
| 75 | return re.sub(pattern, r'\1', text) |
| 76 | |
| 77 | |
| 78 | def 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 | |
| 87 | def RemoveMultipleEmptyLines(text): |
| 88 | """Remove all multiple blank lines.""" |
| 89 | pattern = r'[\n]{3,}' |
| 90 | return re.sub(pattern, '\n\n', text) |
| 91 | |
| 92 | |
| 93 | def 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 | |
| 121 | def 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 | |
| 178 | def 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 | |
| 193 | def 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 | |
| 199 | def 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 | |
| 205 | def 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 | |
| 222 | def SaveFile(filename, text): |
| 223 | os.remove(filename) |
| 224 | f = open(filename, 'w') |
| 225 | f.write(text) |
| 226 | f.close() |
| 227 | |
| 228 | |
| 229 | def 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 | |
| 268 | if __name__ == '__main__': |
| 269 | main() |