#!/usr/bin/env python3 """ GCC/Clang compiler implementation. Handles both GCC and Clang as they use similar command-line interfaces. """ from pathlib import Path from typing import List, Optional from .base import Compiler, CompileResult from build_system.utils import run class GCCCompiler(Compiler): """GCC or Clang compiler implementation.""" def __init__(self, executable: str = "gcc"): """ Initialize GCC/Clang compiler. Args: executable: Compiler executable name ("gcc" or "clang") """ name = "Clang" if "clang" in executable.lower() else "GCC" super().__init__(name, 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 GCC/Clang. 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 (.d file) Returns: CompileResult with object file and dependency file paths 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 dependency generation if requested dep_file = None if dependency_file: cmd.extend(["-MMD", "-MP"]) dep_file = dependency_file # Add source file cmd.append(str(source)) # Add output file cmd.extend(self.get_output_flag(output)) # Run compilation run(cmd) # type: ignore return CompileResult( object_file=output, dependency_file=dep_file, 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 GCC/Clang. 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 linker flags if flags: cmd.extend(flags) # Add library directories if library_dirs: cmd.extend([f"-L{lib_dir}" for lib_dir in library_dirs]) # Add libraries if libraries: cmd.extend([f"-l{lib}" for lib in libraries]) # Add output file cmd.extend(["-o", str(output)]) # Run linker run(cmd) # type: ignore return True def get_object_extension(self) -> str: """Get object file extension for GCC/Clang.""" return ".o" def get_dependency_extension(self) -> str: """Get dependency file extension for GCC/Clang.""" return ".d" def supports_dependency_generation(self) -> bool: """GCC/Clang supports automatic dependency generation.""" return True