YREA-SLS/SLS_C/build_system/compilers/msvc.py

194 lines
5.5 KiB
Python

"""
MSVC (Microsoft Visual C++) compiler implementation.
This module implements the Compiler interface for Microsoft's MSVC compiler,
which has a completely different command-line syntax from GCC/Clang.
"""
from pathlib import Path
from typing import List, Optional
import subprocess
from .base import Compiler
class MSVCCompiler(Compiler):
"""
Microsoft Visual C++ compiler implementation.
MSVC uses different command-line syntax and conventions:
- Flags start with / instead of -
- Different flag names (/Fo vs -o, /Fe vs -o for linking)
- .obj instead of .o for object files
- .exe extension for executables
"""
def __init__(self, executable: str = "cl"):
"""
Initialize MSVC compiler.
Args:
executable: MSVC compiler executable (default: "cl")
"""
super().__init__(executable)
def compile(
self,
source: Path,
output: Path,
*,
includes: Optional[List[Path]] = None,
defines: Optional[dict] = None,
flags: Optional[List[str]] = None,
generate_deps: bool = True,
is_test: bool = False
) -> Path:
"""
Compile a source file using MSVC.
Args:
source: Path to source file
output: Path to output object file
includes: Include directories
defines: Preprocessor defines
flags: Additional compiler flags
generate_deps: Generate dependency file (not used by MSVC)
is_test: Test build (disables optimization)
Returns:
Path to compiled object file
"""
cmd = [self.executable]
# Add user flags first
if flags:
cmd.extend(flags)
# Standard flags
cmd.extend([
"/std:c11", # C11 standard (closest to c99)
"/W4", # Warning level 4 (similar to -Wall -Wextra)
"/WX", # Treat warnings as errors (like -Werror)
"/Zi", # Generate debug info (like -g)
])
# Test builds: disable optimization
if is_test:
cmd.append("/Od") # Disable optimization
# Include directories
if includes:
for inc in includes:
cmd.append(f"/I{inc}")
# Defines
if defines:
for name, value in defines.items():
if value is None:
cmd.append(f"/D{name}")
else:
# MSVC handles string escaping differently
if isinstance(value, str):
# Use backslash to escape quotes in MSVC
escaped = value.replace('"', '\\"')
cmd.append(f"/D{name}=\"{escaped}\"")
else:
cmd.append(f"/D{name}={value}")
# Compile only (don't link)
cmd.append("/c")
# Output file (note: /Fo with no space)
cmd.append(f"/Fo{output}")
# Source file
cmd.append(str(source))
# Ensure output directory exists
output.parent.mkdir(parents=True, exist_ok=True)
# Run compilation
self._run(cmd)
return output
def link(
self,
objects: List[Path],
output: Path,
*,
libraries: Optional[List[str]] = None,
library_paths: Optional[List[Path]] = None,
flags: Optional[List[str]] = None
) -> Path:
"""
Link object files into an executable using MSVC.
Args:
objects: Object files to link
output: Output executable path
libraries: Libraries to link (e.g., ['kernel32', 'user32'])
library_paths: Library search paths
flags: Additional linker flags
Returns:
Path to linked executable
"""
cmd = [self.executable]
# Add user flags first
if flags:
cmd.extend(flags)
# Object files
cmd.extend(str(obj) for obj in objects)
# Library paths
if library_paths:
for path in library_paths:
cmd.append(f"/LIBPATH:{path}")
# Libraries (MSVC uses .lib extension)
if libraries:
for lib in libraries:
# Add .lib if not present
if not lib.endswith('.lib'):
lib = f"{lib}.lib"
cmd.append(lib)
# Output file (note: /Fe with no space)
cmd.append(f"/Fe{output}")
# Link flag
cmd.append("/link")
# Ensure output directory exists
output.parent.mkdir(parents=True, exist_ok=True)
# Run linker
self._run(cmd)
return output
def get_object_extension(self) -> str:
"""Get object file extension for MSVC."""
return ".obj"
def get_executable_extension(self) -> str:
"""Get executable extension for Windows."""
return ".exe"
def _run(self, cmd: List[str]):
"""
Execute a command and handle errors.
Args:
cmd: Command to execute
Raises:
subprocess.CalledProcessError: If compilation/linking fails
"""
print(">>", " ".join(str(c) for c in cmd))
subprocess.check_call(cmd)