YREA-SLS/SLS_C/build_system/targets/base.py

233 lines
6.3 KiB
Python

#!/usr/bin/env python3
"""
Base target interface for the build system.
Defines the abstract Target class that all specific targets must implement.
"""
from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Optional
from enum import Enum
class BuildType(Enum):
"""Type of build to perform."""
RELEASE = "release"
DEBUG = "debug"
TEST = "test"
def __str__(self) -> str:
return self.value
class Target(ABC):
"""Abstract base class for all build targets."""
def __init__(self, name: str):
"""
Initialize the target.
Args:
name: Target name (e.g., "linux", "windows", "rp2040")
"""
self.name = name
self._build_type = BuildType.RELEASE
@property
def build_type(self) -> BuildType:
"""Get the current build type."""
return self._build_type
@build_type.setter
def build_type(self, value: BuildType) -> None:
"""Set the build type."""
self._build_type = value
@abstractmethod
def build(self) -> bool:
"""
Build the target.
Returns:
True if build succeeded, False otherwise
"""
pass
@abstractmethod
def clean(self) -> bool:
"""
Clean build artifacts for this target.
Returns:
True if clean succeeded, False otherwise
"""
pass
def run(self) -> bool:
"""
Run the built executable.
Returns:
True if execution succeeded, False otherwise
Raises:
NotImplementedError: If the target doesn't support running
"""
raise NotImplementedError(f"Target '{self.name}' does not support running")
def test(self) -> bool:
"""
Build and run tests for this target.
Returns:
True if tests passed, False otherwise
Raises:
NotImplementedError: If the target doesn't support testing
"""
raise NotImplementedError(f"Target '{self.name}' does not support testing")
def debug(self) -> bool:
"""
Launch debugger with the built executable.
Returns:
True if debugger launched successfully, False otherwise
Raises:
NotImplementedError: If the target doesn't support debugging
"""
raise NotImplementedError(f"Target '{self.name}' does not support debugging")
def get_output_path(self) -> Optional[Path]:
"""
Get the path to the main output binary.
Returns:
Path to output binary, or None if not applicable
"""
return None
def get_test_output_path(self) -> Optional[Path]:
"""
Get the path to the test binary.
Returns:
Path to test binary, or None if not applicable
"""
return None
def get_build_dir(self) -> Optional[Path]:
"""
Get the build directory for this target.
Returns:
Path to build directory, or None if using shared directories
"""
return None
def supports_command(self, command: str) -> bool:
"""
Check if this target supports a specific command.
Args:
command: Command name ("build", "run", "test", "debug", "clean")
Returns:
True if command is supported, False otherwise
"""
if command == "build":
return True
elif command == "clean":
return True
elif command == "run":
try:
# Check if run() is overridden
return type(self).run != Target.run
except AttributeError:
return False
elif command == "test":
try:
return type(self).test != Target.test
except AttributeError:
return False
elif command == "debug":
try:
return type(self).debug != Target.debug
except AttributeError:
return False
return False
def get_sources(self, exclude: Optional[List[str]] = None) -> List[Path]:
"""
Get list of source files to compile.
Args:
exclude: List of source file names to exclude
Returns:
List of source file paths
"""
from build_system.config import get_config
config = get_config()
sources = list(config.src_dir.glob("*.c"))
if exclude:
sources = [s for s in sources if s.name not in exclude]
return sources
def get_test_sources(self) -> List[Path]:
"""
Get list of test source files.
Returns:
List of test source file paths
"""
from build_system.config import get_config
config = get_config()
if not config.test_dir.exists():
return []
return list(config.test_dir.glob("*.c"))
def generate_tests(self) -> bool:
"""
Generate test files from YAML if needed.
Returns:
True if generation succeeded or was not needed, False otherwise
"""
from build_system.config import get_config
from build_system.utils import run
config = get_config()
script = config.test_script_path
yaml = config.test_yaml_path
output = config.test_output_file
if not script.exists() or not yaml.exists():
print("Test generation skipped (missing files).")
return False
if output.exists() and output.stat().st_mtime > yaml.stat().st_mtime:
return True
try:
run([config.python_command, str(script), str(yaml), str(output)])
return True
except Exception as e:
print(f"ERROR: Test generation failed: {e}")
return False
def __str__(self) -> str:
"""String representation of the target."""
return f"{self.name}"
def __repr__(self) -> str:
"""Detailed string representation."""
return f"Target(name='{self.name}', build_type={self.build_type})"