""" Build system configuration. This module centralizes all build configuration including paths, compiler flags, and build settings. """ import os from pathlib import Path from typing import List, Dict, Optional class Config: """ Centralized build configuration. All paths, flags, and settings are defined here for easy modification. """ def __init__(self, project_root: Optional[Path] = None): """ Initialize build configuration. Args: project_root: Project root directory (default: current directory) """ # Project structure self.project_root = project_root or Path.cwd() self.src_dir = self.project_root / "src" self.test_dir = self.project_root / "tests" self.include_dir = self.project_root / "include" self.obj_dir = self.project_root / "obj" self.bin_dir = self.project_root / "bin" self.build_dir = self.project_root / "build" # Target executables self.main_target = self.bin_dir / "sls" self.test_target = self.bin_dir / "sls_tests" # Build settings self.parallel_jobs = os.cpu_count() or 1 self.verbose = False self.debug = True # Git integration self.git_hash = self._get_git_hash() # Compiler flags by category self._init_compiler_flags() # Platform-specific settings self._init_platform_settings() def _get_git_hash(self) -> str: """Get git commit hash for version info.""" from .utils import git_commit_hash return git_commit_hash() def _init_compiler_flags(self): """Initialize compiler flag sets.""" # Common GCC/Clang flags self.gcc_common_flags = [ "-std=c99", "-Wall", "-Wextra", "-Werror", "-g", # Debug symbols ] self.gcc_test_flags = [ "-std=c99", "-Wall", "-Wextra", "-Wno-unused-function", "-Werror", "-g", "-O0", # No optimization for tests ] # MSVC flags self.msvc_common_flags = [ "/std:c11", "/W4", "/WX", "/Zi", ] self.msvc_test_flags = [ "/std:c11", "/W4", "/WX", "/Zi", "/Od", # No optimization ] # ARM GCC flags for RP2040 self.arm_gcc_flags = [ "-mcpu=cortex-m0plus", "-mthumb", ] def _init_platform_settings(self): """Initialize platform-specific settings.""" # Pico SDK settings self.pico_sdk_path = Path(os.environ.get( "PICO_SDK_PATH", Path.home() / "pico" / "pico-sdk" )) self.pico_build_dir = self.project_root / "build_pico" self.pico_toolchain_file = self.project_root / "pico_arm_gcc_toolchain.cmake" # macOS settings self.macos_min_version = "10.13" def get_defines(self, extra: Optional[Dict] = None) -> Dict: """ Get preprocessor defines. Args: extra: Additional defines to include Returns: Dictionary of defines """ defines = { "GIT_COMMIT_HASH": self.git_hash, } if extra: defines.update(extra) return defines def get_includes(self, extra: Optional[List[Path]] = None) -> List[Path]: """ Get include directories. Args: extra: Additional include directories Returns: List of include directory paths """ includes = [self.include_dir] if extra: includes.extend(extra) return includes def get_libraries(self, target: str = "linux") -> List[str]: """ Get libraries to link based on target. Args: target: Target platform Returns: List of library names """ if target in ("linux", "macos"): return ["m"] # Math library elif target == "windows": return [] # No special libraries needed elif target == "rp2040": return [] # Handled by Pico SDK else: return [] def get_source_files(self, exclude: Optional[List[str]] = None) -> List[Path]: """ Get list of source files. Args: exclude: List of filenames to exclude Returns: List of source file paths """ from .utils import find_sources exclude = exclude or [] return find_sources(self.src_dir, "*.c", exclude) def get_test_files(self) -> List[Path]: """ Get list of test source files. Returns: List of test file paths """ from .utils import find_sources return find_sources(self.test_dir, "*.c") def get_main_sources(self) -> List[Path]: """ Get main program source files (excludes main.c and pico_main.c). Returns: List of source files for linking with tests """ return self.get_source_files(exclude=["main.c", "pico_main.c"]) def get_rp2040_sources(self) -> List[Path]: """ Get source files for RP2040 build. Returns: List of source files (excludes main.c, repl.c, file.c, test files) """ sources = self.get_source_files( exclude=["main.c", "repl.c", "file.c", "test"] ) # Add pico_main.c if it exists pico_main = self.src_dir / "pico_main.c" if pico_main.exists(): sources.append(pico_main) return sources def set_verbose(self, verbose: bool = True): """ Enable/disable verbose output. Args: verbose: Whether to enable verbose output """ self.verbose = verbose return self def set_debug(self, debug: bool = True): """ Enable/disable debug build. Args: debug: Whether to build with debug symbols """ self.debug = debug return self def set_parallel_jobs(self, jobs: int): """ Set number of parallel build jobs. Args: jobs: Number of parallel jobs (0 = auto-detect) """ if jobs <= 0: self.parallel_jobs = os.cpu_count() or 1 else: self.parallel_jobs = jobs return self def __repr__(self) -> str: """String representation of config.""" return ( f"Config(\n" f" project_root={self.project_root}\n" f" src_dir={self.src_dir}\n" f" bin_dir={self.bin_dir}\n" f" git_hash={self.git_hash}\n" f" parallel_jobs={self.parallel_jobs}\n" f")" ) # Global configuration instance _config = None def get_config(project_root: Optional[Path] = None) -> Config: """ Get the global configuration instance. Args: project_root: Project root directory (only used on first call) Returns: Config instance """ global _config if _config is None: _config = Config(project_root) return _config def reset_config(): """Reset the global configuration (mainly for testing).""" global _config _config = None