iT邦幫忙

2026 iThome 鐵人賽

DAY 3
0

因為專案會分成跑引擎的 SDK CMakeList 與實際專案範例的 CMakeList 設定,這邊大致介紹其內容設定與較重要的部分。

CMake

CMake 是常見用來處理 C++ library 的依賴跟環境設定的設定檔。而最常用的為以下兩個

  • CMakeLists.txt
  • CMakePresets.json

CMakeLists.txt

CMakeLists.txt 最重要的事情是告訴 CMake 此專案需要設定那些東西,譬如專案名稱、程式連結、需編譯的程式等等。

以下先用事先測試用的專案當作範例,這是其中接下來用來輸出三角形範例的的 CMakeLists

# 指定此專案要求的最低 CMake 版本
cmake_minimum_required(VERSION 3.25)

# 建立名為 DayX_Triangle 的專案
project(DayX_Triangle LANGUAGES CXX)

# 建立一個名為 DayX_Triangle 的可執行檔 Target
# 一般小程式用 executable 就好
add_executable(DayX_Triangle WIN32
        main.cpp
)

# 要求此專案至少需要 C++20
target_compile_features(DayX_Triangle PRIVATE
        cxx_std_20
)

# 設定 DayX_Triangle 需要連結的 Library
target_link_libraries(DayX_Triangle PRIVATE #PRIVATE 表示這個相依性只屬於 DayX_Triangle
        # MyDXEngineSDK::MyDXEngineSDK:是透過 add_library(... ALIAS ...) 來找到
        MyDXEngineSDK::MyDXEngineSDK 
)

# 複製 DayX_Triangle 執行時需要的相依檔案 EX:DLL
copy_runtime_dependencies(DayX_Triangle)

# 設定 Visual Studio 啟動 Debug 時的 Working Directory
set_target_properties(DayX_Triangle PROPERTIES
        VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)

# add_custom_command 會在編譯完成之後執行額外指令
add_custom_command(TARGET DayX_Triangle POST_BUILD

        COMMAND ${CMAKE_COMMAND} -E copy_directory

        "${CMAKE_CURRENT_SOURCE_DIR}/../assets"

        "$<TARGET_FILE_DIR:DayX_Triangle>/assets"
)

另一個是引擎 SDK 的設定

# 建立名為 MyDXEngineSDK 的靜態函式庫,並指定要編譯進去的原始碼與標頭檔
add_library(MyDXEngineSDK STATIC
        core/graphics_engine.cpp
        #...等等其他程式
)

# 為 MyDXEngineSDK 建立命名空間形式的別名,讓其他 Target 可以用 MyDXEngineSDK::MyDXEngineSDK 連結
add_library(MyDXEngineSDK::MyDXEngineSDK ALIAS MyDXEngineSDK)

# 指定 MyDXEngineSDK 使用 C++20,且這個需求會傳遞給連結它的 Target
target_compile_features(MyDXEngineSDK PUBLIC cxx_std_20)

# 指定 MyDXEngineSDK 使用 pch.h 作為預編譯標頭,以減少重複編譯時間
target_precompile_headers(MyDXEngineSDK PRIVATE
        "${CMAKE_CURRENT_SOURCE_DIR}/core/pch.h"
)

# 設定 MyDXEngineSDK 的 Header 搜尋路徑,並讓連結它的 Target 也能使用這些路徑
target_include_directories(MyDXEngineSDK PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/core
        ${CMAKE_CURRENT_SOURCE_DIR}/system
        ${CMAKE_CURRENT_SOURCE_DIR}
)

# 指定 MyDXEngineSDK 需要連結的系統函式庫
target_link_libraries(MyDXEngineSDK PUBLIC
        Microsoft::DirectX-Headers
        Microsoft::DirectXMath
        Microsoft::DirectXShaderCompiler
        Microsoft::DirectXTK12
        assimp::assimp
        glog::glog
        d3d12
        d3dcompiler
        dxgi
        dxguid
        user32
)

這邊值得提一下的是 add_library 後面還加了 STATIC 的關鍵字,這是較簡單的部屬設定,別的專案只要簡單在 target_link_libraries 打上 add_library 裡設定的名字,他就可以抓到裡面設定的內容。

MyDXEngineSDK 資料夾裡的程式因為主要是只用在此專案的範例,就只需要設定成 STATIC 就好了,如果後面還有時間或許會帶過 SHARED 會需要額外改變的設定。

下表表示其差異

函式庫類型 靜態 Library(Static Library) 動態 Library( Shared Library)
CMake 寫法 add_library(Name STATIC ...) add_library(Name SHARED ...)
連結時機 編譯/連結階段直接整合進執行檔 編譯時記錄依賴,執行時再載入動態 Library
執行時是否需要額外 Library 檔案 通常不需要 通常需要 .dll / .so / .dylib
部署難度 較簡單 需要確保動態 Library 存在於正確位置
多個程式共用 Library 每個程式各自包含一份 多個程式可以共用同一份動態 Library
更新 Library 通常需要重新 Link,甚至重新 Build 某些情況下只替換 DLL 即可,但必須維持 ABI 相容
適合用途 小型引擎、SDK、單一程式 Plugin、共用 Runtime、大型模組化系統

最後則是最外面的 CMakeList 設定

cmake_minimum_required (VERSION 3.21)

if(POLICY CMP0162)
   cmake_policy(SET CMP0162 NEW)
endif()

# 建立 SkylineEngine 專案,設定描述、使用 C++,並指定版本為 1.0.0
project (SkylineEngine
  DESCRIPTION "CMake example for Direct3D 12 Game (Win32) w/ DeviceResources using VCPKG"
  LANGUAGES CXX
  VERSION 1.0.0)

# 提供是否建立測試範本的選項
option(BUILD_TEST_TEMPLATE "Ignore warnings related to TODOs" OFF)

# 提供是否在建置時啟用靜態程式碼分析的選項
option(ENABLE_CODE_ANALYSIS "Use Static Code Analysis on build" OFF)

set(CMAKE_CXX_STANDARD 17)

# 強制要求編譯器必須支援指定的 C++17 標準
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 關閉編譯器特有的 C++ 擴充功能,盡量使用標準 C++
set(CMAKE_CXX_EXTENSIONS OFF)

# 指定靜態 Library 的輸出目錄為 build/lib
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

# 指定動態 Library 的輸出目錄為 build/lib
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")

# 指定執行檔與 Windows DLL 等 Runtime 檔案的輸出目錄為 build/bin
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")

# 載入自訂的 Compiler 與 Linker 相關 CMake 設定
include(CompilerAndLinker.cmake)

find_package(directxmath CONFIG REQUIRED)
find_package(directx-headers CONFIG REQUIRED)
find_package(directx-dxc CONFIG REQUIRED)
find_package(directxtk12 CONFIG REQUIRED)

find_package(assimp CONFIG REQUIRED)

# 指定 gflags 使用帶有 Namespace 的 CMake Target 名稱
set(GFLAGS_USE_TARGET_NAMESPACE ON)

find_package(gflags CONFIG REQUIRED)

find_package(glog CONFIG REQUIRED)

# 定義一個函式,在指定 Target 建置完成後自動複製其執行時需要的 DLL
function(copy_runtime_dependencies target)
   add_custom_command(
           TARGET ${target}
           POST_BUILD
           COMMAND ${CMAKE_COMMAND} -E copy_if_different
           $<TARGET_RUNTIME_DLLS:${target}>
           $<TARGET_FILE_DIR:${target}>
           COMMAND_EXPAND_LISTS
           COMMENT "Copying runtime dependencies for ${target}"
   )
endfunction()

# 使用 add_subdirectory 來把各個資料夾底下的內容設定成子專案並處理其中的 CMakeLists.txt
add_subdirectory(MyDXEngineSDK)
add_subdirectory(iTHelpContest30/DayX_Triangle)

CMakePresets

簡單來說,CMakePresets 是讓 CMake 知道要用什麼環境來執行程式。

以最一開始 Visual Studio 預設的設定來說,光是 Release 與 Debug 就有分成 x64 跟 x86。

如果沒有這些設定,以上面的 x64-debug 來說,就相當於每次都要手動輸入像是下面的內容

cmake -S . -B out/build/x64-debug `
    -G Ninja `
    -DCMAKE_BUILD_TYPE=Debug `
    -DCMAKE_C_COMPILER=cl.exe `
    -DCMAKE_CXX_COMPILER=cl.exe `
    "-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=$env:VSINSTALLDIR\Common7\IDE\CommonExtensions\Microsoft\CMake\cmake\Microsoft\SegmentHeap.cmake"

尤其如果今天你使用一個第三方函式庫,因為引擎的一些環境需要特別去微調裡面的設定時,如果每次都要打上面那串,既容易打錯還容易因為忘記輸出出不符合環境的函式庫。

這時候就可以把這些設定放進 CMakePresets 裡,以下是部分的設定

   ...
   
   {
  "name": "Debug", // Preset 名稱
  "cacheVariables": { // 設定會傳給 CMake Cache 的變
    "CMAKE_BUILD_TYPE": "Debug" // 指定為 Debug 建置模式
  },
  "hidden": true // 隱藏此 Preset,通常作為其他 Preset 繼承用,不直接讓使用者選擇
},
{
  "name": "Release", 
  "cacheVariables": { 
    "CMAKE_BUILD_TYPE": "RelWithDebInfo", // 使用最佳化的 Release 模式,同時保留除錯資訊
    "CMAKE_INTERPROCEDURAL_OPTIMIZATION": true // 啟用跨模組最佳化,例如 MSVC 的 LTCG 或 GCC/Clang 的 LTO
  },
  "hidden": true 
}
   
   ...

當然可以先看看就好,畢竟這部分是真的動過一次後大概就不會再動的設定,但如果知道他是這樣使用的,以後需要客製化輸出環境就會方便許多。

參考資料

CMake Documentation and Community


上一篇
Day 2:環境建置
下一篇
Day 4:C++ 使用到的功能介紹
系列文
因為 AI 看不懂老舊程式,只好乖乖從零開始學 DirectX 12 與 HLSL4
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言