fmt
Build from Sourcefind_package()
FindPkgConfig
FetchContent
連結: Day 14 - Colab
Day 13 介紹了 CMake 提供讓我們搜尋並加入 modules 和 packages 的幾個指令, 今天會來實際使用到我們的 CMake 專案中, 下面會附上今天的範例 code, 也可以去 Colab 自己玩玩看
我們會用 C++ 的一個 open source 專案 fmtlib/fmt 做為例子, 示範如何 從系統上加入該套件
當然, 就如 Day 13 提過的, 我們也可以直接用 FetchContent_*
系列 function 下載並加入我們的 CMake 專案中, 但這部分就留到 Day 15 再介紹吧~
fmt
Build from Sourcefmt
的專案下載下來git clone https://github.com/fmtlib/fmt
cd fmt
cmake -S . \
-B build \
-G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE:STRING=Release
cmake --build build
cmake --install build
這樣就會把 fmt
裝到系統上了, 路徑在 /usr/local/
底下, 可以看到有
lib/cmake/
fmt
提供的 CMake modulesinclude/fmt/*.h
fmt
export 的 header files, 可以讓我們 link 時使用fmt
exportpkg-config
使用的 .pc
檔, 來偷看一下裡面有什麼prefix=/usr/local
exec_prefix=/usr/local
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: fmt
Description: A modern formatting library
Version: 10.1.1
Libs: -L${libdir} -lfmt
Cflags: -I${includedir}
有沒有覺得很熟悉呢?裝好 fmt
之後, 我們就可以將他拉近我們的 CMake 專案使用了
接著就可以使用 Day 13 講過的幾個指令和 modules 了
來看看這次的 project 架構
cmake-example/
├─ src/
│ ├─ cmake/ 👈
│ │ ├─ dependencies.cmake 👈
│ ├─ CMakeLists.txt
│ ├─ main.cpp
├─ CMakeLists.txt
可以看到, 這次我們多了新的 cmake
directory 和其中的 dependencies.cmake
module
根據 CMake 的 convention, cmake
一般會存放專案自己的 modules, 而外部套件則建議單獨在 dependencies.cmake
module 集中處理
再來看看幾個 CMakeLists.txt
和 executable 的設定
CMakeLists.txt
cmake_minimum_required(VERSION 3.25)
project(ItHome2023
LANGUAGES C CXX
VERSION 0.1.0
)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
add_subdirectory(src)
src/main.cpp
#include <iostream>
#include <fmt/core.h>
int main() {
std::cout << fmt::format("Test fmt {}", "specifier") << std::endl;
return 0;
}
find_package()
src/cmake/dependencies.cmake
find_package(fmt REQUIRED)
if (TARGET fmt::fmt-header-only)
message(DEBUG "🎉 Found fmt::fmt-header-only")
else()
message(FATAL_ERROR "Failed to find fmt::fmt-header-only")
endif()
src/CMakeLists.txt
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
include(dependencies)
add_executable(MyApp
main.cpp
)
target_link_libraries(MyApp PRIVATE
fmt::fmt-header-only
)
FindPkgConfig
src/cmake/dependencies.cmake
include(FindPkgConfig)
pkg_check_modules(FMT REQUIRED IMPORTED_TARGET fmt)
if (TARGET PkgConfig::FMT)
message(DEBUG "🎉 Found PkgConfig::FMT")
else()
message(FATAL_ERROR "Failed to find PkgConfig::FMT")
endif()
這裡用 pkg_check_modules(IMPORTED_TARGET)
讓 CMake 幫我們建出 target PkgConfig::FMT
, 我們的 executable 就可以 link 他
src/CMakeLists.txt
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
include(dependencies)
add_executable(MyApp
main.cpp
)
target_link_libraries(MyApp PRIVATE
PkgConfig::FMT
)
FetchContent
src/cmake/dependencies.cmake
include(FetchContent)
FetchContent_Declare(FMT
SOURCE_DIR ${PROJECT_SOURCE_DIR}/../fmt
)
FetchContent_MakeAvailable(FMT)
if (TARGET fmt::fmt-header-only)
message(DEBUG "🎉 Found fmt::fmt-header-only")
else()
message(FATAL_ERROR "Failed to find fmt::fmt-header-only")
endif()
這邊我們設定 SOURCE_DIR
為剛剛下載的 fmt
專案的路徑FetchContent_MakeAvailable()
內部就會用 add_subdirectory()
加入該專案, 所以該專案現在就相當於是我們的 sub-project, 所以我們就可以用 fmt
的 targets 了~
src/CMakeLists.txt
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake)
include(dependencies)
add_executable(MyApp
main.cpp
)
target_link_libraries(MyApp PRIVATE
fmt::fmt-header-only
)
可以看到這個用法的結果和 find_package()
是一樣的
Day 15 會介紹 FetchContent
module 真正好用的功能: 下載 Github 專案並指定 tag 或 commit hash!
以今天的例子來說, 除了用 FindPkgConfig
拉進來的 target name 是由我們自己定義以外, find_package()
和 FetchContent
都拉進來了和 套件名稱不完全相同的 targets, ex. fmt::fmt-header-only
和 fmt::fmt
第一時間可以去查文件, 通常文件會寫要怎麼 link 他的 targets
另一種是直接去 build
底下找 <packageName>-config.cmake
或是 <packageName>-targets.cmake
檔案
以 fmt
來說, 他會產出 fmt-targets.cmake
, 且裡面有一段指令是這樣
foreach(_cmake_expected_target IN ITEMS fmt::fmt fmt::fmt-header-only) 👈
list(APPEND _cmake_expected_targets "${_cmake_expected_target}")
if(TARGET "${_cmake_expected_target}")
list(APPEND _cmake_targets_defined "${_cmake_expected_target}")
else()
list(APPEND _cmake_targets_not_defined "${_cmake_expected_target}")
endif()
endforeach()
從這裡也可以看出來該套件有哪些 targets 能用
明天會繼續來介紹 FetchContent
真正好用的用法, 並且會寫出我們的第一個測試!