1# Copyright (C) 2023 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15# The following script will parse two source trees and list which APIs were added. 16# Example Usage: 17# python3 /ssd/master/packages/services/Car/tools/GenericCarApiBuilder/scripts/diff_apis_versions.py 18# "/ssd/tm-qpr-dev" "/ssd/master" 19 20import sys 21import os 22import subprocess 23import re 24import csv 25 26from tempfile import NamedTemporaryFile 27 28exempt_apis = [ 29 "equals", 30 "hashCode", 31 "finalize", 32 "writeToParcel", 33 "describeContents", 34 "release", 35 "onCarDisconnected", 36 "toString", 37 "dump" 38] 39 40# Strip the fully qualified names from argument names 41# Ex. android.os.Parcel -> Parcel 42def strip_param_prefixes(api): 43 index = api.find("(") 44 args = api[index+1:].split(",") 45 stripped = [arg.split(".")[-1].strip() for arg in args] 46 return api[:index+1] + ",".join(stripped) 47 48# Format an API name into this format packageName.className#methodName() 49def format_name(api): 50 api_split = api.split() 51 formatted = api_split[0] + "." + api_split[1] + "#" + "".join(api_split[3:]) 52 return strip_param_prefixes(formatted) 53 54def strip_param_names(api): 55 argGroup = re.search("\((.*)\)",api) 56 if argGroup is None: 57 return api 58 arg = argGroup.group(0) 59 new_arg = re.sub('[^ (]*?(?=\))|[^ ]*?(?=,)', "", arg) 60 return re.sub("\((.*)\)", new_arg, api) 61 62if (len(sys.argv) < 3): 63 print("Need three arguments: <old repo location> <new repo location>") 64 sys.exit(1) 65oldDir = sys.argv[1] 66newDir = sys.argv[2] 67 68java_cmd= "java -jar " + newDir + "/packages/services/Car/tools/GenericCarApiBuilder" \ 69 "/GenericCarApiBuilder.jar " \ 70 "--print-all-apis " \ 71 "--root-dir " + oldDir 72 73java_cmd_2= "java -jar " + newDir + "/packages/services/Car/tools/GenericCarApiBuilder" \ 74 "/GenericCarApiBuilder.jar " \ 75 "--print-all-apis " \ 76 "--root-dir " + newDir 77 78processes = [] 79cmds = [java_cmd, java_cmd_2] 80for cmd in cmds: 81 f = NamedTemporaryFile() 82 p = subprocess.Popen(cmd, shell=True, stdout=f) 83 processes.append((p, f)) 84 85results = [] 86for p, f in processes: 87 p.wait() 88 f.seek(0) 89 results.append(f.read().decode('utf-8').strip().split("\n")) 90 f.close() 91 92old_apis, new_apis = results[0], results[1] 93old_apis = [strip_param_names(i) for i in old_apis] 94new_apis = [strip_param_names(i) for i in new_apis] 95 96new_apis = [format_name(i) for i in new_apis if i not in old_apis] 97 98# Get the build numbers for the last 5 builds: 99cmd_str = "f1-sql --csv_print_column_names=true --csv_output=true --input_file=buildnums-sql-query --output_file=\"buildnums-results.csv\" --print_queries=false;" 100subprocess.run(cmd_str, shell=True) 101 102build_nums = [] 103with open("buildnums-results.csv") as fp: 104 next(fp) 105 for row in fp: 106 build_nums.append(row.strip()) 107 108for i in build_nums: 109 print(i) 110 111# Get the query results from F1 112data_read = set() 113for build_num in build_nums: 114 f = open('api-coverage-sql-query', 'r') 115 filedata = f.read() 116 f.close() 117 118 newdata = filedata.replace('build_num', build_num) 119 120 f = open('api-coverage-sql-query', 'w') 121 f.write(newdata) 122 f.close() 123 124 print(newdata) 125 cmd_str = "f1-sql --csv_print_column_names=true --csv_output=true --input_file=api-coverage-sql-query --output_file=\"api-coverage-results.csv\" --print_queries=false;" 126 subprocess.run(cmd_str, shell=True) 127 128 # Writeback the old query data. 129 f = open('api-coverage-sql-query', 'w') 130 f.write(filedata) 131 f.close() 132 133 current_data = [] 134 with open("api-coverage-results.csv") as fp: 135 for row in fp: 136 current_data.append(strip_param_prefixes(row.replace("$", ".").replace("\"", "").strip())) 137 138 if len(data_read) == 0: 139 data_read = set(current_data) 140 continue 141 142 # Save the intersection of data over multiple runs. 143 data_read = data_read.intersection(set(current_data)) 144 145print("**********THE FOLLOWING APIS HAVE BEEN ADDED FROM " + oldDir + " TO " + newDir + "************") 146f = open("apis-without-coverage.txt", "w") 147for api in data_read: 148 if api in new_apis and not any(exempt in api for exempt in exempt_apis): 149 print(api) 150 f.write(api + "\n") 151 152f.close() 153 154# Clean up csv files 155subprocess.run("rm api-coverage-results.csv", shell=True) 156subprocess.run("rm buildnums-results.csv", shell=True) 157