1#!/usr/bin/env python 2# Copyright 2018 the V8 project authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6# for py2/py3 compatibility 7from __future__ import print_function 8 9import argparse 10from datetime import datetime 11import re 12import subprocess 13import sys 14 15RE_GITHASH = re.compile(r"^[0-9a-f]{40}") 16RE_AUTHOR_TIME = re.compile(r"^author-time (\d+)$") 17RE_FILENAME = re.compile(r"^filename (.+)$") 18 19def GetBlame(file_path): 20 result = subprocess.check_output( 21 ['git', 'blame', '-t', '--line-porcelain', file_path]) 22 line_iter = iter(result.splitlines()) 23 blame_list = list() 24 current_blame = None 25 while True: 26 line = next(line_iter, None) 27 if line is None: 28 break 29 if RE_GITHASH.match(line): 30 if current_blame is not None: 31 blame_list.append(current_blame) 32 current_blame = {'time': 0, 'filename': None, 'content': None} 33 continue 34 match = RE_AUTHOR_TIME.match(line) 35 if match: 36 current_blame['time'] = datetime.fromtimestamp(int(match.groups()[0])) 37 continue 38 match = RE_FILENAME.match(line) 39 if match: 40 current_blame['filename'] = match.groups()[0] 41 current_blame['content'] = next(line_iter).strip() 42 continue 43 blame_list.append(current_blame) 44 return blame_list 45 46RE_MACRO_END = re.compile(r"\);"); 47RE_DEPRECATE_MACRO = re.compile(r"\(.*?,(.*)\);", re.MULTILINE) 48 49def FilterAndPrint(blame_list, macro, before): 50 index = 0 51 re_macro = re.compile(macro) 52 deprecated = list() 53 while index < len(blame_list): 54 blame = blame_list[index] 55 match = re_macro.search(blame['content']) 56 if match and blame['time'] < before: 57 line = blame['content'] 58 time = blame['time'] 59 pos = match.end() 60 start = -1 61 parens = 0 62 quotes = 0 63 while True: 64 if pos >= len(line): 65 # extend to next line 66 index = index + 1 67 blame = blame_list[index] 68 if line.endswith(','): 69 # add whitespace when breaking line due to comma 70 line = line + ' ' 71 line = line + blame['content'] 72 if line[pos] == '(': 73 parens = parens + 1 74 elif line[pos] == ')': 75 parens = parens - 1 76 if parens == 0: 77 break 78 elif line[pos] == '"': 79 quotes = quotes + 1 80 elif line[pos] == ',' and quotes % 2 == 0 and start == -1: 81 start = pos + 1 82 pos = pos + 1 83 deprecated.append([index + 1, time, line[start:pos].strip()]) 84 index = index + 1 85 print("Marked as " + macro + ": " + str(len(deprecated))) 86 for linenumber, time, content in deprecated: 87 print(str(linenumber).rjust(8) + " : " + str(time) + " : " + content) 88 return len(deprecated) 89 90def ParseOptions(args): 91 parser = argparse.ArgumentParser(description="Collect deprecation statistics") 92 parser.add_argument("file_path", help="Path to v8.h") 93 parser.add_argument("--before", help="Filter by date") 94 options = parser.parse_args(args) 95 if options.before: 96 options.before = datetime.strptime(options.before, '%Y-%m-%d') 97 else: 98 options.before = datetime.now() 99 return options 100 101def Main(args): 102 options = ParseOptions(args) 103 blame_list = GetBlame(options.file_path) 104 FilterAndPrint(blame_list, "V8_DEPRECATE_SOON", options.before) 105 FilterAndPrint(blame_list, "V8_DEPRECATED", options.before) 106 107if __name__ == "__main__": 108 Main(sys.argv[1:]) 109