cmake_minimum_required(VERSION 3.10) get_filename_component(TARGET_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) string(TOUPPER ${TARGET_NAME} TARGET_NAME_UPPER) # ─── Test / Standalone 选项 ─────────────────────────────────────────── if(BUILD_${TARGET_NAME_UPPER}_HAS_TEST) option(BUILD_${TARGET_NAME_UPPER}_STANDALONE "Build ${TARGET_NAME_UPPER} as a standalone executable" OFF) endif() # ─── 判断顶层独立构建 vs 子模块嵌入 ───────────────────────────────── if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(IS_TOP_LEVEL TRUE) message(STATUS "[${TARGET_NAME}] Top-level project — enabling recursive flatten build.") set(NEED_FLATTEN TRUE) else() set(IS_TOP_LEVEL FALSE) set(NEED_FLATTEN FALSE) endif() # ─── 收集源文件 ────────────────────────────────────────────────────── if(NOT NEED_FLATTEN) file(GLOB_RECURSE MODULE_SRCS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.c") # 过滤编辑器临时文件 list(FILTER MODULE_SRCS EXCLUDE REGEX ".*[~]$|.*\\.bak$|.*\\.swp$") endif() # ─── 顶层构建: 扁平化拷贝所有源文件 ───────────────────────────────── if(NEED_FLATTEN) set(FLAT_SRC_DIR "${CMAKE_BINARY_DIR}/flat_sources/${TARGET_NAME}") file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/*.h" ) # 过滤: 构建目录 / CMake 内部文件 / 编辑器临时文件 set(FILTERED_FILES "") foreach(SRC_FILE ${ALL_SOURCE_FILES}) get_filename_component(ABS_FILE ${SRC_FILE} ABSOLUTE) get_filename_component(FILE_NAME ${SRC_FILE} NAME) if(ABS_FILE MATCHES "^${CMAKE_BINARY_DIR}") continue() endif() if(FILE_NAME MATCHES "CMakeCCompilerId\\.c$|CMakeTmp") continue() endif() if(FILE_NAME MATCHES "[~]$|\\.bak$|\\.swp$") continue() endif() list(APPEND FILTERED_FILES ${SRC_FILE}) endforeach() list(LENGTH FILTERED_FILES FILE_COUNT) message(STATUS "[${TARGET_NAME}] Found ${FILE_COUNT} valid files after filtering.") if(NOT FILTERED_FILES) message(FATAL_ERROR "[${TARGET_NAME}] No valid source files found.") endif() # 将所有文件拷贝到 flat 目录(避免同名冲突) set(FLAT_SRCS "") set(COPY_COMMANDS "") foreach(SRC_FILE ${FILTERED_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 ${DEST_FILE}) endif() endforeach() if(NOT FLAT_SRCS) message(FATAL_ERROR "[${TARGET_NAME}] No .c files 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}") set(MODULE_SRCS ${FLAT_SRCS}) set_source_files_properties(${MODULE_SRCS} PROPERTIES GENERATED TRUE) endif() # ─── 无源文件时创建 header-only INTERFACE 库 ──────────────────────── if(NOT MODULE_SRCS AND NOT IS_TOP_LEVEL) message(STATUS "[${TARGET_NAME}] No source files — creating INTERFACE (header-only) library.") add_library(${TARGET_NAME} INTERFACE) set(INTERFACE_INCLUDES "") foreach(inc "${CMAKE_CURRENT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}" "${PROJECT_SOURCE_DIR}/include" ) if(EXISTS "${inc}") list(APPEND INTERFACE_INCLUDES "${inc}") endif() endforeach() target_include_directories(${TARGET_NAME} INTERFACE ${INTERFACE_INCLUDES}) message(STATUS "[${TARGET_NAME}] INTERFACE includes: ${INTERFACE_INCLUDES}") return() endif() # ─── 判断是否构建独立可执行文件 ───────────────────────────────────── set(SHOULD_BUILD_EXECUTABLE FALSE) if(BUILD_${TARGET_NAME_UPPER}_STANDALONE) set(SHOULD_BUILD_EXECUTABLE TRUE) elseif(IS_TOP_LEVEL AND MAIN) set(SHOULD_BUILD_EXECUTABLE TRUE) endif() # ─── 创建目标 ──────────────────────────────────────────────────────── if(SHOULD_BUILD_EXECUTABLE) add_executable(${TARGET_NAME} ${MODULE_SRCS}) target_compile_definitions(${TARGET_NAME} PRIVATE MAIN STANDALONE_BUILD) set_target_properties(${TARGET_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output ) else() if(MY_BUILD_STATIC) add_library(${TARGET_NAME} STATIC ${MODULE_SRCS}) elseif(MY_BUILD_SHARED_LIBS) add_library(${TARGET_NAME} SHARED ${MODULE_SRCS}) else() add_library(${TARGET_NAME} OBJECT ${MODULE_SRCS}) endif() set_target_properties(${TARGET_NAME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/output ) endif() message(STATUS "[${TARGET_NAME}] Configured target: ${TARGET_NAME} (Sources: ${MODULE_SRCS})") # ─── Flatten 依赖 ──────────────────────────────────────────────────── if(NEED_FLATTEN) add_dependencies(${TARGET_NAME} ${TARGET_NAME}_flatten_sources) target_include_directories(${TARGET_NAME} PRIVATE ${FLAT_SRC_DIR}) endif() # ─── 编译宏定义 ───────────────────────────────────────────────────── target_compile_definitions(${TARGET_NAME} PRIVATE ${GLOBAL_ENABLED_MACROS} $<$:DEBUG> ) # ─── Include 目录 ──────────────────────────────────────────────────── set(MY_PUBLIC_INCLUDES "") foreach(inc "${CMAKE_CURRENT_SOURCE_DIR}/include" "${PROJECT_SOURCE_DIR}/include/${TARGET_NAME}" "${PROJECT_SOURCE_DIR}/include" ) if(EXISTS "${inc}") list(APPEND MY_PUBLIC_INCLUDES "${inc}") message(STATUS "[${TARGET_NAME}] Found include: ${inc}") endif() endforeach() if(NOT MY_PUBLIC_INCLUDES) list(APPEND MY_PUBLIC_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") message(STATUS "[${TARGET_NAME}] Fallback include: source dir") endif() target_include_directories(${TARGET_NAME} PUBLIC ${MY_PUBLIC_INCLUDES} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) # ─── 链接依赖 ──────────────────────────────────────────────────────── target_link_libraries(${TARGET_NAME} PRIVATE m) 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(${TARGET_NAME} PUBLIC Threads::Threads PRIVATE ${CMAKE_DL_LIBS} ) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_link_libraries(${TARGET_NAME} PRIVATE rt) endif() endif() # ─── 编译选项 ──────────────────────────────────────────────────────── target_compile_options(${TARGET_NAME} PRIVATE -Wall -Werror -ffunction-sections -fdata-sections -Wno-unused-function $<$:-g -O0> $<$:-O2> ) target_link_options(${TARGET_NAME} PRIVATE -Wl,--gc-sections $<$:-s> )