Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 1 | #!/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. |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 16 | """Verify that one set of hidden API flags is a subset of another.""" |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 17 | |
| 18 | import argparse |
| 19 | import csv |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 20 | import sys |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 21 | from itertools import chain |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 22 | |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 23 | from signature_trie import signature_trie |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 24 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 25 | |
| 26 | def dict_reader(csvfile): |
| 27 | return csv.DictReader( |
| 28 | csvfile, delimiter=",", quotechar="|", fieldnames=["signature"]) |
| 29 | |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 30 | |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 31 | def read_flag_trie_from_file(file): |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 32 | with open(file, "r") as stream: |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 33 | return read_flag_trie_from_stream(stream) |
| 34 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 35 | |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 36 | def read_flag_trie_from_stream(stream): |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 37 | trie = signature_trie() |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 38 | reader = dict_reader(stream) |
| 39 | for row in reader: |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 40 | signature = row["signature"] |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 41 | trie.add(signature, row) |
| 42 | return trie |
| 43 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 44 | |
| 45 | def extract_subset_from_monolithic_flags_as_dict_from_file( |
| 46 | monolithicTrie, patternsFile): |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 47 | """Extract a subset of flags from the dict of monolithic flags. |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 48 | |
| 49 | :param monolithicFlagsDict: the dict containing all the monolithic flags. |
Paul Duffin | 67b9d61 | 2021-07-21 17:38:47 +0100 | [diff] [blame] | 50 | :param patternsFile: a file containing a list of signature patterns that |
| 51 | define the subset. |
| 52 | :return: the dict from signature to row. |
| 53 | """ |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 54 | with open(patternsFile, "r") as stream: |
| 55 | return extract_subset_from_monolithic_flags_as_dict_from_stream( |
| 56 | monolithicTrie, stream) |
Paul Duffin | 67b9d61 | 2021-07-21 17:38:47 +0100 | [diff] [blame] | 57 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 58 | |
| 59 | def extract_subset_from_monolithic_flags_as_dict_from_stream( |
| 60 | monolithicTrie, stream): |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 61 | """Extract a subset of flags from the trie of monolithic flags. |
Paul Duffin | 67b9d61 | 2021-07-21 17:38:47 +0100 | [diff] [blame] | 62 | |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 63 | :param monolithicTrie: the trie containing all the monolithic flags. |
Paul Duffin | 67b9d61 | 2021-07-21 17:38:47 +0100 | [diff] [blame] | 64 | :param stream: a stream containing a list of signature patterns that define |
| 65 | the subset. |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 66 | :return: the dict from signature to row. |
| 67 | """ |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 68 | dict_signature_to_row = {} |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 69 | for pattern in stream: |
| 70 | pattern = pattern.rstrip() |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 71 | rows = monolithicTrie.get_matching_rows(pattern) |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 72 | for row in rows: |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 73 | signature = row["signature"] |
| 74 | dict_signature_to_row[signature] = row |
| 75 | return dict_signature_to_row |
| 76 | |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 77 | |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 78 | def read_signature_csv_from_stream_as_dict(stream): |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 79 | """Read the csv contents from the stream into a dict. |
| 80 | |
| 81 | The first column is assumed to be the signature and used as the |
| 82 | key. |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 83 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 84 | The whole row is stored as the value. |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 85 | :param stream: the csv contents to read |
| 86 | :return: the dict from signature to row. |
| 87 | """ |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 88 | dict_signature_to_row = {} |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 89 | reader = dict_reader(stream) |
| 90 | for row in reader: |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 91 | signature = row["signature"] |
| 92 | dict_signature_to_row[signature] = row |
| 93 | return dict_signature_to_row |
| 94 | |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 95 | |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 96 | def read_signature_csv_from_file_as_dict(csvFile): |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 97 | """Read the csvFile into a dict. |
| 98 | |
| 99 | The first column is assumed to be the signature and used as the |
| 100 | key. |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 101 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 102 | The whole row is stored as the value. |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 103 | :param csvFile: the csv file to read |
| 104 | :return: the dict from signature to row. |
| 105 | """ |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 106 | with open(csvFile, "r") as f: |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 107 | return read_signature_csv_from_stream_as_dict(f) |
| 108 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 109 | |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 110 | def compare_signature_flags(monolithicFlagsDict, modularFlagsDict): |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 111 | """Compare the signature flags between the two dicts. |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 112 | |
| 113 | :param monolithicFlagsDict: the dict containing the subset of the monolithic |
| 114 | flags that should be equal to the modular flags. |
| 115 | :param modularFlagsDict:the dict containing the flags produced by a single |
| 116 | bootclasspath_fragment module. |
| 117 | :return: list of mismatches., each mismatch is a tuple where the first item |
| 118 | is the signature, and the second and third items are lists of the flags from |
| 119 | modular dict, and monolithic dict respectively. |
| 120 | """ |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 121 | mismatchingSignatures = [] |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 122 | # Create a sorted set of all the signatures from both the monolithic and |
| 123 | # modular dicts. |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 124 | allSignatures = sorted( |
| 125 | set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys()))) |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 126 | for signature in allSignatures: |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 127 | monolithicRow = monolithicFlagsDict.get(signature, {}) |
| 128 | monolithicFlags = monolithicRow.get(None, []) |
Paul Duffin | 280bae6 | 2021-07-20 18:03:53 +0100 | [diff] [blame] | 129 | if signature in modularFlagsDict: |
| 130 | modularRow = modularFlagsDict.get(signature, {}) |
| 131 | modularFlags = modularRow.get(None, []) |
| 132 | else: |
| 133 | modularFlags = ["blocked"] |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 134 | if monolithicFlags != modularFlags: |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 135 | mismatchingSignatures.append( |
| 136 | (signature, modularFlags, monolithicFlags)) |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 137 | return mismatchingSignatures |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 138 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 139 | |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 140 | def main(argv): |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 141 | args_parser = argparse.ArgumentParser( |
| 142 | description="Verify that sets of hidden API flags are each a subset of " |
Paul Duffin | b5cd522 | 2022-02-28 19:06:49 +0000 | [diff] [blame^] | 143 | "the monolithic flag file.") |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 144 | args_parser.add_argument("monolithicFlags", help="The monolithic flag file") |
| 145 | args_parser.add_argument( |
| 146 | "modularFlags", |
| 147 | nargs=argparse.REMAINDER, |
| 148 | help="Flags produced by individual bootclasspath_fragment modules") |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 149 | args = args_parser.parse_args(argv[1:]) |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 150 | |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 151 | # Read in all the flags into the trie |
Paul Duffin | 7be9633 | 2021-07-21 16:13:03 +0100 | [diff] [blame] | 152 | monolithicFlagsPath = args.monolithicFlags |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 153 | monolithicTrie = read_flag_trie_from_file(monolithicFlagsPath) |
Paul Duffin | dfa1083 | 2021-05-13 17:31:51 +0100 | [diff] [blame] | 154 | |
Paul Duffin | 53a7607 | 2021-07-21 17:27:09 +0100 | [diff] [blame] | 155 | # For each subset specified on the command line, create dicts for the flags |
Paul Duffin | c11f667 | 2021-07-20 00:04:21 +0100 | [diff] [blame] | 156 | # provided by the subset and the corresponding flags from the complete set |
| 157 | # of flags and compare them. |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 158 | failed = False |
Paul Duffin | 67b9d61 | 2021-07-21 17:38:47 +0100 | [diff] [blame] | 159 | for modularPair in args.modularFlags: |
| 160 | parts = modularPair.split(":") |
| 161 | modularFlagsPath = parts[0] |
| 162 | modularPatternsPath = parts[1] |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 163 | modularFlagsDict = read_signature_csv_from_file_as_dict( |
| 164 | modularFlagsPath) |
| 165 | monolithicFlagsSubsetDict = \ |
| 166 | extract_subset_from_monolithic_flags_as_dict_from_file( |
| 167 | monolithicTrie, modularPatternsPath) |
| 168 | mismatchingSignatures = compare_signature_flags( |
| 169 | monolithicFlagsSubsetDict, modularFlagsDict) |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 170 | if mismatchingSignatures: |
| 171 | failed = True |
| 172 | print("ERROR: Hidden API flags are inconsistent:") |
Paul Duffin | 7be9633 | 2021-07-21 16:13:03 +0100 | [diff] [blame] | 173 | print("< " + modularFlagsPath) |
| 174 | print("> " + monolithicFlagsPath) |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 175 | for mismatch in mismatchingSignatures: |
| 176 | signature = mismatch[0] |
| 177 | print() |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 178 | print("< " + ",".join([signature] + mismatch[1])) |
| 179 | print("> " + ",".join([signature] + mismatch[2])) |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 180 | |
| 181 | if failed: |
| 182 | sys.exit(1) |
| 183 | |
Spandan Das | 559132f | 2021-08-25 18:17:33 +0000 | [diff] [blame] | 184 | |
Paul Duffin | 428c651 | 2021-07-21 15:33:22 +0100 | [diff] [blame] | 185 | if __name__ == "__main__": |
| 186 | main(sys.argv) |