#!/usr/bin/env python3 """ Base compiler interface for the build system. Defines the abstract Compiler class that all specific compilers must implement. """ from abc import ABC, abstractmethod from pathlib import Path from typing import List, Optional from dataclasses import dataclass @dataclass class CompileResult: """Result of a compilation operation.""" object_file: Path dependency_file: Optional[Path] = None success: bool = True class Compiler(ABC): """Abstract base class for all compilers.""" def __init__(self, name: str, executable: str): """ Initialize the compiler. Args: name: Human-readable name of the compiler (e.g., "GCC", "MSVC") executable: Executable name or path (e.g., "gcc", "cl") """ self.name = name self.executable = executable @abstractmethod 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 single source file to an object file. Args: source: Source file path output: Output object file path flags: Additional compiler flags defines: Preprocessor defines (e.g., ["DEBUG", "VERSION=1.0"]) include_dirs: Additional include directories dependency_file: Path for dependency file (.d file) Returns: CompileResult with object file path and success status Raises: subprocess.CalledProcessError: If compilation fails """ pass @abstractmethod 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 or library. Args: objects: List of object file paths output: Output executable or library path flags: Additional linker flags libraries: Libraries to link against (e.g., ["m", "pthread"]) library_dirs: Additional library search directories Returns: True if linking succeeded, False otherwise Raises: subprocess.CalledProcessError: If linking fails """ pass @abstractmethod def get_object_extension(self) -> str: """ Get the file extension for object files. Returns: Object file extension (e.g., ".o" or ".obj") """ pass @abstractmethod def get_dependency_extension(self) -> str: """ Get the file extension for dependency files. Returns: Dependency file extension (e.g., ".d") """ pass def supports_dependency_generation(self) -> bool: """ Check if the compiler supports automatic dependency generation. Returns: True if dependency generation is supported, False otherwise """ return True def get_define_flag(self, define: str) -> str: """ Get the compiler flag for a preprocessor define. Args: define: Define string (e.g., "DEBUG" or "VERSION=1.0") Returns: Compiler-specific define flag (e.g., "-DDEBUG" or "/DDEBUG") """ # Most compilers use -D, but can be overridden return f"-D{define}" def get_include_flag(self, include_dir: Path) -> str: """ Get the compiler flag for an include directory. Args: include_dir: Include directory path Returns: Compiler-specific include flag (e.g., "-Iinclude" or "/Iinclude") """ # Most compilers use -I, but can be overridden return f"-I{include_dir}" def get_output_flag(self, output: Path) -> List[str]: """ Get the compiler flag(s) for specifying output file. Args: output: Output file path Returns: List of flags (e.g., ["-o", "output.o"] or ["/Fooutput.obj"]) """ # Most compilers use -o, but can be overridden return ["-o", str(output)] def get_compile_only_flag(self) -> str: """ Get the flag to compile without linking. Returns: Compile-only flag (e.g., "-c") """ return "-c" def __str__(self) -> str: """String representation of the compiler.""" return f"{self.name} ({self.executable})" def __repr__(self) -> str: """Detailed string representation.""" return f"Compiler(name='{self.name}', executable='{self.executable}')"