在昨天跟前天的範例中,Windows 使用者一定發現了編譯出來的執行檔旁邊有個 fmt.dll。
其實我看到這個 DLL 覺得有點煩。
我把執行檔給同事用,還要連同 DLL 一起給,不然程式就跑不起來?難道不能把函式庫全部打包起來,只產出一個執行檔就好了嗎?這些在 vcpkg 裡面要怎麼設定呢?
其實啊,問題的答案就在 triplet 裡面。

triplet 這個詞有點難懂,中文意思是「三元組」或「三胞胎」。以前是 GNU 用來指定編譯器設定用的專有名詞,被 vcpkg 沿用下來。
只要打開 $VCPKG_ROOT/triplets/ 這個目錄,你就可以看到全部的 triplet。
挑一個打開來看看,發現它就是一個 .cmake 檔案,然後裡面設定了一些變數。
比如說 x64-windows.cmake 的內容如下:
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE dynamic)
set(VCPKG_PROVIDED_FORTRAN ON)
你會發現,這就是一組套件編譯相關的設定,像是可以指定:
再看看 Linux 跟 macOS 的 triplet:
x64-linux.cmake
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Linux)
arm64-osx.cmake
set(VCPKG_TARGET_ARCHITECTURE arm64)
set(VCPKG_CRT_LINKAGE dynamic)
set(VCPKG_LIBRARY_LINKAGE static)
set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
set(VCPKG_OSX_ARCHITECTURES arm64)
看完三個檔案,你會發現 VCPKG_LIBRARY_LINKAGE 這個變數在三個平台上的值不一樣,在 Windows 上是 dynamic,另外兩個平台是 static。這就是 Windows 平台上會產生 fmt.dll 的根本原因。
在前面的章節中,我們完全沒指定 triplet,所以 vcpkg 到底是用哪個呢?
triplet 的命名規則其實很直觀,就是:[CPU 架構]-[作業系統]。
當不指定時,vcpkg 會根據你的建置工具自動推導這兩項資訊:
如果你用 CMake(Manifest 模式):
x64;macOS 則是 Apple Silicon(arm64)。x64-windows 或 arm64-osx。如果你用 Visual Studio(MSBuild 專案):
x64 → 自動使用 x64-windows
Win32(x86) → 自動使用 x86-windows
ARM64 → 自動使用 arm64-windows
這是自動挑選的邏輯,那可以手動指定 Triplet 嗎?當然可以!
文章一開始說,我們希望用靜態連結的方式來編譯第三方函式庫,只產出單一執行檔,避免複製 fmt.dll 的麻煩。
這件事的最佳 Triplet 是 x64-windows-static-md,裡面已經把VCPKG_LIBRARY_LINKAGE 值改為 static,這樣函式庫就會被編譯成靜態的 .lib 而非 .dll。
接著在 CMake 指令中傳入 -DVCPKG_TARGET_TRIPLET 來指定 Triplet:
cmake -S . -B build-static `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-DVCPKG_TARGET_TRIPLET=x64-windows-static-md
cmake --build build-static --config Release
就這樣,編譯過後,打開輸出目錄,裡面只有一個乾乾淨淨的 exe,討人厭的 fmt.dll 已經消失了。因為 fmt 已經變成靜態函式庫,被直接連結融入 exe 檔案裡了。

triplets/ 底下的檔案分兩種:
官方內建(built-in)的放在根目錄,官方 CI 每次都測。目前的內建 triplet 只有這幾個:

社群(community)維護的放在 triplets/community/。裡面有 iOS、MinGW、FreeBSD、WebAssembly 等等一百多個。附帶一提,Intel Mac x64-osx 也是社群維護的。
Visual Studio 要換 triplet 的話,一樣在專案屬性裡改。
專案屬性 → vcpkg:

要直接指定其他 triplet,就在 Triplet 那一欄裡面填入你要的名稱。
x64-windows-static-md 最省事;連 CRT 都要靜態才用 x64-windows-static。x64-osx)都在 community 社群維護區。