#!/usr/bin/env python3 # # Copyright (C) 2023 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. # # Sample Usage: # $ python3 validate_orderfile.py --order-file ../orderfiles/test/example.orderfile # # Try '-h' for a full list of command line arguments. # # Currently, we check four things in an orderfile: # - A partial order is maintained in the orderfile # - All symbols in allowlist must be present in the orderfile # - No symbol in denylist should be present in the orderfile # - The orderfile has a minimum number of symbols import argparse import orderfile_utils def parse_args(): """Parses and returns command line arguments.""" parser = argparse.ArgumentParser(prog="validate_orderfile", description="Validates the orderfile is correct and useful based on flag conditions") parser.add_argument( "--order-file", required=True, help="Orderfile that needs to be validated") parser.add_argument( "--partial", default="", help=f"A partial order of symbols that need to hold in the orderfile." f"Format: A symbol-per-line file with @ or comma separarted values within a quotation." f"For example, you can say @file.txt or 'main,bar,foo'.") parser.add_argument( "--allowlist", default="", help=f"Symbols that have to be present in the orderfile." f"Format: A symbol-per-line file with @ or comma separarted values within a quotation." f"For example, you can say @file.txt or 'main,bar,foo'.") parser.add_argument( "--denylist", default="", help=f"Symbols that should not be in orderfile. Denylist flag has priority over allowlist." f"Format: A symbol-per-line file with @ or comma separarted values within a quotation." f"For example, you can say @file.txt or 'main,bar,foo'.") parser.add_argument( "--min", type=int, default=0, help="Minimum number of entires needed for an orderfile") return parser.parse_args() def main(): args = parse_args() allowlist = orderfile_utils.parse_set(args.allowlist) partial = orderfile_utils.parse_list(args.partial) denylist = orderfile_utils.parse_set(args.denylist) # Check if there are symbols common to both allowlist and denylist # We give priority to denylist so the symbols in the intersection # will be removed from allowlist inter = allowlist.intersection(denylist) allowlist = allowlist.difference(inter) num_entries = 0 file_indices = {} file_present = set() # Read the orderfile with open(args.order_file, "r") as f: for line in f: line = line.strip() # Check if a symbol not allowed is within the orderfile if line in denylist: raise RuntimeError(f"Orderfile should not contain {line}") if line in allowlist: file_present.add(line) file_indices[line] = num_entries num_entries += 1 # Check if there are not a minimum number of symbols in orderfile if num_entries < args.min: raise RuntimeError(f"The orderfile has {num_entries} symbols but it " f"needs at least {args.min} symbols") # Check if all symbols allowed must be allowlist if len(allowlist) != len(file_present): raise RuntimeError("Some symbols in allow-list are not in the orderfile") # Check if partial order passed with flag is maintained within orderfile # The partial order might contain symbols not in the orderfile which we allow # because the order is still maintained. old_index = None curr_symbol = None for symbol in partial: new_index = file_indices.get(symbol) if new_index is not None: if old_index is not None: if new_index < old_index: raise RuntimeError(f"`{curr_symbol}` must be before `{symbol}` in orderfile") old_index = new_index curr_symbol = symbol print("Order file is valid") if __name__ == '__main__': main()