diff --git a/scripts/maint/practracker/includes.py b/scripts/maint/practracker/includes.py index e9b02c35b0..7af378d5c0 100755 --- a/scripts/maint/practracker/includes.py +++ b/scripts/maint/practracker/includes.py @@ -173,6 +173,27 @@ def remove_self_edges(graph): for k in list(graph): graph[k] = [ d for d in graph[k] if d != k ] +def closure(graph): + """Takes a directed graph in as an adjacency mapping (a mapping from + node to a list of the nodes to which it connects), and completes + its closure. + """ + graph = graph.copy() + changed = False + for k in graph.keys(): + graph[k] = set(graph[k]) + while True: + for k in graph.keys(): + sz = len(graph[k]) + for v in list(graph[k]): + graph[k].update(graph.get(v, [])) + if sz != len(graph[k]): + changed = True + + if not changed: + return graph + changed = False + def toposort(graph, limit=100): """Takes a directed graph in as an adjacency mapping (a mapping from node to a list of the nodes to which it connects). Tries to @@ -233,8 +254,25 @@ def walk_c_files(topdir="src"): for err in consider_include_rules(fullpath, f): yield err +def check_subsys_file(fname, uses_dirs): + uses_closure = closure(uses_dirs) + ok = True + previous_subsystems = [] + with open(fname) as f: + for line in f: + _, name, fname = line.split() + fname = re.sub(r'^.*/src/', "", fname) + fname = re.sub(r'^src/', "", fname) + fname = re.sub(r'/[^/]*\.c', "", fname) + for prev in previous_subsystems: + if fname in uses_closure[prev]: + print("INVERSION: {} uses {}".format(prev,fname)) + ok = False + previous_subsystems.append(fname) + return not ok + def run_check_includes(topdir, list_unused=False, log_sorted_levels=False, - list_advisories=False): + list_advisories=False, check_subsystem_order=None): trouble = False for err in walk_c_files(topdir): @@ -259,6 +297,11 @@ def run_check_includes(topdir, list_unused=False, log_sorted_levels=False, uses_dirs[rules.incpath] = rules.getAllowedDirectories() remove_self_edges(uses_dirs) + + if check_subsystem_order: + if check_subsys_file(check_subsystem_order, uses_dirs): + sys.exit(1) + all_levels = toposort(uses_dirs) if log_sorted_levels: @@ -282,6 +325,8 @@ def main(argv): help="List unused lines in .may_include files.") parser.add_argument("--list-advisories", action="store_true", help="List advisories as well as forbidden includes") + parser.add_argument("--check-subsystem-order", action="store", + help="Check a list of subsystems for ordering") parser.add_argument("topdir", default="src", nargs="?", help="Top-level directory for the tor source") args = parser.parse_args(argv[1:]) @@ -289,7 +334,8 @@ def main(argv): run_check_includes(topdir=args.topdir, log_sorted_levels=args.toposort, list_unused=args.list_unused, - list_advisories=args.list_advisories) + list_advisories=args.list_advisories, + check_subsystem_order=args.check_subsystem_order) if __name__ == '__main__': main(sys.argv)