294 lines
7.6 KiB
Python
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
|