From 4d9ccc73c05c9bc9b8e311a0813e24ba62bd20cb Mon Sep 17 00:00:00 2001 From: Nanako <469449812@qq.com> Date: Mon, 7 Apr 2025 21:42:20 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dadd=5Fresource=5Ffile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmake/retrieve_files.cmake | 212 +++++++++++++++++++++---------- src/mirage_image/color.cpp | 3 - src/mirage_image/color.h | 1 + src/mirage_widget/CMakeLists.txt | 5 +- 4 files changed, 150 insertions(+), 71 deletions(-) diff --git a/cmake/retrieve_files.cmake b/cmake/retrieve_files.cmake index b8ed860..ac0f49a 100644 --- a/cmake/retrieve_files.cmake +++ b/cmake/retrieve_files.cmake @@ -158,82 +158,160 @@ function(retrieve_files path out_files) endfunction() #[=======================================================================[ - 用于添加资源文件并在编译后复制到目标文件 (可执行文件或库) 所在目录 - 参数: - TARGET_NAME: 目标 (可执行文件或库) 的名称 - RESOURCE_FILE_PATHS - 一个或多个要复制的资源文件的路径 (可以是相对或绝对路径) +# 用于添加资源文件并在编译后复制到最终可执行文件所在目录 +# 注意:此函数依赖于 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_resources TARGET_NAME) - # 获取 TARGET_NAME 之后的所有参数作为资源文件列表 - set(RESOURCE_FILES ${ARGN}) +function(add_resource_file) + # 定义预期的参数 + set(options "") # 无布尔选项 + set(oneValueArgs TARGET_NAME OUTPUT_SUBDIR) + set(multiValueArgs RESOURCE_FILES) - # 检查目标是否存在 (可选,但推荐) - if(NOT TARGET ${TARGET_NAME}) - message(FATAL_ERROR "目标 '${TARGET_NAME}' 不存在,无法添加资源。") - return() + # 解析传递给函数的参数 + 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() - # **获取目标可执行文件的输出目录** (只需执行一次) - # 对于多配置生成器 (如 Visual Studio, Xcode),使用 $ - # 对于单配置生成器 (如 Makefiles, Ninja),使用 CMAKE_RUNTIME_OUTPUT_DIRECTORY 或 $ - if(CMAKE_CONFIGURATION_TYPES) - set(DESTINATION_DIR "$") + # --- 确定最终可执行文件的目标基础目录 --- + 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() - if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) - set(DESTINATION_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + # 如果是多配置生成器(如 Visual Studio, Xcode),需要考虑配置类型 + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + # 对于多配置,没有单一的顶级运行时目录变量。 + # 可以考虑使用 $ 配合一个已知的可执行文件名,但这会使函数复杂化。 + # 最好的做法是要求用户设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY_ + # 或者我们直接报错,强制用户设置 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_ 变量。") else() - # 使用 $ 通常更健壮 - set(DESTINATION_DIR "$") + # 对于单配置生成器(如 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() - # **遍历所有传入的资源文件** - foreach(RESOURCE_FILE ${RESOURCE_FILES}) - set(ABS_RESOURCE_FILE "") # 重置/初始化 + # --- 准备源文件路径 --- + 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}") - # **处理相对路径和绝对路径,并检查文件是否存在** - if(IS_ABSOLUTE "${RESOURCE_FILE}") - # 如果是绝对路径,直接检查是否存在 - if(EXISTS "${RESOURCE_FILE}") - set(ABS_RESOURCE_FILE "${RESOURCE_FILE}") - else() - message(WARNING "资源文件未找到 (绝对路径,将跳过): ${RESOURCE_FILE}") - continue() # 处理下一个文件 - endif() - else() - # 如果是相对路径,相对于当前 CMakeLists.txt 文件所在目录解析 - set(TEMP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${RESOURCE_FILE}") - if(EXISTS "${TEMP_PATH}") - set(ABS_RESOURCE_FILE "${TEMP_PATH}") - else() - message(WARNING "资源文件未找到 (相对路径,将跳过): ${RESOURCE_FILE} (相对于: ${CMAKE_CURRENT_SOURCE_DIR})") - continue() # 处理下一个文件 - endif() + # 检查文件是否存在 (在配置时发出警告,有助于早期发现错误) + # 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() - - # **检查是否是文件而非目录** (copy_if_different 用于文件) - if(IS_DIRECTORY "${ABS_RESOURCE_FILE}") - message(WARNING "提供的资源是一个目录,而非文件 (将跳过): ${ABS_RESOURCE_FILE}. 请使用其他方法复制目录。") - continue() - endif() - - # **获取资源文件的文件名部分** - get_filename_component(RESOURCE_FILENAME "${ABS_RESOURCE_FILE}" NAME) - - # **为每个文件添加自定义构建后命令** - add_custom_command( - TARGET ${TARGET_NAME} # 关联到目标 - POST_BUILD # 在目标构建完成后执行 - COMMAND ${CMAKE_COMMAND} -E copy_if_different # 使用 cmake 内建命令复制文件 (仅在文件不同时复制) - "${ABS_RESOURCE_FILE}" # 源文件 (绝对路径) - "${DESTINATION_DIR}/${RESOURCE_FILENAME}" # **目标路径** (可执行文件目录 + 文件名) - COMMENT "正在复制资源 ${RESOURCE_FILENAME} 到 ${TARGET_NAME} 的输出目录" # 构建时显示的注释 - VERBATIM # 确保特殊字符被正确处理 - ) - - # (可选) 将资源文件添加到目标源文件中,以便在某些 IDE 中显示 - # 注意:如果文件不在源目录树下,某些 IDE 可能不会正确显示 - # target_sources(${TARGET_NAME} PRIVATE "${ABS_RESOURCE_FILE}") - endforeach() -endfunction() \ No newline at end of file + + # --- 添加自定义命令 --- + # 使用 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() + diff --git a/src/mirage_image/color.cpp b/src/mirage_image/color.cpp index 973a097..ded65db 100644 --- a/src/mirage_image/color.cpp +++ b/src/mirage_image/color.cpp @@ -1,8 +1,5 @@ #include "color.h" -#include -#include - /** * @brief 将十六进制字符转换为整数值 * diff --git a/src/mirage_image/color.h b/src/mirage_image/color.h index 710b890..35a0dfc 100644 --- a/src/mirage_image/color.h +++ b/src/mirage_image/color.h @@ -8,6 +8,7 @@ */ #include +#include /** * @class linear_color diff --git a/src/mirage_widget/CMakeLists.txt b/src/mirage_widget/CMakeLists.txt index 6f5bcd3..352c711 100644 --- a/src/mirage_widget/CMakeLists.txt +++ b/src/mirage_widget/CMakeLists.txt @@ -9,4 +9,7 @@ add_library(${PROJECT_NAME} STATIC ${SRC_FILES}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) target_link_libraries(${PROJECT_NAME} PUBLIC mirage_core mirage_render tomlplusplus::tomlplusplus) -add_resources(${PROJECT_NAME} src/style/default_style.toml) +add_resource_file( + TARGET_NAME ${PROJECT_NAME} + RESOURCE_FILES src/style/default_style.toml +)