1*333d2b36SAndroid Build Coastguard Worker#!/usr/bin/env python 2*333d2b36SAndroid Build Coastguard Worker# 3*333d2b36SAndroid Build Coastguard Worker# Copyright (C) 2018 The Android Open Source Project 4*333d2b36SAndroid Build Coastguard Worker# 5*333d2b36SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*333d2b36SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*333d2b36SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*333d2b36SAndroid Build Coastguard Worker# 9*333d2b36SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*333d2b36SAndroid Build Coastguard Worker# 11*333d2b36SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*333d2b36SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*333d2b36SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*333d2b36SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*333d2b36SAndroid Build Coastguard Worker# limitations under the License. 16*333d2b36SAndroid Build Coastguard Worker"""Verify that one set of hidden API flags is a subset of another.""" 17*333d2b36SAndroid Build Coastguard Worker 18*333d2b36SAndroid Build Coastguard Workerimport argparse 19*333d2b36SAndroid Build Coastguard Workerimport csv 20*333d2b36SAndroid Build Coastguard Workerimport sys 21*333d2b36SAndroid Build Coastguard Workerfrom itertools import chain 22*333d2b36SAndroid Build Coastguard Worker 23*333d2b36SAndroid Build Coastguard Workerfrom signature_trie import signature_trie 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Worker 26*333d2b36SAndroid Build Coastguard Workerdef dict_reader(csv_file): 27*333d2b36SAndroid Build Coastguard Worker return csv.DictReader( 28*333d2b36SAndroid Build Coastguard Worker csv_file, delimiter=",", quotechar="|", fieldnames=["signature"]) 29*333d2b36SAndroid Build Coastguard Worker 30*333d2b36SAndroid Build Coastguard Worker 31*333d2b36SAndroid Build Coastguard Workerdef read_flag_trie_from_file(file): 32*333d2b36SAndroid Build Coastguard Worker with open(file, "r", encoding="utf8") as stream: 33*333d2b36SAndroid Build Coastguard Worker return read_flag_trie_from_stream(stream) 34*333d2b36SAndroid Build Coastguard Worker 35*333d2b36SAndroid Build Coastguard Worker 36*333d2b36SAndroid Build Coastguard Workerdef read_flag_trie_from_stream(stream): 37*333d2b36SAndroid Build Coastguard Worker trie = signature_trie() 38*333d2b36SAndroid Build Coastguard Worker reader = dict_reader(stream) 39*333d2b36SAndroid Build Coastguard Worker for row in reader: 40*333d2b36SAndroid Build Coastguard Worker signature = row["signature"] 41*333d2b36SAndroid Build Coastguard Worker trie.add(signature, row) 42*333d2b36SAndroid Build Coastguard Worker return trie 43*333d2b36SAndroid Build Coastguard Worker 44*333d2b36SAndroid Build Coastguard Worker 45*333d2b36SAndroid Build Coastguard Workerdef extract_subset_from_monolithic_flags_as_dict_from_file( 46*333d2b36SAndroid Build Coastguard Worker monolithic_trie, patterns_file): 47*333d2b36SAndroid Build Coastguard Worker """Extract a subset of flags from the dict of monolithic flags. 48*333d2b36SAndroid Build Coastguard Worker 49*333d2b36SAndroid Build Coastguard Worker :param monolithic_trie: the trie containing all the monolithic flags. 50*333d2b36SAndroid Build Coastguard Worker :param patterns_file: a file containing a list of signature patterns that 51*333d2b36SAndroid Build Coastguard Worker define the subset. 52*333d2b36SAndroid Build Coastguard Worker :return: the dict from signature to row. 53*333d2b36SAndroid Build Coastguard Worker """ 54*333d2b36SAndroid Build Coastguard Worker with open(patterns_file, "r", encoding="utf8") as stream: 55*333d2b36SAndroid Build Coastguard Worker return extract_subset_from_monolithic_flags_as_dict_from_stream( 56*333d2b36SAndroid Build Coastguard Worker monolithic_trie, stream) 57*333d2b36SAndroid Build Coastguard Worker 58*333d2b36SAndroid Build Coastguard Worker 59*333d2b36SAndroid Build Coastguard Workerdef extract_subset_from_monolithic_flags_as_dict_from_stream( 60*333d2b36SAndroid Build Coastguard Worker monolithic_trie, stream): 61*333d2b36SAndroid Build Coastguard Worker """Extract a subset of flags from the trie of monolithic flags. 62*333d2b36SAndroid Build Coastguard Worker 63*333d2b36SAndroid Build Coastguard Worker :param monolithic_trie: the trie containing all the monolithic flags. 64*333d2b36SAndroid Build Coastguard Worker :param stream: a stream containing a list of signature patterns that define 65*333d2b36SAndroid Build Coastguard Worker the subset. 66*333d2b36SAndroid Build Coastguard Worker :return: the dict from signature to row. 67*333d2b36SAndroid Build Coastguard Worker """ 68*333d2b36SAndroid Build Coastguard Worker dict_signature_to_row = {} 69*333d2b36SAndroid Build Coastguard Worker for pattern in stream: 70*333d2b36SAndroid Build Coastguard Worker pattern = pattern.rstrip() 71*333d2b36SAndroid Build Coastguard Worker rows = monolithic_trie.get_matching_rows(pattern) 72*333d2b36SAndroid Build Coastguard Worker for row in rows: 73*333d2b36SAndroid Build Coastguard Worker signature = row["signature"] 74*333d2b36SAndroid Build Coastguard Worker dict_signature_to_row[signature] = row 75*333d2b36SAndroid Build Coastguard Worker return dict_signature_to_row 76*333d2b36SAndroid Build Coastguard Worker 77*333d2b36SAndroid Build Coastguard Worker 78*333d2b36SAndroid Build Coastguard Workerdef read_signature_csv_from_stream_as_dict(stream): 79*333d2b36SAndroid Build Coastguard Worker """Read the csv contents from the stream into a dict. 80*333d2b36SAndroid Build Coastguard Worker 81*333d2b36SAndroid Build Coastguard Worker The first column is assumed to be the signature and used as the 82*333d2b36SAndroid Build Coastguard Worker key. 83*333d2b36SAndroid Build Coastguard Worker 84*333d2b36SAndroid Build Coastguard Worker The whole row is stored as the value. 85*333d2b36SAndroid Build Coastguard Worker :param stream: the csv contents to read 86*333d2b36SAndroid Build Coastguard Worker :return: the dict from signature to row. 87*333d2b36SAndroid Build Coastguard Worker """ 88*333d2b36SAndroid Build Coastguard Worker dict_signature_to_row = {} 89*333d2b36SAndroid Build Coastguard Worker reader = dict_reader(stream) 90*333d2b36SAndroid Build Coastguard Worker for row in reader: 91*333d2b36SAndroid Build Coastguard Worker signature = row["signature"] 92*333d2b36SAndroid Build Coastguard Worker dict_signature_to_row[signature] = row 93*333d2b36SAndroid Build Coastguard Worker return dict_signature_to_row 94*333d2b36SAndroid Build Coastguard Worker 95*333d2b36SAndroid Build Coastguard Worker 96*333d2b36SAndroid Build Coastguard Workerdef read_signature_csv_from_file_as_dict(csv_file): 97*333d2b36SAndroid Build Coastguard Worker """Read the csvFile into a dict. 98*333d2b36SAndroid Build Coastguard Worker 99*333d2b36SAndroid Build Coastguard Worker The first column is assumed to be the signature and used as the 100*333d2b36SAndroid Build Coastguard Worker key. 101*333d2b36SAndroid Build Coastguard Worker 102*333d2b36SAndroid Build Coastguard Worker The whole row is stored as the value. 103*333d2b36SAndroid Build Coastguard Worker :param csv_file: the csv file to read 104*333d2b36SAndroid Build Coastguard Worker :return: the dict from signature to row. 105*333d2b36SAndroid Build Coastguard Worker """ 106*333d2b36SAndroid Build Coastguard Worker with open(csv_file, "r", encoding="utf8") as f: 107*333d2b36SAndroid Build Coastguard Worker return read_signature_csv_from_stream_as_dict(f) 108*333d2b36SAndroid Build Coastguard Worker 109*333d2b36SAndroid Build Coastguard Worker 110*333d2b36SAndroid Build Coastguard Workerdef compare_signature_flags(monolithic_flags_dict, modular_flags_dict, 111*333d2b36SAndroid Build Coastguard Worker implementation_flags): 112*333d2b36SAndroid Build Coastguard Worker """Compare the signature flags between the two dicts. 113*333d2b36SAndroid Build Coastguard Worker 114*333d2b36SAndroid Build Coastguard Worker :param monolithic_flags_dict: the dict containing the subset of the 115*333d2b36SAndroid Build Coastguard Worker monolithic flags that should be equal to the modular flags. 116*333d2b36SAndroid Build Coastguard Worker :param modular_flags_dict:the dict containing the flags produced by a single 117*333d2b36SAndroid Build Coastguard Worker bootclasspath_fragment module. 118*333d2b36SAndroid Build Coastguard Worker :return: list of mismatches., each mismatch is a tuple where the first item 119*333d2b36SAndroid Build Coastguard Worker is the signature, and the second and third items are lists of the flags from 120*333d2b36SAndroid Build Coastguard Worker modular dict, and monolithic dict respectively. 121*333d2b36SAndroid Build Coastguard Worker """ 122*333d2b36SAndroid Build Coastguard Worker mismatching_signatures = [] 123*333d2b36SAndroid Build Coastguard Worker # Create a sorted set of all the signatures from both the monolithic and 124*333d2b36SAndroid Build Coastguard Worker # modular dicts. 125*333d2b36SAndroid Build Coastguard Worker all_signatures = sorted( 126*333d2b36SAndroid Build Coastguard Worker set(chain(monolithic_flags_dict.keys(), modular_flags_dict.keys()))) 127*333d2b36SAndroid Build Coastguard Worker for signature in all_signatures: 128*333d2b36SAndroid Build Coastguard Worker monolithic_row = monolithic_flags_dict.get(signature, {}) 129*333d2b36SAndroid Build Coastguard Worker monolithic_flags = monolithic_row.get(None, []) 130*333d2b36SAndroid Build Coastguard Worker if signature in modular_flags_dict: 131*333d2b36SAndroid Build Coastguard Worker modular_row = modular_flags_dict.get(signature, {}) 132*333d2b36SAndroid Build Coastguard Worker modular_flags = modular_row.get(None, []) 133*333d2b36SAndroid Build Coastguard Worker else: 134*333d2b36SAndroid Build Coastguard Worker modular_flags = implementation_flags 135*333d2b36SAndroid Build Coastguard Worker if monolithic_flags != modular_flags: 136*333d2b36SAndroid Build Coastguard Worker mismatching_signatures.append( 137*333d2b36SAndroid Build Coastguard Worker (signature, modular_flags, monolithic_flags)) 138*333d2b36SAndroid Build Coastguard Worker return mismatching_signatures 139*333d2b36SAndroid Build Coastguard Worker 140*333d2b36SAndroid Build Coastguard Worker 141*333d2b36SAndroid Build Coastguard Workerdef main(argv): 142*333d2b36SAndroid Build Coastguard Worker args_parser = argparse.ArgumentParser( 143*333d2b36SAndroid Build Coastguard Worker description="Verify that sets of hidden API flags are each a subset of " 144*333d2b36SAndroid Build Coastguard Worker "the monolithic flag file. For each module this uses the provided " 145*333d2b36SAndroid Build Coastguard Worker "signature patterns to select a subset of the monolithic flags and " 146*333d2b36SAndroid Build Coastguard Worker "then it compares that subset against the filtered flags provided by " 147*333d2b36SAndroid Build Coastguard Worker "the module. If the module's filtered flags does not contain flags for " 148*333d2b36SAndroid Build Coastguard Worker "a signature then it is assumed to have been filtered out because it " 149*333d2b36SAndroid Build Coastguard Worker "was not part of an API and so is assumed to have the implementation " 150*333d2b36SAndroid Build Coastguard Worker "flags.") 151*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 152*333d2b36SAndroid Build Coastguard Worker "--monolithic-flags", help="The monolithic flag file") 153*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 154*333d2b36SAndroid Build Coastguard Worker "--module-flags", 155*333d2b36SAndroid Build Coastguard Worker action="append", 156*333d2b36SAndroid Build Coastguard Worker help="A colon separated pair of paths. The first is a path to a " 157*333d2b36SAndroid Build Coastguard Worker "filtered set of flags, and the second is a path to a set of " 158*333d2b36SAndroid Build Coastguard Worker "signature patterns that identify the set of classes belonging to " 159*333d2b36SAndroid Build Coastguard Worker "a single bootclasspath_fragment module. Specify once for each module " 160*333d2b36SAndroid Build Coastguard Worker "that needs to be checked.") 161*333d2b36SAndroid Build Coastguard Worker args_parser.add_argument( 162*333d2b36SAndroid Build Coastguard Worker "--implementation-flag", 163*333d2b36SAndroid Build Coastguard Worker action="append", 164*333d2b36SAndroid Build Coastguard Worker help="A flag in the set of flags that identifies a signature which is " 165*333d2b36SAndroid Build Coastguard Worker "not part of an API, i.e. is the signature of a private implementation " 166*333d2b36SAndroid Build Coastguard Worker "member. Specify as many times as necessary to define the " 167*333d2b36SAndroid Build Coastguard Worker "implementation flag set. If this is not specified then the " 168*333d2b36SAndroid Build Coastguard Worker "implementation flag set is empty.") 169*333d2b36SAndroid Build Coastguard Worker args = args_parser.parse_args(argv[1:]) 170*333d2b36SAndroid Build Coastguard Worker 171*333d2b36SAndroid Build Coastguard Worker # Read in all the flags into the trie 172*333d2b36SAndroid Build Coastguard Worker monolithic_flags_path = args.monolithic_flags 173*333d2b36SAndroid Build Coastguard Worker monolithic_trie = read_flag_trie_from_file(monolithic_flags_path) 174*333d2b36SAndroid Build Coastguard Worker 175*333d2b36SAndroid Build Coastguard Worker implementation_flags = args.implementation_flag or [] 176*333d2b36SAndroid Build Coastguard Worker 177*333d2b36SAndroid Build Coastguard Worker # For each subset specified on the command line, create dicts for the flags 178*333d2b36SAndroid Build Coastguard Worker # provided by the subset and the corresponding flags from the complete set 179*333d2b36SAndroid Build Coastguard Worker # of flags and compare them. 180*333d2b36SAndroid Build Coastguard Worker failed = False 181*333d2b36SAndroid Build Coastguard Worker module_pairs = args.module_flags or [] 182*333d2b36SAndroid Build Coastguard Worker for modular_pair in module_pairs: 183*333d2b36SAndroid Build Coastguard Worker parts = modular_pair.split(":") 184*333d2b36SAndroid Build Coastguard Worker modular_flags_path = parts[0] 185*333d2b36SAndroid Build Coastguard Worker modular_patterns_path = parts[1] 186*333d2b36SAndroid Build Coastguard Worker modular_flags_dict = read_signature_csv_from_file_as_dict( 187*333d2b36SAndroid Build Coastguard Worker modular_flags_path) 188*333d2b36SAndroid Build Coastguard Worker monolithic_flags_subset_dict = \ 189*333d2b36SAndroid Build Coastguard Worker extract_subset_from_monolithic_flags_as_dict_from_file( 190*333d2b36SAndroid Build Coastguard Worker monolithic_trie, modular_patterns_path) 191*333d2b36SAndroid Build Coastguard Worker mismatching_signatures = compare_signature_flags( 192*333d2b36SAndroid Build Coastguard Worker monolithic_flags_subset_dict, modular_flags_dict, 193*333d2b36SAndroid Build Coastguard Worker implementation_flags) 194*333d2b36SAndroid Build Coastguard Worker if mismatching_signatures: 195*333d2b36SAndroid Build Coastguard Worker failed = True 196*333d2b36SAndroid Build Coastguard Worker print("ERROR: Hidden API flags are inconsistent:") 197*333d2b36SAndroid Build Coastguard Worker print("< " + modular_flags_path) 198*333d2b36SAndroid Build Coastguard Worker print("> " + monolithic_flags_path) 199*333d2b36SAndroid Build Coastguard Worker for mismatch in mismatching_signatures: 200*333d2b36SAndroid Build Coastguard Worker signature = mismatch[0] 201*333d2b36SAndroid Build Coastguard Worker print() 202*333d2b36SAndroid Build Coastguard Worker print("< " + ",".join([signature] + mismatch[1])) 203*333d2b36SAndroid Build Coastguard Worker print("> " + ",".join([signature] + mismatch[2])) 204*333d2b36SAndroid Build Coastguard Worker 205*333d2b36SAndroid Build Coastguard Worker if failed: 206*333d2b36SAndroid Build Coastguard Worker sys.exit(1) 207*333d2b36SAndroid Build Coastguard Worker 208*333d2b36SAndroid Build Coastguard Worker 209*333d2b36SAndroid Build Coastguard Workerif __name__ == "__main__": 210*333d2b36SAndroid Build Coastguard Worker main(sys.argv) 211