Compare commits
5 Commits
387bbfd9fe
...
a6d867c31e
| Author | SHA1 | Date |
|---|---|---|
|
|
a6d867c31e | |
|
|
a686c2c9e8 | |
|
|
d1c6dbd0b1 | |
|
|
202e8700c2 | |
|
|
2fb369b9d4 |
|
|
@ -0,0 +1,39 @@
|
|||
"""
|
||||
Multi-platform build system for C projects.
|
||||
|
||||
Supports building for:
|
||||
- Linux (native)
|
||||
- Windows (native)
|
||||
- RP2040 (Raspberry Pi Pico)
|
||||
|
||||
Usage:
|
||||
python3 -m build_system [build|run|test|debug|clean] --target=self
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
|
||||
from .config import get_config, set_config, Config
|
||||
from .platform import (
|
||||
Platform,
|
||||
detect_platform,
|
||||
get_platform_name,
|
||||
resolve_target,
|
||||
is_windows,
|
||||
is_linux,
|
||||
is_macos,
|
||||
is_unix
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"get_config",
|
||||
"set_config",
|
||||
"Config",
|
||||
"Platform",
|
||||
"detect_platform",
|
||||
"get_platform_name",
|
||||
"resolve_target",
|
||||
"is_windows",
|
||||
"is_linux",
|
||||
"is_macos",
|
||||
"is_unix",
|
||||
]
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
"""
|
||||
Compiler implementations for different toolchains.
|
||||
"""
|
||||
|
||||
from .base import Compiler, CompileResult
|
||||
|
||||
__all__ = [
|
||||
"Compiler",
|
||||
"CompileResult",
|
||||
]
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
ARM GCC compiler implementation for embedded targets.
|
||||
Used primarily for RP2040 and other ARM Cortex-M microcontrollers.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from .base_gcc import BaseGCCCompiler
|
||||
from .base import CompileResult
|
||||
|
||||
|
||||
class ARMGCCCompiler(BaseGCCCompiler):
|
||||
"""ARM GCC compiler for embedded targets."""
|
||||
|
||||
def __init__(self, executable: str = "arm-none-eabi-gcc", cpu: str = "cortex-m0plus"):
|
||||
"""
|
||||
Initialize ARM GCC compiler.
|
||||
|
||||
Args:
|
||||
executable: Compiler executable name
|
||||
cpu: Target CPU (e.g., "cortex-m0plus", "cortex-m4")
|
||||
"""
|
||||
super().__init__("ARM GCC", executable)
|
||||
self.cpu = cpu
|
||||
self.thumb = True # Use Thumb instruction set by default
|
||||
|
||||
def _get_cpu_flags(self) -> List[str]:
|
||||
"""
|
||||
Get CPU-specific flags.
|
||||
|
||||
Returns:
|
||||
List of CPU-specific compiler flags
|
||||
"""
|
||||
flags = [f"-mcpu={self.cpu}"]
|
||||
if self.thumb:
|
||||
flags.append("-mthumb")
|
||||
return flags
|
||||
|
||||
def compile(self,
|
||||
source: Path,
|
||||
output: Path,
|
||||
flags: Optional[List[str]] = None,
|
||||
defines: Optional[List[str]] = None,
|
||||
include_dirs: Optional[List[Path]] = None,
|
||||
dependency_file: Optional[Path] = None) -> CompileResult:
|
||||
"""
|
||||
Compile a source file using ARM GCC.
|
||||
|
||||
Args:
|
||||
source: Source file path
|
||||
output: Output object file path
|
||||
flags: Additional compiler flags
|
||||
defines: Preprocessor defines
|
||||
include_dirs: Additional include directories
|
||||
dependency_file: Path for dependency file (.d file)
|
||||
|
||||
Returns:
|
||||
CompileResult with object file and dependency file paths
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: If compilation fails
|
||||
"""
|
||||
# Prepend CPU-specific flags to user flags
|
||||
cpu_flags = self._get_cpu_flags()
|
||||
combined_flags = cpu_flags + (flags or [])
|
||||
|
||||
# Call parent compile with combined flags
|
||||
return super().compile(
|
||||
source=source,
|
||||
output=output,
|
||||
flags=combined_flags,
|
||||
defines=defines,
|
||||
include_dirs=include_dirs,
|
||||
dependency_file=dependency_file
|
||||
)
|
||||
|
||||
def link(self,
|
||||
objects: List[Path],
|
||||
output: Path,
|
||||
flags: Optional[List[str]] = None,
|
||||
libraries: Optional[List[str]] = None,
|
||||
library_dirs: Optional[List[Path]] = None) -> bool:
|
||||
"""
|
||||
Link object files into an executable using ARM GCC.
|
||||
|
||||
Args:
|
||||
objects: List of object file paths
|
||||
output: Output executable path
|
||||
flags: Additional linker flags
|
||||
libraries: Libraries to link against
|
||||
library_dirs: Additional library search directories
|
||||
|
||||
Returns:
|
||||
True if linking succeeded
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: If linking fails
|
||||
"""
|
||||
# Prepend CPU-specific flags to user flags
|
||||
cpu_flags = self._get_cpu_flags()
|
||||
combined_flags = cpu_flags + (flags or [])
|
||||
|
||||
# Call parent link with combined flags
|
||||
return super().link(
|
||||
objects=objects,
|
||||
output=output,
|
||||
flags=combined_flags,
|
||||
libraries=libraries,
|
||||
library_dirs=library_dirs
|
||||
)
|
||||
|
||||
def set_cpu(self, cpu: str) -> None:
|
||||
"""
|
||||
Set the target CPU.
|
||||
|
||||
Args:
|
||||
cpu: Target CPU name (e.g., "cortex-m0plus", "cortex-m4")
|
||||
"""
|
||||
self.cpu = cpu
|
||||
|
||||
def set_thumb(self, enabled: bool) -> None:
|
||||
"""
|
||||
Enable or disable Thumb instruction set.
|
||||
|
||||
Args:
|
||||
enabled: True to enable Thumb, False to disable
|
||||
"""
|
||||
self.thumb = enabled
|
||||
|
||||
|
||||
def detect_arm_gcc_compiler() -> Optional[ARMGCCCompiler]:
|
||||
"""
|
||||
Detect and return an available ARM GCC compiler.
|
||||
|
||||
Returns:
|
||||
ARMGCCCompiler instance if found, None otherwise
|
||||
"""
|
||||
from build_system.utils import which
|
||||
|
||||
# Try to find ARM GCC
|
||||
if which("arm-none-eabi-gcc"):
|
||||
return ARMGCCCompiler()
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def check_arm_toolchain() -> bool:
|
||||
"""
|
||||
Check if ARM toolchain is available.
|
||||
|
||||
Returns:
|
||||
True if ARM toolchain is found, False otherwise
|
||||
"""
|
||||
from build_system.utils import check_tool
|
||||
|
||||
if check_tool("arm-none-eabi-gcc"):
|
||||
return True
|
||||
else:
|
||||
print(
|
||||
"ARM toolchain not found. Install arm-none-eabi-gcc:\n"
|
||||
" Ubuntu/Debian: sudo apt install gcc-arm-none-eabi\n"
|
||||
" macOS: brew install arm-none-eabi-gcc"
|
||||
)
|
||||
return False
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Base GCC-like compiler implementation.
|
||||
Provides common functionality for GCC, Clang, and other GCC-compatible compilers.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from .base import Compiler, CompileResult
|
||||
from build_system.utils import run
|
||||
|
||||
|
||||
class BaseGCCCompiler(Compiler):
|
||||
"""Base class for GCC-compatible compilers (GCC, Clang, etc.)."""
|
||||
|
||||
def __init__(self, name: str, executable: str):
|
||||
"""
|
||||
Initialize base GCC-compatible compiler.
|
||||
|
||||
Args:
|
||||
name: Human-readable compiler name
|
||||
executable: Compiler executable name
|
||||
"""
|
||||
super().__init__(name, executable)
|
||||
|
||||
def compile(self,
|
||||
source: Path,
|
||||
output: Path,
|
||||
flags: Optional[List[str]] = None,
|
||||
defines: Optional[List[str]] = None,
|
||||
include_dirs: Optional[List[Path]] = None,
|
||||
dependency_file: Optional[Path] = None) -> CompileResult:
|
||||
"""
|
||||
Compile a source file using GCC-compatible compiler.
|
||||
|
||||
Args:
|
||||
source: Source file path
|
||||
output: Output object file path
|
||||
flags: Additional compiler flags
|
||||
defines: Preprocessor defines
|
||||
include_dirs: Additional include directories
|
||||
dependency_file: Path for dependency file (.d file)
|
||||
|
||||
Returns:
|
||||
CompileResult with object file and dependency file paths
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: If compilation fails
|
||||
"""
|
||||
# Ensure output directory exists
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Build command
|
||||
cmd = [self.executable]
|
||||
|
||||
# Add flags
|
||||
if flags:
|
||||
cmd.extend(flags)
|
||||
|
||||
# Add compile-only flag
|
||||
cmd.append(self.get_compile_only_flag())
|
||||
|
||||
# Add defines
|
||||
if defines:
|
||||
cmd.extend([self.get_define_flag(d) for d in defines])
|
||||
|
||||
# Add include directories
|
||||
if include_dirs:
|
||||
cmd.extend([self.get_include_flag(d) for d in include_dirs])
|
||||
|
||||
# Add dependency generation if requested
|
||||
dep_file = None
|
||||
if dependency_file:
|
||||
cmd.extend(["-MMD", "-MP"])
|
||||
dep_file = dependency_file
|
||||
|
||||
# Add source file
|
||||
cmd.append(str(source))
|
||||
|
||||
# Add output file
|
||||
cmd.extend(self.get_output_flag(output))
|
||||
|
||||
# Run compilation
|
||||
run(cmd) # type: ignore
|
||||
|
||||
return CompileResult(
|
||||
object_file=output,
|
||||
dependency_file=dep_file,
|
||||
success=True
|
||||
)
|
||||
|
||||
def link(self,
|
||||
objects: List[Path],
|
||||
output: Path,
|
||||
flags: Optional[List[str]] = None,
|
||||
libraries: Optional[List[str]] = None,
|
||||
library_dirs: Optional[List[Path]] = None) -> bool:
|
||||
"""
|
||||
Link object files into an executable using GCC-compatible compiler.
|
||||
|
||||
Args:
|
||||
objects: List of object file paths
|
||||
output: Output executable path
|
||||
flags: Additional linker flags
|
||||
libraries: Libraries to link against
|
||||
library_dirs: Additional library search directories
|
||||
|
||||
Returns:
|
||||
True if linking succeeded
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: If linking fails
|
||||
"""
|
||||
# Ensure output directory exists
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Build command
|
||||
cmd = [self.executable]
|
||||
|
||||
# Add object files
|
||||
cmd.extend([str(obj) for obj in objects])
|
||||
|
||||
# Add linker flags
|
||||
if flags:
|
||||
cmd.extend(flags)
|
||||
|
||||
# Add library directories
|
||||
if library_dirs:
|
||||
cmd.extend([f"-L{lib_dir}" for lib_dir in library_dirs])
|
||||
|
||||
# Add libraries
|
||||
if libraries:
|
||||
cmd.extend([f"-l{lib}" for lib in libraries])
|
||||
|
||||
# Add output file
|
||||
cmd.extend(["-o", str(output)])
|
||||
|
||||
# Run linker
|
||||
run(cmd) # type: ignore
|
||||
|
||||
return True
|
||||
|
||||
def get_object_extension(self) -> str:
|
||||
"""Get object file extension for GCC-compatible compilers."""
|
||||
return ".o"
|
||||
|
||||
def get_dependency_extension(self) -> str:
|
||||
"""Get dependency file extension for GCC-compatible compilers."""
|
||||
return ".d"
|
||||
|
||||
def supports_dependency_generation(self) -> bool:
|
||||
"""GCC-compatible compilers support automatic dependency generation."""
|
||||
return True
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Clang compiler implementation.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base_gcc import BaseGCCCompiler
|
||||
import subprocess
|
||||
|
||||
|
||||
class ClangCompiler(BaseGCCCompiler):
|
||||
"""Clang compiler implementation."""
|
||||
|
||||
def __init__(self, executable: str = "clang"):
|
||||
"""
|
||||
Initialize Clang compiler.
|
||||
|
||||
Args:
|
||||
executable: Compiler executable name ("clang")
|
||||
"""
|
||||
super().__init__("Clang", executable)
|
||||
|
||||
def get_version(self) -> Optional[str]:
|
||||
"""
|
||||
Get the Clang version string.
|
||||
|
||||
Returns:
|
||||
Version string if available, None otherwise
|
||||
"""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[self.executable, "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False
|
||||
)
|
||||
if result.returncode == 0 and result.stdout:
|
||||
# Parse first line of version output
|
||||
first_line = result.stdout.strip().split('\n')[0]
|
||||
return first_line
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def detect_clang_compiler() -> Optional[ClangCompiler]:
|
||||
"""
|
||||
Detect and return an available Clang compiler.
|
||||
|
||||
Returns:
|
||||
ClangCompiler instance if found, None otherwise
|
||||
"""
|
||||
from build_system.utils import which
|
||||
|
||||
# Try to find Clang
|
||||
compilers = ["clang", "clang-18", "clang-17", "clang-16", "clang-15"]
|
||||
|
||||
for compiler in compilers:
|
||||
if which(compiler):
|
||||
return ClangCompiler(compiler)
|
||||
|
||||
return None
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
GCC compiler implementation.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from .base_gcc import BaseGCCCompiler
|
||||
|
||||
|
||||
class GCCCompiler(BaseGCCCompiler):
|
||||
"""GCC compiler implementation."""
|
||||
|
||||
def __init__(self, executable: str = "gcc"):
|
||||
"""
|
||||
Initialize GCC compiler.
|
||||
|
||||
Args:
|
||||
executable: Compiler executable name ("gcc")
|
||||
"""
|
||||
super().__init__("GCC", executable)
|
||||
|
||||
def get_version(self) -> Optional[str]:
|
||||
"""
|
||||
Get the GCC version string.
|
||||
|
||||
Returns:
|
||||
Version string if available, None otherwise
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[self.executable, "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False
|
||||
)
|
||||
if result.returncode == 0 and result.stdout:
|
||||
# Parse first line of version output
|
||||
first_line = result.stdout.strip().split('\n')[0]
|
||||
return first_line
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def detect_gcc_compiler() -> Optional[GCCCompiler]:
|
||||
"""
|
||||
Detect and return an available GCC compiler.
|
||||
|
||||
Returns:
|
||||
GCCCompiler instance if found, None otherwise
|
||||
"""
|
||||
from build_system.utils import which
|
||||
|
||||
# Try to find GCC
|
||||
compilers = ["gcc", "gcc-13", "gcc-12", "gcc-11", "cc"]
|
||||
|
||||
for compiler in compilers:
|
||||
if which(compiler):
|
||||
return GCCCompiler(compiler)
|
||||
|
||||
return None
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
"""
|
||||
Build target implementations for different platforms.
|
||||
"""
|
||||
|
||||
from .base import Target, BuildType
|
||||
|
||||
__all__ = [
|
||||
"Target",
|
||||
"BuildType",
|
||||
]
|
||||
Loading…
Reference in New Issue