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 |
phoglund@webrtc.org | 1723857 | 2013-02-15 09:43:20 +0000 | [diff] [blame] | 13 | C++ code style. This script does not indent code; use clang-reformat-chrome.py |
| 14 | as described in go/webrtc/engineering/reformatting-gips---google. |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 15 | """ |
| 16 | |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 17 | __author__ = 'mflodman@webrtc.org (Magnus Flodman)' |
| 18 | |
| 19 | import fnmatch |
| 20 | import os |
| 21 | import re |
| 22 | import subprocess |
| 23 | import sys |
| 24 | |
| 25 | |
| 26 | def LowerWord(obj): |
| 27 | """Helper for DeCamelCase.""" |
phoglund@webrtc.org | 943770b | 2013-01-02 15:46:43 +0000 | [diff] [blame] | 28 | optional_last_letters = obj.group(3) or '' |
| 29 | return obj.group(1) + '_' + obj.group(2).lower() + optional_last_letters |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 30 | |
| 31 | |
| 32 | def DeCamelCase(text): |
| 33 | """De-camelize variable names.""" |
phoglund@webrtc.org | 943770b | 2013-01-02 15:46:43 +0000 | [diff] [blame] | 34 | pattern = re.compile(r'(?<=[ _*\(\&\!])([a-z]+)(?<!k)([A-Z]+)([a-z])?') |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 35 | while re.search(pattern, text): |
| 36 | text = re.sub(pattern, LowerWord, text) |
| 37 | return text |
| 38 | |
| 39 | |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 40 | def MoveUnderScore(text): |
| 41 | """Moves the underscore from beginning of variable name to the end.""" |
| 42 | # TODO(mflodman) Replace \1 with ?-expression. |
| 43 | # We don't want to change macros and #defines though, so don't do anything |
| 44 | # if the first character is uppercase (normal variables shouldn't have that). |
phoglund@webrtc.org | 943770b | 2013-01-02 15:46:43 +0000 | [diff] [blame] | 45 | pattern = r'([ \*\!\&\(\[\]])_(?!_)(?![A-Z])(\w+)' |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 46 | return re.sub(pattern, r'\1\2_', text) |
| 47 | |
| 48 | |
phoglund@webrtc.org | 943770b | 2013-01-02 15:46:43 +0000 | [diff] [blame] | 49 | def PostfixToPrefixInForLoops(text): |
| 50 | """Converts x++ to ++x in the increment part of a for loop.""" |
| 51 | pattern = r'(for \(.*;.*;) (\w+)\+\+\)' |
| 52 | return re.sub(pattern, r'\1++\2)', text) |
| 53 | |
| 54 | |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 55 | def CPPComments(text): |
| 56 | """Remove all C-comments and replace with C++ comments.""" |
| 57 | |
| 58 | # Keep the copyright header style. |
| 59 | line_list = text.splitlines(True) |
| 60 | copyright_list = line_list[0:10] |
| 61 | code_list = line_list[10:] |
| 62 | copy_text = ''.join(copyright_list) |
| 63 | code_text = ''.join(code_list) |
| 64 | |
| 65 | # Remove */ for C-comments, don't care about trailing blanks. |
| 66 | comment_end = re.compile(r'\n[ ]*\*/[ ]*') |
| 67 | code_text = re.sub(comment_end, '', code_text) |
| 68 | comment_end = re.compile(r'\*/') |
| 69 | code_text = re.sub(comment_end, '', code_text) |
| 70 | # Remove comment lines in the middle of comments, replace with C++ comments. |
phoglund@webrtc.org | 78bec2d | 2012-12-03 08:48:07 +0000 | [diff] [blame] | 71 | comment_star = re.compile(r'(?<=\n)[ ]*(?!\*\w)\*[ ]*') |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 72 | code_text = re.sub(comment_star, r'// ', code_text) |
| 73 | # Remove start of C comment and replace with C++ comment. |
| 74 | comment_start = re.compile(r'/\*[ ]*\n') |
| 75 | code_text = re.sub(comment_start, '', code_text) |
| 76 | comment_start = re.compile(r'/\*[ ]*(.)') |
| 77 | code_text = re.sub(comment_start, r'// \1', code_text) |
| 78 | |
| 79 | # Add copyright info. |
| 80 | return copy_text + code_text |
| 81 | |
| 82 | |
| 83 | def SortIncludeHeaders(text, filename): |
| 84 | """Sorts all include headers in alphabetic order. |
| 85 | |
| 86 | The file's own header goes first, followed by system headers and then |
| 87 | project headers. This function will exit if we detect any fancy #ifdef logic |
| 88 | among the includes - that's a lot harder to sort. |
| 89 | |
| 90 | Args: |
| 91 | text: The file text. |
| 92 | filename: The file we are reformatting. |
| 93 | |
| 94 | Returns: |
| 95 | The text with includes sorted. |
| 96 | """ |
| 97 | # Get all includes in file. |
| 98 | include_pattern = re.compile('#include.+\n') |
| 99 | includes = re.findall(include_pattern, text) |
| 100 | |
| 101 | # Sort system headers and project headers separately. |
| 102 | sys_includes = [] |
| 103 | project_includes = [] |
| 104 | self_include = '' |
| 105 | sys_pattern = re.compile('#include <') |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 106 | h_filename, _ = os.path.splitext(os.path.basename(filename)) |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 107 | |
| 108 | for item in includes: |
| 109 | if re.search(h_filename, item): |
| 110 | self_include = item |
| 111 | elif re.search(sys_pattern, item): |
| 112 | sys_includes.append(item) |
| 113 | else: |
| 114 | project_includes.append(item) |
| 115 | |
| 116 | sys_includes = sorted(sys_includes) |
| 117 | project_includes = sorted(project_includes) |
| 118 | headers = (self_include + '\n' + ''.join(sys_includes) + '\n' + |
| 119 | ''.join(project_includes)) |
| 120 | |
| 121 | # Replace existing headers with the sorted string. |
| 122 | text_no_hdrs = re.sub(include_pattern, r'???', text) |
| 123 | |
phoglund@webrtc.org | 943770b | 2013-01-02 15:46:43 +0000 | [diff] [blame] | 124 | # Insert sorted headers unless we detect #ifdefs right next to the headers. |
| 125 | if re.search(r'(#ifdef|#ifndef|#if).*\s*\?{3,}\s*#endif', text_no_hdrs): |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 126 | print 'WARNING: Include headers not sorted in ' + filename |
| 127 | return text |
| 128 | |
| 129 | return_text = re.sub(r'\?{3,}', headers, text_no_hdrs, 1) |
| 130 | if re.search(r'\?{3,}', text_no_hdrs): |
| 131 | # Remove possible remaining ???. |
| 132 | return_text = re.sub(r'\?{3,}', r'', return_text) |
| 133 | |
| 134 | return return_text |
| 135 | |
| 136 | |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 137 | def AddPath(match): |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 138 | """Helper for adding file path for WebRTC header files, ignoring other.""" |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 139 | file_to_examine = match.group(1) + '.h' |
| 140 | # TODO(mflodman) Use current directory and find webrtc/. |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 141 | for path, _, files in os.walk('./webrtc'): |
| 142 | for filename in files: |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 143 | if fnmatch.fnmatch(filename, file_to_examine): |
| 144 | path_name = os.path.join(path, filename).replace('./', '') |
| 145 | return '#include "%s"\n' % path_name |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 146 | |
| 147 | # No path found, return original string. |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 148 | return '#include "'+ file_to_examine + '"\n' |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 149 | |
| 150 | |
| 151 | def AddHeaderPath(text): |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 152 | """Add path to all included header files that have no path yet.""" |
| 153 | headers = re.compile('#include "(.+).h"\n') |
| 154 | return re.sub(headers, AddPath, text) |
| 155 | |
| 156 | |
| 157 | def AddWebrtcToOldSrcRelativePath(match): |
| 158 | file_to_examine = match.group(1) + '.h' |
| 159 | path, filename = os.path.split(file_to_examine) |
| 160 | dirs_in_webrtc = [name for name in os.listdir('./webrtc') |
| 161 | if os.path.isdir(os.path.join('./webrtc', name))] |
| 162 | for dir_in_webrtc in dirs_in_webrtc: |
| 163 | if path.startswith(dir_in_webrtc): |
| 164 | return '#include "%s"\n' % os.path.join('webrtc', path, filename) |
| 165 | return '#include "%s"\n' % file_to_examine |
| 166 | |
| 167 | def AddWebrtcPrefixToOldSrcRelativePaths(text): |
| 168 | """For all paths starting with for instance video_engine, add webrtc/.""" |
| 169 | headers = re.compile('#include "(.+).h"\n') |
| 170 | return re.sub(headers, AddWebrtcToOldSrcRelativePath, text) |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 171 | |
| 172 | |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 173 | def FixIncludeGuards(text, file_name): |
| 174 | """Change include guard according to the stantard.""" |
| 175 | # Remove a possible webrtc/ from the path. |
| 176 | file_name = re.sub(r'(webrtc\/)(.+)', r'\2', file_name) |
| 177 | new_guard = 'WEBRTC_' + file_name |
| 178 | new_guard = new_guard.upper() |
| 179 | new_guard = re.sub(r'([/\.])', r'_', new_guard) |
| 180 | new_guard += '_' |
| 181 | |
| 182 | text = re.sub(r'#ifndef WEBRTC_.+\n', r'#ifndef ' + new_guard + '\n', text, 1) |
| 183 | text = re.sub(r'#define WEBRTC_.+\n', r'#define ' + new_guard + '\n', text, 1) |
| 184 | text = re.sub(r'#endif *\/\/ *WEBRTC_.+\n', r'#endif // ' + new_guard + '\n', |
| 185 | text, 1) |
| 186 | |
| 187 | return text |
| 188 | |
| 189 | |
| 190 | def SaveFile(filename, text): |
| 191 | os.remove(filename) |
| 192 | f = open(filename, 'w') |
| 193 | f.write(text) |
| 194 | f.close() |
| 195 | |
| 196 | |
| 197 | def main(): |
| 198 | args = sys.argv[1:] |
| 199 | if not args: |
| 200 | print 'Usage: %s <filename>' % sys.argv[0] |
| 201 | sys.exit(1) |
| 202 | |
| 203 | for filename in args: |
| 204 | f = open(filename) |
| 205 | text = f.read() |
| 206 | f.close() |
| 207 | |
| 208 | text = DeCamelCase(text) |
| 209 | text = MoveUnderScore(text) |
phoglund@webrtc.org | 943770b | 2013-01-02 15:46:43 +0000 | [diff] [blame] | 210 | text = PostfixToPrefixInForLoops(text) |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 211 | text = CPPComments(text) |
| 212 | text = AddHeaderPath(text) |
phoglund@webrtc.org | e3b2bc6 | 2012-11-23 09:09:59 +0000 | [diff] [blame] | 213 | text = AddWebrtcPrefixToOldSrcRelativePaths(text) |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 214 | text = SortIncludeHeaders(text, filename) |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 215 | |
| 216 | # Remove the original file and re-create it with the reformatted content. |
| 217 | SaveFile(filename, text) |
| 218 | |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 219 | if filename.endswith('.h'): |
| 220 | f = open(filename) |
| 221 | text = f.read() |
| 222 | f.close() |
phoglund@webrtc.org | 3ec52c0 | 2012-11-21 13:28:52 +0000 | [diff] [blame] | 223 | text = FixIncludeGuards(text, filename) |
| 224 | SaveFile(filename, text) |
| 225 | |
| 226 | print filename + ' done.' |
| 227 | |
| 228 | |
| 229 | if __name__ == '__main__': |
| 230 | main() |