blob: fe4e359ddab7186991bca498d57adcabbbc455e0 [file] [log] [blame]
Paul Duffindfa10832021-05-13 17:31:51 +01001#!/usr/bin/env python
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16"""
17Verify that one set of hidden API flags is a subset of another.
18"""
19
20import argparse
21import csv
Paul Duffin53a76072021-07-21 17:27:09 +010022from itertools import chain
Paul Duffindfa10832021-05-13 17:31:51 +010023
Paul Duffindfa10832021-05-13 17:31:51 +010024def dict_reader(input):
25 return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
26
Paul Duffin53a76072021-07-21 17:27:09 +010027def extract_subset_from_monolithic_flags_as_dict(monolithicFlagsDict, signatures):
28 """
29 Extract a subset of flags from the dict containing all the monolithic flags.
30
31 :param monolithicFlagsDict: the dict containing all the monolithic flags.
32 :param signatures: a list of signature that define the subset.
33 :return: the dict from signature to row.
34 """
35 dict = {}
36 for signature in signatures:
37 dict[signature] = monolithicFlagsDict.get(signature, {})
38 return dict
39
Paul Duffin428c6512021-07-21 15:33:22 +010040def read_signature_csv_from_stream_as_dict(stream):
41 """
42 Read the csv contents from the stream into a dict. The first column is assumed to be the
43 signature and used as the key. The whole row is stored as the value.
44
45 :param stream: the csv contents to read
46 :return: the dict from signature to row.
47 """
48 dict = {}
49 reader = dict_reader(stream)
50 for row in reader:
Paul Duffindfa10832021-05-13 17:31:51 +010051 signature = row['signature']
Paul Duffin428c6512021-07-21 15:33:22 +010052 dict[signature] = row
53 return dict
Paul Duffindfa10832021-05-13 17:31:51 +010054
Paul Duffin428c6512021-07-21 15:33:22 +010055def read_signature_csv_from_file_as_dict(csvFile):
56 """
57 Read the csvFile into a dict. The first column is assumed to be the
58 signature and used as the key. The whole row is stored as the value.
59
60 :param csvFile: the csv file to read
61 :return: the dict from signature to row.
62 """
63 with open(csvFile, 'r') as f:
64 return read_signature_csv_from_stream_as_dict(f)
65
66def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
67 """
68 Compare the signature flags between the two dicts.
69
70 :param monolithicFlagsDict: the dict containing the subset of the monolithic
71 flags that should be equal to the modular flags.
72 :param modularFlagsDict:the dict containing the flags produced by a single
73 bootclasspath_fragment module.
74 :return: list of mismatches., each mismatch is a tuple where the first item
75 is the signature, and the second and third items are lists of the flags from
76 modular dict, and monolithic dict respectively.
77 """
Paul Duffindfa10832021-05-13 17:31:51 +010078 mismatchingSignatures = []
Paul Duffin53a76072021-07-21 17:27:09 +010079 # Create a sorted set of all the signatures from both the monolithic and
80 # modular dicts.
81 allSignatures = sorted(set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
82 for signature in allSignatures:
Paul Duffin428c6512021-07-21 15:33:22 +010083 monolithicRow = monolithicFlagsDict.get(signature, {})
84 monolithicFlags = monolithicRow.get(None, [])
Paul Duffin53a76072021-07-21 17:27:09 +010085 modularRow = modularFlagsDict.get(signature, {})
86 modularFlags = modularRow.get(None, [])
Paul Duffin428c6512021-07-21 15:33:22 +010087 if monolithicFlags != modularFlags:
88 mismatchingSignatures.append((signature, modularFlags, monolithicFlags))
89 return mismatchingSignatures
Paul Duffindfa10832021-05-13 17:31:51 +010090
Paul Duffin428c6512021-07-21 15:33:22 +010091def main(argv):
Paul Duffin7be96332021-07-21 16:13:03 +010092 args_parser = argparse.ArgumentParser(description='Verify that sets of hidden API flags are each a subset of the monolithic flag file.')
93 args_parser.add_argument('monolithicFlags', help='The monolithic flag file')
94 args_parser.add_argument('modularFlags', nargs=argparse.REMAINDER, help='Flags produced by individual bootclasspath_fragment modules')
Paul Duffin428c6512021-07-21 15:33:22 +010095 args = args_parser.parse_args(argv[1:])
Paul Duffindfa10832021-05-13 17:31:51 +010096
Paul Duffin7be96332021-07-21 16:13:03 +010097 # Read in the monolithic flags into a dict indexed by signature
98 monolithicFlagsPath = args.monolithicFlags
99 monolithicFlagsDict = read_signature_csv_from_file_as_dict(monolithicFlagsPath)
Paul Duffindfa10832021-05-13 17:31:51 +0100100
Paul Duffin53a76072021-07-21 17:27:09 +0100101 # For each subset specified on the command line, create dicts for the flags
102 # provided by the subset and the corresponding flags from the complete set of
103 # flags and compare them.
Paul Duffin428c6512021-07-21 15:33:22 +0100104 failed = False
Paul Duffin7be96332021-07-21 16:13:03 +0100105 for modularFlagsPath in args.modularFlags:
106 modularFlagsDict = read_signature_csv_from_file_as_dict(modularFlagsPath)
Paul Duffin53a76072021-07-21 17:27:09 +0100107 monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict(monolithicFlagsDict, modularFlagsDict.keys())
108 mismatchingSignatures = compare_signature_flags(monolithicFlagsSubsetDict, modularFlagsDict)
Paul Duffin428c6512021-07-21 15:33:22 +0100109 if mismatchingSignatures:
110 failed = True
111 print("ERROR: Hidden API flags are inconsistent:")
Paul Duffin7be96332021-07-21 16:13:03 +0100112 print("< " + modularFlagsPath)
113 print("> " + monolithicFlagsPath)
Paul Duffin428c6512021-07-21 15:33:22 +0100114 for mismatch in mismatchingSignatures:
115 signature = mismatch[0]
116 print()
117 print("< " + ",".join([signature]+ mismatch[1]))
118 print("> " + ",".join([signature]+ mismatch[2]))
119
120 if failed:
121 sys.exit(1)
122
123if __name__ == "__main__":
124 main(sys.argv)