This commit is contained in:
2025-05-28 16:50:50 +08:00
commit 76a4edd6ed
15 changed files with 903 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/.idea
/cmake-*

6
.gitmodules vendored Normal file
View File

@@ -0,0 +1,6 @@
[submodule "third_party/SDL"]
path = third_party/SDL
url = https://github.com/libsdl-org/SDL.git
[submodule "third_party/SDL_ttf"]
path = third_party/SDL_ttf
url = https://github.com/libsdl-org/SDL_ttf.git

34
CMakeLists.txt Normal file
View File

@@ -0,0 +1,34 @@
#
# project: mirage
#
cmake_minimum_required(VERSION 3.21)
project(mirage)
include(cmake/project_cpp_standard.cmake)
set_cpp_standard(23)
include(cmake/retrieve_files.cmake)
include(cmake/detect_os.cmake)
include(cmake/config_macos.cmake)
include(cmake/compile_shaders.cmake)
include(cmake/mingw_dll.cmake)
include(cmake/mirage_utils.cmake)
# 配置输出目录
configure_project_defaults()
# 如果是Debug模式, 添加宏定义
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
add_definitions(-DDEBUG=1)
else ()
add_definitions(-DDEBUG=0)
endif ()
add_subdirectory(third_party/SDL)
add_subdirectory(third_party/SDL_ttf)
SET(SRC_FILES)
retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES)
add_executable(learn ${SRC_FILES})
target_include_directories(learn PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_link_libraries(learn SDL3-static SDL3_ttf-static)

0
cache/shader_paths.txt vendored Normal file
View File

115
cmake/compile_shaders.cmake Normal file
View File

@@ -0,0 +1,115 @@
include(CMakeParseArguments)
# 查找Python解释器
find_package(Python3 REQUIRED)
# 存储脚本路径
set(SHADER_COMPILE_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/tools/compile_shaders.py")
# 在cache目录下创建着色器目录.txt
set(SHADER_PATH_FILE ${CMAKE_CURRENT_SOURCE_DIR}/cache/shader_paths.txt)
file(REMOVE ${SHADER_PATH_FILE})
file(WRITE ${SHADER_PATH_FILE} "")
# 添加着色器目录的函数
# 参数:
# path: 要添加的包含 .slang 着色器文件的目录路径
# 功能:
# 1. 将路径转换为适合目标系统的格式 (特别是处理 Cygwin)。
# 2. 将路径追加到 SHADER_PATH_FILE 指定的文件中。
# 3. 查找指定路径下的所有 .slang 文件。
# 4. 将找到的 .slang 文件添加为 CMake 配置的依赖项,
# 以便在这些文件更改时触发重新配置。
# 注意:
# - 需要在使用此函数前定义 CMAKE_BINARY_DIR 和 SHADER_PATH_FILE 变量。
# 例如: set(SHADER_PATH_FILE ${CMAKE_BINARY_DIR}/shader_paths.txt)
# - 假定 SHADER_PATH_FILE 的使用者期望接收适合其环境的路径格式
# (此处在 Cygwin 下转换为 Windows 格式)。
function(add_mirage_shader_directory path)
# 检查 SHADER_PATH_FILE 变量是否已定义
if(NOT DEFINED SHADER_PATH_FILE)
message(FATAL_ERROR "**错误**: SHADER_PATH_FILE 变量未定义。请在使用 add_mirage_shader_directory 前设置此变量。")
endif()
# 获取绝对路径以确保一致性
get_filename_component(abs_path ${path} ABSOLUTE)
# 保存用于文件操作和写入文件的路径变量
set(path_to_write ${abs_path})
# 如果是cygwin环境, 需要转换路径为Windows格式
if (CYGWIN)
message(STATUS "检测到 Cygwin 环境,尝试使用 cygpath 转换路径: ${abs_path}")
# **关键点**: 使用 cygpath -w 将 Cygwin 路径转换为 Windows 路径
execute_process(
COMMAND cygpath -w ${abs_path}
OUTPUT_VARIABLE path_windows
OUTPUT_STRIP_TRAILING_WHITESPACE # 去除可能的尾随空格
RESULT_VARIABLE cygpath_result
ERROR_QUIET # 如果 cygpath 不存在或失败,则不显示错误,但检查 result
)
if(cygpath_result EQUAL 0 AND path_windows) # 检查 cygpath 是否成功执行并有输出
# 更新用于写入文件的路径
set(path_to_write ${path_windows})
message(STATUS "路径已成功转换为 Windows 格式: ${path_to_write}")
else()
# 如果 cygpath 失败或未找到,发出警告,并回退到原始路径
message(WARNING "无法使用 cygpath 转换路径 ${abs_path}。将使用原始路径。请确保 cygpath 在系统 PATH 中。")
# path_to_write 保持为 abs_path
endif()
endif()
# 将可能已转换的路径写入shader_paths.txt
# **关键点**: 追加路径到 ${SHADER_PATH_FILE}
file(APPEND ${SHADER_PATH_FILE} "${path_to_write}\n")
# 查找目录下的所有 .slang 文件,包括子目录
# **关键点**: 递归查找 ${abs_path} 目录下的 *.slang 文件
file(GLOB_RECURSE SHADER_FILES LIST_DIRECTORIES false "${abs_path}/*.slang")
# 设置依赖关系当shader文件变化时重新运行CMake配置
# **关键点**: 将着色器文件添加为 CMake 配置依赖项
foreach(SHADER_FILE ${SHADER_FILES})
get_filename_component(ABS_SHADER_FILE ${SHADER_FILE} ABSOLUTE)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${ABS_SHADER_FILE})
endforeach()
# **关键点**: 输出状态消息,告知用户已添加目录
message(STATUS "添加着色器源文件目录: ${path_to_write}")
endfunction()
set(SHADER_COMPILE_ARGS "")
set(SHADER_SHDC "")
if (WIN32 OR CYGWIN)
list(APPEND SHADER_COMPILE_ARGS "--hlsl")
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/win_mirage_shdc.exe)
elseif (APPLE)
list(APPEND SHADER_COMPILE_ARGS "--metal")
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/mac_mirage_shdc)
else()
list(APPEND SHADER_COMPILE_ARGS "--glsl")
set(SHADER_SHDC ${MIRAGE_ROOT_DIR}/tools/linux_mirage_shdc)
endif()
message(STATUS "使用着色器编译器: ${SHADER_SHDC}")
# 如果是Debug模式, 添加--debug选项
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
list(APPEND SHADER_COMPILE_ARGS "--debug")
endif ()
# 添加编译目标, 调用tools/compile_shaders.py
add_custom_target(compile_shaders ALL
COMMAND ${Python3_EXECUTABLE} ${SHADER_COMPILE_SCRIPT}
--shdc ${SHADER_SHDC}
--shader_list ${SHADER_PATH_FILE}
${SHADER_COMPILE_ARGS}
COMMENT "编译着色器"
VERBATIM
)
# 定义一个函数,将编译着色器作为其他目标的依赖
function(add_shader_dependencies target)
add_dependencies(${target} compile_shaders)
endfunction()

16
cmake/config_macos.cmake Normal file
View File

@@ -0,0 +1,16 @@
# 如果是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)
endif ()

View File

@@ -0,0 +1,42 @@
function(configure_glfw_native target)
# 检测操作系统
if(WIN32)
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_WIN32)
message(STATUS "公开 GLFW 原生 Win32 API")
elseif(APPLE)
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_COCOA)
message(STATUS "公开 GLFW 原生 Cocoa API")
elseif(UNIX)
# 对于 Unix-like 系统,我们需要进一步检测
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
# 检测 Wayland
find_package(Wayland)
if(Wayland_FOUND)
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_WAYLAND)
message(STATUS "公开 GLFW 原生 Wayland API")
else()
# 如果没有 Wayland默认使用 X11
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_X11)
message(STATUS "公开 GLFW 原生 X11 API")
endif()
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD|OpenBSD|NetBSD")
# BSD 系统通常使用 X11
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_X11)
message(STATUS "公开 BSD 的 GLFW 原生 X11 API")
else()
message(WARNING "未知的类 Unix 系统GLFW 原生 API 可能无法正确暴露")
endif()
elseif (ANDROID)
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_ANDROID)
message(STATUS "公开 GLFW 原生 Android API")
else()
message(WARNING "未知作系统GLFW 原生 API 可能无法正确暴露")
endif()
# 对于 EGL 支持,你可能需要额外的检测
# 这里我们简单地为所有非 Windows 和非 macOS 系统启用它
if(NOT WIN32 AND NOT APPLE)
target_compile_definitions(${target} PRIVATE GLFW_EXPOSE_NATIVE_EGL)
message(STATUS "公开 GLFW 原生 EGL API")
endif()
endfunction()

131
cmake/detect_os.cmake Normal file
View File

@@ -0,0 +1,131 @@
# 定义一个函数,为指定的目标添加操作系统和架构相关的预处理器定义
function(add_os_definitions target)
# 检查 target 参数是否提供
if(NOT target)
message(FATAL_ERROR "函数 add_os_definitions 需要一个 target 参数。")
return()
endif()
# --- 阶段 1: 确定宏的值 ---
# 初始化所有平台、架构和特性宏的值为 0
set(mirage_def_windows 0)
set(mirage_def_macos 0)
set(mirage_def_linux 0)
set(mirage_def_freebsd 0)
set(mirage_def_ios 0)
set(mirage_def_android 0)
set(mirage_def_cygwin 0)
set(mirage_def_unix 0)
set(mirage_def_posix 0)
set(mirage_def_mobile 0)
set(mirage_def_arch_64bit 0)
set(mirage_def_arch_32bit 0)
# -- 操作系统检测与赋值 --
# 注意检测顺序:优先检测更具体的平台
if(CYGWIN)
# Cygwin 环境比较特殊,它在 Windows 上模拟 Unix
set(mirage_def_windows 1) # 基础是 Windows
set(mirage_def_cygwin 1) # 明确是 Cygwin
set(mirage_def_unix 1) # 提供 Unix API
set(mirage_def_posix 1) # 提供 POSIX API
message(STATUS "检测到 **Cygwin** 环境 (运行于 Windows)")
elseif(WIN32)
# 非 Cygwin 的 Windows 环境 (MSVC, MinGW, etc.)
set(mirage_def_windows 1)
message(STATUS "检测到 **Windows** 操作系统 (非 Cygwin)")
elseif(ANDROID)
# Android 平台 (通常需要特定工具链设置 ANDROID 变量)
set(mirage_def_android 1)
set(mirage_def_unix 1) # Android NDK 基于 Unix
set(mirage_def_posix 1) # NDK 提供 POSIX API
set(mirage_def_mobile 1) # 移动平台
message(STATUS "检测到 **Android** 操作系统")
elseif(IOS)
# iOS 平台 (通常需要特定工具链设置 IOS 变量)
# 需要在 APPLE 之前判断,因为 iOS 下 APPLE 也为 TRUE
set(mirage_def_ios 1)
set(mirage_def_unix 1) # iOS (Darwin) 基于 Unix
set(mirage_def_posix 1) # 提供 POSIX API
set(mirage_def_mobile 1) # 移动平台
message(STATUS "检测到 **iOS** 操作系统")
elseif(APPLE)
# 此时排除了 iOS确定是 macOS
set(mirage_def_macos 1)
set(mirage_def_unix 1) # macOS (Darwin) 基于 Unix
set(mirage_def_posix 1) # 提供 POSIX API
message(STATUS "检测到 **macOS** 操作系统")
elseif(UNIX)
# 此时排除了 Apple, Android, Cygwin 的其他 Unix-like 系统
set(mirage_def_unix 1)
set(mirage_def_posix 1)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(mirage_def_linux 1)
message(STATUS "检测到 **Linux** 操作系统")
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
set(mirage_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(mirage_def_arch_64bit 1)
set(mirage_def_arch_32bit 0) # 明确设置为 0
message(STATUS "检测到 **64-bit** 架构")
elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
set(mirage_def_arch_64bit 0) # 明确设置为 0
set(mirage_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()
# --- 阶段 2: 组装定义列表 ---
set(definitions_list "") # 初始化空列表
# 添加平台定义
list(APPEND definitions_list "MIRAGE_PLATFORM_WINDOWS=${mirage_def_windows}")
list(APPEND definitions_list "MIRAGE_PLATFORM_MACOS=${mirage_def_macos}")
list(APPEND definitions_list "MIRAGE_PLATFORM_LINUX=${mirage_def_linux}")
list(APPEND definitions_list "MIRAGE_PLATFORM_FREEBSD=${mirage_def_freebsd}")
list(APPEND definitions_list "MIRAGE_PLATFORM_IOS=${mirage_def_ios}")
list(APPEND definitions_list "MIRAGE_PLATFORM_ANDROID=${mirage_def_android}")
list(APPEND definitions_list "MIRAGE_PLATFORM_CYGWIN=${mirage_def_cygwin}")
# 添加架构定义
list(APPEND definitions_list "MIRAGE_PLATFORM_ARCH_64BIT=${mirage_def_arch_64bit}")
list(APPEND definitions_list "MIRAGE_PLATFORM_ARCH_32BIT=${mirage_def_arch_32bit}")
# 添加特性定义
list(APPEND definitions_list "MIRAGE_PLATFORM_UNIX=${mirage_def_unix}")
list(APPEND definitions_list "MIRAGE_PLATFORM_POSIX=${mirage_def_posix}")
list(APPEND definitions_list "MIRAGE_PLATFORM_IS_MOBILE=${mirage_def_mobile}")
# --- 阶段 3: 应用所有定义 ---
# **关键:使用一次调用将所有定义添加到目标**
if(definitions_list) # 确保列表非空
target_compile_definitions(${target} PUBLIC ${definitions_list})
endif()
# 函数作用域结束时mirage_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
View 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/mirage_utils.cmake Normal file
View 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()

View File

@@ -0,0 +1,85 @@
# 函数:设置 C++ 标准及相关编译选项
# 参数 standard: 需要设置的 C++ 标准版本 (例如 11, 14, 17, 20, 23)
function(set_cpp_standard standard)
# --- 参数验证 ---
set(VALID_STANDARDS 11 14 17 20 23 26) # 定义支持的 C++ 标准列表
list(FIND VALID_STANDARDS ${standard} _standard_index) # 查找 standard 是否在列表中
if(_standard_index EQUAL -1) # 如果未找到
message(WARNING "**非标准 C++ 版本**: ${standard}。支持的版本有: ${VALID_STANDARDS}")
# 可选:可以设置为一个默认值或停止配置
# message(FATAL_ERROR "不支持的 C++ 标准: ${standard}")
# set(standard 17) # 或者设置为默认值,例如 C++17
# message(WARNING "已将 C++ 标准设置为默认值: ${standard}")
endif()
# --- 设置 C++ 标准 ---
# 指定需要的 C++ 标准,设置到父作用域,使其对调用者定义的 target 生效
set(CMAKE_CXX_STANDARD ${standard} PARENT_SCOPE)
# **强制要求此标准**,如果编译器不支持则配置时报错
set(CMAKE_CXX_STANDARD_REQUIRED ON PARENT_SCOPE)
# **禁用编译器特定的扩展**,使用更纯粹的标准 C++
set(CMAKE_CXX_EXTENSIONS OFF PARENT_SCOPE)
# --- 平台特定设置 ---
if(WIN32 OR CYGWIN)
# 为 Windows 定义 UNICODE 宏
add_definitions(-DUNICODE -D_UNICODE)
# 可选:添加 WIN32_LEAN_AND_MEAN 以减少 Windows 头文件包含,加快编译速度
# add_definitions(-DWIN32_LEAN_AND_MEAN)
message(STATUS "为 Windows 添加 UNICODE 定义")
endif()
# --- 编译器特定设置 ---
if(MSVC)
# **设置源代码和执行字符集为 UTF-8**
add_compile_options(/utf-8)
# **强制 MSVC 正确设置 __cplusplus 宏**,以便代码能准确判断 C++ 标准
add_compile_options(/Zc:__cplusplus)
# **设置警告级别为 W4** (较高警告级别)
add_compile_options(/W4)
# **禁用特定警告C4100 未使用的形参** (有时用于接口兼容性)
add_compile_options(/wd4100)
# 禁用特定警告C4996 使用了被标记为否决的函数或变量 (例如一些旧的 CRT 函数)
add_compile_options(/wd4996)
message(STATUS "为 MSVC 添加特定编译选项: /utf-8 /Zc:__cplusplus /W4 /wd4100 /wd4996")
endif()
# GCC/Clang 特定选项
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# **启用常用警告**
add_compile_options(-Wall -Wextra)
# **禁用未使用参数的警告** (与 MSVC 的 /wd4100 对应)
add_compile_options(-Wno-unused-parameter)
# **设置输入和执行字符集为 UTF-8** (对应 MSVC 的 /utf-8)
# 这有助于处理源代码中的 UTF-8 字符,并影响运行时字符编码,但控制台本身的显示需要环境配合
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -finput-charset=UTF-8 -fexec-charset=UTF-8")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finput-charset=UTF-8 -fexec-charset=UTF-8")
# 根据 C++ 标准添加特定警告 (C++17 及以上)
if(${standard} GREATER 14)
# **启用阴影变量警告和非虚析构函数警告**
add_compile_options(-Wshadow -Wnon-virtual-dtor)
message(STATUS "为 GCC/Clang (C++${CMAKE_CXX_STANDARD}) 添加额外警告: -Wshadow -Wnon-virtual-dtor")
endif()
message(STATUS "为 GCC/Clang 添加特定编译选项: -Wall -Wextra -Wno-unused-parameter -finput-charset=UTF-8 -fexec-charset=UTF-8")
# 注意: 控制台/终端的 UTF-8 输出显示通常还需要操作系统环境的配合 (例如 Linux/macOS 终端本身设置Windows下 chcp 65001)
endif()
# MinGW 特定设置 (通常在 Windows 上使用 GCC 工具链)
if(MINGW)
message(STATUS "检测到 MinGW 编译器")
# 如果使用了 C++17 或更高版本, 可能需要链接 libstdc++exp 以支持 <filesystem> 等特性
if(${standard} GREATER 14)
message(STATUS "**为 MinGW C++${CMAKE_CXX_STANDARD} 添加 libstdc++fs 库依赖** (用于 <filesystem>)")
# 注意:较新版本的 MinGW 可能不再需要显式链接,或者需要链接的是 -lstdc++fs
# link_libraries() 会影响后续所有 target更推荐使用 target_link_libraries()
# 这里暂时保留 link_libraries(),但建议后续根据具体 target 调整
# 尝试链接 stdc++fs这在较新的 MinGW 中更常见用于 <filesystem>
# link_libraries(-lstdc++fs)
# 如果链接 -lstdc++fs 失败,可以尝试回退到 -lstdc++exp
link_libraries(-lstdc++exp)
endif()
endif()
message(STATUS "**C++ 标准已设置为: c++${standard}**")
endfunction()

316
cmake/retrieve_files.cmake Normal file
View File

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

69
src/main.cpp Normal file
View File

@@ -0,0 +1,69 @@
//
// Created by 46944 on 25-5-21.
//
#include <thread>
#include "SDL3/SDL_init.h"
#include "SDL3/SDL_render.h"
#include "SDL3/SDL_video.h"
#include "SDL3/SDL_log.h"
#include "SDL3/SDL_timer.h"
#include "SDL3_ttf/SDL_ttf.h"
int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window* window = SDL_CreateWindow("Hello World", 640, 480, 0);
if (window == nullptr) {
SDL_Log("Could not create window: %s", SDL_GetError());
return 1;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, "direct3d11");
if (renderer == nullptr) {
SDL_Log("Could not create renderer: %s", SDL_GetError());
SDL_DestroyWindow(window);
return 1;
}
TTF_Init();
auto text_engine = TTF_CreateRendererTextEngine(renderer);
if (text_engine == nullptr) {
SDL_Log("Could not create text engine: %s", SDL_GetError());
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
return 1;
}
TTF_Font* font = TTF_OpenFont("C:/Windows/Fonts/Arial.ttf", 24);
if (font == nullptr) {
SDL_Log("Could not open font: %s", SDL_GetError());
TTF_DestroyRendererTextEngine(text_engine);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
return 1;
}
SDL_Color color = { 255, 255, 255, 255 };
auto text_surface = TTF_RenderText_Solid(font, "Hello World", 0, color);
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, text_surface);
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderTexture(renderer, texture, nullptr, nullptr);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Event e;
while (true) {
if (!SDL_PollEvent(&e)) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
continue;
}
if (e.type == SDL_EVENT_QUIT) {
break;
}
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
TTF_Quit();
return 0;
}

1
third_party/SDL vendored Submodule

Submodule third_party/SDL added at b8e055ce64

1
third_party/SDL_ttf vendored Submodule

Submodule third_party/SDL_ttf added at b9e7e9381b