Some changes
This commit is contained in:
parent
d7c23225f3
commit
627fadfea1
|
|
@ -0,0 +1,67 @@
|
||||||
|
{
|
||||||
|
"paths": {
|
||||||
|
"src_dir": "src",
|
||||||
|
"test_dir": "tests",
|
||||||
|
"include_dir": "include",
|
||||||
|
"obj_dir": "obj",
|
||||||
|
"bin_dir": "bin",
|
||||||
|
"build_dir": "build"
|
||||||
|
},
|
||||||
|
"targets": {
|
||||||
|
"main": "sls",
|
||||||
|
"test": "sls_tests"
|
||||||
|
},
|
||||||
|
"parallel_jobs": 0,
|
||||||
|
"verbose": false,
|
||||||
|
"debug": true,
|
||||||
|
"gcc_common_flags": [
|
||||||
|
"-std=c99",
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
"-Werror",
|
||||||
|
"-g"
|
||||||
|
],
|
||||||
|
"gcc_test_flags": [
|
||||||
|
"-std=c99",
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
"-Wno-unused-function",
|
||||||
|
"-Werror",
|
||||||
|
"-g",
|
||||||
|
"-O0"
|
||||||
|
],
|
||||||
|
"msvc_common_flags": [
|
||||||
|
"/std:c11",
|
||||||
|
"/W4",
|
||||||
|
"/WX",
|
||||||
|
"/Zi"
|
||||||
|
],
|
||||||
|
"msvc_test_flags": [
|
||||||
|
"/std:c11",
|
||||||
|
"/W4",
|
||||||
|
"/WX",
|
||||||
|
"/Zi",
|
||||||
|
"/Od"
|
||||||
|
],
|
||||||
|
"arm_gcc_flags": [
|
||||||
|
"-mcpu=cortex-m0plus",
|
||||||
|
"-mthumb"
|
||||||
|
],
|
||||||
|
"pico": {
|
||||||
|
"sdk_path_env": "PICO_SDK_PATH",
|
||||||
|
"sdk_path_default": "~/pico/pico-sdk",
|
||||||
|
"build_dir": "build_pico",
|
||||||
|
"toolchain_file": "pico_arm_gcc_toolchain.cmake"
|
||||||
|
},
|
||||||
|
"macos": {
|
||||||
|
"min_version": "10.13"
|
||||||
|
},
|
||||||
|
"source_excludes": {
|
||||||
|
"rp2040": [
|
||||||
|
"main.c",
|
||||||
|
"repl.c",
|
||||||
|
"file.c",
|
||||||
|
"test"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,118 +2,116 @@
|
||||||
Build system configuration.
|
Build system configuration.
|
||||||
|
|
||||||
This module centralizes all build configuration including paths,
|
This module centralizes all build configuration including paths,
|
||||||
compiler flags, and build settings.
|
compiler flags, and build settings. Configuration can be loaded from
|
||||||
|
a default JSON file or overridden programmatically.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Optional
|
from typing import List, Dict, Optional
|
||||||
|
|
||||||
|
from importlib import resources
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: This should be utilized more throughout the build system.
|
||||||
class Config:
|
class Config:
|
||||||
"""
|
"""
|
||||||
Centralized build configuration.
|
Centralized build configuration.
|
||||||
|
|
||||||
All paths, flags, and settings are defined here for easy modification.
|
All paths, flags, and settings are defined here for easy modification.
|
||||||
|
Configuration is loaded from a default JSON file and can be overridden.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, project_root: Optional[Path] = None):
|
def __init__(self, project_root: Optional[Path] = None, config_file: Optional[Path] = None):
|
||||||
"""
|
"""
|
||||||
Initialize build configuration.
|
Initialize build configuration.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
project_root: Project root directory (default: current directory)
|
project_root: Project root directory (default: current directory)
|
||||||
|
config_file: Custom config file path (default: uses built-in config.json)
|
||||||
"""
|
"""
|
||||||
|
# Load default configuration
|
||||||
|
self._load_default_config(config_file)
|
||||||
|
|
||||||
# Project structure
|
# Project structure
|
||||||
self.project_root = project_root or Path.cwd()
|
self.project_root = project_root or Path.cwd()
|
||||||
self.src_dir = self.project_root / "src"
|
self._setup_paths()
|
||||||
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
|
# Build settings
|
||||||
self.parallel_jobs = os.cpu_count() or 1
|
self.parallel_jobs = self._data.get("parallel_jobs", os.cpu_count() or 1)
|
||||||
self.verbose = False
|
self.verbose = self._data.get("verbose", False)
|
||||||
self.debug = True
|
self.debug = self._data.get("debug", True)
|
||||||
|
|
||||||
# Git integration
|
# Git integration
|
||||||
self.git_hash = self._get_git_hash()
|
self.git_hash = self._get_git_hash()
|
||||||
|
|
||||||
# Compiler flags by category
|
# Compiler flags
|
||||||
self._init_compiler_flags()
|
self.gcc_common_flags = self._data.get("gcc_common_flags", [])
|
||||||
|
self.gcc_test_flags = self._data.get("gcc_test_flags", [])
|
||||||
|
self.msvc_common_flags = self._data.get("msvc_common_flags", [])
|
||||||
|
self.msvc_test_flags = self._data.get("msvc_test_flags", [])
|
||||||
|
self.arm_gcc_flags = self._data.get("arm_gcc_flags", [])
|
||||||
|
|
||||||
# Platform-specific settings
|
# Platform-specific settings
|
||||||
self._init_platform_settings()
|
self._setup_platform_settings()
|
||||||
|
|
||||||
|
def _load_default_config(self, config_file: Optional[Path] = None):
|
||||||
|
"""
|
||||||
|
Load configuration from JSON file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config_file: Custom config file, or None to use default
|
||||||
|
"""
|
||||||
|
if config_file and config_file.exists():
|
||||||
|
# Load custom config file
|
||||||
|
with open(config_file, 'r') as f:
|
||||||
|
self._data = json.load(f)
|
||||||
|
else:
|
||||||
|
# Load default config from package resources
|
||||||
|
config_text = resources.files("build_system").joinpath("config.json").read_text()
|
||||||
|
self._data = json.loads(config_text)
|
||||||
|
|
||||||
|
def _setup_paths(self):
|
||||||
|
"""Setup directory paths from configuration."""
|
||||||
|
paths = self._data.get("paths", {})
|
||||||
|
|
||||||
|
self.src_dir = self.project_root / paths.get("src_dir", "src")
|
||||||
|
self.test_dir = self.project_root / paths.get("test_dir", "tests")
|
||||||
|
self.include_dir = self.project_root / paths.get("include_dir", "include")
|
||||||
|
self.obj_dir = self.project_root / paths.get("obj_dir", "obj")
|
||||||
|
self.bin_dir = self.project_root / paths.get("bin_dir", "bin")
|
||||||
|
self.build_dir = self.project_root / paths.get("build_dir", "build")
|
||||||
|
|
||||||
|
# Target executables
|
||||||
|
targets = self._data.get("targets", {})
|
||||||
|
self.main_target = self.bin_dir / targets.get("main", "sls")
|
||||||
|
self.test_target = self.bin_dir / targets.get("test", "sls_tests")
|
||||||
|
|
||||||
|
def _setup_platform_settings(self):
|
||||||
|
"""Setup platform-specific settings from configuration."""
|
||||||
|
pico_config = self._data.get("pico", {})
|
||||||
|
|
||||||
|
# Pico SDK settings
|
||||||
|
sdk_env = pico_config.get("sdk_path_env", "PICO_SDK_PATH")
|
||||||
|
sdk_default = pico_config.get("sdk_path_default", "~/pico/pico-sdk")
|
||||||
|
sdk_default = Path(sdk_default).expanduser()
|
||||||
|
|
||||||
|
self.pico_sdk_path = Path(os.environ.get(sdk_env, sdk_default))
|
||||||
|
self.pico_build_dir = self.project_root / pico_config.get("build_dir", "build_pico")
|
||||||
|
self.pico_toolchain_file = self.project_root / pico_config.get(
|
||||||
|
"toolchain_file", "pico_arm_gcc_toolchain.cmake"
|
||||||
|
)
|
||||||
|
|
||||||
|
# macOS settings
|
||||||
|
macos_config = self._data.get("macos", {})
|
||||||
|
self.macos_min_version = macos_config.get("min_version", "10.13")
|
||||||
|
|
||||||
def _get_git_hash(self) -> str:
|
def _get_git_hash(self) -> str:
|
||||||
"""Get git commit hash for version info."""
|
"""Get git commit hash for version info."""
|
||||||
from .utils import git_commit_hash
|
from .utils import git_commit_hash
|
||||||
return 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:
|
def get_defines(self, extra: Optional[Dict] = None) -> Dict:
|
||||||
"""
|
"""
|
||||||
Get preprocessor defines.
|
Get preprocessor defines.
|
||||||
|
|
@ -210,9 +208,8 @@ class Config:
|
||||||
Returns:
|
Returns:
|
||||||
List of source files (excludes main.c, repl.c, file.c, test files)
|
List of source files (excludes main.c, repl.c, file.c, test files)
|
||||||
"""
|
"""
|
||||||
sources = self.get_source_files(
|
excludes = self._data.get("source_excludes", {}).get("rp2040", [])
|
||||||
exclude=["main.c", "repl.c", "file.c", "test"]
|
sources = self.get_source_files(exclude=excludes)
|
||||||
)
|
|
||||||
|
|
||||||
# Add pico_main.c if it exists
|
# Add pico_main.c if it exists
|
||||||
pico_main = self.src_dir / "pico_main.c"
|
pico_main = self.src_dir / "pico_main.c"
|
||||||
|
|
@ -254,6 +251,16 @@ class Config:
|
||||||
self.parallel_jobs = jobs
|
self.parallel_jobs = jobs
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def save_config(self, output_file: Path):
|
||||||
|
"""
|
||||||
|
Save current configuration to a JSON file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_file: Path to output JSON file
|
||||||
|
"""
|
||||||
|
with open(output_file, 'w') as f:
|
||||||
|
json.dump(self._data, f, indent=2)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""String representation of config."""
|
"""String representation of config."""
|
||||||
return (
|
return (
|
||||||
|
|
@ -271,19 +278,20 @@ class Config:
|
||||||
_config = None
|
_config = None
|
||||||
|
|
||||||
|
|
||||||
def get_config(project_root: Optional[Path] = None) -> Config:
|
def get_config(project_root: Optional[Path] = None, config_file: Optional[Path] = None) -> Config:
|
||||||
"""
|
"""
|
||||||
Get the global configuration instance.
|
Get the global configuration instance.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
project_root: Project root directory (only used on first call)
|
project_root: Project root directory (only used on first call)
|
||||||
|
config_file: Custom config file (only used on first call)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Config instance
|
Config instance
|
||||||
"""
|
"""
|
||||||
global _config
|
global _config
|
||||||
if _config is None:
|
if _config is None:
|
||||||
_config = Config(project_root)
|
_config = Config(project_root, config_file)
|
||||||
return _config
|
return _config
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ class Target(ABC):
|
||||||
output = self.get_output_path()
|
output = self.get_output_path()
|
||||||
self.link_executable(objects, output)
|
self.link_executable(objects, output)
|
||||||
|
|
||||||
print(f"\n✓ Build successful: {output}\n")
|
print(f"\nBuild successful: {output}\n")
|
||||||
|
|
||||||
def build_tests(self):
|
def build_tests(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -111,7 +111,7 @@ class Target(ABC):
|
||||||
output = self.get_test_output_path()
|
output = self.get_test_output_path()
|
||||||
self.link_executable(test_objects + shared_objects, output)
|
self.link_executable(test_objects + shared_objects, output)
|
||||||
|
|
||||||
print(f"\n✓ Test build successful: {output}\n")
|
print(f"\nTest build successful: {output}\n")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -199,7 +199,7 @@ class Target(ABC):
|
||||||
rm_tree(self.config.bin_dir)
|
rm_tree(self.config.bin_dir)
|
||||||
print(f"Removed {self.config.bin_dir}")
|
print(f"Removed {self.config.bin_dir}")
|
||||||
|
|
||||||
print("\n✓ Clean complete\n")
|
print("\nClean complete\n")
|
||||||
|
|
||||||
def compile_source(self, source: Path, is_test: bool = False) -> Path:
|
def compile_source(self, source: Path, is_test: bool = False) -> Path:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from pathlib import Path
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Is this needed?
|
||||||
def mkdir(path: Path):
|
def mkdir(path: Path):
|
||||||
"""
|
"""
|
||||||
Create a directory if it doesn't exist.
|
Create a directory if it doesn't exist.
|
||||||
|
|
@ -25,6 +26,7 @@ def mkdir(path: Path):
|
||||||
path.mkdir(parents=True, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Is this needed?
|
||||||
def rm_tree(path: Path):
|
def rm_tree(path: Path):
|
||||||
"""
|
"""
|
||||||
Remove a directory tree if it exists.
|
Remove a directory tree if it exists.
|
||||||
|
|
@ -36,6 +38,7 @@ def rm_tree(path: Path):
|
||||||
shutil.rmtree(path, ignore_errors=True)
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Is this needed?
|
||||||
def run_command(cmd: list, cwd: Optional[Path] = None, capture_output: bool = False):
|
def run_command(cmd: list, cwd: Optional[Path] = None, capture_output: bool = False):
|
||||||
"""
|
"""
|
||||||
Run a command and optionally capture its output.
|
Run a command and optionally capture its output.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue