From 720842c4484ef5bb00aa167c0cfc401825e3e0da Mon Sep 17 00:00:00 2001 From: Kyler Date: Fri, 19 Dec 2025 14:58:28 -0700 Subject: [PATCH] Implemented msvc.py --- SLS_C/build_system/compilers/msvc.py | 224 +++++++++++++++++++++++++++ 1 file changed, 224 insertions(+) diff --git a/SLS_C/build_system/compilers/msvc.py b/SLS_C/build_system/compilers/msvc.py index e69de29..a2796d6 100644 --- a/SLS_C/build_system/compilers/msvc.py +++ b/SLS_C/build_system/compilers/msvc.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python3 +""" +MSVC (Microsoft Visual C++) compiler implementation. +Used for Windows native builds. +""" + +from pathlib import Path +from typing import List, Optional +from .base import Compiler, CompileResult +from build_system.utils import run +import subprocess + + +class MSVCCompiler(Compiler): + """Microsoft Visual C++ compiler implementation.""" + + def __init__(self, executable: str = "cl"): + """ + Initialize MSVC compiler. + + Args: + executable: Compiler executable name ("cl") + """ + super().__init__("MSVC", executable) + + def compile(self, + source: Path, + output: Path, + flags: Optional[List[str]] = None, + defines: Optional[List[str]] = None, + include_dirs: Optional[List[Path]] = None, + dependency_file: Optional[Path] = None) -> CompileResult: + """ + Compile a source file using MSVC. + + Args: + source: Source file path + output: Output object file path + flags: Additional compiler flags + defines: Preprocessor defines + include_dirs: Additional include directories + dependency_file: Path for dependency file (not used by MSVC) + + Returns: + CompileResult with object file path + + Raises: + subprocess.CalledProcessError: If compilation fails + """ + # Ensure output directory exists + output.parent.mkdir(parents=True, exist_ok=True) + + # Build command + cmd = [self.executable] + + # Add flags + if flags: + cmd.extend(flags) + + # Add compile-only flag + cmd.append(self.get_compile_only_flag()) + + # Add defines + if defines: + cmd.extend([self.get_define_flag(d) for d in defines]) + + # Add include directories + if include_dirs: + cmd.extend([self.get_include_flag(d) for d in include_dirs]) + + # Add output file (MSVC uses /Fo) + cmd.extend(self.get_output_flag(output)) + + # Add source file + cmd.append(str(source)) + + # Run compilation + run(cmd) # type: ignore + + return CompileResult( + object_file=output, + dependency_file=None, # MSVC doesn't generate .d files + success=True + ) + + def link(self, + objects: List[Path], + output: Path, + flags: Optional[List[str]] = None, + libraries: Optional[List[str]] = None, + library_dirs: Optional[List[Path]] = None) -> bool: + """ + Link object files into an executable using MSVC. + + Args: + objects: List of object file paths + output: Output executable path + flags: Additional linker flags + libraries: Libraries to link against + library_dirs: Additional library search directories + + Returns: + True if linking succeeded + + Raises: + subprocess.CalledProcessError: If linking fails + """ + # Ensure output directory exists + output.parent.mkdir(parents=True, exist_ok=True) + + # Build command + cmd = [self.executable] + + # Add object files + cmd.extend([str(obj) for obj in objects]) + + # Add output file (MSVC uses /Fe) + cmd.append(f"/Fe{output}") + + # Add linker flags + if flags: + cmd.extend(flags) + + # Add library directories + if library_dirs: + cmd.extend([f"/LIBPATH:{lib_dir}" for lib_dir in library_dirs]) + + # Add libraries (MSVC uses .lib extension) + if libraries: + for lib in libraries: + # Add .lib if not present + lib_name = lib if lib.endswith('.lib') else f"{lib}.lib" + cmd.append(lib_name) + + # Run linker + run(cmd) # type: ignore + + return True + + def get_object_extension(self) -> str: + """Get object file extension for MSVC.""" + return ".obj" + + def get_dependency_extension(self) -> str: + """Get dependency file extension for MSVC.""" + return ".d" # Not actually used by MSVC + + def supports_dependency_generation(self) -> bool: + """MSVC does not support automatic dependency generation like GCC.""" + return False + + def get_define_flag(self, define: str) -> str: + """Get the MSVC flag for a preprocessor define.""" + return f"/D{define}" + + def get_include_flag(self, include_dir: Path) -> str: + """Get the MSVC flag for an include directory.""" + return f"/I{include_dir}" + + def get_output_flag(self, output: Path) -> List[str]: + """Get the MSVC flag for specifying output file.""" + return [f"/Fo{output}"] + + def get_compile_only_flag(self) -> str: + """Get the MSVC flag to compile without linking.""" + return "/c" + + def get_version(self) -> Optional[str]: + """ + Get the MSVC version string. + + Returns: + Version string if available, None otherwise + """ + try: + result = subprocess.run( + [self.executable], + capture_output=True, + text=True, + check=False + ) + # MSVC prints version info to stderr when called with no args + if result.stderr: + lines = result.stderr.strip().split('\n') + for line in lines: + if "Microsoft" in line or "Version" in line: + return line.strip() + except Exception: + pass + + return None + + +def detect_msvc_compiler() -> Optional[MSVCCompiler]: + """ + Detect and return an available MSVC compiler. + + Returns: + MSVCCompiler instance if found, None otherwise + """ + from build_system.utils import which + + # Try to find MSVC + if which("cl"): + return MSVCCompiler() + + return None + + +def check_msvc_environment() -> bool: + """ + Check if MSVC environment is properly set up. + + Returns: + True if MSVC is available and environment is configured, False otherwise + """ + from build_system.utils import check_tool + + if check_tool("cl"): + return True + else: + print("Make sure to run from a Visual Studio Developer Command Prompt") + print("or run vcvarsall.bat to set up the environment.") + return False