You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

214 lines
8.5 KiB

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}
$<$<CONFIG:Debug>: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
$<$<CONFIG:Debug>:-g -O0>
$<$<CONFIG:Release>:-O2>
)
target_link_options(${TARGET_NAME} PRIVATE
-Wl,--gc-sections
$<$<CONFIG:Release>:-s>
)