#!/usr/bin/python # Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. # # Use of this source code is governed by a BSD-style license # that can be found in the LICENSE file in the root of the source # tree. An additional intellectual property rights grant can be found # in the file PATENTS. All contributing project authors may # be found in the AUTHORS file in the root of the source tree. """WebRTC reformat script. This script is used to reformat WebRTC code from the old code style to Google C++ code style. This script does not indent code; use clang-reformat-chrome.py as described in go/webrtc/engineering/reformatting-gips---google. """ __author__ = 'mflodman@webrtc.org (Magnus Flodman)' import fnmatch import os import re import subprocess import sys def LowerWord(obj): """Helper for DeCamelCase.""" optional_last_letters = obj.group(3) or '' return obj.group(1) + '_' + obj.group(2).lower() + optional_last_letters def DeCamelCase(text): """De-camelize variable names. This function will look at any stringLikeThis and format it in steps. The sequence will be stringLikeThis -> string_likeThis -> string_like_this. """ possible_tokens_before_vars = '[ _*\(\&\!\[]' pattern = re.compile(r'(?<=' + possible_tokens_before_vars + ')' + # Match some lower-case characters '([a-z]+)' + # Don't match kFoo, !kFoo, [kFoo], etc '(?<!' + possible_tokens_before_vars + 'k)' + # Match some upper-case characters '([A-Z]+)([a-z])?') while re.search(pattern, text): text = re.sub(pattern, LowerWord, text) return text def MoveUnderScore(text): """Moves the underscore from beginning of variable name to the end.""" # TODO(mflodman) Replace \1 with ?-expression. # We don't want to change macros and #defines though, so don't do anything # if the first character is uppercase (normal variables shouldn't have that). pattern = r'([ \*\!\&\(\[\]])_(?!_)(?![A-Z])(\w+)' return re.sub(pattern, r'\1\2_', text) def PostfixToPrefixInForLoops(text): """Converts x++ to ++x in the increment part of a for loop.""" pattern = r'(for \(.*;.*;) (\w+)\+\+\)' return re.sub(pattern, r'\1++\2)', text) def SortIncludeHeaders(text, filename): """Sorts all include headers in alphabetic order. The file's own header goes first, followed by system headers and then project headers. This function will exit if we detect any fancy #ifdef logic among the includes - that's a lot harder to sort. Args: text: The file text. filename: The file we are reformatting. Returns: The text with includes sorted. """ # Get all includes in file. include_pattern = re.compile('#include.+\n') includes = re.findall(include_pattern, text) # Sort system headers and project headers separately. sys_includes = [] project_includes = [] self_include = '' sys_pattern = re.compile('#include <') h_filename, _ = os.path.splitext(os.path.basename(filename)) for item in includes: if re.search(h_filename + '\.', item): self_include = item elif re.search(sys_pattern, item): sys_includes.append(item) else: project_includes.append(item) sys_includes = sorted(sys_includes) project_includes = sorted(project_includes) headers = (self_include + '\n' + ''.join(sys_includes) + '\n' + ''.join(project_includes)) # Replace existing headers with the sorted string. text_no_hdrs = re.sub(include_pattern, r'???', text) # Insert sorted headers unless we detect #ifdefs right next to the headers. if re.search(r'(#ifdef|#ifndef|#if).*\s*\?{3,}\s*#endif', text_no_hdrs): print 'WARNING: Include headers not sorted in ' + filename return text return_text = re.sub(r'\?{3,}', headers, text_no_hdrs, 1) if re.search(r'\?{3,}', text_no_hdrs): # Remove possible remaining ???. return_text = re.sub(r'\?{3,}', r'', return_text) return return_text def AddPath(match): """Helper for adding file path for WebRTC header files, ignoring other.""" file_to_examine = match.group(1) + '.h' # TODO(mflodman) Use current directory and find webrtc/. for path, _, files in os.walk('./webrtc'): for filename in files: if fnmatch.fnmatch(filename, file_to_examine): path_name = os.path.join(path, filename).replace('./', '') return '#include "%s"\n' % path_name # No path found, return original string. return '#include "'+ file_to_examine + '"\n' def AddHeaderPath(text): """Add path to all included header files that have no path yet.""" headers = re.compile('#include "(.+).h"\n') return re.sub(headers, AddPath, text) def AddWebrtcToOldSrcRelativePath(match): file_to_examine = match.group(1) + '.h' path, filename = os.path.split(file_to_examine) dirs_in_webrtc = [name for name in os.listdir('./webrtc') if os.path.isdir(os.path.join('./webrtc', name))] for dir_in_webrtc in dirs_in_webrtc: if path.startswith(dir_in_webrtc): return '#include "%s"\n' % os.path.join('webrtc', path, filename) return '#include "%s"\n' % file_to_examine def AddWebrtcPrefixToOldSrcRelativePaths(text): """For all paths starting with for instance video_engine, add webrtc/.""" headers = re.compile('#include "(.+).h"\n') return re.sub(headers, AddWebrtcToOldSrcRelativePath, text) def FixIncludeGuards(text, file_name): """Change include guard according to the stantard.""" # Remove a possible webrtc/ from the path. file_name = re.sub(r'(webrtc\/)(.+)', r'\2', file_name) new_guard = 'WEBRTC_' + file_name new_guard = new_guard.upper() new_guard = re.sub(r'([/\.])', r'_', new_guard) new_guard += '_' text = re.sub(r'#ifndef WEBRTC_.+\n', r'#ifndef ' + new_guard + '\n', text, 1) text = re.sub(r'#define WEBRTC_.+\n', r'#define ' + new_guard + '\n', text, 1) text = re.sub(r'#endif *\/\/ *WEBRTC_.+\n', r'#endif // ' + new_guard + '\n', text, 1) return text def SaveFile(filename, text): os.remove(filename) f = open(filename, 'w') f.write(text) f.close() def main(): args = sys.argv[1:] if not args: print 'Usage: %s <filename>' % sys.argv[0] sys.exit(1) for filename in args: f = open(filename) text = f.read() f.close() text = DeCamelCase(text) text = MoveUnderScore(text) text = PostfixToPrefixInForLoops(text) text = AddHeaderPath(text) text = AddWebrtcPrefixToOldSrcRelativePaths(text) text = SortIncludeHeaders(text, filename) # Remove the original file and re-create it with the reformatted content. SaveFile(filename, text) if filename.endswith('.h'): f = open(filename) text = f.read() f.close() text = FixIncludeGuards(text, filename) SaveFile(filename, text) print filename + ' done.' if __name__ == '__main__': main()