1#!/usr/bin/env python3 2# 3# Copyright (C) 2023 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# 17# Sample Usage: 18# $ python3 validate_orderfile.py --order-file ../orderfiles/test/example.orderfile 19# 20# Try '-h' for a full list of command line arguments. 21# 22# Currently, we check four things in an orderfile: 23# - A partial order is maintained in the orderfile 24# - All symbols in allowlist must be present in the orderfile 25# - No symbol in denylist should be present in the orderfile 26# - The orderfile has a minimum number of symbols 27 28import argparse 29import orderfile_utils 30 31def parse_args(): 32 """Parses and returns command line arguments.""" 33 parser = argparse.ArgumentParser(prog="validate_orderfile", 34 description="Validates the orderfile is correct and useful based on flag conditions") 35 36 parser.add_argument( 37 "--order-file", 38 required=True, 39 help="Orderfile that needs to be validated") 40 41 parser.add_argument( 42 "--partial", 43 default="", 44 help=f"A partial order of symbols that need to hold in the orderfile." 45 f"Format: A symbol-per-line file with @ or comma separarted values within a quotation." 46 f"For example, you can say @file.txt or 'main,bar,foo'.") 47 48 parser.add_argument( 49 "--allowlist", 50 default="", 51 help=f"Symbols that have to be present in the orderfile." 52 f"Format: A symbol-per-line file with @ or comma separarted values within a quotation." 53 f"For example, you can say @file.txt or 'main,bar,foo'.") 54 55 parser.add_argument( 56 "--denylist", 57 default="", 58 help=f"Symbols that should not be in orderfile. Denylist flag has priority over allowlist." 59 f"Format: A symbol-per-line file with @ or comma separarted values within a quotation." 60 f"For example, you can say @file.txt or 'main,bar,foo'.") 61 62 parser.add_argument( 63 "--min", 64 type=int, 65 default=0, 66 help="Minimum number of entires needed for an orderfile") 67 68 return parser.parse_args() 69 70def main(): 71 args = parse_args() 72 73 allowlist = orderfile_utils.parse_set(args.allowlist) 74 partial = orderfile_utils.parse_list(args.partial) 75 denylist = orderfile_utils.parse_set(args.denylist) 76 77 # Check if there are symbols common to both allowlist and denylist 78 # We give priority to denylist so the symbols in the intersection 79 # will be removed from allowlist 80 inter = allowlist.intersection(denylist) 81 allowlist = allowlist.difference(inter) 82 83 num_entries = 0 84 file_indices = {} 85 file_present = set() 86 87 # Read the orderfile 88 with open(args.order_file, "r") as f: 89 for line in f: 90 line = line.strip() 91 92 # Check if a symbol not allowed is within the orderfile 93 if line in denylist: 94 raise RuntimeError(f"Orderfile should not contain {line}") 95 96 if line in allowlist: 97 file_present.add(line) 98 99 file_indices[line] = num_entries 100 num_entries += 1 101 102 # Check if there are not a minimum number of symbols in orderfile 103 if num_entries < args.min: 104 raise RuntimeError(f"The orderfile has {num_entries} symbols but it " 105 f"needs at least {args.min} symbols") 106 107 # Check if all symbols allowed must be allowlist 108 if len(allowlist) != len(file_present): 109 raise RuntimeError("Some symbols in allow-list are not in the orderfile") 110 111 # Check if partial order passed with flag is maintained within orderfile 112 # The partial order might contain symbols not in the orderfile which we allow 113 # because the order is still maintained. 114 old_index = None 115 curr_symbol = None 116 for symbol in partial: 117 new_index = file_indices.get(symbol) 118 if new_index is not None: 119 if old_index is not None: 120 if new_index < old_index: 121 raise RuntimeError(f"`{curr_symbol}` must be before `{symbol}` in orderfile") 122 old_index = new_index 123 curr_symbol = symbol 124 125 print("Order file is valid") 126 127if __name__ == '__main__': 128 main() 129