git / moonbite-core / contrib/devtools/circular-dependencies.py · raw

#!/usr/bin/env python3# Copyright (c) 2018-2019 The Bitcoin Core developers# Distributed under the MIT software license, see the accompanying# file COPYING or http://www.opensource.org/licenses/mit-license.php.import sysimport reMAPPING = {    'core_read.cpp': 'core_io.cpp',    'core_write.cpp': 'core_io.cpp',}# Directories with header-based modules, where the assumption that .cpp files# define functions and variables declared in corresponding .h files is# incorrect.HEADER_MODULE_PATHS = [    'interfaces/']def module_name(path):    if path in MAPPING:        path = MAPPING[path]    if any(path.startswith(dirpath) for dirpath in HEADER_MODULE_PATHS):        return path    if path.endswith(".h"):        return path[:-2]    if path.endswith(".c"):        return path[:-2]    if path.endswith(".cpp"):        return path[:-4]    return Nonefiles = dict()deps = dict()RE = re.compile("^#include <(.*)>")# Iterate over files, and create list of modulesfor arg in sys.argv[1:]:    module = module_name(arg)    if module is None:        print("Ignoring file %s (does not constitute module)\n" % arg)    else:        files[arg] = module        deps[module] = set()# Iterate again, and build list of direct dependencies for each module# TODO: implement support for multiple include directoriesfor arg in sorted(files.keys()):    module = files[arg]    with open(arg, 'r', encoding="utf8") as f:        for line in f:            match = RE.match(line)            if match:                include = match.group(1)                included_module = module_name(include)                if included_module is not None and included_module in deps and included_module != module:                    deps[module].add(included_module)# Loop to find the shortest (remaining) circular dependencyhave_cycle = Falsewhile True:    shortest_cycle = None    for module in sorted(deps.keys()):        # Build the transitive closure of dependencies of module        closure = dict()        for dep in deps[module]:            closure[dep] = []        while True:            old_size = len(closure)            old_closure_keys = sorted(closure.keys())            for src in old_closure_keys:                for dep in deps[src]:                    if dep not in closure:                        closure[dep] = closure[src] + [src]            if len(closure) == old_size:                break        # If module is in its own transitive closure, it's a circular dependency; check if it is the shortest        if module in closure and (shortest_cycle is None or len(closure[module]) + 1 < len(shortest_cycle)):            shortest_cycle = [module] + closure[module]    if shortest_cycle is None:        break    # We have the shortest circular dependency; report it    module = shortest_cycle[0]    print("Circular dependency: %s" % (" -> ".join(shortest_cycle + [module])))    # And then break the dependency to avoid repeating in other cycles    deps[shortest_cycle[-1]] = deps[shortest_cycle[-1]] - set([module])    have_cycle = Truesys.exit(1 if have_cycle else 0)