From c89acbbdfb500b50c925c18cc0576e15f22e78c0 Mon Sep 17 00:00:00 2001 From: Kyler Date: Wed, 3 Dec 2025 23:57:33 -0700 Subject: [PATCH] Started Raspberry Pi Pico build target --- SLS_C/.gitignore | 2 + SLS_C/build.py | 252 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 237 insertions(+), 17 deletions(-) diff --git a/SLS_C/.gitignore b/SLS_C/.gitignore index 12ad133..e7db8f1 100644 --- a/SLS_C/.gitignore +++ b/SLS_C/.gitignore @@ -1,4 +1,6 @@ obj/ bin/ +build_pico/ *.o *.pdb +CMakeLists.txt diff --git a/SLS_C/build.py b/SLS_C/build.py index 4ee04c0..709d181 100644 --- a/SLS_C/build.py +++ b/SLS_C/build.py @@ -4,6 +4,7 @@ import sys import subprocess from pathlib import Path import shutil +import platform # --------------------------------------------------------------------- # CONFIG @@ -16,22 +17,101 @@ BIN_DIR = Path("bin") TARGET = BIN_DIR / "sls" 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 COMMON_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Werror", "-Iinclude", "-g"] TEST_FLAGS = ["-std=c99", "-Wall", "-Wextra", "-Wno-unused-function", "-Werror", "-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 MSVC_FLAGS = ["/std:c11", "/Zi", "/Iinclude"] MSVC_TEST_FLAGS = MSVC_FLAGS + [] +# RP2040 settings +RP2040_CMAKE_TEMPLATE = """cmake_minimum_required(VERSION 3.13) + +# Pico SDK initialization +include($ENV{{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 # --------------------------------------------------------------------- -def detect_compiler(): - if os.name == "nt": +def detect_compiler(target_platform=None): + """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 ("gcc", "gcc") + elif target == "macos": + # Prefer clang on macOS + if shutil.which("clang"): + return ("clang", "gcc") + return ("gcc", "gcc") + else: + return ("gcc", "gcc") CC, CC_KIND = detect_compiler() @@ -79,7 +159,7 @@ def mkdir(p: Path): def run(cmd): - print(">>", " ".join(cmd)) + print(">>", " ".join(str(c) for c in cmd)) subprocess.check_call(cmd) @@ -96,7 +176,7 @@ def is_up_to_date(src, obj, dep): # --------------------------------------------------------------------- # BUILD RULES # --------------------------------------------------------------------- -def compile_source(src: Path, is_test=False): +def compile_source(src: Path, is_test=False, target_platform=None): mkdir(OBJ_DIR) obj = OBJ_DIR / (src.stem + ".o") dep = OBJ_DIR / (src.stem + ".d") @@ -104,13 +184,19 @@ def compile_source(src: Path, is_test=False): if is_up_to_date(src, obj, dep): 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 - cmd = [CC] + flags + ["/Fo" + str(obj), "/c", str(src), + cmd = [compiler] + flags + ["/Fo" + str(obj), "/c", str(src), f"/DGIT_COMMIT_HASH=\"{GIT_HASH}\""] else: - flags = TEST_FLAGS if is_test else COMMON_FLAGS - cmd = [CC] + flags + [ + if target_platform == "macos": + flags = MACOS_FLAGS if not is_test else TEST_FLAGS + ["-mmacosx-version-min=10.13"] + else: + flags = TEST_FLAGS if is_test else COMMON_FLAGS + + cmd = [compiler] + flags + [ f"-DGIT_COMMIT_HASH=\"{GIT_HASH}\"", "-MMD", "-MP", "-c", str(src), "-o", str(obj) @@ -120,12 +206,14 @@ def compile_source(src: Path, is_test=False): return obj -def link_executable(objects, output: Path): +def link_executable(objects, output: Path, target_platform=None): mkdir(BIN_DIR) - if CC_KIND == "msvc": - cmd = [CC] + list(map(str, objects)) + ["/Fe" + str(output)] + compiler, kind = detect_compiler(target_platform) + + if kind == "msvc": + cmd = [compiler] + list(map(str, objects)) + ["/Fe" + str(output)] else: - cmd = [CC] + list(map(str, objects)) + ["-lm", "-o", str(output)] + cmd = [compiler] + list(map(str, objects)) + ["-lm", "-o", str(output)] run(cmd) @@ -148,6 +236,96 @@ def generate_tests(): 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 + ) + + 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 + cmake_cmd = [ + "cmake", + "-B", str(PICO_BUILD_DIR), + "-S", ".", + f"-DPICO_SDK_PATH={PICO_SDK_PATH}" + ] + 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 # --------------------------------------------------------------------- @@ -171,6 +349,10 @@ def build_tests(): def clean(): shutil.rmtree(OBJ_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.") @@ -194,20 +376,49 @@ def debug_tests(): 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 # --------------------------------------------------------------------- def main(): if len(sys.argv) < 2: - print("Usage: python3 build.py [all|build|test|run|debug|clean]") + show_help() return cmd = sys.argv[1] match cmd: - case "all" | "main": - build_main() - case "build": + case "all" | "main" | "build": build_main() case "run": run_main() @@ -217,8 +428,15 @@ def main(): debug_tests() case "clean": clean() + case "macos": + build_macos() + case "pico" | "rp2040": + build_rp2040() + case "help" | "-h" | "--help": + show_help() case _: print(f"Unknown target: {cmd}") + print("Run 'python3 build.py help' for usage information") if __name__ == "__main__":