# ============================================================================ # Top-level CMakeLists.txt for the color_switcher Pico firmware project # ============================================================================ # This file is intentionally kept thin: it orchestrates the build phases in # the strict order CMake requires. All real configuration lives in the # cmake_config/*.cmake fragments so each concern is isolated and easy to find. # # Build phase order (forced by the Pico SDK + CMake semantics): # 1. project_config - variables only, no side effects # 2. mcu_config - defines mcu_init / mcu_sdk_config / mcu_link_target # 3. mcu_init() - SDK bootstrap (MUST run before project()) # 4. project() - declares the CMake project using project_config vars # 5. mcu_sdk_config() - pico_sdk_init() (MUST run after project()) # 6. sources_config - collects PROJECT_SOURCES and PROJECT_INCLUDE_DIRS # 7. add_executable() - the actual firmware build target # 8. mcu_link_target() - target_link_libraries + stdio + UF2 output # ============================================================================ # Require CMake 3.13 or newer. The Pico SDK uses features (like # target_link_libraries on object libraries) that were added in 3.13, # so older versions will fail to configure with cryptic errors. cmake_minimum_required(VERSION 3.13) # The real project root is one level above this file (this CMakeLists.txt # lives in /cmake/). Everything outside the build system - the # src/ tree, the Dockerfile, etc. - is referenced via PROJECT_ROOT_DIR so # we never have to sprinkle "../" paths throughout the cmake fragments. get_filename_component(PROJECT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE) # Make cmake_config/ searchable so include(foo) resolves to # cmake_config/foo.cmake without needing the full path each time. list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_config) # Phase 1 - project-wide variables (PROJECT_NAME, PROJECT_VERSION, etc.) include(project_config) # Phase 2 - MCU helper macros/functions (no side effects, just definitions) include(mcu_config) # Phase 3 - Pico SDK bootstrap. MUST run before project() so the ARM # cross-compile toolchain is configured before CMake enables languages. mcu_init() # Phase 4 - declare the CMake project using variables from project_config. # This triggers CMake to detect the (already-configured) cross compiler. project(${PROJECT_NAME} VERSION ${PROJECT_VERSION} LANGUAGES ${PROJECT_LANGUAGES}) # Phase 5 - register every Pico SDK library as a CMake target so we can # pick which ones to link later. This does NOT pull libraries into the # final binary - mcu_link_target() does that. mcu_sdk_config() # Phase 6 - collect the list of .c files and include directories into # PROJECT_SOURCES and PROJECT_INCLUDE_DIRS variables. include(sources_config) # Phase 7 - declare the firmware executable and its include paths. add_executable(${PROJECT_NAME} ${PROJECT_SOURCES}) target_include_directories(${PROJECT_NAME} PRIVATE ${PROJECT_INCLUDE_DIRS}) # Phase 8 - link only the SDK libraries we actually use (pico_stdlib, # hardware_uart), route stdio over USB-CDC, and emit the .uf2 firmware file. mcu_link_target(${PROJECT_NAME})