Files
Tahgibu/tools/check-includes.py
T
2026-10-24 11:52:16 -03:00

55 lines
2.0 KiB
Python

#!/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)