branch_switcher/cmake/retrieve_files.cmake
daiqingshuang 6eb14af6e1 init
2025-04-10 18:29:49 +08:00

318 lines
14 KiB
CMake
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#[=======================================================================[
:
platform: 平台标识符 (windows|linux|mac|mobile|desktop)
is_match:
#]=======================================================================]
function(is_current_platform platform is_match)
# 设置默认值为TRUE用于未知平台
set(matches FALSE)
if(platform STREQUAL "windows")
if(WIN32)
set(matches TRUE)
endif()
elseif(platform STREQUAL "linux")
if(UNIX AND NOT APPLE)
set(matches TRUE)
endif()
elseif(platform STREQUAL "mac")
if(APPLE AND NOT IOS)
set(matches TRUE)
endif()
elseif(platform STREQUAL "ios")
if(IOS)
set(matches TRUE)
endif()
elseif(platform STREQUAL "android")
if(ANDROID)
set(matches TRUE)
endif()
# 添加对unix平台的支持
elseif(platform STREQUAL "unix")
if(UNIX)
set(matches TRUE)
endif()
elseif(platform STREQUAL "mobile")
if(ANDROID OR IOS)
set(matches TRUE)
endif()
elseif(platform STREQUAL "desktop")
if(WIN32 OR (UNIX AND NOT APPLE) OR (APPLE AND NOT IOS))
set(matches TRUE)
endif()
else()
# 未知平台标识,默认匹配
set(matches TRUE)
endif()
set(${is_match} ${matches} PARENT_SCOPE)
endfunction()
#[=======================================================================[
:
path:
extension:
out_files:
#]=======================================================================]
function(retrieve_files_custom path extension out_files)
# 1. 参数验证
if(NOT IS_DIRECTORY "${path}")
message(WARNING "错误:目录 '${path}' 不存在")
return()
endif()
message(STATUS "正在检索目录: ${path}")
# 2. 构建文件匹配模式
set(file_patterns "")
foreach(ext IN LISTS extension)
list(APPEND file_patterns "${path}/*.${ext}")
endforeach()
# 3. 递归查找所有匹配的文件
file(GLOB_RECURSE found_files
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
CONFIGURE_DEPENDS ${file_patterns}
)
# 4. 处理找到的文件
set(filtered_files "")
foreach(current_file IN LISTS found_files)
# 4.1 获取文件所在目录
get_filename_component(file_dir "${current_file}" DIRECTORY)
string(REPLACE "/" ";" dir_components "${file_dir}")
# 4.2 检查平台兼容性
set(should_skip_file FALSE)
set(found_platform_dir FALSE)
foreach(dir_name IN LISTS dir_components)
# 检查是否是平台相关目录
if(dir_name MATCHES "^(windows|linux|mac|ios|android|unix|mobile|desktop)$")
set(found_platform_dir TRUE)
is_current_platform(${dir_name} platform_matches)
if(NOT platform_matches)
set(should_skip_file TRUE)
break()
endif()
endif()
endforeach()
# 如果文件需要跳过,继续处理下一个文件
if(should_skip_file)
continue()
endif()
# 4.3 添加符合条件的文件
list(APPEND filtered_files "${current_file}")
# 4.4 设置IDE文件分组
# 计算相对路径作为分组名称
get_filename_component(root_abs_path "${path}" ABSOLUTE)
get_filename_component(file_dir_abs_path "${file_dir}" ABSOLUTE)
file(RELATIVE_PATH group_path "${root_abs_path}" "${file_dir_abs_path}")
# 处理根目录的特殊情况
if(group_path STREQUAL ".")
set(group_name "")
else()
string(REPLACE "/" "\\" group_name "${group_path}")
endif()
# 创建IDE分组
source_group("${group_name}" FILES "${current_file}")
endforeach()
# 5. 设置输出变量
set(${out_files} ${filtered_files} PARENT_SCOPE)
endfunction()
#[=======================================================================[
便
#]=======================================================================]
function(retrieve_files path out_files)
# 设置基础文件类型
set(file_extensions
"h" # 头文件
"hpp" # C++头文件
"ini" # 配置文件
"cpp" # C++源文件
"c" # C源文件
"ixx" # C++20模块文件
)
# 针对Mac平台添加额外文件类型
if(APPLE)
list(APPEND file_extensions "mm") # Objective-C++源文件
endif()
# 执行文件检索
set(temp_files "")
retrieve_files_custom(${path} "${file_extensions}" temp_files)
# 合并结果到输出变量
set(${out_files} ${${out_files}} ${temp_files} PARENT_SCOPE)
endfunction()
#[=======================================================================[
# 用于添加资源文件并在编译后复制到最终可执行文件所在目录
# 注意:此函数依赖于 CMAKE_RUNTIME_OUTPUT_DIRECTORY 或 EXECUTABLE_OUTPUT_PATH
# 变量的设置,以确定可执行文件的输出目录。请确保在项目中设置了其中之一。
#
# 参数:
# TARGET_NAME: (必需) - 关联的目标 (库或可执行文件) 的名称。
# 资源复制命令将在 TARGET_NAME 构建后执行。
# RESOURCE_FILES: (必需) - 一个或多个要复制的资源文件的路径列表 (相对或绝对)
# OUTPUT_SUBDIR: (可选) - 相对于可执行文件输出目录的子目录路径 (例如 "assets")
#
# 例子:
# # 确保设置了可执行文件输出目录 (通常在顶层 CMakeLists.txt)
# set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
#
# # 添加库
# add_library(my_lib STATIC src/my_lib.cpp)
#
# # 添加资源到 my_lib但复制到最终可执行文件的输出目录下的 'config' 子目录
# add_resource_file(
# TARGET_NAME my_lib
# RESOURCE_FILES config/settings.json config/defaults.ini
# OUTPUT_SUBDIR config
# )
#
# # 添加可执行文件
# add_executable(my_app main.cpp)
# target_link_libraries(my_app PRIVATE my_lib)
#
# # 添加 my_app 的资源,复制到可执行文件输出目录的根目录
# add_resource_file(
# TARGET_NAME my_app
# RESOURCE_FILES assets/icon.png
# )
#]=======================================================================]
function(add_resource_file)
# 定义预期的参数
set(options "") # 无布尔选项
set(oneValueArgs TARGET_NAME OUTPUT_SUBDIR)
set(multiValueArgs RESOURCE_FILES)
# 解析传递给函数的参数
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# --- 参数验证 ---
if(NOT ARG_TARGET_NAME)
message(FATAL_ERROR "**add_resource_file**: **缺少必需参数** **TARGET_NAME**.")
endif()
if(NOT ARG_RESOURCE_FILES)
message(FATAL_ERROR "**add_resource_file**: **缺少必需参数** **RESOURCE_FILES**.")
endif()
if(NOT TARGET ${ARG_TARGET_NAME})
message(WARNING "**add_resource_file**: 目标 '${ARG_TARGET_NAME}' (尚)不存在。请确保在调用 add_executable/add_library('${ARG_TARGET_NAME}') 之后调用此函数。")
# 即使目标尚不存在仍然尝试配置命令。CMake通常能处理好依赖关系。
endif()
# --- 确定最终可执行文件的目标基础目录 ---
set(DESTINATION_BASE "")
if(DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY AND CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(DESTINATION_BASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
elseif(DEFINED EXECUTABLE_OUTPUT_PATH AND EXECUTABLE_OUTPUT_PATH)
# EXECUTABLE_OUTPUT_PATH 是旧变量,但也检查一下
set(DESTINATION_BASE "${EXECUTABLE_OUTPUT_PATH}")
else()
# 如果是多配置生成器(如 Visual Studio, Xcode需要考虑配置类型
get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(is_multi_config)
# 对于多配置,没有单一的顶级运行时目录变量。
# 可以考虑使用 $<OUTPUT_DIRECTORY> 配合一个已知的可执行文件名,但这会使函数复杂化。
# 最好的做法是要求用户设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG>
# 或者我们直接报错,强制用户设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY
message(FATAL_ERROR "**add_resource_file**: **无法确定可执行文件输出目录**。请在您的项目中设置 **CMAKE_RUNTIME_OUTPUT_DIRECTORY** 变量 (例如 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"\${CMAKE_BINARY_DIR}/bin\"))。对于多配置生成器,可能需要设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG> 变量。")
else()
# 对于单配置生成器(如 Makefiles, Ninja可以默认到 CMAKE_BINARY_DIR
set(DESTINATION_BASE "${CMAKE_BINARY_DIR}")
message(WARNING "**add_resource_file**: **未设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY**。默认将资源复制到 CMAKE_BINARY_DIR ('${CMAKE_BINARY_DIR}')。强烈建议设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY 以获得可预测的行为。")
endif()
# message(FATAL_ERROR "**add_resource_file**: **无法确定可执行文件输出目录**。请在您的项目中设置 **CMAKE_RUNTIME_OUTPUT_DIRECTORY** 变量 (例如 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"\${CMAKE_BINARY_DIR}/bin\"))。")
endif()
# 处理子目录
set(DESTINATION_DIR "${DESTINATION_BASE}") # 默认目标目录
if(ARG_OUTPUT_SUBDIR)
# 清理子目录路径字符串
string(STRIP "${ARG_OUTPUT_SUBDIR}" _subdir)
if(IS_ABSOLUTE "${_subdir}")
message(FATAL_ERROR "**add_resource_file**: **OUTPUT_SUBDIR** ('${ARG_OUTPUT_SUBDIR}') **必须是相对路径**。")
else()
# 移除可能存在的前导/后导斜杠,以便干净地拼接路径
string(REGEX REPLACE "^[/\\\\]+" "" _subdir "${_subdir}")
string(REGEX REPLACE "[/\\\\]+$" "" _subdir "${_subdir}")
if(_subdir) # 仅当子目录清理后非空时才追加
set(DESTINATION_DIR "${DESTINATION_BASE}/${_subdir}")
endif()
endif()
endif()
# --- 准备源文件路径 ---
set(ABS_RESOURCE_FILES "")
foreach(RESOURCE_FILE ${ARG_RESOURCE_FILES})
get_filename_component(RESOURCE_FILE_REALPATH "${RESOURCE_FILE}" REALPATH BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
# if(IS_ABSOLUTE "${RESOURCE_FILE}")
# # 如果已经是绝对路径,直接使用
# list(APPEND ABS_RESOURCE_FILES "${RESOURCE_FILE}")
# else()
# # 如果是相对路径,相对于当前源目录进行解析
# list(APPEND ABS_RESOURCE_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_FILE}")
# endif()
list(APPEND ABS_RESOURCE_FILES "${RESOURCE_FILE_REALPATH}")
# 检查文件是否存在 (在配置时发出警告,有助于早期发现错误)
# list(GET ABS_RESOURCE_FILES -1 _current_abs_file) # 获取刚才添加的绝对路径
if(NOT EXISTS "${RESOURCE_FILE_REALPATH}")
message(WARNING "**add_resource_file**: **资源文件** '${RESOURCE_FILE}' (解析为 '${RESOURCE_FILE_REALPATH}') **在配置时不存在**。")
endif()
endforeach()
# --- 添加自定义命令 ---
# 使用 add_custom_command 在目标构建完成后执行复制操作
if(ABS_RESOURCE_FILES) # 确保有文件需要复制
# 注意DESTINATION_DIR 可能包含特定于配置的路径(例如,如果 CMAKE_RUNTIME_OUTPUT_DIRECTORY
# 设置为 ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin
# add_custom_command 的 COMMAND 参数在构建时执行,此时这些变量/生成器表达式已解析。
add_custom_command(
TARGET ${ARG_TARGET_NAME}
POST_BUILD # 指定在目标构建之后执行
# 步骤 1: 确保目标目录存在 (copy_if_different 不会创建目录)
COMMAND ${CMAKE_COMMAND} -E make_directory "${DESTINATION_DIR}"
# 步骤 2: 复制文件
COMMAND ${CMAKE_COMMAND} -E copy_if_different # 使用CMake内置命令复制仅当文件不同时
${ABS_RESOURCE_FILES} # 要复制的源文件列表(绝对路径)
"${DESTINATION_DIR}" # 最终可执行文件所在的目标目录 (带引号以处理空格)
COMMENT "为 ${ARG_TARGET_NAME} 将资源复制到可执行文件目录: ${DESTINATION_DIR}..." # 构建时显示的注释
VERBATIM # 确保参数(尤其是路径和生成器表达式)被正确处理
)
else()
message(WARNING "**add_resource_file**: 没有有效的资源文件提供给目标 '${ARG_TARGET_NAME}'。")
endif()
# --- 可选: 将资源文件添加到 IDE 项目结构中 ---
if(ABS_RESOURCE_FILES)
set(_source_group_name "Resource Files") # 基础组名
if(ARG_OUTPUT_SUBDIR)
# 使用与目标目录结构匹配的组名
string(STRIP "${ARG_OUTPUT_SUBDIR}" _clean_subdir)
string(REPLACE "\\" "/" _clean_subdir "${_clean_subdir}") # 统一使用正斜杠
string(REGEX REPLACE "^[/]+" "" _clean_subdir "${_clean_subdir}")
string(REGEX REPLACE "[/]+$" "" _clean_subdir "${_clean_subdir}")
if(_clean_subdir)
set(_source_group_name "Resource Files/${_clean_subdir}")
endif()
endif()
# 使用 source_group 将文件添加到 IDE 的指定组下
source_group(${_source_group_name} FILES ${ABS_RESOURCE_FILES})
endif()
endfunction()