1#!/usr/bin/env python3
2#
3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
4#
5# SPDX-License-Identifier: BSD-2-Clause
6#
7
8"""
9Script for reporting circular #includes in pre-processed sel4 source.
10Exits with a status of 0 if no circular dependencies are found, otherwise
11prints circular dependency and exits with a status of -1.
12"""
13
14import sys
15import re
16import argparse
17
18
19def main(parse_args):
20    """
21    Reads pre-processed sel4 source from standard input.
22    If a circular dependency is found, the chain of includes
23    resulting in the loop is printed out.
24    """
25
26    ignore_re = None
27    ignore_args = parse_args.ignore
28    if ignore_args and len(ignore_args):
29        ignore_args = [re.escape(ignore) for ignore in ignore_args]
30        ignore_re_string = '(' + '|'.join(ignore_args) + ')'
31        ignore_re = re.compile(r'^# 1 ".*' + ignore_re_string + '"')
32
33    header_re = re.compile(r'^# (\d+) "(.*\..)"')
34
35    file_stack = []
36
37    for line in sys.stdin:
38
39        if ignore_re and ignore_re.match(line):
40            continue
41
42        match = header_re.match(line)
43
44        if match is None:
45            continue
46
47        depth = int(match.group(1))
48        header = match.group(2)
49
50        if depth == 1:
51            # found a new header
52            if header in file_stack:
53                print("Circular includes found:")
54                print("\n".join(file_stack))
55                print(header)
56                return -1
57            else:
58                file_stack.append(header)
59        else:
60            # popped back up to an earlier header
61            while file_stack[-1] != header:
62                file_stack.pop()
63
64    return 0
65
66
67if __name__ == "__main__":
68
69    parser = argparse.ArgumentParser()
70    parser.add_argument('--ignore', nargs='+',
71                        help="Files to ignore when parsing the sel4 source")
72    args = parser.parse_args()
73
74    sys.exit(main(args))
75