171 lines
5.0 KiB
Python
171 lines
5.0 KiB
Python
#!/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}')"
|