186 lines
5.0 KiB
Python
186 lines
5.0 KiB
Python
"""
|
|
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)
|
|
|