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