• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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