#!/usr/bin/env python3 """Busca dependencias circulares entre cabeceras públicas y entre módulos internos (por #include "...").""" import os, re, sys root = os.path.join(os.path.dirname(__file__), "..") def includes(path): out = [] for line in open(path, encoding="utf-8"): m = re.match(r'\s*#\s*include\s+"([^"]+)"', line) if m: out.append(m.group(1)) return out def find_cycle(graph): state, stack = {}, [] def dfs(n): state[n] = 1; stack.append(n) for m in graph.get(n, []): if state.get(m) == 1: return stack[stack.index(m):] + [m] if state.get(m) is None: c = dfs(m) if c: return c stack.pop(); state[n] = 2 return None for n in list(graph): if state.get(n) is None: c = dfs(n) if c: return c return None # 1) cabeceras públicas pub = os.path.join(root, "include", "tahgibu") g = {} for f in sorted(os.listdir(pub)): g[f] = [i.split("/")[-1] for i in includes(os.path.join(pub, f)) if i.startswith("tahgibu/")] c = find_cycle(g) print("Cabeceras públicas:", "CICLO " + " -> ".join(c) if c else "sin ciclos (%d cabeceras)" % len(g)) # 2) módulos internos (carpetas de src/, sin ui/ ni platform/) por dependencias de archivos .c/.h src = os.path.join(root, "src") mods = {} for d, _, files in os.walk(src): for f in files: if not f.endswith((".c", ".h")): continue p = os.path.join(d, f) mod = os.path.relpath(d, src).split(os.sep)[0] for inc in includes(p): tgt = os.path.normpath(os.path.join(d, inc)) if tgt.startswith(src) and os.path.exists(tgt): tmod = os.path.relpath(os.path.dirname(tgt), src).split(os.sep)[0] if tmod != mod: mods.setdefault(mod, set()).add(tmod) for m, deps in sorted(mods.items()): print(" %-12s -> %s" % (m, ", ".join(sorted(deps)))) c = find_cycle({k: sorted(v) for k, v in mods.items()}) print("Módulos internos:", "CICLO " + " -> ".join(c) if c else "sin ciclos") sys.exit(0)