blob: 83faa23bccdd476f6ebad5ffe7eeb96c64252492 [file] [log] [blame]
#!/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()