YREA-SLS/SLS_C/build_system/config.py

294 lines
7.6 KiB
Python

"""
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