blob: 8579321aac4659b332c8c85a9dff22b1404ae346 [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 Duffin67b9d612021-07-21 17:38:47 +010027def extract_subset_from_monolithic_flags_as_dict_from_file(monolithicFlagsDict, patternsFile):
Paul Duffin53a76072021-07-21 17:27:09 +010028 """
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.
Paul Duffin67b9d612021-07-21 17:38:47 +010032 :param patternsFile: a file containing a list of signature patterns that
33 define the subset.
34 :return: the dict from signature to row.
35 """
36 with open(patternsFile, 'r') as stream:
37 return extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicFlagsDict, stream)
38
39def extract_subset_from_monolithic_flags_as_dict_from_stream(monolithicFlagsDict, stream):
40 """
41 Extract a subset of flags from the dict containing all the monolithic flags.
42
43 :param monolithicFlagsDict: the dict containing all the monolithic flags.
44 :param stream: a stream containing a list of signature patterns that define
45 the subset.
Paul Duffin53a76072021-07-21 17:27:09 +010046 :return: the dict from signature to row.
47 """
48 dict = {}
Paul Duffin67b9d612021-07-21 17:38:47 +010049 for signature in stream:
50 signature = signature.rstrip()
Paul Duffin53a76072021-07-21 17:27:09 +010051 dict[signature] = monolithicFlagsDict.get(signature, {})
52 return dict
53
Paul Duffin428c6512021-07-21 15:33:22 +010054def read_signature_csv_from_stream_as_dict(stream):
55 """
56 Read the csv contents from the stream into a dict. The first column is assumed to be the
57 signature and used as the key. The whole row is stored as the value.
58
59 :param stream: the csv contents to read
60 :return: the dict from signature to row.
61 """
62 dict = {}
63 reader = dict_reader(stream)
64 for row in reader:
Paul Duffindfa10832021-05-13 17:31:51 +010065 signature = row['signature']
Paul Duffin428c6512021-07-21 15:33:22 +010066 dict[signature] = row
67 return dict
Paul Duffindfa10832021-05-13 17:31:51 +010068
Paul Duffin428c6512021-07-21 15:33:22 +010069def read_signature_csv_from_file_as_dict(csvFile):
70 """
71 Read the csvFile into a dict. The first column is assumed to be the
72 signature and used as the key. The whole row is stored as the value.
73
74 :param csvFile: the csv file to read
75 :return: the dict from signature to row.
76 """
77 with open(csvFile, 'r') as f:
78 return read_signature_csv_from_stream_as_dict(f)
79
80def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
81 """
82 Compare the signature flags between the two dicts.
83
84 :param monolithicFlagsDict: the dict containing the subset of the monolithic
85 flags that should be equal to the modular flags.
86 :param modularFlagsDict:the dict containing the flags produced by a single
87 bootclasspath_fragment module.
88 :return: list of mismatches., each mismatch is a tuple where the first item
89 is the signature, and the second and third items are lists of the flags from
90 modular dict, and monolithic dict respectively.
91 """
Paul Duffindfa10832021-05-13 17:31:51 +010092 mismatchingSignatures = []
Paul Duffin53a76072021-07-21 17:27:09 +010093 # Create a sorted set of all the signatures from both the monolithic and
94 # modular dicts.
95 allSignatures = sorted(set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
96 for signature in allSignatures:
Paul Duffin428c6512021-07-21 15:33:22 +010097 monolithicRow = monolithicFlagsDict.get(signature, {})
98 monolithicFlags = monolithicRow.get(None, [])
Paul Duffin53a76072021-07-21 17:27:09 +010099 modularRow = modularFlagsDict.get(signature, {})
100 modularFlags = modularRow.get(None, [])
Paul Duffin428c6512021-07-21 15:33:22 +0100101 if monolithicFlags != modularFlags:
102 mismatchingSignatures.append((signature, modularFlags, monolithicFlags))
103 return mismatchingSignatures
Paul Duffindfa10832021-05-13 17:31:51 +0100104
Paul Duffin428c6512021-07-21 15:33:22 +0100105def main(argv):
Paul Duffin7be96332021-07-21 16:13:03 +0100106 args_parser = argparse.ArgumentParser(description='Verify that sets of hidden API flags are each a subset of the monolithic flag file.')
107 args_parser.add_argument('monolithicFlags', help='The monolithic flag file')
108 args_parser.add_argument('modularFlags', nargs=argparse.REMAINDER, help='Flags produced by individual bootclasspath_fragment modules')
Paul Duffin428c6512021-07-21 15:33:22 +0100109 args = args_parser.parse_args(argv[1:])
Paul Duffindfa10832021-05-13 17:31:51 +0100110
Paul Duffin7be96332021-07-21 16:13:03 +0100111 # Read in the monolithic flags into a dict indexed by signature
112 monolithicFlagsPath = args.monolithicFlags
113 monolithicFlagsDict = read_signature_csv_from_file_as_dict(monolithicFlagsPath)
Paul Duffindfa10832021-05-13 17:31:51 +0100114
Paul Duffin53a76072021-07-21 17:27:09 +0100115 # For each subset specified on the command line, create dicts for the flags
116 # provided by the subset and the corresponding flags from the complete set of
117 # flags and compare them.
Paul Duffin428c6512021-07-21 15:33:22 +0100118 failed = False
Paul Duffin67b9d612021-07-21 17:38:47 +0100119 for modularPair in args.modularFlags:
120 parts = modularPair.split(":")
121 modularFlagsPath = parts[0]
122 modularPatternsPath = parts[1]
Paul Duffin7be96332021-07-21 16:13:03 +0100123 modularFlagsDict = read_signature_csv_from_file_as_dict(modularFlagsPath)
Paul Duffin67b9d612021-07-21 17:38:47 +0100124 monolithicFlagsSubsetDict = extract_subset_from_monolithic_flags_as_dict_from_file(monolithicFlagsDict, modularPatternsPath)
Paul Duffin53a76072021-07-21 17:27:09 +0100125 mismatchingSignatures = compare_signature_flags(monolithicFlagsSubsetDict, modularFlagsDict)
Paul Duffin428c6512021-07-21 15:33:22 +0100126 if mismatchingSignatures:
127 failed = True
128 print("ERROR: Hidden API flags are inconsistent:")
Paul Duffin7be96332021-07-21 16:13:03 +0100129 print("< " + modularFlagsPath)
130 print("> " + monolithicFlagsPath)
Paul Duffin428c6512021-07-21 15:33:22 +0100131 for mismatch in mismatchingSignatures:
132 signature = mismatch[0]
133 print()
134 print("< " + ",".join([signature]+ mismatch[1]))
135 print("> " + ",".join([signature]+ mismatch[2]))
136
137 if failed:
138 sys.exit(1)
139
140if __name__ == "__main__":
141 main(sys.argv)