123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- #!/usr/bin/env python
- #
- # Copyright (C) 2018 The Android Open Source Project
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- """Verify that one set of hidden API flags is a subset of another."""
- import argparse
- import csv
- import sys
- from itertools import chain
- from signature_trie import signature_trie
- def dict_reader(csv_file):
- return csv.DictReader(
- csv_file, delimiter=",", quotechar="|", fieldnames=["signature"])
- def read_flag_trie_from_file(file):
- with open(file, "r", encoding="utf8") as stream:
- return read_flag_trie_from_stream(stream)
- def read_flag_trie_from_stream(stream):
- trie = signature_trie()
- reader = dict_reader(stream)
- for row in reader:
- signature = row["signature"]
- trie.add(signature, row)
- return trie
- def extract_subset_from_monolithic_flags_as_dict_from_file(
- monolithic_trie, patterns_file):
- """Extract a subset of flags from the dict of monolithic flags.
- :param monolithic_trie: the trie containing all the monolithic flags.
- :param patterns_file: a file containing a list of signature patterns that
- define the subset.
- :return: the dict from signature to row.
- """
- with open(patterns_file, "r", encoding="utf8") as stream:
- return extract_subset_from_monolithic_flags_as_dict_from_stream(
- monolithic_trie, stream)
- def extract_subset_from_monolithic_flags_as_dict_from_stream(
- monolithic_trie, stream):
- """Extract a subset of flags from the trie of monolithic flags.
- :param monolithic_trie: the trie containing all the monolithic flags.
- :param stream: a stream containing a list of signature patterns that define
- the subset.
- :return: the dict from signature to row.
- """
- dict_signature_to_row = {}
- for pattern in stream:
- pattern = pattern.rstrip()
- rows = monolithic_trie.get_matching_rows(pattern)
- for row in rows:
- signature = row["signature"]
- dict_signature_to_row[signature] = row
- return dict_signature_to_row
- def read_signature_csv_from_stream_as_dict(stream):
- """Read the csv contents from the stream into a dict.
- The first column is assumed to be the signature and used as the
- key.
- The whole row is stored as the value.
- :param stream: the csv contents to read
- :return: the dict from signature to row.
- """
- dict_signature_to_row = {}
- reader = dict_reader(stream)
- for row in reader:
- signature = row["signature"]
- dict_signature_to_row[signature] = row
- return dict_signature_to_row
- def read_signature_csv_from_file_as_dict(csv_file):
- """Read the csvFile into a dict.
- The first column is assumed to be the signature and used as the
- key.
- The whole row is stored as the value.
- :param csv_file: the csv file to read
- :return: the dict from signature to row.
- """
- with open(csv_file, "r", encoding="utf8") as f:
- return read_signature_csv_from_stream_as_dict(f)
- def compare_signature_flags(monolithic_flags_dict, modular_flags_dict,
- implementation_flags):
- """Compare the signature flags between the two dicts.
- :param monolithic_flags_dict: the dict containing the subset of the
- monolithic flags that should be equal to the modular flags.
- :param modular_flags_dict:the dict containing the flags produced by a single
- bootclasspath_fragment module.
- :return: list of mismatches., each mismatch is a tuple where the first item
- is the signature, and the second and third items are lists of the flags from
- modular dict, and monolithic dict respectively.
- """
- mismatching_signatures = []
- # Create a sorted set of all the signatures from both the monolithic and
- # modular dicts.
- all_signatures = sorted(
- set(chain(monolithic_flags_dict.keys(), modular_flags_dict.keys())))
- for signature in all_signatures:
- monolithic_row = monolithic_flags_dict.get(signature, {})
- monolithic_flags = monolithic_row.get(None, [])
- if signature in modular_flags_dict:
- modular_row = modular_flags_dict.get(signature, {})
- modular_flags = modular_row.get(None, [])
- else:
- modular_flags = implementation_flags
- if monolithic_flags != modular_flags:
- mismatching_signatures.append(
- (signature, modular_flags, monolithic_flags))
- return mismatching_signatures
- def main(argv):
- args_parser = argparse.ArgumentParser(
- description="Verify that sets of hidden API flags are each a subset of "
- "the monolithic flag file. For each module this uses the provided "
- "signature patterns to select a subset of the monolithic flags and "
- "then it compares that subset against the filtered flags provided by "
- "the module. If the module's filtered flags does not contain flags for "
- "a signature then it is assumed to have been filtered out because it "
- "was not part of an API and so is assumed to have the implementation "
- "flags.")
- args_parser.add_argument(
- "--monolithic-flags", help="The monolithic flag file")
- args_parser.add_argument(
- "--module-flags",
- action="append",
- help="A colon separated pair of paths. The first is a path to a "
- "filtered set of flags, and the second is a path to a set of "
- "signature patterns that identify the set of classes belonging to "
- "a single bootclasspath_fragment module. Specify once for each module "
- "that needs to be checked.")
- args_parser.add_argument(
- "--implementation-flag",
- action="append",
- help="A flag in the set of flags that identifies a signature which is "
- "not part of an API, i.e. is the signature of a private implementation "
- "member. Specify as many times as necessary to define the "
- "implementation flag set. If this is not specified then the "
- "implementation flag set is empty.")
- args = args_parser.parse_args(argv[1:])
- # Read in all the flags into the trie
- monolithic_flags_path = args.monolithic_flags
- monolithic_trie = read_flag_trie_from_file(monolithic_flags_path)
- implementation_flags = args.implementation_flag or []
- # For each subset specified on the command line, create dicts for the flags
- # provided by the subset and the corresponding flags from the complete set
- # of flags and compare them.
- failed = False
- module_pairs = args.module_flags or []
- for modular_pair in module_pairs:
- parts = modular_pair.split(":")
- modular_flags_path = parts[0]
- modular_patterns_path = parts[1]
- modular_flags_dict = read_signature_csv_from_file_as_dict(
- modular_flags_path)
- monolithic_flags_subset_dict = \
- extract_subset_from_monolithic_flags_as_dict_from_file(
- monolithic_trie, modular_patterns_path)
- mismatching_signatures = compare_signature_flags(
- monolithic_flags_subset_dict, modular_flags_dict,
- implementation_flags)
- if mismatching_signatures:
- failed = True
- print("ERROR: Hidden API flags are inconsistent:")
- print("< " + modular_flags_path)
- print("> " + monolithic_flags_path)
- for mismatch in mismatching_signatures:
- signature = mismatch[0]
- print()
- print("< " + ",".join([signature] + mismatch[1]))
- print("> " + ",".join([signature] + mismatch[2]))
- if failed:
- sys.exit(1)
- if __name__ == "__main__":
- main(sys.argv)
|