commit 76a4edd6ed665cfff8e967f7b936ffd0d2aa7d88 Author: nanako <469449812@qq.com> Date: Wed May 28 16:50:50 2025 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..125f2b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea +/cmake-* \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9bea1c8 --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1312080 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/cache/shader_paths.txt b/cache/shader_paths.txt new file mode 100644 index 0000000..e69de29 diff --git a/cmake/compile_shaders.cmake b/cmake/compile_shaders.cmake new file mode 100644 index 0000000..dbc936e --- /dev/null +++ b/cmake/compile_shaders.cmake @@ -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() diff --git a/cmake/config_macos.cmake b/cmake/config_macos.cmake new file mode 100644 index 0000000..ea2f398 --- /dev/null +++ b/cmake/config_macos.cmake @@ -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 () diff --git a/cmake/configure_glfw_native.cmake b/cmake/configure_glfw_native.cmake new file mode 100644 index 0000000..aff54e8 --- /dev/null +++ b/cmake/configure_glfw_native.cmake @@ -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() diff --git a/cmake/detect_os.cmake b/cmake/detect_os.cmake new file mode 100644 index 0000000..7ff84be --- /dev/null +++ b/cmake/detect_os.cmake @@ -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) diff --git a/cmake/mingw_dll.cmake b/cmake/mingw_dll.cmake new file mode 100644 index 0000000..da09762 --- /dev/null +++ b/cmake/mingw_dll.cmake @@ -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}" + "$" + COMMENT "Copying ${DLL} to output directory" + VERBATIM) + else() + message(WARNING "DLL not found: ${MINGW_DIR}/${DLL}") + endif() + endforeach() + endif() +endfunction() \ No newline at end of file diff --git a/cmake/mirage_utils.cmake b/cmake/mirage_utils.cmake new file mode 100644 index 0000000..4a4b4bb --- /dev/null +++ b/cmake/mirage_utils.cmake @@ -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), 可执行文件将位于 /bin/ + # 对于多配置生成器 (如 Visual Studio, Xcode), CMake 通常会自动在此路径下附加配置名称 + # (例如 /bin/Debug/, /bin/Release/) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin CACHE PATH "Directory for runtime executables") + message(STATUS "运行时输出目录设置为: ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + + # **设置库文件输出路径 (共享库和静态库)**: + # 规则同上,库文件将位于 /lib/ 或 /lib// + 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() \ No newline at end of file diff --git a/cmake/project_cpp_standard.cmake b/cmake/project_cpp_standard.cmake new file mode 100644 index 0000000..c3a8348 --- /dev/null +++ b/cmake/project_cpp_standard.cmake @@ -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 以支持 等特性 + if(${standard} GREATER 14) + message(STATUS "**为 MinGW C++${CMAKE_CXX_STANDARD} 添加 libstdc++fs 库依赖** (用于 )") + # 注意:较新版本的 MinGW 可能不再需要显式链接,或者需要链接的是 -lstdc++fs + # link_libraries() 会影响后续所有 target,更推荐使用 target_link_libraries() + # 这里暂时保留 link_libraries(),但建议后续根据具体 target 调整 + # 尝试链接 stdc++fs,这在较新的 MinGW 中更常见用于 +# link_libraries(-lstdc++fs) + # 如果链接 -lstdc++fs 失败,可以尝试回退到 -lstdc++exp + link_libraries(-lstdc++exp) + endif() + endif() + + message(STATUS "**C++ 标准已设置为: c++${standard}**") +endfunction() diff --git a/cmake/retrieve_files.cmake b/cmake/retrieve_files.cmake new file mode 100644 index 0000000..ca067b0 --- /dev/null +++ b/cmake/retrieve_files.cmake @@ -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) + # 对于多配置,没有单一的顶级运行时目录变量。 + # 可以考虑使用 $ 配合一个已知的可执行文件名,但这会使函数复杂化。 + # 最好的做法是要求用户设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY_ + # 或者我们直接报错,强制用户设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY + message(FATAL_ERROR "**add_resource_file**: **无法确定可执行文件输出目录**。请在您的项目中设置 **CMAKE_RUNTIME_OUTPUT_DIRECTORY** 变量 (例如 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"\${CMAKE_BINARY_DIR}/bin\"))。对于多配置生成器,可能需要设置 CMAKE_RUNTIME_OUTPUT_DIRECTORY_ 变量。") + else() + # 对于单配置生成器(如 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() diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..398dc87 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,69 @@ +// +// Created by 46944 on 25-5-21. +// + +#include + +#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; +} diff --git a/third_party/SDL b/third_party/SDL new file mode 160000 index 0000000..b8e055c --- /dev/null +++ b/third_party/SDL @@ -0,0 +1 @@ +Subproject commit b8e055ce6492e89a8a26995534d35b6e712b9e36 diff --git a/third_party/SDL_ttf b/third_party/SDL_ttf new file mode 160000 index 0000000..b9e7e93 --- /dev/null +++ b/third_party/SDL_ttf @@ -0,0 +1 @@ +Subproject commit b9e7e9381bc8a3e816de8a2b2b76b0749238e69a