diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index e01999f..9a99b27 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -3,105 +3,86 @@ 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) +# ─── 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() -set(FINAL_MODULE_SRCS "") -set(FLAT_SRC_DIR "${CMAKE_BINARY_DIR}/flat_sources/${TARGET_NAME}") -set(NEED_FLATTEN FALSE) - +# ─── 判断顶层独立构建 vs 子模块嵌入 ───────────────────────────────── 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.") + message(STATUS "[${TARGET_NAME}] 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}) + set(NEED_FLATTEN FALSE) +endif() + +# ─── 收集源文件 ────────────────────────────────────────────────────── +if(NOT NEED_FLATTEN) + file(GLOB MODULE_SRCS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.c") + # 过滤编辑器临时文件 + list(FILTER MODULE_SRCS EXCLUDE REGEX ".*[~]$|.*\\.bak$|.*\\.swp$") endif() +# ─── 顶层构建: 扁平化拷贝所有源文件 ───────────────────────────────── if(NEED_FLATTEN) - message(STATUS "[${TARGET_NAME}] Searching for files in: ${CMAKE_CURRENT_SOURCE_DIR}") + set(FLAT_SRC_DIR "${CMAKE_BINARY_DIR}/flat_sources/${TARGET_NAME}") - file(GLOB_RECURSE ALL_SOURCE_FILES - CONFIGURE_DEPENDS + file(GLOB_RECURSE ALL_SOURCE_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.c" "${CMAKE_CURRENT_SOURCE_DIR}/*.h" ) - set(FILTERED_SOURCE_FILES "") + # 过滤: 构建目录 / 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() - - get_filename_component(FILE_NAME ${SRC_FILE} NAME) - if(FILE_NAME MATCHES "CMakeCCompilerId\\.c$" OR FILE_NAME MATCHES "CMakeTmp") + if(FILE_NAME MATCHES "CMakeCCompilerId\\.c$|CMakeTmp") continue() endif() - - if(FILE_NAME MATCHES ".*~$" OR FILE_NAME MATCHES "\\.bak$" OR FILE_NAME MATCHES "\\.swp$") + if(FILE_NAME MATCHES "[~]$|\\.bak$|\\.swp$") continue() endif() - list(APPEND FILTERED_SOURCE_FILES ${SRC_FILE}) + list(APPEND FILTERED_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.") + 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() - set(FLAT_SRCS_LIST "") + # 将所有文件拷贝到 flat 目录(避免同名冲突) + set(FLAT_SRCS "") set(COPY_COMMANDS "") - - foreach(SRC_FILE ${ALL_SOURCE_FILES}) + foreach(SRC_FILE ${FILTERED_FILES}) get_filename_component(FILE_NAME ${SRC_FILE} NAME) - get_filename_component(FILE_EXT ${SRC_FILE} EXT) - + 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() + 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}) + list(APPEND FLAT_SRCS ${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!") + if(NOT FLAT_SRCS) + message(FATAL_ERROR "[${TARGET_NAME}] No .c files to compile!") endif() add_custom_target(${TARGET_NAME}_flatten_sources @@ -109,161 +90,124 @@ if(NEED_FLATTEN) 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() + set(MODULE_SRCS ${FLAT_SRCS}) + set_source_files_properties(${MODULE_SRCS} PROPERTIES GENERATED TRUE) endif() -set(EXPLICIT_STANDALONE OFF) -if(DEFINED BUILD_${TARGET_NAME_UPPER}_STANDALONE AND BUILD_${TARGET_NAME_UPPER}_STANDALONE) - set(EXPLICIT_STANDALONE ON) -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(IMPLICIT_STANDALONE OFF) -if(IS_TOP_LEVEL AND DEFINED MAIN AND MAIN) - set(IMPLICIT_STANDALONE ON) + 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() -if(EXPLICIT_STANDALONE OR IMPLICIT_STANDALONE) +# ─── 判断是否构建独立可执行文件 ───────────────────────────────────── +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) -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})") + 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() - set(CURRENT_TARGET_NAME ${TARGET_NAME}) if(MY_BUILD_STATIC) add_library(${TARGET_NAME} STATIC ${MODULE_SRCS}) + elseif(MY_BUILD_SHARED_LIBS) + add_library(${TARGET_NAME} SHARED ${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}) + 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() -set(MY_PUBLIC_INCLUDES "") +message(STATUS "[${TARGET_NAME}] Configured target: ${TARGET_NAME} (Sources: ${MODULE_SRCS})") -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") +# ─── Flatten 依赖 ──────────────────────────────────────────────────── +if(NEED_FLATTEN) + add_dependencies(${TARGET_NAME} ${TARGET_NAME}_flatten_sources) + target_include_directories(${TARGET_NAME} PRIVATE ${FLAT_SRC_DIR}) 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() +# ─── 编译宏定义 ───────────────────────────────────────────────────── +target_compile_definitions(${TARGET_NAME} + PRIVATE ${GLOBAL_ENABLED_MACROS} + $<$:DEBUG> +) -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() +# ─── 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) - 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}") + list(APPEND MY_PUBLIC_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}") + message(STATUS "[${TARGET_NAME}] Fallback include: source dir") endif() -target_include_directories(${CURRENT_TARGET_NAME} - PUBLIC ${MY_PUBLIC_INCLUDES} +target_include_directories(${TARGET_NAME} + PUBLIC ${MY_PUBLIC_INCLUDES} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_link_libraries(${CURRENT_TARGET_NAME} - PRIVATE m - ) +# ─── 链接依赖 ──────────────────────────────────────────────────────── +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。") + message(STATUS ">>> 裸机/RTOS 环境 (${CMAKE_SYSTEM_NAME}),屏蔽 pthread") else() find_package(Threads REQUIRED) - target_link_libraries(${CURRENT_TARGET_NAME} - PUBLIC Threads::Threads + target_link_libraries(${TARGET_NAME} + PUBLIC Threads::Threads PRIVATE ${CMAKE_DL_LIBS} ) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - target_link_libraries(${CURRENT_TARGET_NAME} PRIVATE rt) + target_link_libraries(${TARGET_NAME} PRIVATE rt) endif() endif() -target_compile_options(${CURRENT_TARGET_NAME} - PRIVATE - -Wall - -Werror - $<$:-g -O0> - $<$:-O2> - -ffunction-sections -fdata-sections +# ─── 编译选项 ──────────────────────────────────────────────────────── +target_compile_options(${TARGET_NAME} PRIVATE + -Wall -Werror + -ffunction-sections -fdata-sections + $<$:-g -O0> + $<$:-O2> ) -target_link_options(${CURRENT_TARGET_NAME} - PRIVATE - -Wl,--gc-sections - $<$:-s> +target_link_options(${TARGET_NAME} PRIVATE + -Wl,--gc-sections + $<$:-s> ) - -message(STATUS "[${TARGET_NAME}] Configured target: ${CURRENT_TARGET_NAME}") \ No newline at end of file