diff --git a/SLS_C/build_system/targets/base.py b/SLS_C/build_system/targets/base.py index e69de29..d36e45d 100644 --- a/SLS_C/build_system/targets/base.py +++ b/SLS_C/build_system/targets/base.py @@ -0,0 +1,232 @@ +#!/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})"