初始化Mirai项目结构,包括核心组件和测试框架。
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/.idea
|
||||||
|
/cmake-build-*
|
||||||
27
CMakeLists.txt
Normal file
27
CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
|
||||||
|
# ================================================================================================
|
||||||
|
# vcpkg 工具链集成
|
||||||
|
# 必须在 project() 之前设置
|
||||||
|
# ================================================================================================
|
||||||
|
if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE)
|
||||||
|
set(CMAKE_TOOLCHAIN_FILE "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
|
||||||
|
CACHE STRING "Vcpkg toolchain file")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(mirai_project)
|
||||||
|
|
||||||
|
include(cmake/retrieve_files.cmake)
|
||||||
|
include(cmake/detect_os.cmake)
|
||||||
|
include(cmake/config_macos.cmake)
|
||||||
|
include(cmake/mirai_utils.cmake)
|
||||||
|
include(cmake/project_cpp_standard.cmake)
|
||||||
|
|
||||||
|
setup_project_options(
|
||||||
|
STANDARD 23
|
||||||
|
INTERFACE_TARGET mirai_project_options
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
add_subdirectory(example)
|
||||||
|
add_subdirectory(tests)
|
||||||
108
cmake/compiler_options.cmake
Normal file
108
cmake/compiler_options.cmake
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# ================================================================================================
|
||||||
|
# MIRAI Framework - 编译器选项配置模块
|
||||||
|
# 描述: 配置编译器特定选项、警告级别和优化设置
|
||||||
|
# ================================================================================================
|
||||||
|
|
||||||
|
# ================================================================================================
|
||||||
|
# 配置编译器选项
|
||||||
|
# ================================================================================================
|
||||||
|
function(configure_compiler_options)
|
||||||
|
# 使用生成器表达式为多配置生成器(如Visual Studio)正确配置选项
|
||||||
|
if(MSVC)
|
||||||
|
add_compile_options(
|
||||||
|
/wd5054 # 禁用 C5054: 枚举之间的 & 运算符弃用警告
|
||||||
|
/wd4324 # 禁用 C4324: 由于对齐说明符,结构被填充警告
|
||||||
|
)
|
||||||
|
|
||||||
|
# Debug配置特定设置
|
||||||
|
add_compile_options(
|
||||||
|
$<$<CONFIG:Debug>:/Zi> # 调试信息
|
||||||
|
$<$<CONFIG:Debug>:/Od> # 禁用优化
|
||||||
|
$<$<CONFIG:Debug>:/RTC1> # 运行时检查
|
||||||
|
$<$<CONFIG:Debug>:/W4> # 严格警告
|
||||||
|
)
|
||||||
|
add_compile_definitions(
|
||||||
|
$<$<CONFIG:Debug>:MIRAI_DEBUG_BUILD>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Release配置特定设置
|
||||||
|
add_compile_options(
|
||||||
|
$<$<CONFIG:Release>:/O2> # 最大优化
|
||||||
|
$<$<CONFIG:Release>:/Ob2> # 内联展开
|
||||||
|
$<$<CONFIG:Release>:/Zi> # 调试信息(用于调试Release版本)
|
||||||
|
)
|
||||||
|
add_compile_definitions(
|
||||||
|
$<$<CONFIG:Release>:MIRAI_RELEASE_BUILD>
|
||||||
|
$<$<CONFIG:Release>:NDEBUG>
|
||||||
|
)
|
||||||
|
|
||||||
|
# RelWithDebInfo配置
|
||||||
|
add_compile_options(
|
||||||
|
$<$<CONFIG:RelWithDebInfo>:/O2>
|
||||||
|
$<$<CONFIG:RelWithDebInfo>:/Ob1>
|
||||||
|
$<$<CONFIG:RelWithDebInfo>:/Zi>
|
||||||
|
)
|
||||||
|
add_compile_definitions(
|
||||||
|
$<$<CONFIG:RelWithDebInfo>:MIRAI_RELEASE_BUILD>
|
||||||
|
$<$<CONFIG:RelWithDebInfo>:NDEBUG>
|
||||||
|
)
|
||||||
|
|
||||||
|
# MinSizeRel配置
|
||||||
|
add_compile_options(
|
||||||
|
$<$<CONFIG:MinSizeRel>:/O1>
|
||||||
|
$<$<CONFIG:MinSizeRel>:/Ob1>
|
||||||
|
$<$<CONFIG:MinSizeRel>:/Zi>
|
||||||
|
)
|
||||||
|
add_compile_definitions(
|
||||||
|
$<$<CONFIG:MinSizeRel>:MIRAI_RELEASE_BUILD>
|
||||||
|
$<$<CONFIG:MinSizeRel>:NDEBUG>
|
||||||
|
)
|
||||||
|
|
||||||
|
add_compile_definitions(
|
||||||
|
_ENABLE_EXTENDED_ALIGNED_STORAGE
|
||||||
|
)
|
||||||
|
|
||||||
|
message(STATUS "MSVC编译器选项已配置,支持多配置生成器")
|
||||||
|
else()
|
||||||
|
# 非MSVC编译器的传统配置
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
add_compile_definitions(MIRAI_DEBUG_BUILD)
|
||||||
|
add_compile_options(-g -O0 -fno-omit-frame-pointer)
|
||||||
|
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||||
|
message(STATUS "Debug模式: 启用调试符号,禁用优化,启用严格警告")
|
||||||
|
else()
|
||||||
|
add_compile_definitions(MIRAI_RELEASE_BUILD NDEBUG)
|
||||||
|
add_compile_options(-O3 -DNDEBUG -march=native)
|
||||||
|
message(STATUS "Release模式: 启用最高级别优化")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 启用更好的诊断信息
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||||
|
add_compile_options(-fdiagnostics-color=always)
|
||||||
|
add_compile_options(-ftemplate-backtrace-limit=0)
|
||||||
|
message(STATUS "启用彩色诊断和完整模板回溯")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
add_compile_definitions(MIRAI_MSVC=1)
|
||||||
|
add_compile_definitions(MIRAI_GCC=0)
|
||||||
|
add_compile_definitions(MIRAI_CLANG=0)
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
add_compile_definitions(MIRAI_MSVC=0)
|
||||||
|
add_compile_definitions(MIRAI_GCC=1)
|
||||||
|
add_compile_definitions(MIRAI_CLANG=0)
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
add_compile_definitions(MIRAI_MSVC=0)
|
||||||
|
add_compile_definitions(MIRAI_GCC=0)
|
||||||
|
add_compile_definitions(MIRAI_CLANG=1)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ================================================================================================
|
||||||
|
# 应用编译器配置
|
||||||
|
# ================================================================================================
|
||||||
|
function(apply_compiler_configuration)
|
||||||
|
configure_compiler_options()
|
||||||
|
message(STATUS "编译器配置完成")
|
||||||
|
endfunction()
|
||||||
41
cmake/config_macos.cmake
Normal file
41
cmake/config_macos.cmake
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
# 如果是Macos
|
||||||
|
if (APPLE)
|
||||||
|
# 获取 Homebrew 安装的 libomp 路径
|
||||||
|
execute_process(
|
||||||
|
COMMAND brew --prefix libomp
|
||||||
|
OUTPUT_VARIABLE HOMEBREW_LIBOMP_PATH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置 OpenMP 路径变量
|
||||||
|
set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${HOMEBREW_LIBOMP_PATH}/include")
|
||||||
|
set(OpenMP_CXX_LIB_NAMES "omp")
|
||||||
|
set(OpenMP_omp_LIBRARY "${HOMEBREW_LIBOMP_PATH}/lib/libomp.dylib")
|
||||||
|
enable_language(OBJC OBJCXX)
|
||||||
|
|
||||||
|
# ============================================================================================
|
||||||
|
# Apple Accelerate框架支持 (包含AMX向量指令集)
|
||||||
|
# ============================================================================================
|
||||||
|
# 检测是否为Apple Silicon (ARM64)
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
|
||||||
|
message(STATUS "Detected Apple Silicon (${CMAKE_SYSTEM_PROCESSOR})")
|
||||||
|
|
||||||
|
# 查找Accelerate框架(macOS系统自带)
|
||||||
|
find_library(ACCELERATE_FRAMEWORK Accelerate)
|
||||||
|
if(ACCELERATE_FRAMEWORK)
|
||||||
|
message(STATUS "Found Accelerate framework: ${ACCELERATE_FRAMEWORK}")
|
||||||
|
# 设置全局变量供其他模块使用
|
||||||
|
set(APPLE_ACCELERATE_LIBRARY ${ACCELERATE_FRAMEWORK} PARENT_SCOPE)
|
||||||
|
set(HAS_ACCELERATE TRUE PARENT_SCOPE)
|
||||||
|
# 启用Accelerate编译选项
|
||||||
|
add_compile_definitions(MIRAI_ENABLE_ACCELERATE)
|
||||||
|
else()
|
||||||
|
message(WARNING "Accelerate framework not found on macOS")
|
||||||
|
set(HAS_ACCELERATE FALSE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(STATUS "Not Apple Silicon, Accelerate framework disabled")
|
||||||
|
set(HAS_ACCELERATE FALSE PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
205
cmake/detect_os.cmake
Normal file
205
cmake/detect_os.cmake
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
# =============================================================================
|
||||||
|
# 顶层平台检测变量
|
||||||
|
# 这些变量可在整个项目中使用,用于条件编译和依赖选择
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# 桌面平台检测
|
||||||
|
set(MIRAI_DESKTOP_PLATFORM OFF)
|
||||||
|
if(WIN32 OR (UNIX AND NOT ANDROID AND NOT IOS))
|
||||||
|
set(MIRAI_DESKTOP_PLATFORM ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 移动平台检测
|
||||||
|
set(MIRAI_MOBILE_PLATFORM OFF)
|
||||||
|
if(ANDROID OR IOS)
|
||||||
|
set(MIRAI_MOBILE_PLATFORM ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 各平台标志
|
||||||
|
set(MIRAI_PLATFORM_WINDOWS OFF)
|
||||||
|
set(MIRAI_PLATFORM_LINUX OFF)
|
||||||
|
set(MIRAI_PLATFORM_MACOS OFF)
|
||||||
|
set(MIRAI_PLATFORM_ANDROID OFF)
|
||||||
|
set(MIRAI_PLATFORM_IOS OFF)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
set(MIRAI_PLATFORM_WINDOWS ON)
|
||||||
|
elseif(ANDROID)
|
||||||
|
set(MIRAI_PLATFORM_ANDROID ON)
|
||||||
|
elseif(IOS)
|
||||||
|
set(MIRAI_PLATFORM_IOS ON)
|
||||||
|
elseif(APPLE)
|
||||||
|
set(MIRAI_PLATFORM_MACOS ON)
|
||||||
|
elseif(UNIX)
|
||||||
|
set(MIRAI_PLATFORM_LINUX ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# 平台定义函数(为目标添加详细的平台宏)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# 定义一个函数,为指定的目标添加操作系统和架构相关的预处理器定义
|
||||||
|
function(add_os_definitions target)
|
||||||
|
# 检查 target 参数是否提供
|
||||||
|
if(NOT target)
|
||||||
|
message(FATAL_ERROR "函数 add_os_definitions 需要一个 target 参数。")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- 阶段 1: 确定宏的值 ---
|
||||||
|
|
||||||
|
# 初始化所有平台、架构和特性宏的值为 0
|
||||||
|
set(mirai_def_windows 0)
|
||||||
|
set(mirai_def_macos 0)
|
||||||
|
set(mirai_def_linux 0)
|
||||||
|
set(mirai_def_freebsd 0)
|
||||||
|
set(mirai_def_ios 0)
|
||||||
|
set(mirai_def_android 0)
|
||||||
|
set(mirai_def_cygwin 0)
|
||||||
|
set(mirai_def_unix 0)
|
||||||
|
set(mirai_def_posix 0)
|
||||||
|
set(mirai_def_mobile 0)
|
||||||
|
set(mirai_def_arch_64bit 0)
|
||||||
|
set(mirai_def_arch_32bit 0)
|
||||||
|
set(mirai_def_x86 0)
|
||||||
|
set(mirai_def_arm 0)
|
||||||
|
set(mirai_def_riscv 0)
|
||||||
|
set(mirai_def_apple 0) # 用于 iOS 和 macOS 的通用定义
|
||||||
|
set(mirai_def_debug 0) # 当前是否处于调试模式
|
||||||
|
|
||||||
|
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
set(mirai_def_debug 1)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# -- 操作系统检测与赋值 --
|
||||||
|
# 注意检测顺序:优先检测更具体的平台
|
||||||
|
if(CYGWIN)
|
||||||
|
# Cygwin 环境比较特殊,它在 Windows 上模拟 Unix
|
||||||
|
set(mirai_def_windows 1) # 基础是 Windows
|
||||||
|
set(mirai_def_cygwin 1) # 明确是 Cygwin
|
||||||
|
set(mirai_def_unix 1) # 提供 Unix API
|
||||||
|
set(mirai_def_posix 1) # 提供 POSIX API
|
||||||
|
# message(STATUS "检测到 **Cygwin** 环境 (运行于 Windows)")
|
||||||
|
elseif(WIN32)
|
||||||
|
# 非 Cygwin 的 Windows 环境 (MSVC, MinGW, etc.)
|
||||||
|
set(mirai_def_windows 1)
|
||||||
|
# message(STATUS "检测到 **Windows** 操作系统 (非 Cygwin)")
|
||||||
|
elseif(ANDROID)
|
||||||
|
# Android 平台 (通常需要特定工具链设置 ANDROID 变量)
|
||||||
|
set(mirai_def_android 1)
|
||||||
|
set(mirai_def_unix 1) # Android NDK 基于 Unix
|
||||||
|
set(mirai_def_posix 1) # NDK 提供 POSIX API
|
||||||
|
set(mirai_def_mobile 1) # 移动平台
|
||||||
|
# message(STATUS "检测到 **Android** 操作系统")
|
||||||
|
elseif(IOS)
|
||||||
|
# iOS 平台 (通常需要特定工具链设置 IOS 变量)
|
||||||
|
# 需要在 APPLE 之前判断,因为 iOS 下 APPLE 也为 TRUE
|
||||||
|
set(mirai_def_ios 1)
|
||||||
|
set(mirai_def_unix 1) # iOS (Darwin) 基于 Unix
|
||||||
|
set(mirai_def_posix 1) # 提供 POSIX API
|
||||||
|
set(mirai_def_mobile 1) # 移动平台
|
||||||
|
set(mirai_def_apple 1) # iOS 是 Apple 生态的一部分
|
||||||
|
# message(STATUS "检测到 **iOS** 操作系统")
|
||||||
|
elseif(APPLE)
|
||||||
|
# 此时排除了 iOS,确定是 macOS
|
||||||
|
set(mirai_def_macos 1)
|
||||||
|
set(mirai_def_unix 1) # macOS (Darwin) 基于 Unix
|
||||||
|
set(mirai_def_posix 1) # 提供 POSIX API
|
||||||
|
set(mirai_def_apple 1) # macOS 是 Apple 生态的一部分
|
||||||
|
message(STATUS "检测到 **macOS** 操作系统")
|
||||||
|
elseif(UNIX)
|
||||||
|
# 此时排除了 Apple, Android, Cygwin 的其他 Unix-like 系统
|
||||||
|
set(mirai_def_unix 1)
|
||||||
|
set(mirai_def_posix 1)
|
||||||
|
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||||
|
set(mirai_def_linux 1)
|
||||||
|
# message(STATUS "检测到 **Linux** 操作系统")
|
||||||
|
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||||
|
set(mirai_def_freebsd 1)
|
||||||
|
# message(STATUS "检测到 **FreeBSD** 操作系统")
|
||||||
|
else()
|
||||||
|
message(WARNING "检测到未知的 类Unix 操作系统: ${CMAKE_SYSTEM_NAME}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "检测到未知的操作系统: ${CMAKE_SYSTEM_NAME}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# -- 架构检测与赋值 --
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(mirai_def_arch_64bit 1)
|
||||||
|
set(mirai_def_arch_32bit 0) # 明确设置为 0
|
||||||
|
# message(STATUS "检测到 **64-bit** 架构")
|
||||||
|
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
set(mirai_def_arch_64bit 0) # 明确设置为 0
|
||||||
|
set(mirai_def_arch_32bit 1)
|
||||||
|
# message(STATUS "检测到 **32-bit** 架构")
|
||||||
|
else()
|
||||||
|
# 对于未知或未定义的指针大小,两者都保持 0
|
||||||
|
message(WARNING "无法明确检测到 32-bit 或 64-bit 架构 (CMAKE_SIZEOF_VOID_P = ${CMAKE_SIZEOF_VOID_P})。将两者都设置为 0。")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 检测特定架构类型
|
||||||
|
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86|i386|i486|i586|i686|i786|x86_64|AMD64")
|
||||||
|
set(mirai_def_x86 1)
|
||||||
|
# message(STATUS "检测到 **x86/x64** 架构")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|aarch64|ARM64")
|
||||||
|
set(mirai_def_arm 1)
|
||||||
|
# message(STATUS "检测到 **ARM** 架构")
|
||||||
|
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "riscv|riscv64|riscv32")
|
||||||
|
set(mirai_def_riscv 1)
|
||||||
|
# message(STATUS "检测到 **RISC-V** 架构")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- 阶段 2: 组装定义列表 ---
|
||||||
|
set(definitions_list "") # 初始化空列表
|
||||||
|
|
||||||
|
# 添加平台定义
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_WINDOWS=${mirai_def_windows}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_MACOS=${mirai_def_macos}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_LINUX=${mirai_def_linux}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_FREEBSD=${mirai_def_freebsd}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_IOS=${mirai_def_ios}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_ANDROID=${mirai_def_android}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_CYGWIN=${mirai_def_cygwin}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_APPLE=${mirai_def_apple}") # 用于 iOS 和 macOS 的通用定义
|
||||||
|
|
||||||
|
# 添加架构定义
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_ARCH_64BIT=${mirai_def_arch_64bit}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_ARCH_32BIT=${mirai_def_arch_32bit}")
|
||||||
|
|
||||||
|
# 添加特性定义
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_UNIX=${mirai_def_unix}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_POSIX=${mirai_def_posix}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_IS_MOBILE=${mirai_def_mobile}")
|
||||||
|
|
||||||
|
list(APPEND definitions_list "MIRAI_DEBUG=${mirai_def_debug}") # 当前是否处于调试模式
|
||||||
|
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_X86=${mirai_def_x86}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_ARM=${mirai_def_arm}")
|
||||||
|
list(APPEND definitions_list "MIRAI_PLATFORM_RISCV=${mirai_def_riscv}")
|
||||||
|
|
||||||
|
# --- 阶段 3: 应用所有定义 ---
|
||||||
|
# **关键:使用一次调用将所有定义添加到目标**
|
||||||
|
if(definitions_list) # 确保列表非空
|
||||||
|
get_target_property(target_type ${target} TYPE)
|
||||||
|
if(target_type STREQUAL "INTERFACE_LIBRARY")
|
||||||
|
target_compile_definitions(${target} INTERFACE ${definitions_list})
|
||||||
|
else()
|
||||||
|
target_compile_definitions(${target} PUBLIC ${definitions_list})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 函数作用域结束时,mirai_def_* 变量会自动销毁,无需显式 unset
|
||||||
|
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# --- 使用示例 ---
|
||||||
|
# project(MyProject)
|
||||||
|
# add_executable(my_app main.c)
|
||||||
|
#
|
||||||
|
# # 调用函数为 my_app 添加平台定义
|
||||||
|
# add_os_definitions(my_app)
|
||||||
|
#
|
||||||
|
# # 你也可以为库调用
|
||||||
|
# # add_library(my_lib STATIC my_lib.c)
|
||||||
|
# # add_os_definitions(my_lib)
|
||||||
41
cmake/mingw_dll.cmake
Normal file
41
cmake/mingw_dll.cmake
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
function(auto_copy_mingw_dll target)
|
||||||
|
if(MINGW)
|
||||||
|
# 获取MinGW目录
|
||||||
|
get_filename_component(MINGW_DIR ${CMAKE_CXX_COMPILER} PATH)
|
||||||
|
|
||||||
|
# 根据你的环境调整DLL列表
|
||||||
|
set(MINGW_DLLS
|
||||||
|
"libstdc++-6.dll"
|
||||||
|
# "libgcc_s_dw2-1.dll"
|
||||||
|
"libgcc_s_seh-1.dll"
|
||||||
|
"libwinpthread-1.dll"
|
||||||
|
"libbz2-1.dll"
|
||||||
|
"libbrotlidec.dll"
|
||||||
|
"libbrotlicommon.dll"
|
||||||
|
"libharfbuzz-0.dll"
|
||||||
|
"libpng16-16.dll"
|
||||||
|
"zlib1.dll"
|
||||||
|
"libglib-2.0-0.dll"
|
||||||
|
"libfreetype-6.dll"
|
||||||
|
"libgraphite2.dll"
|
||||||
|
"libintl-8.dll"
|
||||||
|
"libpcre2-8-0.dll"
|
||||||
|
"libiconv-2.dll"
|
||||||
|
)
|
||||||
|
|
||||||
|
foreach(DLL ${MINGW_DLLS})
|
||||||
|
# 检查文件是否存在
|
||||||
|
if(EXISTS "${MINGW_DIR}/${DLL}")
|
||||||
|
add_custom_command(TARGET ${target} POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
"${MINGW_DIR}/${DLL}"
|
||||||
|
"$<TARGET_FILE_DIR:${target}>"
|
||||||
|
COMMENT "Copying ${DLL} to output directory"
|
||||||
|
VERBATIM)
|
||||||
|
else()
|
||||||
|
message(WARNING "DLL not found: ${MINGW_DIR}/${DLL}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
44
cmake/mirai_utils.cmake
Normal file
44
cmake/mirai_utils.cmake
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
# 定义一个函数来配置项目的默认设置
|
||||||
|
# 这包括设置输出目录和项目根目录变量
|
||||||
|
function(configure_project_defaults)
|
||||||
|
# 检查是否在顶层 CMakeLists.txt 中调用 (可选但推荐)
|
||||||
|
# 确保 CMAKE_SOURCE_DIR 和 CMAKE_CURRENT_SOURCE_DIR 相同
|
||||||
|
if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||||
|
message(WARNING "configure_project_defaults() 应该在项目的根 CMakeLists.txt 中调用。")
|
||||||
|
# 如果您确实需要在子目录中设置不同的根目录,请调整此逻辑
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- 配置输出目录 ---
|
||||||
|
# 使用 CMAKE_BINARY_DIR 作为基础构建目录
|
||||||
|
# ${CMAKE_BINARY_DIR} 指向您配置 CMake 时指定的构建目录
|
||||||
|
# 例如,在 CLion 中通常是 cmake-build-debug 或 cmake-build-release
|
||||||
|
# 如果手动运行 cmake ..,它就是您运行 cmake 命令的目录
|
||||||
|
|
||||||
|
# **设置可执行文件输出路径**:
|
||||||
|
# 对于单配置生成器 (如 Makefiles, Ninja), 可执行文件将位于 <build>/bin/
|
||||||
|
# 对于多配置生成器 (如 Visual Studio, Xcode), CMake 通常会自动在此路径下附加配置名称
|
||||||
|
# (例如 <build>/bin/Debug/, <build>/bin/Release/)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin CACHE PATH "Directory for runtime executables")
|
||||||
|
message(STATUS "运行时输出目录设置为: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
|
||||||
|
|
||||||
|
# **设置库文件输出路径 (共享库和静态库)**:
|
||||||
|
# 规则同上,库文件将位于 <build>/lib/ 或 <build>/lib/<Config>/
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib CACHE PATH "Directory for shared libraries")
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib CACHE PATH "Directory for static libraries")
|
||||||
|
message(STATUS "库输出目录设置为: ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
|
||||||
|
message(STATUS "存档输出目录设置为: ${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}")
|
||||||
|
|
||||||
|
# --- 提示 ---
|
||||||
|
# 这种全局设置输出目录的方法对于中小型项目是常见的。
|
||||||
|
# 对于更复杂的项目或需要更细粒度控制的情况,可以考虑为每个目标(target)单独设置输出目录属性:
|
||||||
|
# 例如:
|
||||||
|
# add_executable(my_app main.cpp)
|
||||||
|
# set_target_properties(my_app PROPERTIES
|
||||||
|
# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/executables"
|
||||||
|
# )
|
||||||
|
# add_library(my_lib STATIC my_lib.cpp)
|
||||||
|
# set_target_properties(my_lib PROPERTIES
|
||||||
|
# ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/static_libs"
|
||||||
|
# )
|
||||||
|
endfunction()
|
||||||
118
cmake/project_cpp_standard.cmake
Normal file
118
cmake/project_cpp_standard.cmake
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# 文件: cmake/CompilerSetup.cmake
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
# 函数:setup_project_options
|
||||||
|
# 描述:配置项目级的 C++ 标准、编译器警告、定义和依赖。
|
||||||
|
# 此函数遵循现代 CMake 实践,将所有配置封装到一个 INTERFACE 库中。
|
||||||
|
#
|
||||||
|
# 参数:
|
||||||
|
# standard - (必选) C++ 标准版本 (例如 17, 20, 23)。
|
||||||
|
# INTERFACE_TARGET - (必选) 用于接收创建的 INTERFACE 库名称的变量名。
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# include(cmake/CompilerSetup.cmake)
|
||||||
|
# setup_project_options(
|
||||||
|
# STANDARD 20
|
||||||
|
# INTERFACE_TARGET my_project_options
|
||||||
|
# )
|
||||||
|
# # ... 定义你的可执行文件或库
|
||||||
|
# add_executable(my_app main.cpp)
|
||||||
|
# # ... 将配置应用到目标上
|
||||||
|
# target_link_libraries(my_app PRIVATE ${my_project_options})
|
||||||
|
# ==============================================================================
|
||||||
|
function(setup_project_options)
|
||||||
|
# --- 参数解析 ---
|
||||||
|
set(options "") # 无单值选项
|
||||||
|
set(oneValueArgs STANDARD INTERFACE_TARGET) # 定义接收单个值的参数
|
||||||
|
set(multiValueArgs "") # 无多值选项
|
||||||
|
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
# --- 参数验证 ---
|
||||||
|
if(NOT ARG_STANDARD OR NOT ARG_INTERFACE_TARGET)
|
||||||
|
message(FATAL_ERROR "setup_project_options 必须提供 STANDARD 和 INTERFACE_TARGET 参数。")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(VALID_STANDARDS 11 14 17 20 23)
|
||||||
|
list(FIND VALID_STANDARDS ${ARG_STANDARD} _standard_index)
|
||||||
|
if(_standard_index EQUAL -1)
|
||||||
|
message(FATAL_ERROR "不支持的 C++ 标准: ${ARG_STANDARD}。有效值: ${VALID_STANDARDS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- 创建 INTERFACE 库 ---
|
||||||
|
# 这是现代 CMake 的核心:创建一个虚拟目标来承载所有配置属性。
|
||||||
|
add_library(${ARG_INTERFACE_TARGET} INTERFACE)
|
||||||
|
message(STATUS "创建配置接口库: ${ARG_INTERFACE_TARGET}")
|
||||||
|
|
||||||
|
# --- 设置 C++ 标准 (应用到接口库) ---
|
||||||
|
target_compile_features(${ARG_INTERFACE_TARGET} INTERFACE cxx_std_${ARG_STANDARD})
|
||||||
|
|
||||||
|
# --- 设置通用编译定义和选项 ---
|
||||||
|
# 使用 target_compile_definitions 和 target_compile_options,并指定 INTERFACE
|
||||||
|
# 这样任何链接到此库的目标都会继承这些属性。
|
||||||
|
|
||||||
|
# --- 平台特定设置 ---
|
||||||
|
if(WIN32)
|
||||||
|
target_compile_definitions(${ARG_INTERFACE_TARGET} INTERFACE UNICODE _UNICODE)
|
||||||
|
message(STATUS "为 Windows 添加 UNICODE 定义")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- 编译器特定设置 ---
|
||||||
|
if(MSVC)
|
||||||
|
# MSVC 特定选项
|
||||||
|
target_compile_options(${ARG_INTERFACE_TARGET} INTERFACE
|
||||||
|
/W4 # 更高警告等级
|
||||||
|
# /WX # 将警告视为错误 (可选,但推荐)
|
||||||
|
/EHsc
|
||||||
|
/utf-8 # 源码和执行字符集设为 UTF-8
|
||||||
|
/Zc:__cplusplus # 修正 __cplusplus 宏
|
||||||
|
/wd4100 # 禁用警告: 未使用的形参
|
||||||
|
/wd4996 # 禁用警告: 使用了被标记为否决的函数
|
||||||
|
)
|
||||||
|
message(STATUS "为 MSVC 添加特定编译选项")
|
||||||
|
else() # GCC / Clang / AppleClang
|
||||||
|
# 通用于 GCC 和 Clang 的选项
|
||||||
|
target_compile_options(${ARG_INTERFACE_TARGET} INTERFACE
|
||||||
|
-Wall
|
||||||
|
-Wextra
|
||||||
|
-Wpedantic # 更加严格的警告
|
||||||
|
-Werror # 将所有警告视为错误 (可选,但推荐)
|
||||||
|
-Wno-unused-parameter
|
||||||
|
)
|
||||||
|
|
||||||
|
# C++17 及以上标准的额外警告
|
||||||
|
if(${ARG_STANDARD} GREATER_EQUAL 17)
|
||||||
|
target_compile_options(${ARG_INTERFACE_TARGET} INTERFACE
|
||||||
|
-Wshadow
|
||||||
|
-Wnon-virtual-dtor
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 【核心修复】区分处理 AppleClang 和标准 Clang/GCC
|
||||||
|
# AppleClang 不支持 -finput-charset/-fexec-charset,并默认源码为 UTF-8
|
||||||
|
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "AppleClang")
|
||||||
|
target_compile_options(${ARG_INTERFACE_TARGET} INTERFACE
|
||||||
|
-finput-charset=UTF-8
|
||||||
|
-fexec-charset=UTF-8
|
||||||
|
)
|
||||||
|
message(STATUS "为 GCC/Clang 添加 UTF-8 字符集选项")
|
||||||
|
else()
|
||||||
|
message(STATUS "检测到 AppleClang,源码假定为 UTF-8,跳过字符集选项")
|
||||||
|
endif()
|
||||||
|
message(STATUS "为 GCC/Clang 添加特定编译选项")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- MinGW 特定设置 ---
|
||||||
|
if(MINGW)
|
||||||
|
# 为 C++17 及以上的 <filesystem> 支持添加链接库
|
||||||
|
if(${ARG_STANDARD} GREATER_EQUAL 17)
|
||||||
|
# 使用 target_link_libraries,这才是正确的方式
|
||||||
|
target_link_libraries(${ARG_INTERFACE_TARGET} INTERFACE -lstdc++fs)
|
||||||
|
message(STATUS "为 MinGW C++${ARG_STANDARD} 添加 libstdc++fs 依赖 (用于 <filesystem>)")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# --- 将 INTERFACE 库的名称返回给调用者 ---
|
||||||
|
set(${ARG_INTERFACE_TARGET} ${ARG_INTERFACE_TARGET} PARENT_SCOPE)
|
||||||
|
message(STATUS "C++${ARG_STANDARD} 项目配置完成,请链接到 ${ARG_INTERFACE_TARGET} 目标。")
|
||||||
|
|
||||||
|
endfunction()
|
||||||
617
cmake/retrieve_files.cmake
Normal file
617
cmake/retrieve_files.cmake
Normal file
@@ -0,0 +1,617 @@
|
|||||||
|
#[=======================================================================[
|
||||||
|
平台匹配检查函数
|
||||||
|
参数:
|
||||||
|
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 OR CYGWIN)
|
||||||
|
set(matches TRUE)
|
||||||
|
endif()
|
||||||
|
elseif(platform STREQUAL "linux")
|
||||||
|
if(UNIX AND NOT APPLE)
|
||||||
|
set(matches TRUE)
|
||||||
|
endif()
|
||||||
|
elseif(platform STREQUAL "macos")
|
||||||
|
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()
|
||||||
|
elseif(platform STREQUAL "web")
|
||||||
|
if(EMSCRIPTEN)
|
||||||
|
set(matches TRUE)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# 未知平台标识,默认匹配
|
||||||
|
set(matches TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(${is_match} ${matches} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#[=======================================================================[
|
||||||
|
主文件检索函数
|
||||||
|
参数:
|
||||||
|
path: 要检索的根目录路径
|
||||||
|
extension: 文件扩展名列表
|
||||||
|
out_files: 输出变量名,将包含筛选后的文件列表
|
||||||
|
out_cmake_files: (可选) 输出变量名,将包含找到的 .cmake 文件列表
|
||||||
|
#]=======================================================================]
|
||||||
|
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 ${path}
|
||||||
|
CONFIGURE_DEPENDS ${file_patterns}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4. 处理找到的文件
|
||||||
|
set(filtered_files "")
|
||||||
|
set(cmake_files_to_include "")
|
||||||
|
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|macos|ios|android|unix|mobile|desktop|web)$")
|
||||||
|
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.3.1 自动 include .cmake 文件
|
||||||
|
get_filename_component(file_extension "${current_file}" LAST_EXT)
|
||||||
|
if(file_extension STREQUAL ".cmake")
|
||||||
|
get_filename_component(cmake_file_abs "${current_file}" ABSOLUTE)
|
||||||
|
list(APPEND cmake_files_to_include "${cmake_file_abs}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
# 将找到的 .cmake 文件传递给父作用域
|
||||||
|
set(RETRIEVED_CMAKE_FILES ${cmake_files_to_include} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#[=======================================================================[
|
||||||
|
便捷封装函数
|
||||||
|
自动处理常见文件扩展名,针对不同平台添加特定文件类型
|
||||||
|
#]=======================================================================]
|
||||||
|
function(retrieve_files path out_files)
|
||||||
|
# 设置基础文件类型
|
||||||
|
set(file_extensions
|
||||||
|
"h" # 头文件
|
||||||
|
"hpp" # C++头文件
|
||||||
|
"ini" # 配置文件
|
||||||
|
"cpp" # C++源文件
|
||||||
|
"c" # C源文件
|
||||||
|
"cc"
|
||||||
|
"ixx" # C++20模块文件
|
||||||
|
"cmake" # CMake脚本文件
|
||||||
|
)
|
||||||
|
|
||||||
|
# 针对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)
|
||||||
|
# 将 .cmake 文件列表传递到父作用域
|
||||||
|
set(RETRIEVED_CMAKE_FILES ${RETRIEVED_CMAKE_FILES} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
#[=======================================================================[
|
||||||
|
Proto文件编译和gRPC绑定生成函数
|
||||||
|
参数:
|
||||||
|
TARGET_NAME: (必需) - 要创建的库目标名称
|
||||||
|
PROTO_PATH: (必需) - 包含.proto文件的目录路径
|
||||||
|
OUTPUT_PATH: (可选) - 生成文件的输出目录,默认为${CMAKE_CURRENT_BINARY_DIR}/generated
|
||||||
|
GRPC_ENABLED: (可选) - 是否生成gRPC绑定,默认为TRUE
|
||||||
|
PROTO_IMPORT_DIRS:(可选) - proto文件的额外导入目录列表
|
||||||
|
EXPORT_MACRO: (可选) - 导出宏定义(用于Windows DLL导出)
|
||||||
|
|
||||||
|
例子:
|
||||||
|
compile_proto_files(
|
||||||
|
TARGET_NAME my_proto_lib
|
||||||
|
PROTO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/protos
|
||||||
|
OUTPUT_PATH ${CMAKE_BINARY_DIR}/proto_gen
|
||||||
|
GRPC_ENABLED TRUE
|
||||||
|
PROTO_IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/third_party/protos
|
||||||
|
)
|
||||||
|
#]=======================================================================]
|
||||||
|
function(compile_proto_files)
|
||||||
|
# 定义预期的参数
|
||||||
|
set(options GRPC_ENABLED)
|
||||||
|
set(oneValueArgs TARGET_NAME PROTO_PATH OUTPUT_PATH EXPORT_MACRO)
|
||||||
|
set(multiValueArgs PROTO_IMPORT_DIRS)
|
||||||
|
|
||||||
|
# 解析传递给函数的参数
|
||||||
|
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
# 参数验证
|
||||||
|
if(NOT ARG_TARGET_NAME)
|
||||||
|
message(FATAL_ERROR "**compile_proto_files**: **缺少必需参数** **TARGET_NAME**.")
|
||||||
|
endif()
|
||||||
|
if(NOT ARG_PROTO_PATH)
|
||||||
|
message(FATAL_ERROR "**compile_proto_files**: **缺少必需参数** **PROTO_PATH**.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 设置默认值
|
||||||
|
if(NOT DEFINED ARG_GRPC_ENABLED)
|
||||||
|
set(ARG_GRPC_ENABLED TRUE)
|
||||||
|
endif()
|
||||||
|
if(NOT ARG_OUTPUT_PATH)
|
||||||
|
set(ARG_OUTPUT_PATH "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 查找Protobuf和gRPC
|
||||||
|
find_package(Protobuf CONFIG REQUIRED)
|
||||||
|
if(ARG_GRPC_ENABLED)
|
||||||
|
find_package(gRPC QUIET)
|
||||||
|
if(NOT gRPC_FOUND)
|
||||||
|
# 如果找不到gRPC包,尝试手动查找
|
||||||
|
find_program(GRPC_CPP_PLUGIN grpc_cpp_plugin)
|
||||||
|
if(NOT GRPC_CPP_PLUGIN)
|
||||||
|
message(FATAL_ERROR "**compile_proto_files**: **找不到gRPC C++插件**. 请确保已安装gRPC.")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(GRPC_CPP_PLUGIN $<TARGET_FILE:gRPC::grpc_cpp_plugin>)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 创建输出目录
|
||||||
|
file(MAKE_DIRECTORY ${ARG_OUTPUT_PATH})
|
||||||
|
|
||||||
|
get_filename_component(PROTO_PATH "${ARG_PROTO_PATH}" ABSOLUTE)
|
||||||
|
|
||||||
|
# 递归查找所有.proto文件
|
||||||
|
file(GLOB_RECURSE PROTO_FILES
|
||||||
|
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
CONFIGURE_DEPENDS
|
||||||
|
"${PROTO_PATH}/*.proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT PROTO_FILES)
|
||||||
|
message(WARNING "**compile_proto_files**: 在 '${PROTO_PATH}' 中未找到任何.proto文件")
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "找到 ${CMAKE_CURRENT_SOURCE_DIR} Proto文件: ${PROTO_FILES}")
|
||||||
|
|
||||||
|
# 准备生成的文件列表
|
||||||
|
set(PROTO_SRCS)
|
||||||
|
set(PROTO_HDRS)
|
||||||
|
set(GRPC_SRCS)
|
||||||
|
set(GRPC_HDRS)
|
||||||
|
|
||||||
|
# 构建导入路径参数
|
||||||
|
set(PROTO_IMPORT_ARGS)
|
||||||
|
list(APPEND PROTO_IMPORT_ARGS "-I${PROTO_PATH}")
|
||||||
|
foreach(IMPORT_DIR ${ARG_PROTO_IMPORT_DIRS})
|
||||||
|
list(APPEND PROTO_IMPORT_ARGS "-I${IMPORT_DIR}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 添加一个自定义命令用于生成前清空目标文件夹中的pb.cc和pb.h文件
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${ARG_OUTPUT_PATH}/.cleaned
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E rm -f ${ARG_OUTPUT_PATH}/*.pb.cc ${ARG_OUTPUT_PATH}/*.pb.h ${ARG_OUTPUT_PATH}/*.grpc.pb.cc ${ARG_OUTPUT_PATH}/*.grpc.pb.h
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E touch ${ARG_OUTPUT_PATH}/.cleaned
|
||||||
|
COMMENT "清理旧的生成文件: ${ARG_OUTPUT_PATH}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
# 为每个proto文件生成代码
|
||||||
|
foreach(PROTO_FILE ${PROTO_FILES})
|
||||||
|
# 获取proto文件的绝对路径
|
||||||
|
get_filename_component(PROTO_FILE_ABS "${PROTO_FILE}" ABSOLUTE BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
get_filename_component(PROTO_NAME_WE "${PROTO_FILE}" NAME_WE)
|
||||||
|
get_filename_component(PROTO_DIR "${PROTO_FILE}" DIRECTORY)
|
||||||
|
|
||||||
|
# 计算相对路径以保持目录结构
|
||||||
|
if(PROTO_DIR)
|
||||||
|
file(RELATIVE_PATH REL_DIR "${PROTO_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}/${PROTO_DIR}")
|
||||||
|
set(OUTPUT_SUBDIR "${ARG_OUTPUT_PATH}/${REL_DIR}")
|
||||||
|
else()
|
||||||
|
set(OUTPUT_SUBDIR "${ARG_OUTPUT_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 创建输出子目录
|
||||||
|
file(MAKE_DIRECTORY ${OUTPUT_SUBDIR})
|
||||||
|
|
||||||
|
# 生成的文件路径
|
||||||
|
set(PROTO_SRC "${OUTPUT_SUBDIR}/${PROTO_NAME_WE}.pb.cc")
|
||||||
|
set(PROTO_HDR "${OUTPUT_SUBDIR}/${PROTO_NAME_WE}.pb.h")
|
||||||
|
list(APPEND PROTO_SRCS ${PROTO_SRC})
|
||||||
|
list(APPEND PROTO_HDRS ${PROTO_HDR})
|
||||||
|
|
||||||
|
# 基础protobuf生成命令
|
||||||
|
set(PROTOC_ARGS
|
||||||
|
${PROTO_IMPORT_ARGS}
|
||||||
|
"--cpp_out=${ARG_OUTPUT_PATH}"
|
||||||
|
"${PROTO_FILE_ABS}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ARG_GRPC_ENABLED)
|
||||||
|
set(GRPC_SRC "${OUTPUT_SUBDIR}/${PROTO_NAME_WE}.grpc.pb.cc")
|
||||||
|
set(GRPC_HDR "${OUTPUT_SUBDIR}/${PROTO_NAME_WE}.grpc.pb.h")
|
||||||
|
list(APPEND GRPC_SRCS ${GRPC_SRC})
|
||||||
|
list(APPEND GRPC_HDRS ${GRPC_HDR})
|
||||||
|
|
||||||
|
# 添加自定义命令生成protobuf和gRPC代码
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${PROTO_SRC} ${PROTO_HDR} ${GRPC_SRC} ${GRPC_HDR}
|
||||||
|
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
|
||||||
|
${PROTOC_ARGS}
|
||||||
|
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
|
||||||
|
${PROTO_IMPORT_ARGS}
|
||||||
|
"--grpc_out=${ARG_OUTPUT_PATH}"
|
||||||
|
"--plugin=protoc-gen-grpc=${GRPC_CPP_PLUGIN}"
|
||||||
|
"${PROTO_FILE_ABS}"
|
||||||
|
DEPENDS ${PROTO_FILE_ABS}
|
||||||
|
COMMENT "生成Protobuf和gRPC代码: ${PROTO_FILE}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# 只生成protobuf代码
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${PROTO_SRC} ${PROTO_HDR}
|
||||||
|
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
|
||||||
|
${PROTOC_ARGS}
|
||||||
|
DEPENDS ${PROTO_FILE_ABS}
|
||||||
|
COMMENT "生成Protobuf代码: ${PROTO_FILE}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 创建库目标
|
||||||
|
add_library(${ARG_TARGET_NAME} STATIC
|
||||||
|
${PROTO_SRCS}
|
||||||
|
${PROTO_HDRS}
|
||||||
|
${GRPC_SRCS}
|
||||||
|
${GRPC_HDRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置包含目录
|
||||||
|
target_include_directories(${ARG_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${ARG_OUTPUT_PATH}>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
|
||||||
|
# 链接必要的库
|
||||||
|
target_link_libraries(${ARG_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
protobuf::libprotobuf
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ARG_GRPC_ENABLED)
|
||||||
|
if(gRPC_FOUND)
|
||||||
|
target_link_libraries(${ARG_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
gRPC::grpc++
|
||||||
|
gRPC::grpc++_reflection
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# 手动查找并链接gRPC库
|
||||||
|
find_library(GRPC_LIBRARY grpc++)
|
||||||
|
find_library(GRPC_REFLECTION_LIBRARY grpc++_reflection)
|
||||||
|
if(GRPC_LIBRARY AND GRPC_REFLECTION_LIBRARY)
|
||||||
|
target_link_libraries(${ARG_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
${GRPC_LIBRARY}
|
||||||
|
${GRPC_REFLECTION_LIBRARY}
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
message(WARNING "**compile_proto_files**: 无法找到gRPC库,请手动链接")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 设置导出宏(如果提供)
|
||||||
|
if(ARG_EXPORT_MACRO)
|
||||||
|
target_compile_definitions(${ARG_TARGET_NAME}
|
||||||
|
PRIVATE ${ARG_EXPORT_MACRO}_EXPORTS
|
||||||
|
INTERFACE ${ARG_EXPORT_MACRO}_IMPORTS
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 设置C++标准
|
||||||
|
target_compile_features(${ARG_TARGET_NAME} PUBLIC cxx_std_11)
|
||||||
|
|
||||||
|
# IDE文件分组
|
||||||
|
source_group("Proto Files" FILES ${PROTO_FILES})
|
||||||
|
source_group("Generated Files\\Protobuf" FILES ${PROTO_SRCS} ${PROTO_HDRS})
|
||||||
|
if(ARG_GRPC_ENABLED)
|
||||||
|
source_group("Generated Files\\gRPC" FILES ${GRPC_SRCS} ${GRPC_HDRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 输出信息
|
||||||
|
message(STATUS "创建Proto库目标: ${ARG_TARGET_NAME}")
|
||||||
|
message(STATUS " Proto文件数量: ${CMAKE_CURRENT_SOURCE_DIR} list length: ${PROTO_FILES}")
|
||||||
|
message(STATUS " 输出目录: ${ARG_OUTPUT_PATH}")
|
||||||
|
message(STATUS " gRPC支持: ${ARG_GRPC_ENABLED}")
|
||||||
|
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()
|
||||||
|
|
||||||
|
function(simple_executable)
|
||||||
|
set(source_files "")
|
||||||
|
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} source_files)
|
||||||
|
add_executable(${PROJECT_NAME} ${source_files})
|
||||||
|
|
||||||
|
# 自动 include 检索到的 .cmake 文件
|
||||||
|
if(RETRIEVED_CMAKE_FILES)
|
||||||
|
foreach(cmake_file IN LISTS RETRIEVED_CMAKE_FILES)
|
||||||
|
message(STATUS "自动 include CMake 文件: ${cmake_file}")
|
||||||
|
include("${cmake_file}")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC mirai_project_options)
|
||||||
|
message(STATUS "创建可执行文件目标: ${PROJECT_NAME},引用路径: ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
add_os_definitions(${PROJECT_NAME})
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(simple_library library_type)
|
||||||
|
set(source_files "")
|
||||||
|
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR} source_files)
|
||||||
|
add_library(${PROJECT_NAME} ${library_type} ${source_files})
|
||||||
|
|
||||||
|
# 自动 include 检索到的 .cmake 文件
|
||||||
|
if(RETRIEVED_CMAKE_FILES)
|
||||||
|
foreach(cmake_file IN LISTS RETRIEVED_CMAKE_FILES)
|
||||||
|
message(STATUS "自动 include CMake 文件: ${cmake_file}")
|
||||||
|
include("${cmake_file}")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(library_type STREQUAL "INTERFACE")
|
||||||
|
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
target_link_libraries(${PROJECT_NAME} INTERFACE mirai_project_options)
|
||||||
|
else()
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC mirai_project_options)
|
||||||
|
endif()
|
||||||
|
message(STATUS "创建库目标: ${PROJECT_NAME},类型: ${library_type},引用路径: ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
|
add_os_definitions(${PROJECT_NAME})
|
||||||
|
endfunction()
|
||||||
515
cmake/shader_compile.cmake
Normal file
515
cmake/shader_compile.cmake
Normal file
@@ -0,0 +1,515 @@
|
|||||||
|
# ============================================================================
|
||||||
|
# MIRAI 着色器编译 CMake 模块
|
||||||
|
# ============================================================================
|
||||||
|
# 提供自动化 GLSL 着色器编译和代码生成功能
|
||||||
|
# 使用 glslangValidator 或 glslc (shaderc) 编译 GLSL 到 SPIR-V
|
||||||
|
#
|
||||||
|
# 主要函数:
|
||||||
|
# add_glsl_shader_library() - 创建 GLSL 着色器库目标
|
||||||
|
# compile_glsl_shader() - 编译单个 GLSL 着色器
|
||||||
|
#
|
||||||
|
# 辅助函数:
|
||||||
|
# find_python_with_jinja2() - 查找带 Jinja2 的 Python
|
||||||
|
# find_glsl_compiler() - 查找 GLSL 编译器
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
include_guard(GLOBAL)
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# find_python_with_jinja2 - 查找带 Jinja2 模块的 Python
|
||||||
|
# ============================================================================
|
||||||
|
function(find_python_with_jinja2 OUT_PYTHON_EXECUTABLE)
|
||||||
|
# 首先查找 Python
|
||||||
|
find_package(Python3 COMPONENTS Interpreter QUIET)
|
||||||
|
|
||||||
|
if(NOT Python3_FOUND)
|
||||||
|
message(FATAL_ERROR "Python3 not found. Please install Python 3.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 检查 Jinja2 是否已安装
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${Python3_EXECUTABLE} -c "import jinja2; print(jinja2.__version__)"
|
||||||
|
RESULT_VARIABLE JINJA2_CHECK_RESULT
|
||||||
|
OUTPUT_VARIABLE JINJA2_VERSION
|
||||||
|
ERROR_QUIET
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT JINJA2_CHECK_RESULT EQUAL 0)
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Python Jinja2 module not found.\n"
|
||||||
|
"Please install it with: ${Python3_EXECUTABLE} -m pip install jinja2"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Found Python with Jinja2: ${Python3_EXECUTABLE} (Jinja2 ${JINJA2_VERSION})")
|
||||||
|
set(${OUT_PYTHON_EXECUTABLE} "${Python3_EXECUTABLE}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# find_glsl_compiler - 查找 GLSL 编译器 (glslangValidator 或 glslc)
|
||||||
|
# ============================================================================
|
||||||
|
function(find_glsl_compiler OUT_COMPILER OUT_COMPILER_TYPE)
|
||||||
|
# 首先尝试查找 glslc (shaderc)
|
||||||
|
find_program(GLSLC_EXECUTABLE glslc
|
||||||
|
HINTS
|
||||||
|
$ENV{VULKAN_SDK}/Bin
|
||||||
|
$ENV{VULKAN_SDK}/bin
|
||||||
|
${Vulkan_GLSLC_EXECUTABLE}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GLSLC_EXECUTABLE)
|
||||||
|
message(STATUS "Found GLSL compiler: ${GLSLC_EXECUTABLE} (glslc)")
|
||||||
|
set(${OUT_COMPILER} "${GLSLC_EXECUTABLE}" PARENT_SCOPE)
|
||||||
|
set(${OUT_COMPILER_TYPE} "glslc" PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 尝试查找 glslangValidator
|
||||||
|
find_program(GLSLANG_VALIDATOR_EXECUTABLE glslangValidator
|
||||||
|
HINTS
|
||||||
|
$ENV{VULKAN_SDK}/Bin
|
||||||
|
$ENV{VULKAN_SDK}/bin
|
||||||
|
${Vulkan_GLSLANG_VALIDATOR_EXECUTABLE}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GLSLANG_VALIDATOR_EXECUTABLE)
|
||||||
|
message(STATUS "Found GLSL compiler: ${GLSLANG_VALIDATOR_EXECUTABLE} (glslangValidator)")
|
||||||
|
set(${OUT_COMPILER} "${GLSLANG_VALIDATOR_EXECUTABLE}" PARENT_SCOPE)
|
||||||
|
set(${OUT_COMPILER_TYPE} "glslangValidator" PARENT_SCOPE)
|
||||||
|
return()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"No GLSL compiler found. Please install Vulkan SDK or shaderc.\n"
|
||||||
|
"Expected: glslc or glslangValidator in PATH or VULKAN_SDK"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# 内部变量设置
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
# 着色器编译器目标名称
|
||||||
|
set(MIRAI_SHADER_COMPILER_TARGET "mirai_shader_compile")
|
||||||
|
|
||||||
|
# 代码生成脚本路径
|
||||||
|
set(MIRAI_SHADER_GENERATOR_SCRIPT "${CMAKE_SOURCE_DIR}/tools/generate_shader_bindings.py")
|
||||||
|
|
||||||
|
# 模板目录
|
||||||
|
set(MIRAI_SHADER_TEMPLATE_DIR "${CMAKE_SOURCE_DIR}/tools/templates")
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# _mirai_ensure_shader_compiler - 内部函数,确保着色器编译器可用
|
||||||
|
# ============================================================================
|
||||||
|
function(_mirai_ensure_shader_compiler)
|
||||||
|
# 检查编译器目标是否存在
|
||||||
|
if(NOT TARGET ${MIRAI_SHADER_COMPILER_TARGET})
|
||||||
|
message(FATAL_ERROR
|
||||||
|
"Shader compiler target '${MIRAI_SHADER_COMPILER_TARGET}' not found.\n"
|
||||||
|
"Make sure add_subdirectory(tools/shader_compile) is called before using add_glsl_shader_library()."
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# _get_shader_stage_from_extension - 从文件扩展名获取着色器阶段
|
||||||
|
# ============================================================================
|
||||||
|
function(_get_shader_stage_from_extension FILE_PATH OUT_STAGE)
|
||||||
|
get_filename_component(EXT "${FILE_PATH}" EXT)
|
||||||
|
string(TOLOWER "${EXT}" EXT_LOWER)
|
||||||
|
|
||||||
|
if(EXT_LOWER STREQUAL ".vert")
|
||||||
|
set(${OUT_STAGE} "vert" PARENT_SCOPE)
|
||||||
|
elseif(EXT_LOWER STREQUAL ".frag")
|
||||||
|
set(${OUT_STAGE} "frag" PARENT_SCOPE)
|
||||||
|
elseif(EXT_LOWER STREQUAL ".comp")
|
||||||
|
set(${OUT_STAGE} "comp" PARENT_SCOPE)
|
||||||
|
elseif(EXT_LOWER STREQUAL ".geom")
|
||||||
|
set(${OUT_STAGE} "geom" PARENT_SCOPE)
|
||||||
|
elseif(EXT_LOWER STREQUAL ".tesc")
|
||||||
|
set(${OUT_STAGE} "tesc" PARENT_SCOPE)
|
||||||
|
elseif(EXT_LOWER STREQUAL ".tese")
|
||||||
|
set(${OUT_STAGE} "tese" PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${OUT_STAGE} "" PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# compile_glsl_shader - 编译单个 GLSL 着色器
|
||||||
|
# ============================================================================
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# compile_glsl_shader(
|
||||||
|
# INPUT shader.vert
|
||||||
|
# OUTPUT shader.vert.spv
|
||||||
|
# STAGE vert
|
||||||
|
# DEFINES DEBUG FEATURE_X
|
||||||
|
# INCLUDE_DIRS include/
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# 参数:
|
||||||
|
# INPUT - 输入 GLSL 文件路径(必需)
|
||||||
|
# OUTPUT - 输出 SPIR-V 文件路径(必需)
|
||||||
|
# STAGE - 着色器阶段 (vert/frag/comp/geom/tesc/tese)(可选,自动检测)
|
||||||
|
# DEFINES - 预处理器定义(可选)
|
||||||
|
# INCLUDE_DIRS - include 目录(可选)
|
||||||
|
#
|
||||||
|
function(compile_glsl_shader)
|
||||||
|
cmake_parse_arguments(SHADER "" "INPUT;OUTPUT;STAGE" "DEFINES;INCLUDE_DIRS" ${ARGN})
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if(NOT SHADER_INPUT)
|
||||||
|
message(FATAL_ERROR "compile_glsl_shader: INPUT is required")
|
||||||
|
endif()
|
||||||
|
if(NOT SHADER_OUTPUT)
|
||||||
|
message(FATAL_ERROR "compile_glsl_shader: OUTPUT is required")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 自动检测着色器阶段
|
||||||
|
if(NOT SHADER_STAGE)
|
||||||
|
_get_shader_stage_from_extension("${SHADER_INPUT}" SHADER_STAGE)
|
||||||
|
if(NOT SHADER_STAGE)
|
||||||
|
message(FATAL_ERROR "compile_glsl_shader: Cannot determine shader stage from extension. Please specify STAGE.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 查找 GLSL 编译器
|
||||||
|
find_glsl_compiler(GLSL_COMPILER GLSL_COMPILER_TYPE)
|
||||||
|
|
||||||
|
# 构建编译命令参数
|
||||||
|
set(COMPILE_ARGS "")
|
||||||
|
|
||||||
|
if(GLSL_COMPILER_TYPE STREQUAL "glslc")
|
||||||
|
# glslc 参数
|
||||||
|
list(APPEND COMPILE_ARGS "-fshader-stage=${SHADER_STAGE}")
|
||||||
|
list(APPEND COMPILE_ARGS "--target-env=vulkan1.3")
|
||||||
|
list(APPEND COMPILE_ARGS "-o" "${SHADER_OUTPUT}")
|
||||||
|
|
||||||
|
# 添加 include 目录
|
||||||
|
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
|
||||||
|
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 添加宏定义
|
||||||
|
foreach(DEF ${SHADER_DEFINES})
|
||||||
|
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
list(APPEND COMPILE_ARGS "${SHADER_INPUT}")
|
||||||
|
else()
|
||||||
|
# glslangValidator 参数
|
||||||
|
list(APPEND COMPILE_ARGS "-V") # 生成 SPIR-V
|
||||||
|
list(APPEND COMPILE_ARGS "--target-env" "vulkan1.3")
|
||||||
|
list(APPEND COMPILE_ARGS "-S" "${SHADER_STAGE}")
|
||||||
|
list(APPEND COMPILE_ARGS "-o" "${SHADER_OUTPUT}")
|
||||||
|
|
||||||
|
# 添加 include 目录
|
||||||
|
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
|
||||||
|
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 添加宏定义
|
||||||
|
foreach(DEF ${SHADER_DEFINES})
|
||||||
|
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
list(APPEND COMPILE_ARGS "${SHADER_INPUT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 创建编译命令
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${SHADER_OUTPUT}"
|
||||||
|
COMMAND ${GLSL_COMPILER} ${COMPILE_ARGS}
|
||||||
|
DEPENDS "${SHADER_INPUT}"
|
||||||
|
COMMENT "Compiling GLSL shader: ${SHADER_INPUT}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# add_glsl_shader_library - 添加 GLSL 着色器库到现有目标
|
||||||
|
# ============================================================================
|
||||||
|
#
|
||||||
|
# 将 GLSL 着色器编译和代码生成附加到现有的编译目标上
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# # 方案1: 自动搜索着色器
|
||||||
|
# add_glsl_shader_library(TARGET mirai_shader
|
||||||
|
# SHADER_PATH shaders/glsl
|
||||||
|
# INCLUDE_DIRS shaders/glsl/common
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# # 方案2: 指定具体着色器文件
|
||||||
|
# add_glsl_shader_library(TARGET mirai_shader
|
||||||
|
# SHADERS shaders/ui/rect.vert shaders/ui/rect.frag
|
||||||
|
# INCLUDE_DIRS shaders/common
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# 参数:
|
||||||
|
# TARGET - 现有的编译目标(必需)
|
||||||
|
# SHADERS - 具体的着色器文件列表(可选)
|
||||||
|
# SHADER_PATH - 着色器搜索目录,相对于 CMAKE_CURRENT_SOURCE_DIR(可选)
|
||||||
|
# INCLUDE_DIRS - 着色器 include 路径(可选)
|
||||||
|
# DEFINES - 预处理器定义(可选)
|
||||||
|
# RECURSIVE - 是否递归搜索子目录(可选,默认 OFF)
|
||||||
|
#
|
||||||
|
function(add_glsl_shader_library)
|
||||||
|
# 解析参数
|
||||||
|
set(options RECURSIVE)
|
||||||
|
set(oneValueArgs TARGET SHADER_PATH)
|
||||||
|
set(multiValueArgs SHADERS INCLUDE_DIRS DEFINES)
|
||||||
|
cmake_parse_arguments(SHADER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
# 验证必需参数
|
||||||
|
if(NOT SHADER_TARGET)
|
||||||
|
message(FATAL_ERROR "add_glsl_shader_library: TARGET is required")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 验证目标存在
|
||||||
|
if(NOT TARGET ${SHADER_TARGET})
|
||||||
|
message(FATAL_ERROR "add_glsl_shader_library: Target '${SHADER_TARGET}' does not exist")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 收集着色器文件
|
||||||
|
set(ALL_SHADERS "${SHADER_SHADERS}")
|
||||||
|
|
||||||
|
if(SHADER_SHADER_PATH)
|
||||||
|
set(SHADER_EXTENSIONS "vert;frag;comp;geom;tesc;tese")
|
||||||
|
|
||||||
|
if(SHADER_RECURSIVE)
|
||||||
|
foreach(EXT ${SHADER_EXTENSIONS})
|
||||||
|
file(GLOB_RECURSE FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.${EXT}")
|
||||||
|
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
|
||||||
|
endforeach()
|
||||||
|
else()
|
||||||
|
foreach(EXT ${SHADER_EXTENSIONS})
|
||||||
|
file(GLOB FOUND_SHADERS "${CMAKE_CURRENT_SOURCE_DIR}/${SHADER_SHADER_PATH}/*.${EXT}")
|
||||||
|
list(APPEND ALL_SHADERS ${FOUND_SHADERS})
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 去重
|
||||||
|
if(ALL_SHADERS)
|
||||||
|
list(REMOVE_DUPLICATES ALL_SHADERS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 设置输出目录
|
||||||
|
set(SHADER_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||||
|
set(SHADER_INTERMEDIATE_DIR "${CMAKE_CURRENT_BINARY_DIR}/shader_intermediate")
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${SHADER_OUTPUT_DIR}")
|
||||||
|
file(MAKE_DIRECTORY "${SHADER_INTERMEDIATE_DIR}")
|
||||||
|
|
||||||
|
# 确保编译器可用
|
||||||
|
_mirai_ensure_shader_compiler()
|
||||||
|
|
||||||
|
# 查找 Python with Jinja2
|
||||||
|
find_python_with_jinja2(PYTHON_EXECUTABLE)
|
||||||
|
|
||||||
|
# 查找 GLSL 编译器
|
||||||
|
find_glsl_compiler(GLSL_COMPILER GLSL_COMPILER_TYPE)
|
||||||
|
|
||||||
|
# 构建 include 路径参数
|
||||||
|
set(INCLUDE_ARGS "")
|
||||||
|
foreach(INC_DIR ${SHADER_INCLUDE_DIRS})
|
||||||
|
if(IS_ABSOLUTE "${INC_DIR}")
|
||||||
|
list(APPEND INCLUDE_ARGS "${INC_DIR}")
|
||||||
|
else()
|
||||||
|
list(APPEND INCLUDE_ARGS "${CMAKE_CURRENT_SOURCE_DIR}/${INC_DIR}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 收集生成的文件
|
||||||
|
set(ALL_GENERATED_HEADERS "")
|
||||||
|
set(ALL_SPV_FILES "")
|
||||||
|
|
||||||
|
# 按着色器名称分组(去掉扩展名后相同的文件归为一组)
|
||||||
|
set(SHADER_GROUPS "")
|
||||||
|
foreach(SHADER_SOURCE ${ALL_SHADERS})
|
||||||
|
get_filename_component(SHADER_NAME_WE "${SHADER_SOURCE}" NAME_WE)
|
||||||
|
get_filename_component(SHADER_DIR "${SHADER_SOURCE}" DIRECTORY)
|
||||||
|
|
||||||
|
# 创建唯一的组标识符
|
||||||
|
string(REPLACE "/" "_" DIR_SAFE "${SHADER_DIR}")
|
||||||
|
set(GROUP_ID "${DIR_SAFE}_${SHADER_NAME_WE}")
|
||||||
|
|
||||||
|
# 添加到组列表
|
||||||
|
list(APPEND SHADER_GROUPS "${GROUP_ID}")
|
||||||
|
list(APPEND ${GROUP_ID}_SOURCES "${SHADER_SOURCE}")
|
||||||
|
set(${GROUP_ID}_NAME "${SHADER_NAME_WE}")
|
||||||
|
set(${GROUP_ID}_DIR "${SHADER_DIR}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 去重组列表
|
||||||
|
if(SHADER_GROUPS)
|
||||||
|
list(REMOVE_DUPLICATES SHADER_GROUPS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 处理每个着色器组
|
||||||
|
foreach(GROUP_ID ${SHADER_GROUPS})
|
||||||
|
set(GROUP_SOURCES "${${GROUP_ID}_SOURCES}")
|
||||||
|
set(GROUP_NAME "${${GROUP_ID}_NAME}")
|
||||||
|
set(GROUP_DIR "${${GROUP_ID}_DIR}")
|
||||||
|
|
||||||
|
# 每个着色器组的输出目录
|
||||||
|
set(SPIRV_OUTPUT_DIR "${SHADER_INTERMEDIATE_DIR}/${GROUP_NAME}")
|
||||||
|
set(GENERATED_HPP "${SHADER_OUTPUT_DIR}/${GROUP_NAME}_bindings.hpp")
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${SPIRV_OUTPUT_DIR}")
|
||||||
|
|
||||||
|
set(GROUP_SPV_FILES "")
|
||||||
|
|
||||||
|
# 编译组内的每个着色器
|
||||||
|
foreach(SHADER_SOURCE ${GROUP_SOURCES})
|
||||||
|
get_filename_component(SHADER_FILENAME "${SHADER_SOURCE}" NAME)
|
||||||
|
_get_shader_stage_from_extension("${SHADER_SOURCE}" SHADER_STAGE)
|
||||||
|
|
||||||
|
if(NOT SHADER_STAGE)
|
||||||
|
message(WARNING "Skipping unknown shader type: ${SHADER_SOURCE}")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(SPV_OUTPUT "${SPIRV_OUTPUT_DIR}/${SHADER_FILENAME}.spv")
|
||||||
|
|
||||||
|
# 构建编译命令参数
|
||||||
|
set(COMPILE_ARGS "")
|
||||||
|
|
||||||
|
if(GLSL_COMPILER_TYPE STREQUAL "glslc")
|
||||||
|
list(APPEND COMPILE_ARGS "-fshader-stage=${SHADER_STAGE}")
|
||||||
|
list(APPEND COMPILE_ARGS "--target-env=vulkan1.3")
|
||||||
|
list(APPEND COMPILE_ARGS "-o" "${SPV_OUTPUT}")
|
||||||
|
|
||||||
|
foreach(INC_DIR ${INCLUDE_ARGS})
|
||||||
|
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(DEF ${SHADER_DEFINES})
|
||||||
|
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
list(APPEND COMPILE_ARGS "${SHADER_SOURCE}")
|
||||||
|
else()
|
||||||
|
list(APPEND COMPILE_ARGS "-V")
|
||||||
|
list(APPEND COMPILE_ARGS "--target-env" "vulkan1.3")
|
||||||
|
list(APPEND COMPILE_ARGS "-S" "${SHADER_STAGE}")
|
||||||
|
list(APPEND COMPILE_ARGS "-o" "${SPV_OUTPUT}")
|
||||||
|
|
||||||
|
foreach(INC_DIR ${INCLUDE_ARGS})
|
||||||
|
list(APPEND COMPILE_ARGS "-I${INC_DIR}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(DEF ${SHADER_DEFINES})
|
||||||
|
list(APPEND COMPILE_ARGS "-D${DEF}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
list(APPEND COMPILE_ARGS "${SHADER_SOURCE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 创建编译命令
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${SPV_OUTPUT}"
|
||||||
|
COMMAND ${GLSL_COMPILER} ${COMPILE_ARGS}
|
||||||
|
DEPENDS "${SHADER_SOURCE}"
|
||||||
|
COMMENT "Compiling GLSL: ${SHADER_FILENAME}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
list(APPEND GROUP_SPV_FILES "${SPV_OUTPUT}")
|
||||||
|
list(APPEND ALL_SPV_FILES "${SPV_OUTPUT}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 使用 mirai_shader_compile 工具生成反射和绑定
|
||||||
|
if(GROUP_SPV_FILES)
|
||||||
|
# 创建标记文件表示编译完成
|
||||||
|
set(COMPILED_MARKER "${SPIRV_OUTPUT_DIR}/.compiled")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${COMPILED_MARKER}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E touch "${COMPILED_MARKER}"
|
||||||
|
DEPENDS ${GROUP_SPV_FILES}
|
||||||
|
COMMENT "GLSL shaders compiled: ${GROUP_NAME}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
# 生成绑定头文件
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${GENERATED_HPP}"
|
||||||
|
COMMAND $<TARGET_FILE:${MIRAI_SHADER_COMPILER_TARGET}>
|
||||||
|
--dir "${SPIRV_OUTPUT_DIR}"
|
||||||
|
--output "${GENERATED_HPP}"
|
||||||
|
--name "${GROUP_NAME}"
|
||||||
|
DEPENDS "${COMPILED_MARKER}" ${MIRAI_SHADER_COMPILER_TARGET}
|
||||||
|
COMMENT "Generating bindings: ${GROUP_NAME}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
list(APPEND ALL_GENERATED_HEADERS "${GENERATED_HPP}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# 附加到目标:添加生成目录到 include 路径
|
||||||
|
target_include_directories(${SHADER_TARGET} PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${SHADER_OUTPUT_DIR}>
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加编译依赖
|
||||||
|
if(ALL_GENERATED_HEADERS)
|
||||||
|
add_custom_target(${SHADER_TARGET}_shaders DEPENDS ${ALL_GENERATED_HEADERS})
|
||||||
|
add_dependencies(${SHADER_TARGET} ${SHADER_TARGET}_shaders)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# 导出变量
|
||||||
|
set(${SHADER_TARGET}_SHADER_HEADERS ${ALL_GENERATED_HEADERS} PARENT_SCOPE)
|
||||||
|
set(${SHADER_TARGET}_SHADER_OUTPUT_DIR ${SHADER_OUTPUT_DIR} PARENT_SCOPE)
|
||||||
|
set(${SHADER_TARGET}_SPV_FILES ${ALL_SPV_FILES} PARENT_SCOPE)
|
||||||
|
|
||||||
|
message(STATUS "Added GLSL shaders to target: ${SHADER_TARGET}")
|
||||||
|
message(STATUS " Output: ${SHADER_OUTPUT_DIR}")
|
||||||
|
if(ALL_SHADERS)
|
||||||
|
list(LENGTH ALL_SHADERS SHADER_COUNT)
|
||||||
|
message(STATUS " Shaders: ${SHADER_COUNT} files")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# ============================================================================
|
||||||
|
# add_shader_library - 兼容性别名(调用 add_glsl_shader_library)
|
||||||
|
# ============================================================================
|
||||||
|
#
|
||||||
|
# 为了向后兼容,保留 add_shader_library 函数名
|
||||||
|
# 现在默认使用 GLSL 编译
|
||||||
|
#
|
||||||
|
function(add_shader_library)
|
||||||
|
# 解析参数
|
||||||
|
set(options RECURSIVE)
|
||||||
|
set(oneValueArgs TARGET SHADER_PATH)
|
||||||
|
set(multiValueArgs SHADERS REF_PATHS DEFINES)
|
||||||
|
cmake_parse_arguments(SHADER "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
||||||
|
|
||||||
|
# 转换参数并调用新函数
|
||||||
|
set(FORWARD_ARGS "TARGET" "${SHADER_TARGET}")
|
||||||
|
|
||||||
|
if(SHADER_SHADER_PATH)
|
||||||
|
list(APPEND FORWARD_ARGS "SHADER_PATH" "${SHADER_SHADER_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SHADER_SHADERS)
|
||||||
|
list(APPEND FORWARD_ARGS "SHADERS" ${SHADER_SHADERS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SHADER_REF_PATHS)
|
||||||
|
list(APPEND FORWARD_ARGS "INCLUDE_DIRS" ${SHADER_REF_PATHS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SHADER_DEFINES)
|
||||||
|
list(APPEND FORWARD_ARGS "DEFINES" ${SHADER_DEFINES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SHADER_RECURSIVE)
|
||||||
|
list(APPEND FORWARD_ARGS "RECURSIVE")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_glsl_shader_library(${FORWARD_ARGS})
|
||||||
|
endfunction()
|
||||||
5
example/CMakeLists.txt
Normal file
5
example/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
project(mirai_example)
|
||||||
|
|
||||||
|
simple_executable()
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC mirai)
|
||||||
3
example/main.cpp
Normal file
3
example/main.cpp
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
25
src/CMakeLists.txt
Normal file
25
src/CMakeLists.txt
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
project(mirai)
|
||||||
|
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
find_package(SDL3 CONFIG REQUIRED)
|
||||||
|
find_package(harfbuzz CONFIG REQUIRED)
|
||||||
|
find_package(Freetype CONFIG REQUIRED)
|
||||||
|
find_package(yoga CONFIG REQUIRED)
|
||||||
|
find_package(Eigen3 CONFIG REQUIRED)
|
||||||
|
find_package(spdlog CONFIG REQUIRED)
|
||||||
|
find_package(fmt CONFIG REQUIRED)
|
||||||
|
find_package(Stb REQUIRED)
|
||||||
|
|
||||||
|
simple_library(STATIC)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||||
|
Vulkan::Vulkan
|
||||||
|
SDL3::SDL3
|
||||||
|
Freetype::Freetype
|
||||||
|
harfbuzz::harfbuzz
|
||||||
|
yoga::yogacore
|
||||||
|
Eigen3::Eigen
|
||||||
|
spdlog::spdlog
|
||||||
|
fmt::fmt
|
||||||
|
)
|
||||||
|
target_link_directories(${PROJECT_NAME} PUBLIC ${Stb_INCLUDE_DIR})
|
||||||
10
src/core/object.cpp
Normal file
10
src/core/object.cpp
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#include "object.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
std::atomic<object_id> object::next_id_{1};
|
||||||
|
|
||||||
|
object::~object() = default;
|
||||||
|
object::object() : id_(next_id_.fetch_add(1, std::memory_order_relaxed)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
228
src/core/object.h
Normal file
228
src/core/object.h
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
using object_id = u64;
|
||||||
|
constexpr object_id invalid_object_id = 0;
|
||||||
|
|
||||||
|
struct type_info {
|
||||||
|
std::string_view name;
|
||||||
|
size_type hash;
|
||||||
|
const type_info* parent;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool operator==(const type_info& other) const noexcept {
|
||||||
|
return hash == other.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool operator!=(const type_info& other) const noexcept {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr bool is_derived_from(const type_info& base) const noexcept {
|
||||||
|
auto current = this;
|
||||||
|
while (current) {
|
||||||
|
if (*current == base) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current = current->parent;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
consteval auto compile_time_hash(std::string_view str) noexcept {
|
||||||
|
size_type hash = 14695981039346656037ULL;
|
||||||
|
for (char c : str) {
|
||||||
|
hash ^= static_cast<size_type>(c);
|
||||||
|
hash *= 1099511628211ULL;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
consteval auto get_type_name() noexcept {
|
||||||
|
#if defined(__clang__)
|
||||||
|
constexpr std::string_view prefix = "[T = ";
|
||||||
|
constexpr std::string_view suffix = "]";
|
||||||
|
constexpr std::string_view function = __PRETTY_FUNCTION__;
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
constexpr std::string_view prefix = "with T = ";
|
||||||
|
constexpr std::string_view suffix = "]";
|
||||||
|
constexpr std::string_view function = __PRETTY_FUNCTION__;
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
constexpr std::string_view prefix = "get_type_name<";
|
||||||
|
constexpr std::string_view suffix = ">(void)";
|
||||||
|
constexpr std::string_view function = __FUNCSIG__;
|
||||||
|
#else
|
||||||
|
return typeid(T).name();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const auto start = function.find(prefix) + prefix.size();
|
||||||
|
const auto end = function.rfind(suffix);
|
||||||
|
return function.substr(start, end - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
[[nodiscard]] constexpr const auto& get_type_info() noexcept {
|
||||||
|
static type_info info{
|
||||||
|
get_type_name<T>(),
|
||||||
|
compile_time_hash(get_type_name<T>()),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
template<typename T, typename Parent>
|
||||||
|
[[nodiscard]] constexpr const auto& get_type_info_with_parent() noexcept {
|
||||||
|
if constexpr (std::is_same_v<Parent, void>) {
|
||||||
|
static type_info info{
|
||||||
|
get_type_name<T>(),
|
||||||
|
compile_time_hash(get_type_name<T>()),
|
||||||
|
nullptr
|
||||||
|
};
|
||||||
|
return info;
|
||||||
|
} else {
|
||||||
|
static type_info info{
|
||||||
|
get_type_name<T>(),
|
||||||
|
compile_time_hash(get_type_name<T>()),
|
||||||
|
&get_type_info_with_parent<Parent, typename Parent::parent_t>()
|
||||||
|
};
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class object : public std::enable_shared_from_this<object> {
|
||||||
|
public:
|
||||||
|
using ptr = std::shared_ptr<object>;
|
||||||
|
using weak_ptr = std::weak_ptr<object>;
|
||||||
|
using parent_t = void;
|
||||||
|
|
||||||
|
virtual ~object();
|
||||||
|
|
||||||
|
object(const object&) = delete;
|
||||||
|
object& operator=(const object&) = delete;
|
||||||
|
object(object&&) = delete;
|
||||||
|
object& operator=(object&&) = delete;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr virtual auto get_type() const noexcept -> const type_info& {
|
||||||
|
return get_type_info<object>();
|
||||||
|
}
|
||||||
|
[[nodiscard]] constexpr virtual auto get_parent_type() const noexcept -> const type_info* {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr auto type_name() const noexcept {
|
||||||
|
return get_type().name;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] constexpr auto is() const noexcept {
|
||||||
|
return get_type().is_derived_from(get_type_info<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] auto as() noexcept -> T* {
|
||||||
|
if (is<T>()) {
|
||||||
|
return static_cast<T*>(this);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] auto as() const noexcept -> const T* {
|
||||||
|
if (is<T>()) {
|
||||||
|
return static_cast<const T*>(this);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
#if MIRAI_DEBUG
|
||||||
|
void set_debug_name(std::string name) {
|
||||||
|
debug_name_ = std::move(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string& get_debug_name() const noexcept {
|
||||||
|
return debug_name_;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string debug_name_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] auto id() const noexcept {
|
||||||
|
return id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = object> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] auto shared_from_this_as() noexcept -> std::shared_ptr<T> {
|
||||||
|
return std::dynamic_pointer_cast<T>(shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = object> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] auto shared_from_this_as() const noexcept -> std::shared_ptr<const T> {
|
||||||
|
return std::dynamic_pointer_cast<const T>(shared_from_this());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = object> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] auto weak_from_this_as() noexcept -> std::weak_ptr<T> {
|
||||||
|
return std::weak_ptr<T>(shared_from_this_as<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T = object> requires std::derived_from<T, object>
|
||||||
|
[[nodiscard]] auto weak_from_this_as() const noexcept -> std::weak_ptr<const T> {
|
||||||
|
return std::weak_ptr<const T>(shared_from_this_as<T>());
|
||||||
|
}
|
||||||
|
protected:
|
||||||
|
object();
|
||||||
|
|
||||||
|
virtual void on_created() {}
|
||||||
|
virtual void on_destroying() {}
|
||||||
|
private:
|
||||||
|
object_id id_;
|
||||||
|
static std::atomic<object_id> next_id_;
|
||||||
|
friend class object_registry;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct object_deleter {
|
||||||
|
void operator()(T* ptr) {
|
||||||
|
if (ptr) {
|
||||||
|
ptr->on_destroying();
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MIRAI_OBJECT_TYPE_INFO(class_name, parent_class) \
|
||||||
|
public: \
|
||||||
|
using parent_t = parent_class; \
|
||||||
|
[[nodiscard]] constexpr virtual const mirai::type_info& get_type() const noexcept override { \
|
||||||
|
return mirai::get_type_info_with_parent<class_name, parent_t>(); \
|
||||||
|
} \
|
||||||
|
[[nodiscard]] constexpr virtual const mirai::type_info* get_parent_type() const noexcept override { \
|
||||||
|
return &mirai::get_type_info<parent_t>(); \
|
||||||
|
} \
|
||||||
|
friend struct mirai::object_factory; \
|
||||||
|
template<typename T_> \
|
||||||
|
friend struct mirai::object_deleter; \
|
||||||
|
friend class object_registry;
|
||||||
|
|
||||||
|
struct object_factory {
|
||||||
|
template<typename T, typename ...Args> requires std::derived_from<T, object>
|
||||||
|
static auto create(Args&&... args) {
|
||||||
|
auto ptr = std::shared_ptr<T>(new T(std::forward<Args>(args)...), object_deleter<T>{});
|
||||||
|
ptr->on_created();
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename ...Args> requires std::derived_from<T, object>
|
||||||
|
auto make_obj(Args&&... args) {
|
||||||
|
return object_factory::create<T>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/core/types.h
Normal file
27
src/core/types.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cwchar>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
namespace mirai {
|
||||||
|
using u8 = std::uint8_t;
|
||||||
|
using u16 = std::uint16_t;
|
||||||
|
using u32 = std::uint32_t;
|
||||||
|
using u64 = std::uint64_t;
|
||||||
|
using i8 = std::int8_t;
|
||||||
|
using i16 = std::int16_t;
|
||||||
|
using i32 = std::int32_t;
|
||||||
|
using i64 = std::int64_t;
|
||||||
|
using f32 = float;
|
||||||
|
using f64 = double;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using index_type = std::ptrdiff_t;
|
||||||
|
using ptrdiff_type = std::ptrdiff_t;
|
||||||
|
using uintptr_type = std::uintptr_t;
|
||||||
|
using intptr_type = std::intptr_t;
|
||||||
|
|
||||||
|
using byte_type = std::byte;
|
||||||
|
using byte_span = std::span<byte_type>;
|
||||||
|
using const_byte_span = std::span<const byte_type>;
|
||||||
|
}
|
||||||
8
tests/CMakeLists.txt
Normal file
8
tests/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
project(mirai_tests)
|
||||||
|
|
||||||
|
enable_testing()
|
||||||
|
find_package(GTest CONFIG REQUIRED)
|
||||||
|
|
||||||
|
simple_executable()
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE mirai GTest::gtest GTest::gtest_main)
|
||||||
|
add_test(AllTestsInMain ${PROJECT_NAME})
|
||||||
25
tests/main.cpp
Normal file
25
tests/main.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "object_test.h"
|
||||||
|
|
||||||
|
TEST(test_object, derived) {
|
||||||
|
auto test_obj = mirai::make_obj<test_class1>();
|
||||||
|
auto test_obj2 = mirai::make_obj<test_class2>();
|
||||||
|
|
||||||
|
// 检查类型信息
|
||||||
|
|
||||||
|
// test_obj应该继承自mirai::object
|
||||||
|
EXPECT_TRUE(test_obj->is<mirai::object>());
|
||||||
|
EXPECT_TRUE(test_obj2->is<mirai::object>());
|
||||||
|
|
||||||
|
// test_obj应该是test_class1类型,但不是test_class2类型
|
||||||
|
EXPECT_TRUE(test_obj->is<test_class1>());
|
||||||
|
EXPECT_FALSE(test_obj->is<test_class2>());
|
||||||
|
|
||||||
|
// test_obj2应该是test_class2类型,也是test_class1类型
|
||||||
|
EXPECT_TRUE(test_obj2->is<test_class2>());
|
||||||
|
EXPECT_TRUE(test_obj2->is<test_class1>());
|
||||||
|
|
||||||
|
// test_obj的类型名称检查
|
||||||
|
EXPECT_EQ(test_obj->type_name(), "class test_class1");
|
||||||
|
EXPECT_EQ(test_obj2->type_name(), "class test_class2");
|
||||||
|
}
|
||||||
11
tests/object_test.h
Normal file
11
tests/object_test.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "core/object.h"
|
||||||
|
|
||||||
|
class test_class1 : public mirai::object {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(test_class1, mirai::object)
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class test_class2 : public test_class1 {
|
||||||
|
MIRAI_OBJECT_TYPE_INFO(test_class2, test_class1)
|
||||||
|
};
|
||||||
78
vcpkg.json
Normal file
78
vcpkg.json
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json",
|
||||||
|
"name": "mirai",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "Modern C++23 GUI Framework based on Vulkan 1.3",
|
||||||
|
"homepage": "https://github.com/mirai-framework/mirai",
|
||||||
|
"license": "MIT",
|
||||||
|
"supports": "windows | linux | osx",
|
||||||
|
"builtin-baseline": "5422eb983d4ca8bc4851ac9771d6e74554efc5c8",
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"name": "sdl3",
|
||||||
|
"version>=": "3.2.28"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "spirv-cross",
|
||||||
|
"version>=": "1.3.296.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "nlohmann-json",
|
||||||
|
"version>=": "3.12.0#1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stb",
|
||||||
|
"version>=": "2024-07-29#1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "freetype",
|
||||||
|
"version>=": "2.13.3",
|
||||||
|
"features": ["bzip2", "png", "zlib"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "harfbuzz",
|
||||||
|
"version>=": "12.2.0",
|
||||||
|
"features": ["freetype"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "yoga",
|
||||||
|
"version>=": "3.2.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "eigen3",
|
||||||
|
"version>=": "3.4.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "spdlog",
|
||||||
|
"version>=": "1.16.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fmt",
|
||||||
|
"version>=": "12.1.0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "gtest",
|
||||||
|
"version>=": "1.17.0#2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"overrides": [],
|
||||||
|
"features": {
|
||||||
|
"tests": {
|
||||||
|
"description": "Build unit tests",
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"name": "gtest",
|
||||||
|
"version>=": "1.17.0#2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"examples": {
|
||||||
|
"description": "Build example applications",
|
||||||
|
"dependencies": []
|
||||||
|
},
|
||||||
|
"docs": {
|
||||||
|
"description": "Build documentation",
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user