cmake_minimum_required(VERSION 3.10) get_filename_component(TARGET_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) string(TOUPPER ${TARGET_NAME} TARGET_NAME_UPPER) if(DEFINED BUILD_${TARGET_NAME_UPPER}_HAS_TEST) set(MY_HAS_TEST ${BUILD_${TARGET_NAME_UPPER}_HAS_TEST}) else() set(MY_HAS_TEST OFF) endif() if(MY_HAS_TEST) option(BUILD_${TARGET_NAME_UPPER}_STANDALONE "Build ${TARGET_NAME_UPPER} as a standalone executable" OFF) # mark_as_advanced(BUILD_${TARGET_NAME_UPPER}_STANDALONE) endif() set(FINAL_MODULE_SRCS "") set(FLAT_SRC_DIR "${CMAKE_BINARY_DIR}/flat_sources/${TARGET_NAME}") set(NEED_FLATTEN FALSE) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(IS_TOP_LEVEL TRUE) message(STATUS "[${TARGET_NAME}] Detected Top-Level Project. Enabling recursive flatten build.") set(NEED_FLATTEN TRUE) else() set(IS_TOP_LEVEL FALSE) file(GLOB ALL_C_FILES "*.c") set(MODULE_SRCS "") foreach(SRC_FILE ${ALL_C_FILES}) get_filename_component(FILE_NAME ${SRC_FILE} NAME) if(FILE_NAME MATCHES ".*~$" OR FILE_NAME MATCHES "\\.bak$" OR FILE_NAME MATCHES "\\.swp$") continue() else() list(APPEND MODULE_SRCS ${SRC_FILE}) endif() endforeach() set(FINAL_MODULE_SRCS ${MODULE_SRCS}) endif() if(NEED_FLATTEN) message(STATUS "[${TARGET_NAME}] Searching for files in: ${CMAKE_CURRENT_SOURCE_DIR}") file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/*.h" ) set(FILTERED_SOURCE_FILES "") foreach(SRC_FILE ${ALL_SOURCE_FILES}) get_filename_component(ABS_FILE ${SRC_FILE} ABSOLUTE) if(ABS_FILE MATCHES "^${CMAKE_BINARY_DIR}") continue() endif() get_filename_component(FILE_NAME ${SRC_FILE} NAME) if(FILE_NAME MATCHES "CMakeCCompilerId\\.c$" OR FILE_NAME MATCHES "CMakeTmp") continue() endif() if(FILE_NAME MATCHES ".*~$" OR FILE_NAME MATCHES "\\.bak$" OR FILE_NAME MATCHES "\\.swp$") continue() endif() list(APPEND FILTERED_SOURCE_FILES ${SRC_FILE}) endforeach() set(ALL_SOURCE_FILES ${FILTERED_SOURCE_FILES}) list(LENGTH ALL_SOURCE_FILES FILE_COUNT) message(STATUS "[${TARGET_NAME}] Found ${FILE_COUNT} valid files after filtering.") if(NOT ALL_SOURCE_FILES) message(FATAL_ERROR "[${TARGET_NAME}] No valid source files found after filtering.") endif() set(FLAT_SRCS_LIST "") set(COPY_COMMANDS "") foreach(SRC_FILE ${ALL_SOURCE_FILES}) get_filename_component(FILE_NAME ${SRC_FILE} NAME) get_filename_component(FILE_EXT ${SRC_FILE} EXT) set(DEST_FILE "${FLAT_SRC_DIR}/${FILE_NAME}") if(EXISTS "${DEST_FILE}") message(WARNING "[${TARGET_NAME}] Filename conflict: '${FILE_NAME}'. Skipping ${SRC_FILE}.") continue() endif() list(APPEND COPY_COMMANDS COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_FILE} ${DEST_FILE} ) if(FILE_EXT STREQUAL ".c") list(APPEND FLAT_SRCS_LIST ${DEST_FILE}) endif() endforeach() set(FINAL_MODULE_SRCS ${FLAT_SRCS_LIST}) if(NOT FINAL_MODULE_SRCS) message(FATAL_ERROR "[${TARGET_NAME}] No .c files found to compile!") endif() add_custom_target(${TARGET_NAME}_flatten_sources ${COPY_COMMANDS} COMMENT "[${TARGET_NAME}] Flattening sources..." BYPRODUCTS ${FLAT_SRC_DIR} ) set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${FLAT_SRC_DIR}") endif() set(MODULE_SRCS ${FINAL_MODULE_SRCS}) if(NOT MODULE_SRCS AND NOT NEED_FLATTEN) if(NOT IS_TOP_LEVEL) message(STATUS "[${TARGET_NAME}] No source files found. Creating INTERFACE (header-only) library.") add_library(${TARGET_NAME} INTERFACE) set(INTERFACE_INCLUDES "") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include") list(APPEND INTERFACE_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/include") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}") list(APPEND INTERFACE_INCLUDES "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/include") list(APPEND INTERFACE_INCLUDES "${PROJECT_SOURCE_DIR}/include") endif() target_include_directories(${TARGET_NAME} INTERFACE ${INTERFACE_INCLUDES}) message(STATUS "[${TARGET_NAME}] INTERFACE includes: ${INTERFACE_INCLUDES}") return() endif() endif() set(EXPLICIT_STANDALONE OFF) if(DEFINED BUILD_${TARGET_NAME_UPPER}_STANDALONE AND BUILD_${TARGET_NAME_UPPER}_STANDALONE) set(EXPLICIT_STANDALONE ON) endif() set(IMPLICIT_STANDALONE OFF) if(IS_TOP_LEVEL AND DEFINED MAIN AND MAIN) set(IMPLICIT_STANDALONE ON) endif() if(EXPLICIT_STANDALONE OR IMPLICIT_STANDALONE) set(SHOULD_BUILD_EXECUTABLE TRUE) else() set(SHOULD_BUILD_EXECUTABLE FALSE) endif() if(NEED_FLATTEN AND FINAL_MODULE_SRCS) set_source_files_properties(${FINAL_MODULE_SRCS} PROPERTIES GENERATED TRUE) endif() if(SHOULD_BUILD_EXECUTABLE) set(CURRENT_TARGET_NAME ${TARGET_NAME}) add_executable(${CURRENT_TARGET_NAME} ${MODULE_SRCS}) if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(${CURRENT_TARGET_NAME} PUBLIC DEBUG) endif() target_compile_definitions(${TARGET_NAME} PRIVATE ${GLOBAL_ENABLED_MACROS}) if(NEED_FLATTEN) add_dependencies(${CURRENT_TARGET_NAME} ${TARGET_NAME}_flatten_sources) target_include_directories(${CURRENT_TARGET_NAME} PRIVATE ${FLAT_SRC_DIR}) endif() target_compile_definitions(${CURRENT_TARGET_NAME} PRIVATE MAIN STANDALONE_BUILD) set_target_properties(${CURRENT_TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output) message(STATUS "[${TARGET_NAME}] Building Standalone: ${CURRENT_TARGET_NAME} (Sources count: ${MODULE_SRCS})") else() set(CURRENT_TARGET_NAME ${TARGET_NAME}) if(MY_BUILD_STATIC) add_library(${TARGET_NAME} STATIC ${MODULE_SRCS}) else() if(A_BUILD_MAIN_AS_STATIC_LIB) add_library(${TARGET_NAME} OBJECT ${MODULE_SRCS}) else() add_library(${TARGET_NAME} SHARED ${MODULE_SRCS}) endif() endif() if(CMAKE_BUILD_TYPE STREQUAL "Debug") target_compile_definitions(${TARGET_NAME} PUBLIC DEBUG) endif() target_compile_definitions(${TARGET_NAME} PRIVATE ${GLOBAL_ENABLED_MACROS}) if(NEED_FLATTEN) add_dependencies(${CURRENT_TARGET_NAME} ${TARGET_NAME}_flatten_sources) target_include_directories(${CURRENT_TARGET_NAME} PRIVATE ${FLAT_SRC_DIR}) endif() set_target_properties(${TARGET_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output ) endif() set(MY_PUBLIC_INCLUDES "") if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include") list(APPEND MY_PUBLIC_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/include") message(STATUS "[${TARGET_NAME}] Found local include: ${CMAKE_CURRENT_SOURCE_DIR}/include") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}") list(APPEND MY_PUBLIC_INCLUDES "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}") message(STATUS "[${TARGET_NAME}] Found global sub-include: ${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}") endif() if(EXISTS "${PROJECT_SOURCE_DIR}/include") list(APPEND MY_PUBLIC_INCLUDES "${PROJECT_SOURCE_DIR}/include") message(STATUS "[${TARGET_NAME}] Found global root include: ${PROJECT_SOURCE_DIR}/include") endif() if(NOT MY_PUBLIC_INCLUDES) if(NEED_FLATTEN) list(APPEND MY_PUBLIC_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") else() list(APPEND MY_PUBLIC_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") endif() message(STATUS "[${TARGET_NAME}] Fallback to source dir: ${CMAKE_CURRENT_SOURCE_DIR}") endif() target_include_directories(${CURRENT_TARGET_NAME} PUBLIC ${MY_PUBLIC_INCLUDES} ${PROJECT_SOURCE_DIR}/../Core/Inc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) target_link_libraries(${CURRENT_TARGET_NAME} PRIVATE m stm32cubemx ) string(TOLOWER "${CMAKE_SYSTEM_NAME}" SYSTEM_NAME_LOWER) if(CMAKE_CROSSCOMPILING AND NOT SYSTEM_NAME_LOWER MATCHES "linux|windows|darwin|android") message(STATUS ">>> 检测到裸机/RTOS 环境 (${CMAKE_SYSTEM_NAME}),屏蔽 pthread。") else() find_package(Threads REQUIRED) target_link_libraries(${CURRENT_TARGET_NAME} PUBLIC Threads::Threads PRIVATE ${CMAKE_DL_LIBS} ) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(${CURRENT_TARGET_NAME} PRIVATE rt) endif() endif() target_compile_options(${CURRENT_TARGET_NAME} PRIVATE -Wall $<$:-g -O0> $<$:-O2> -ffunction-sections -fdata-sections ) target_link_options(${CURRENT_TARGET_NAME} PRIVATE -Wl,--gc-sections $<$:-s> ) message(STATUS "[${TARGET_NAME}] Configured target: ${CURRENT_TARGET_NAME}")