from pathlib import Path from .models import DiscoveryResult, HarnessConfig, ProjectKind, ProjectSelection from .tdd_policy import find_cpp_files class DiscoveryError(RuntimeError): pass def _cmake_entry(source: Path) -> Path | None: for name in ("CMakePresets.json", "CMakeUserPresets.json", "CMakeLists.txt"): candidate = source / name if candidate.is_file(): return candidate return None def _single_msbuild(root: Path, explicit: Path | None) -> Path: if explicit is not None: if explicit.is_file() and explicit.suffix.lower() in {".sln", ".vcxproj"}: return explicit raise DiscoveryError(f"configured MSBuild project is invalid: {explicit}") solutions = sorted(root.glob("*.sln")) if len(solutions) > 1: raise DiscoveryError("multiple .sln files found; set msbuild.solution") if solutions: return solutions[0] projects = sorted(root.glob("*.vcxproj")) if len(projects) > 1: raise DiscoveryError("multiple .vcxproj files found; set msbuild.solution") if projects: return projects[0] raise DiscoveryError("no root .sln or .vcxproj was found") def discover_project(root: Path, config: HarnessConfig) -> DiscoveryResult: root = root.resolve() if config.project_type == "cmake": entry = _cmake_entry(config.cmake.source_dir) if entry is None: raise DiscoveryError( f"no CMake project found in {config.cmake.source_dir}" ) return DiscoveryResult(ProjectSelection(ProjectKind.CMAKE, entry)) if config.project_type == "msbuild": project = _single_msbuild(root, config.msbuild.solution) return DiscoveryResult(ProjectSelection(ProjectKind.MSBUILD, project)) entry = _cmake_entry(root) if entry is not None: return DiscoveryResult(ProjectSelection(ProjectKind.CMAKE, entry)) solutions = sorted(root.glob("*.sln")) if len(solutions) > 1: raise DiscoveryError("multiple .sln files found; set msbuild.solution") if solutions: return DiscoveryResult( ProjectSelection(ProjectKind.MSBUILD, solutions[0]) ) projects = sorted(root.glob("*.vcxproj")) if len(projects) > 1: raise DiscoveryError("multiple .vcxproj files found; set msbuild.solution") if projects: return DiscoveryResult( ProjectSelection(ProjectKind.MSBUILD, projects[0]) ) cpp_files = find_cpp_files(root) if cpp_files: raise DiscoveryError( "C/C++ files exist but no CMakeLists.txt, preset, .sln, or .vcxproj " "was found; configure projectType and project path" ) return DiscoveryResult(None, ())