""" GCC and Clang compiler implementation. This module implements the Compiler interface for GCC and Clang, which share similar command-line interfaces. """ from pathlib import Path from typing import List, Optional import subprocess from .base import Compiler class GCCCompiler(Compiler): """ GCC/Clang compiler implementation. Works with both gcc and clang as they use compatible command-line syntax. """ def __init__(self, executable: str = "gcc", is_clang: bool = False): """ Initialize GCC/Clang compiler. Args: executable: Compiler executable name (default: "gcc") is_clang: Whether this is clang (affects some flags) """ super().__init__(executable) self.is_clang = is_clang 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 GCC/Clang. 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 is_test: Test build (disables optimization) Returns: Path to compiled object file """ cmd = [self.executable] # Add user flags first (can be overridden by later flags) if flags: cmd.extend(flags) # Standard flags cmd.extend([ "-std=c99", "-Wall", "-Wextra", "-Werror", "-g" # Debug symbols ]) # Test builds: disable optimization and allow unused functions if is_test: cmd.extend(["-O0", "-Wno-unused-function"]) # 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: # Properly escape string values if isinstance(value, str): cmd.append(f"-D{name}=\"{value}\"") else: cmd.append(f"-D{name}={value}") # Dependency generation if generate_deps: cmd.extend(["-MMD", "-MP"]) # Compile only cmd.extend(["-c", str(source)]) # Output file cmd.extend(["-o", str(output)]) # 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 GCC/Clang. Args: objects: Object files to link output: Output executable path libraries: Libraries to link (e.g., ['m', 'pthread']) 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"-L{path}") # Libraries if libraries: for lib in libraries: cmd.append(f"-l{lib}") # Output file cmd.extend(["-o", str(output)]) # 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 GCC/Clang.""" return ".o" def get_executable_extension(self) -> str: """Get executable extension (empty for Unix-like systems).""" return "" 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)