82 lines
3.1 KiB
Python
82 lines
3.1 KiB
Python
"""Sln extractor. Moved verbatim from graphify/extract.py."""
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
from pathlib import Path
|
|
from graphify.extractors.base import _make_id
|
|
|
|
|
|
def extract_sln(path: Path) -> dict:
|
|
"""Extract projects and inter-project dependencies from a .sln file."""
|
|
try:
|
|
src = path.read_text(encoding="utf-8", errors="replace")
|
|
except OSError:
|
|
return {"nodes": [], "edges": [], "error": f"cannot read {path}"}
|
|
|
|
file_nid = _make_id(str(path))
|
|
str_path = str(path)
|
|
nodes: list[dict] = [{"id": file_nid, "label": path.name, "file_type": "code",
|
|
"source_file": str_path, "source_location": None}]
|
|
edges: list[dict] = []
|
|
seen_ids: set[str] = set()
|
|
seen_ids.add(file_nid)
|
|
|
|
_PROJECT_RE = re.compile(
|
|
r'Project\("[^"]*"\)\s*=\s*"([^"]+)"\s*,\s*"([^"]+)"\s*,\s*"([^"]*)"'
|
|
)
|
|
_DEP_RE = re.compile(r'\{([0-9a-fA-F-]+)\}\s*=\s*\{([0-9a-fA-F-]+)\}')
|
|
|
|
guid_to_nid: dict[str, str] = {}
|
|
|
|
for m in _PROJECT_RE.finditer(src):
|
|
proj_name = m.group(1)
|
|
proj_path = m.group(2).replace("\\", "/")
|
|
proj_guid = m.group(3).strip("{}")
|
|
|
|
try:
|
|
abs_proj = str((path.parent / proj_path).resolve())
|
|
except Exception:
|
|
abs_proj = proj_path
|
|
proj_nid = _make_id(abs_proj)
|
|
if proj_nid and proj_nid not in seen_ids:
|
|
seen_ids.add(proj_nid)
|
|
nodes.append({"id": proj_nid, "label": proj_name,
|
|
"file_type": "code", "source_file": abs_proj,
|
|
"source_location": None})
|
|
edges.append({"source": file_nid, "target": proj_nid,
|
|
"relation": "contains", "confidence": "EXTRACTED",
|
|
"source_file": str_path, "weight": 1.0})
|
|
if proj_guid:
|
|
guid_to_nid[proj_guid.lower()] = proj_nid
|
|
|
|
in_dep_section = False
|
|
current_proj_guid: str | None = None
|
|
_PROJECT_LINE_RE = re.compile(r'Project\("[^"]*"\)\s*=\s*"[^"]+"\s*,\s*"[^"]+"\s*,\s*"\{([^}]+)\}"')
|
|
for line in src.splitlines():
|
|
proj_line_m = _PROJECT_LINE_RE.search(line)
|
|
if proj_line_m:
|
|
current_proj_guid = proj_line_m.group(1).lower()
|
|
continue
|
|
if line.strip() == "EndProject":
|
|
current_proj_guid = None
|
|
continue
|
|
if "ProjectSection(ProjectDependencies)" in line:
|
|
in_dep_section = True
|
|
continue
|
|
if in_dep_section and "EndProjectSection" in line:
|
|
in_dep_section = False
|
|
continue
|
|
if in_dep_section and current_proj_guid:
|
|
dep_m = _DEP_RE.search(line)
|
|
if dep_m:
|
|
to_guid = dep_m.group(1).lower()
|
|
from_nid = guid_to_nid.get(current_proj_guid)
|
|
to_nid = guid_to_nid.get(to_guid)
|
|
if from_nid and to_nid and from_nid != to_nid:
|
|
edges.append({"source": from_nid, "target": to_nid,
|
|
"relation": "imports", "confidence": "EXTRACTED",
|
|
"source_file": str_path, "weight": 1.0})
|
|
|
|
return {"nodes": nodes, "edges": edges}
|