Compare commits

...

2 Commits

Author SHA1 Message Date
Kyler Olsen c1c54277a7 Fixed path error 2025-12-03 23:57:45 -07:00
Kyler Olsen c89acbbdfb Started Raspberry Pi Pico build target 2025-12-03 23:57:33 -07:00
2 changed files with 238 additions and 17 deletions

2
SLS_C/.gitignore vendored
View File

@ -1,4 +1,6 @@
obj/ obj/
bin/ bin/
build_pico/
*.o *.o
*.pdb *.pdb
CMakeLists.txt

View File

@ -4,6 +4,7 @@ import sys
import subprocess import subprocess
from pathlib import Path from pathlib import Path
import shutil import shutil
import platform
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# CONFIG # CONFIG
@ -16,21 +17,101 @@ BIN_DIR = Path("bin")
TARGET = BIN_DIR / "sls" TARGET = BIN_DIR / "sls"
TEST_TARGET = BIN_DIR / "sls_tests" TEST_TARGET = BIN_DIR / "sls_tests"
# Platform-specific settings
PICO_SDK_PATH = os.environ.get("PICO_SDK_PATH", Path.home() / "pico/pico-sdk")
PICO_BUILD_DIR = Path("build_pico")
# Unix gcc/clang flags # Unix gcc/clang flags
COMMON_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Werror", "-Iinclude", "-g"] COMMON_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Werror", "-Iinclude", "-g"]
TEST_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Wno-unused-function", "-Werror", TEST_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Wno-unused-function", "-Werror",
"-Iinclude", "-g", "-O0"] "-Iinclude", "-g", "-O0"]
# macOS-specific flags (for cross-compilation if needed)
MACOS_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Werror", "-Iinclude", "-g",
"-mmacosx-version-min=10.13"]
# Windows MSVC flags # Windows MSVC flags
MSVC_FLAGS = ["/std:c11", "/Zi", "/Iinclude"] MSVC_FLAGS = ["/std:c11", "/Zi", "/Iinclude"]
MSVC_TEST_FLAGS = MSVC_FLAGS + [] MSVC_TEST_FLAGS = MSVC_FLAGS + []
# RP2040 settings
RP2040_CMAKE_TEMPLATE = """cmake_minimum_required(VERSION 3.13)
# Pico SDK initialization
set(PICO_SDK_PATH "{pico_sdk_path}")
include({pico_sdk_path}/external/pico_sdk_import.cmake)
project({project_name} C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
# Main executable
add_executable({project_name}
{source_files}
)
# Add include directories
target_include_directories({project_name} PRIVATE
${{CMAKE_CURRENT_LIST_DIR}}/include
)
# Add compile definitions
target_compile_definitions({project_name} PRIVATE
PICO_BUILD=1
GIT_COMMIT_HASH="{git_hash}"
)
# Link libraries
target_link_libraries({project_name}
pico_stdlib
hardware_uart
hardware_gpio
)
# Enable USB/UART output
pico_enable_stdio_usb({project_name} 1)
pico_enable_stdio_uart({project_name} 1)
# Create map/bin/hex/uf2 files
pico_add_extra_outputs({project_name})
"""
# ---------------------------------------------------------------------
# PLATFORM DETECTION
# ---------------------------------------------------------------------
def detect_platform():
"""Detect the current operating system"""
system = platform.system()
if system == "Darwin":
return "macos"
elif system == "Windows":
return "windows"
elif system == "Linux":
return "linux"
return "unknown"
PLATFORM = detect_platform()
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# COMPILER DETECTION # COMPILER DETECTION
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
def detect_compiler(): def detect_compiler(target_platform=None):
if os.name == "nt": """Detect appropriate compiler for the target platform"""
if target_platform == "rp2040":
return ("arm-none-eabi-gcc", "gcc")
target = target_platform or PLATFORM
if target == "windows" or os.name == "nt":
return ("cl", "msvc") return ("cl", "msvc")
elif target == "macos":
# Prefer clang on macOS
if shutil.which("clang"):
return ("clang", "gcc")
return ("gcc", "gcc")
else:
return ("gcc", "gcc") return ("gcc", "gcc")
CC, CC_KIND = detect_compiler() CC, CC_KIND = detect_compiler()
@ -79,7 +160,7 @@ def mkdir(p: Path):
def run(cmd): def run(cmd):
print(">>", " ".join(cmd)) print(">>", " ".join(str(c) for c in cmd))
subprocess.check_call(cmd) subprocess.check_call(cmd)
@ -96,7 +177,7 @@ def is_up_to_date(src, obj, dep):
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# BUILD RULES # BUILD RULES
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
def compile_source(src: Path, is_test=False): def compile_source(src: Path, is_test=False, target_platform=None):
mkdir(OBJ_DIR) mkdir(OBJ_DIR)
obj = OBJ_DIR / (src.stem + ".o") obj = OBJ_DIR / (src.stem + ".o")
dep = OBJ_DIR / (src.stem + ".d") dep = OBJ_DIR / (src.stem + ".d")
@ -104,13 +185,19 @@ def compile_source(src: Path, is_test=False):
if is_up_to_date(src, obj, dep): if is_up_to_date(src, obj, dep):
return obj return obj
if CC_KIND == "msvc": compiler, kind = detect_compiler(target_platform)
if kind == "msvc":
flags = MSVC_TEST_FLAGS if is_test else MSVC_FLAGS flags = MSVC_TEST_FLAGS if is_test else MSVC_FLAGS
cmd = [CC] + flags + ["/Fo" + str(obj), "/c", str(src), cmd = [compiler] + flags + ["/Fo" + str(obj), "/c", str(src),
f"/DGIT_COMMIT_HASH=\"{GIT_HASH}\""] f"/DGIT_COMMIT_HASH=\"{GIT_HASH}\""]
else:
if target_platform == "macos":
flags = MACOS_FLAGS if not is_test else TEST_FLAGS + ["-mmacosx-version-min=10.13"]
else: else:
flags = TEST_FLAGS if is_test else COMMON_FLAGS flags = TEST_FLAGS if is_test else COMMON_FLAGS
cmd = [CC] + flags + [
cmd = [compiler] + flags + [
f"-DGIT_COMMIT_HASH=\"{GIT_HASH}\"", f"-DGIT_COMMIT_HASH=\"{GIT_HASH}\"",
"-MMD", "-MP", "-MMD", "-MP",
"-c", str(src), "-o", str(obj) "-c", str(src), "-o", str(obj)
@ -120,12 +207,14 @@ def compile_source(src: Path, is_test=False):
return obj return obj
def link_executable(objects, output: Path): def link_executable(objects, output: Path, target_platform=None):
mkdir(BIN_DIR) mkdir(BIN_DIR)
if CC_KIND == "msvc": compiler, kind = detect_compiler(target_platform)
cmd = [CC] + list(map(str, objects)) + ["/Fe" + str(output)]
if kind == "msvc":
cmd = [compiler] + list(map(str, objects)) + ["/Fe" + str(output)]
else: else:
cmd = [CC] + list(map(str, objects)) + ["-lm", "-o", str(output)] cmd = [compiler] + list(map(str, objects)) + ["-lm", "-o", str(output)]
run(cmd) run(cmd)
@ -148,6 +237,96 @@ def generate_tests():
return True return True
# ---------------------------------------------------------------------
# RP2040 BUILD
# ---------------------------------------------------------------------
def check_pico_sdk():
"""Check if Pico SDK is available"""
sdk_path = Path(PICO_SDK_PATH)
if not sdk_path.exists():
print(f"ERROR: Pico SDK not found at {sdk_path}")
print("Please set PICO_SDK_PATH environment variable or install SDK at ~/pico/pico-sdk")
return False
return True
def generate_pico_cmake(project_name="sls"):
"""Generate CMakeLists.txt for RP2040 build"""
sources = list(SRC_DIR.glob("*.c"))
# Exclude test runner if present
sources = [s for s in sources if "test" not in s.stem.lower()]
source_files = "\n".join(f" {s}" for s in sources)
cmake_content = RP2040_CMAKE_TEMPLATE.format(
project_name=project_name,
source_files=source_files,
git_hash=GIT_HASH,
pico_sdk_path=PICO_SDK_PATH
)
cmake_file = Path("CMakeLists.txt")
with open(cmake_file, "w") as f:
f.write(cmake_content)
print(f"Generated {cmake_file}")
def build_rp2040():
"""Build for RP2040 using CMake"""
if not check_pico_sdk():
return False
print("\n=== Building for RP2040 ===\n")
# Generate CMakeLists.txt
generate_pico_cmake()
# Create build directory
mkdir(PICO_BUILD_DIR)
# Run CMake (no need to pass PICO_SDK_PATH since it's in CMakeLists.txt)
cmake_cmd = [
"cmake",
"-B", str(PICO_BUILD_DIR),
"-S", "."
]
run(cmake_cmd)
# Build
build_cmd = ["cmake", "--build", str(PICO_BUILD_DIR), "-j4"]
run(build_cmd)
# Show output files
uf2_file = PICO_BUILD_DIR / "sls.uf2"
if uf2_file.exists():
print(f"\nBuild successful! UF2 file: {uf2_file}")
print("To flash: Copy this file to your Pico in BOOTSEL mode")
return True
# ---------------------------------------------------------------------
# MACOS BUILD
# ---------------------------------------------------------------------
def build_macos():
"""Build for macOS (can be run on macOS or cross-compile on Linux)"""
print("\n=== Building for macOS ===\n")
if PLATFORM != "macos" and PLATFORM != "linux":
print("ERROR: macOS builds require macOS or Linux with osxcross")
return False
sources = list(SRC_DIR.glob("*.c"))
objects = [compile_source(s, is_test=False, target_platform="macos") for s in sources]
macos_target = BIN_DIR / "sls_macos"
link_executable(objects, macos_target, target_platform="macos")
print(f"\nBuild successful! Binary: {macos_target}")
return True
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# TARGETS # TARGETS
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
@ -171,6 +350,10 @@ def build_tests():
def clean(): def clean():
shutil.rmtree(OBJ_DIR, ignore_errors=True) shutil.rmtree(OBJ_DIR, ignore_errors=True)
shutil.rmtree(BIN_DIR, ignore_errors=True) shutil.rmtree(BIN_DIR, ignore_errors=True)
shutil.rmtree(PICO_BUILD_DIR, ignore_errors=True)
cmake_file = Path("CMakeLists.txt")
if cmake_file.exists():
cmake_file.unlink()
print("Cleaned.") print("Cleaned.")
@ -194,20 +377,49 @@ def debug_tests():
subprocess.call(["gdb", str(TEST_TARGET)]) subprocess.call(["gdb", str(TEST_TARGET)])
def show_help():
help_text = """
Build Script for Multi-Platform Compilation
============================================
Usage: python3 build.py [command]
Commands:
all, main, build Build for current platform
run Build and run program
test Build and run tests
debug Build tests and run debugger
clean Remove build artifacts
macos Build for macOS
pico, rp2040 Build for RP2040 (Raspberry Pi Pico)
help Show this help message
Environment Variables:
PICO_SDK_PATH Path to Pico SDK (default: ~/pico/pico-sdk)
Examples:
python3 build.py build # Build for current platform
python3 build.py pico # Build for RP2040
python3 build.py macos # Build for macOS
python3 build.py clean # Clean all build files
"""
print(help_text)
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
# ENTRY POINT # ENTRY POINT
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
def main(): def main():
if len(sys.argv) < 2: if len(sys.argv) < 2:
print("Usage: python3 build.py [all|build|test|run|debug|clean]") show_help()
return return
cmd = sys.argv[1] cmd = sys.argv[1]
match cmd: match cmd:
case "all" | "main": case "all" | "main" | "build":
build_main()
case "build":
build_main() build_main()
case "run": case "run":
run_main() run_main()
@ -217,8 +429,15 @@ def main():
debug_tests() debug_tests()
case "clean": case "clean":
clean() clean()
case "macos":
build_macos()
case "pico" | "rp2040":
build_rp2040()
case "help" | "-h" | "--help":
show_help()
case _: case _:
print(f"Unknown target: {cmd}") print(f"Unknown target: {cmd}")
print("Run 'python3 build.py help' for usage information")
if __name__ == "__main__": if __name__ == "__main__":