CMake 学习笔记
CMake 學(xué)習(xí)筆記
CMake 已經(jīng)是 C++ 構(gòu)建系統(tǒng)的事實(shí)標(biāo)準(zhǔn)。
主要是對(duì)小彭老師的 C++ 視頻課程中 CMake 相關(guān)部分的一些筆記和整理,視頻鏈接如下
- 學(xué) C++ 從 CMake 學(xué)起
- 現(xiàn)代 CMake 高級(jí)教程
包含視頻中的代碼和 PPT 的倉(cāng)庫(kù)見(jiàn)以下鏈接
https://github.com/parallel101/course
本筆記重點(diǎn)關(guān)注與 CMake 相關(guān)的一些知識(shí)點(diǎn),需要的前置知識(shí)為 C++ 本身的頭文件機(jī)制、編譯流程、Makefile 的基本認(rèn)知等內(nèi)容,所以不會(huì)贅述課程中出現(xiàn)的一些很基本的內(nèi)容。
目錄-
CMake 學(xué)習(xí)筆記
-
學(xué) C++ 從 CMake 學(xué)起
- 基本的 C++ 編譯相關(guān)的命令
- CMake 簡(jiǎn)介
- 靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
- CMake 中的靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
- CMake 中的子模塊
- CMake 中其他目標(biāo)選項(xiàng)
-
第三方庫(kù)
- 純頭文件引入
- 子模塊引入
- 引用系統(tǒng)中預(yù)安裝的庫(kù)
- 包管理器
-
現(xiàn)代 CMake 高級(jí)教程
-
命令行小技巧
-B和--build選項(xiàng)-D選項(xiàng)-G選項(xiàng)
- 添加源文件
-
項(xiàng)目變量配置
- 構(gòu)建模式
CMAKE_BUILD_TYPE project函數(shù)相關(guān)變量- C++ 標(biāo)準(zhǔn)變量
CMAKE_CXX_STANDARD
- 構(gòu)建模式
- 標(biāo)準(zhǔn) C++ 項(xiàng)目模板
-
鏈接庫(kù)文件
- 對(duì)象庫(kù)
add_library的默認(rèn)參數(shù)- 動(dòng)態(tài)庫(kù)無(wú)法鏈接靜態(tài)庫(kù)
- Windows 下的動(dòng)態(tài)鏈接庫(kù)
- 對(duì)象的屬性
-
鏈接第三方庫(kù)
- 以鏈接 tbb 為例
- Windows 使用
find_package查找第三方庫(kù) - 鏈接 Qt5
- 可選依賴
- 輸出和變量
-
變量與緩存
- CMake 緩存
- 緩存變量
- 緩存變量類型
- 繞開(kāi)緩存
-
跨平臺(tái)與編譯器
- 宏變量跨平臺(tái)
- 編譯器
- 條件與判斷
-
變量與作用域
- 變量的傳播
- 獨(dú)立作用域
- 變量的訪問(wèn)
-
其他小建議
- CCache:編譯加速緩存
- 添加 run 偽目標(biāo)
- 添加 configure 偽目標(biāo)
-
命令行小技巧
-
學(xué) C++ 從 CMake 學(xué)起
學(xué) C++ 從 CMake 學(xué)起
基本的 C++ 編譯相關(guān)的命令
編譯單文件為可執(zhí)行文件
g++/clang++ main.cpp -o main.out
查看 binary 文件的反匯編代碼
objdump -D binary | less
查看 binary 文件的共享庫(kù)依賴
ldd binary
CMake 簡(jiǎn)介
- 構(gòu)建系統(tǒng)的構(gòu)建系統(tǒng)。
- 跨平臺(tái),只需要一份 CMakeLists.txt 文件就可以在不同的平臺(tái)上使用相應(yīng)的構(gòu)建系統(tǒng)來(lái)構(gòu)建項(xiàng)目。
- 自動(dòng)檢測(cè)源文件和頭文件之間的依賴關(guān)系,導(dǎo)出到 Makefile 里。
- 自動(dòng)檢測(cè)編譯器,使用對(duì)應(yīng)的 flag。
靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
- 靜態(tài)庫(kù)相當(dāng)于直接把代碼插入到生成的可執(zhí)行文件中,會(huì)導(dǎo)致體積變大,但是只需要一個(gè)文件即可運(yùn)行
- 動(dòng)態(tài)庫(kù)則只在生成的可執(zhí)行文件中生成“插樁”函數(shù),當(dāng)可執(zhí)行文件被加載時(shí)會(huì)讀取指定目錄中的 .dll 文件,加載到內(nèi)存中空閑的位置,并且替換相應(yīng)的“插樁”指向的地址為加載后的地址,這個(gè)過(guò)程稱為重定向。這樣以后函數(shù)被調(diào)用就會(huì)跳轉(zhuǎn)到動(dòng)態(tài)加載的地址去。
CMake 中的靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)
使用 add_library 生成庫(kù)文件
add_library(test STATIC source1.cpp source2.cpp) # 生成靜態(tài)庫(kù) libtest.a
add_library(test SHARED source1.cpp source2.cpp) # 生成動(dòng)態(tài)庫(kù) libtest.so
創(chuàng)建庫(kù)之后,要在某個(gè)可執(zhí)行文件中使用該庫(kù),只需要:
target_link_libraries(myexec PUBLIC test)
CMake 中的子模塊
在根目錄的 CMakeLists.txt 文件中使用 add_subdirectory 添加子目錄,然后在子目錄中也寫(xiě)一個(gè) CMakeLists.txt,然后在其中定義庫(kù),所有的子模塊都可以使用這個(gè)庫(kù)。目錄結(jié)構(gòu)如下
project
├── CMakeLists.txt
├── hellolib
│?? ├── CMakeLists.txt
│?? ├── hello.cpp
│?? └── hello.h
└── main.cpp
其中根目錄下的 CMakeLists.txt 文件如下
cmake_minimum_required(VERSION 3.12)
project(hellocmake LANGUAGES CXX)
add_subdirectory(hellolib)
add_executable(a.out main.cpp)
target_link_libraries(a.out PUBLIC hellolib)
hellolib 子目錄下的 CMakeLists.txt 文件如下
add_library(hellolib STATIC hello.cpp)
target_include_directories(hellolib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) # 添加當(dāng)前目錄到 hellolib 的頭文件搜索路徑中, PUBLIC 表示傳播給 hellolib 的用戶
其中 target_include_directories 的作用是將當(dāng)前目錄添加到 hellolib 庫(kù)的頭文件搜索路徑中,這樣在 hellolib 庫(kù)中的頭文件就可以直接使用 #include "hello.h" 的方式來(lái)引用了,甚至可以用 #include <hello.h> 的方式來(lái)引用,因?yàn)?CMake 會(huì)自動(dòng)將當(dāng)前目錄添加到 hellolib 庫(kù)的頭文件搜索路徑中。
而其中 PUBLIC 的作用是將這個(gè)屬性傳播給 hellolib 庫(kù)的用戶,這樣 hellolib 的用戶也可以直接使用 #include "hello.h" 的方式來(lái)引用了,例如在根目錄下的 main.cpp 中可以直接使用 #include "hello.h" 的方式來(lái)引用。
如果不想讓 hellolib 的用戶自動(dòng)添加這個(gè)頭文件搜索路徑,可以使用 PRIVATE 屬性,這樣只有 hellolib 庫(kù)內(nèi)部才能使用 #include "hello.h" 的方式來(lái)引用。
CMake 中其他目標(biāo)選項(xiàng)
target_include_directories(myapp PUBLIC /usr/include/eigen3) # 添加頭文件搜索目錄
target_link_libraries(myapp PUBLIC hellolib) # 添加要鏈接的庫(kù)
target_add_definitions(myapp PUBLIC MY_MACRO=1) # 添加一個(gè)宏定義
target_add_definitions(myapp PUBLIC -DMY_MACRO=1) # 與 MY_MACRO=1 等價(jià)
target_compile_options(myapp PUBLIC -fopenmp) # 添加編譯器命令行選項(xiàng)
target_sources(myapp PUBLIC hello.cpp other.cpp) # 添加要編譯的源文件
現(xiàn)在已經(jīng)不推薦使用不針對(duì)特定目標(biāo)的命令了,例如 add_definitions、include_directories、link_libraries 等,而是使用 target_xxx 的方式來(lái)添加屬性,這樣可以針對(duì)特定的目標(biāo)添加屬性,而不是添加全局屬性。
第三方庫(kù)
純頭文件引入
這里是一些好用的 header-only 庫(kù):
- nothings/stb - 大名鼎鼎的 stb_image 系列,涵蓋圖像,聲音,字體等,只需單頭文件!
- Neargye/magic_enum - 枚舉類型的反射,如枚舉轉(zhuǎn)字符串等(實(shí)現(xiàn)方式很巧妙)
- g-truc/glm - 模仿 GLSL 語(yǔ)法的數(shù)學(xué)矢量/矩陣庫(kù)(附帶一些常用函數(shù),隨機(jī)數(shù)生成等)
- Tencent/rapidjson - 單純的 JSON 庫(kù),甚至沒(méi)依賴 STL(可定制性高,工程美學(xué)經(jīng)典)
- ericniebler/range-v3 - C++20 ranges 庫(kù)就是受到他啟發(fā)(完全是頭文件組成)
- fmtlib/fmt - 格式化庫(kù),提供 std::format 的替代品(需要 -DFMT_HEADER_ONLY)
- gabime/spdlog - 能適配控制臺(tái),安卓等多后端的日志庫(kù)(和 fmt 沖突!)
優(yōu)點(diǎn):簡(jiǎn)單方便,只需要把他們的 include 目錄或頭文件下載下來(lái),然后 include_directories(spdlog/include) 即可。
缺點(diǎn):函數(shù)直接實(shí)現(xiàn)在頭文件里,沒(méi)有提前編譯,從而需要重復(fù)編譯同樣內(nèi)容,編譯時(shí)間長(zhǎng)。
子模塊引入
直接把相應(yīng)的庫(kù)放到工程的根目錄,然后 add_subdirectory 即可。
這些庫(kù)能夠很好地支持作為子模塊引入:
- fmtlib/fmt - 格式化庫(kù),提供 std::format 的替代品
- gabime/spdlog - 能適配控制臺(tái),安卓等多后端的日志庫(kù)
- ericniebler/range-v3 - C++20 ranges 庫(kù)就是受到他啟發(fā)
- g-truc/glm - 模仿 GLSL 語(yǔ)法的數(shù)學(xué)矢量/矩陣庫(kù)
- abseil/abseil-cpp - 旨在補(bǔ)充標(biāo)準(zhǔn)庫(kù)沒(méi)有的常用功能
- bombela/backward-cpp - 實(shí)現(xiàn)了 C++ 的堆棧回溯便于調(diào)試
- google/googletest - 谷歌單元測(cè)試框架
- google/benchmark - 谷歌性能評(píng)估框架
- glfw/glfw - OpenGL 窗口和上下文管理
- libigl/libigl - 各種圖形學(xué)算法大合集
引用系統(tǒng)中預(yù)安裝的庫(kù)
可以通過(guò) find_package 命令尋找系統(tǒng)中的包/庫(kù):
find_package(fmt REQUIRED)
target_link_libraries(myexec PUBLIC fmt::fmt)
值得注意的是,這里不是簡(jiǎn)單的 fmt 而是 fmt::fmt,這是因?yàn)楝F(xiàn)代 CMake 認(rèn)為一個(gè)包 (package) 可以提供多個(gè)庫(kù),又稱組件 (components),比如 TBB 這個(gè)包,就包含了 tbb, tbbmalloc, tbbmalloc_proxy 這三個(gè)組件。
可以指定使用哪些組件,如下
find_package(TBB REQUIRED COMPONENTS tbb tbbmalloc REQUIRED)
target_link_libraries(myexec PUBLIC TBB::tbb TBB::tbbmalloc)
包管理器
Linux 可以用系統(tǒng)自帶的包管理器(如 apt)安裝 C++ 包,例如
sudo apt install libfmt-dev
Windows 則沒(méi)有自帶的包管理器。因此可以用跨平臺(tái)的 vcpkg:https://github.com/microsoft/vcpkg
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg install fmt:x64-windows
cd ..
cmake -B build -DCMAKE_TOOLCHAIN_FILE="%CD%/vcpkg/scripts/buildsystems/vcpkg.cmake"
現(xiàn)代 CMake 高級(jí)教程
現(xiàn)代 CMake 主要指 3.x 版本的 CMake,提供了更加方便的命令行指令和更加清晰簡(jiǎn)潔的語(yǔ)法。
命令行小技巧
-B 和 --build 選項(xiàng)
現(xiàn)代 CMake 提供了跨平臺(tái)的 -B 和 --build 指令,更加方便好用。
cmake -B build
cmake --build build -j4
cmake --build build --target install
CMake 項(xiàng)目的構(gòu)建分為兩步,
- 第一步是
cmake -B build,稱為配置階段(configure),這時(shí)只檢測(cè)環(huán)境并生成構(gòu)建規(guī)則
會(huì)在 build 目錄下生成本地構(gòu)建系統(tǒng)能識(shí)別的項(xiàng)目文件(Makefile 或是 .sln) - 第二步是
cmake --build build,稱為構(gòu)建階段(build),這時(shí)才實(shí)際調(diào)用編譯器來(lái)編譯代碼
-D 選項(xiàng)
在配置階段可以通過(guò) -D 設(shè)置緩存變量。第二次配置時(shí),之前的 -D 添加仍然會(huì)被保留。
# 設(shè)置安裝路徑為 /opt/openvdb-8.0(會(huì)安裝到 /opt/openvdb-8.0/lib/libopenvdb.so)
cmake -B build -DCMAKE_INSTALL_PREFIX=/opt/openvdb-8.0
# 設(shè)置構(gòu)建模式為發(fā)布模式(開(kāi)啟全部?jī)?yōu)化)
cmake -B build -DCMAKE_BUILD_TYPE=Release
# 第二次配置時(shí)沒(méi)有 -D 參數(shù),但是之前的 -D 設(shè)置的變量都會(huì)被保留
#(此時(shí)緩存 CMakeCache.txt 文件里仍有之前定義的 CMAKE_BUILD_TYPE 和 CMAKE_INSTALL_PREFIX)
cmake -B build
-G 選項(xiàng)
Linux 系統(tǒng)上的 CMake 默認(rèn)用是 Unix Makefiles 生成器;Windows 系統(tǒng)默認(rèn)是 Visual Studio 2019 生成器;MacOS 系統(tǒng)默認(rèn)是 Xcode 生成器。
可以用 -G 參數(shù)改用別的生成器,例如 cmake -G Ninja 會(huì)生成 Ninja 這個(gè)構(gòu)建系統(tǒng)的構(gòu)建規(guī)則。
Ninja 是一個(gè)高性能,跨平臺(tái)的構(gòu)建系統(tǒng),Linux、Windows、MacOS 上都可以用。
Ninja 是專為性能優(yōu)化的構(gòu)建系統(tǒng)(比 MSbuild 和 Makefile 效率要高),他和 CMake 結(jié)合都是行業(yè)標(biāo)準(zhǔn)了,推薦使用。
添加源文件
-
直接在
add_executable中添加源文件add_executable(myapp main.cpp hello.cpp) -
先創(chuàng)建目標(biāo),然后添加源文件
add_executable(myapp) target_sources(myapp PUBLIC main.cpp hello.cpp) -
使用 GLOB 自動(dòng)查找源文件
file(GLOB sources *.cpp *.h) add_executable(myapp ${sources})這種方法有一些缺點(diǎn),比如如果添加了新的源文件,需要重新運(yùn)行 CMake 才能生效。
可以使用 CONFIGURE_DEPENDS 選項(xiàng)來(lái)解決這個(gè)問(wèn)題,但是這個(gè)選項(xiàng)只有在 CMake 3.12 以上才支持。file(GLOB sources CONFIGURE_DEPENDS *.cpp *.h) add_executable(myapp ${sources})另外,現(xiàn)在這樣沒(méi)法遞歸搜索子目錄,如果要遞歸搜索子目錄,可以使用
file(GLOB_RECURSE)命令。file(GLOB_RECURSE sources CONFIGURE_DEPENDS *.cpp *.h) add_executable(myapp ${sources}) -
使用
aux_source_directory命令,可以自動(dòng)搜集需要的文件后綴名aux_source_directory(. sources) add_executable(myapp ${sources})這種方法和 GLOB 類似,也有一些缺點(diǎn),比如如果添加了新的源文件,需要重新運(yùn)行 CMake 才能生效。
另外,這種方法也沒(méi)法遞歸搜索子目錄,需要自己指定子目錄
aux_source_directory(. sources) aux_source_directory(./subdir sources) add_executable(myapp ${sources})
值得注意的一點(diǎn)是 .h 文件不添加到源文件中仍然可以正常編譯運(yùn)行,但是添加之后可以使得 IDE 中可以顯示文件,因此建議是將 .h 文件也添加到源文件中。
另外 GLOB_RECURSE 會(huì)把 build 目錄里生成的臨時(shí) .cpp 文件也加進(jìn)來(lái),一般建議將源碼統(tǒng)一放到 src 目錄中。
項(xiàng)目變量配置
構(gòu)建模式 CMAKE_BUILD_TYPE
Release、Debug、MinSizeRel、RelWithDebInfo 是 CMake 內(nèi)置的構(gòu)建模式,可以通過(guò) cmake -B build -DCMAKE_BUILD_TYPE=Release 來(lái)指定構(gòu)建模式,其具體含義如下
| Build Mode | Compiler Flags | Meaning |
|---|---|---|
| Release | -O3 -DNDEBUG | Optimized, no debug |
| Debug | -O0 -g | Debug symbols enabled |
| MinSizeRel | -Os -DNDEBUG | Optimized, minimal size |
| RelWithDebInfo | -O2 -g -DNDEBUG | Optimized, with debug symbols |
NDEBUG 宏會(huì)移除代碼中的 assert 語(yǔ)句。
CMake 中 CMAKE_BUILD_TYPE 的默認(rèn)值為 "",即不指定構(gòu)建模式,這時(shí)默認(rèn)使用 Debug 模式。如果想設(shè)置默認(rèn)模式為 Release 模式,可以在 CMakeLists.txt 中添加如下代碼
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
project 函數(shù)相關(guān)變量
CMake 中的 project 函數(shù)會(huì)定義一些變量,這些變量可以在 CMakeLists.txt 中使用,也可以在 C++ 代碼中使用。
project(myproject VERSION 1.0.0 LANGUAGES CXX)
message("PROJECT_NAME: ${PROJECT_NAME}")
message("PROJECT_VERSION: ${PROJECT_VERSION}")
message("PROJECT_SOURCE_DIR: ${PROJECT_SOURCE_DIR}")
message("PROJECT_BINARY_DIR: ${PROJECT_BINARY_DIR}")
message("CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}")
message("CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
message("myproject_SOURCE_DIR: ${myproject_SOURCE_DIR}")
message("myproject_SOURCE_DIR: ${${PROJECT_NAME}_SOURCE_DIR}") # 與上面一樣,展示了嵌套 $ 功能
PROJECT_SOURCE_DIR 代表最近一次調(diào)用 project 語(yǔ)句的 CMakeLists.txt 的目錄
CMAKE_CURRENT_SOURCE_DIR 代表當(dāng)前 CMakelists.txt 所在的目錄。
CMAKE_SOURCE_DIR 代表頂層 CMakeLists.txt 所在的目錄。(不建議使用,無(wú)法作為子項(xiàng)目使用)
更多變量和內(nèi)容可以查看 project 語(yǔ)句的官方文檔
https://cmake.org/cmake/help/latest/command/project.html
C++ 標(biāo)準(zhǔn)變量 CMAKE_CXX_STANDARD
set(CMAKE_CXX_STANDARD 17) # 設(shè)置 C++ 標(biāo)準(zhǔn)為 C++17
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 必須使用指定的標(biāo)準(zhǔn)
set(CMAKE_CXX_EXTENSIONS ON) # 啟用 GCC 特有的一些擴(kuò)展功能
project(myproject VERSION 1.0.0 LANGUAGES CXX)
上面幾條命令比較容易理解,值得注意的一點(diǎn)是 project 函數(shù)放在設(shè)置之后,這樣 CMake 可以在 project 函數(shù)里對(duì)編譯器進(jìn)行一些檢測(cè),看看他能不能支持 C++17 的特性。
標(biāo)準(zhǔn) C++ 項(xiàng)目模板
cmake_minimum_required(VERSION 3.15)
set(CMake_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
project(myproject LANGUAGES CXX)
if (PROJECT_BINARY_DIR STREQUAL PROJECT_SOURCE_DIR)
message(WARNING "In-source build detected! Please build out-of-source!")
endif()
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
if (WIN32)
add_definitions(-DNOMINMAX -D_USE_MATH_DEFINES)
endif()
if (NOT MSVC)
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
message(STATUS "Found ccache: ${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
endif()
endif()
鏈接庫(kù)文件
對(duì)象庫(kù)
參考鏈接https://www.scivision.dev/cmake-object-libraries
對(duì)象庫(kù)是 CMake 自創(chuàng)的,繞開(kāi)了編譯器和操作系統(tǒng)的各種繁瑣規(guī)則,保證了跨平臺(tái)統(tǒng)一性,類似于靜態(tài)庫(kù),但不生成 .a 文件,只由 CMake 本身記住該庫(kù)生成了哪些對(duì)象文件。在自己的項(xiàng)目中,推薦全部用對(duì)象庫(kù)來(lái)組織代碼。
add_library(mylib OBJECT lib.cpp)
add_executable(myapp main.cpp $<TARGET_OBJECTS:mylib>)
另外使用靜態(tài)庫(kù)時(shí) GCC 編譯器會(huì)自動(dòng)剔除沒(méi)有引用符號(hào)的對(duì)象,如果使用靜態(tài)庫(kù)下面的程序會(huì)僅輸出 world 而沒(méi)有 hello,對(duì)象庫(kù)則不會(huì)有這種問(wèn)題。
// lib.cpp
#include <cstdio>
static int unused = printf("hello\n");
// main.cpp
#include <cstdio>
int main() {
printf("world\n");
}
add_library 的默認(rèn)參數(shù)
當(dāng)不填寫(xiě) add_library 的靜態(tài)庫(kù)/動(dòng)態(tài)庫(kù)參數(shù)時(shí),CMake 會(huì)根據(jù) BUILD_SHARED_LIBS 變量來(lái)決定是生成靜態(tài)庫(kù)還是動(dòng)態(tài)庫(kù),未指定 BUILD_SHARED_LIBS 時(shí)默認(rèn)生成靜態(tài)庫(kù)。
可以通過(guò)命令行參數(shù)或者 CMake 語(yǔ)句來(lái)指定 BUILD_SHARED_LIBS 變量:
cmake -B build -DBUILD_SHARED_LIBS:BOOL=ON
if (NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS OFF)
endif()
動(dòng)態(tài)庫(kù)無(wú)法鏈接靜態(tài)庫(kù)
當(dāng)我們要編譯一個(gè) so 提供給外部使用,這個(gè) so 本身依賴一些第三方庫(kù)。但是我們卻希望 so 的使用者不用關(guān)心該 so 對(duì)其他庫(kù)的依賴。很自然的是會(huì)想到在編譯 so 的時(shí)候把依賴到的第三方庫(kù)靜態(tài)鏈接進(jìn)來(lái)。
然而靜態(tài)庫(kù)中的代碼位置都是確定的,而動(dòng)態(tài)庫(kù)中的代碼位置是不確定的,因此動(dòng)態(tài)庫(kù)無(wú)法鏈接靜態(tài)庫(kù)。通過(guò)將靜態(tài)庫(kù)也編譯成位置無(wú)關(guān)的代碼(Position Independent Code,PIC),就可以解決這個(gè)問(wèn)題,實(shí)現(xiàn)這一點(diǎn)有兩種方式,一種是設(shè)置全局變量,另一種是設(shè)置目標(biāo)變量。
# 設(shè)置全局變量
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
add_library(mylib STATIC lib.cpp)
add_library(mylib_shared SHARED lib.cpp)
target_link_libraries(mylib_shared PRIVATE mylib)
# 設(shè)置目標(biāo)變量
add_library(mylib STATIC lib.cpp)
set_target_properties(mylib_shared PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_library(mylib_shared SHARED lib.cpp)
target_link_libraries(mylib_shared PRIVATE mylib)
Windows 下的動(dòng)態(tài)鏈接庫(kù)
需要在需要導(dǎo)出的內(nèi)容前面加上 __declspec(dllexport),在需要導(dǎo)入的內(nèi)容前面加上 __declspec(dllimport)。
// lib.h
# pragma once
#ifdef _MSC_VER
#ifdef BUILD_SHARED_LIBS
#define LIB_API __declspec(dllexport)
#else
#define LIB_API __declspec(dllimport)
#endif
#else
#define LIB_API
#endif
LIB_API void hello();
// lib.cpp
#include "lib.h"
#include <cstdio>
LIB_API void hello() { printf("hello\n"); }
另外由于 Windows 不支持 RPATH,因此動(dòng)態(tài)庫(kù)的 dll 文件需要放在可執(zhí)行文件的同一目錄下,或者放在系統(tǒng)目錄下,在子模塊的情況下就需要將子模塊的輸出目錄設(shè)置為可執(zhí)行文件的同一目錄下。
add_library(mylib SHARED lib.cpp)
set_target_properties(TARGET mylib PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR})
set_target_properties(TARGET mylib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR})
對(duì)象的屬性
set_property 命令可以設(shè)置對(duì)象的屬性,例如
add_executable(myapp main.cpp)
set_property(TARGET myapp PROPERTY CXX_STANDARD 17) # 采用 C++ 標(biāo)準(zhǔn)為 C++17 (默認(rèn) 11)
set_property(TARGET myapp PROPERTY CXX_STANDARD_REQUIRED ON) # 必須使用指定的標(biāo)準(zhǔn) (默認(rèn) OFF)
set_property(TARGET myapp PROPERTY WIN32_EXECUTABLE ON) # 生成 Windows 窗口程序,不啟動(dòng)控制臺(tái),只有 GUI 界面(默認(rèn) OFF)
set_property(TARGET myapp PROPERTY LINK_WHAT_YOU_USE ON) # 告訴編譯器不要自動(dòng)剔除沒(méi)有使用的符號(hào)(默認(rèn) OFF)
set_property(TARGET myapp PROPERTY LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 設(shè)置動(dòng)態(tài)庫(kù)文件輸出目錄
set_property(TARGET myapp PROPERTY ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 設(shè)置靜態(tài)庫(kù)文件輸出目錄
set_property(TARGET myapp PROPERTY RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # 設(shè)置可執(zhí)行文件輸出目錄
也可以使用 set_target_properties 命令來(lái)一次設(shè)置多條對(duì)象的屬性,例如
add_executable(myapp main.cpp)
set_target_properties(myapp PROPERTIES
CXX_STANDARD 17 # 采用 C++ 標(biāo)準(zhǔn)為 C++17 (默認(rèn) 11)
CXX_STANDARD_REQUIRED ON # 必須使用指定的標(biāo)準(zhǔn) (默認(rèn) OFF)
WIN32_EXECUTABLE ON # 生成 Windows 窗口程序,不啟動(dòng)控制臺(tái),只有 GUI 界面(默認(rèn) OFF)
LINK_WHAT_YOU_USE ON # 告訴編譯器不要自動(dòng)剔除沒(méi)有使用的符號(hào)(默認(rèn) OFF)
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib # 設(shè)置動(dòng)態(tài)庫(kù)文件輸出目錄
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib # 設(shè)置靜態(tài)庫(kù)文件輸出目錄
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin # 設(shè)置可執(zhí)行文件輸出目錄
)
也可以設(shè)置全局的屬性
set(CMAKE_CXX_STANDARD 17) # 設(shè)置 C++ 標(biāo)準(zhǔn)為 C++17 (默認(rèn) 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) # 必須使用指定的標(biāo)準(zhǔn) (默認(rèn) OFF)
set(CMAKE_WIN32_EXECUTABLE ON) # 生成 Windows 窗口程序,不啟動(dòng)控制臺(tái),只有 GUI 界面(默認(rèn) OFF)
set(CMAKE_LINK_WHAT_YOU_USE ON) # 告訴編譯器不要自動(dòng)剔除沒(méi)有使用的符號(hào)(默認(rèn) OFF)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 設(shè)置動(dòng)態(tài)庫(kù)文件輸出目錄
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) # 設(shè)置靜態(tài)庫(kù)文件輸出目錄
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # 設(shè)置可執(zhí)行文件輸出目錄
add_executable(myapp main.cpp)
鏈接第三方庫(kù)
以鏈接 tbb 為例
例如需要鏈接 tbb 庫(kù),直接鏈接的話 CMake 會(huì)在系統(tǒng)的庫(kù)目錄中查找 tbb,但是在 Windows 上沒(méi)有系統(tǒng)的庫(kù)目錄,因此需要指定 tbb 的位置,而且頭文件也需要指定,非常麻煩。
有一個(gè)優(yōu)雅的做法是使用現(xiàn)代的 find_package 命令,這個(gè)命令會(huì)查找 /usr/lib/cmake/TBB/TBBConfig.cmake 這個(gè)配置文件(地址不一定一樣,大概就是查找?guī)斓奈募哪夸洠⒏鶕?jù)里面的配置信息創(chuàng)建 TBB::tbb 這個(gè)偽對(duì)象(他實(shí)際指向真正的 tbb 庫(kù)文件路徑 /usr/lib/libtbb.so)。
find_package(TBB CONFIG REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE TBB::tbb)
其中 CONFIG 的作用是只查找 TBBConfig.cmake 這個(gè)配置文件,不查找 FindTBB.cmake 這個(gè)腳本文件(項(xiàng)目作者常把他塞在 cmake/ 目錄里并添加到 CMAKE_MODULE_PATH),建議加,加上能保證項(xiàng)目尋找包的 .cmake 腳本與系統(tǒng)自帶的版本是適配的,而不是項(xiàng)目作者當(dāng)年下載的版本的 .cmake 腳本。
- 不加 CONFIG:會(huì)先查找 TBBConfig.cmake,如果找不到再查找 FindTBB.cmake
- 加了 CONFIG:只查找 TBBConfig.cmake,不查找 FindTBB.cmake
這個(gè)配置文件一般由庫(kù)的作者提供,通過(guò)包管理器安裝包后也會(huì)自動(dòng)安裝這個(gè)文件,其內(nèi)容大概如下
# Create imported target TBB::tbb
add_library(TBB::tbb SHARED IMPORTED)
set_target_properties(TBB::tbb PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "\$<\$<CONFIG:DEBUG>:TBB_USE_DEBUG>"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
# Create imported target TBB::tbbmalloc
add_library(TBB::tbbmalloc SHARED IMPORTED)
set_target_properties(TBB::tbbmalloc PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "\$<\$<CONFIG:DEBUG>:TBB_USE_DEBUG>"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
# Create imported target TBB::tbbmalloc_proxy
add_library(TBB::tbbmalloc_proxy SHARED IMPORTED)
set_target_properties(TBB::tbbmalloc_proxy PROPERTIES
INTERFACE_COMPILE_DEFINITIONS "\$<\$<CONFIG:DEBUG>:TBB_USE_DEBUG>"
INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
)
Windows 使用 find_package 查找第三方庫(kù)
Windows 上并沒(méi)有庫(kù)文件目錄,因此需要手動(dòng)指定 TBBConfig.cmake 文件的位置,有許多方式可以做到這一點(diǎn),例如設(shè)置 CMAKE_MODULE_PATH 變量,也可以設(shè)置 TBB_DIR 變量,可以在 CMakeLists.txt 中設(shè)置,也可以在命令行中設(shè)置(推薦,因?yàn)槭怯脩粝嚓P(guān)的內(nèi)容,每個(gè)人的安裝路徑都不一樣),例如
cmake -B build -DTBB_DIR="C:/Program Files/TBB/cmake"
鏈接 Qt5
直接用以下方式鏈接 Qt5 會(huì)報(bào)錯(cuò)
find_package(Qt5 REQUIRED)
add_executable(myapp main.cpp)
報(bào)錯(cuò)信息為
CMake Error at CMakeLists.txt:6 (find_package):
Found package configuration file:
/usr/lib/cmake/Qt5/Qt5Config.cmake
but it set Qt5_FOUND to FALSE so package "Qt5" is considered to be NOT
FOUND. Reason given by package:
The Qt5 package requires at least one component
原因是 Qt5 具有多個(gè)組件,需要指定鏈接哪些組件,find_package 生成的偽對(duì)象 (imported target) 都按照“包名:: 組件名”的格式命名。例如
find_package(Qt5 COMPONENTS Widgets Gui REQUIRED)
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE Qt5::Widgets Qt5::Gui)
可選依賴
如果希望某個(gè)庫(kù)是可選的,可以不使用 find_package 的 REQUIRED 參數(shù),然后如下定義相應(yīng)的宏
find_package(TBB CONFIG)
if (TBB_FOUND) # 也可以用 if (TARGET TBB::tbb)
message(STATUS "TBB found")
add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE TBB::tbb)
target_compile_definitions(myapp PRIVATE WITH_TBB)
else()
message(WARNING "TBB not found! using serial version")
于是在 cpp 文件中可以這樣寫(xiě)
#include <cstdio>
#ifdef WITH_TBB
#include <tbb/parallel_for.h>
#endif
int main() {
#ifdef WITH_TBB
tbb::parallel_for(0, 100, [](int i) { printf("%d\n", i); });
#else
for (int i = 0; i < 100; i++) printf("%d\n", i);
#endif
輸出和變量
message 命令可以輸出信息,例如
message(STATUS "STATUS means status info with -- prefix")
message(WARNING "WARNING means warning info")
message(AUTHOR_WARNING "AUTHOR_WARNING is only useful for project authors and can be closed by -Wno-dev")
message(SEND_ERROR "SEND_ERROR means error info and continue to run")
message(FATAL_ERROR "FATAL_ERROR means error info and stop running")
message 也可以打印變量的值,例如
set(myvar "hello world")
message(STATUS "myvar = ${myvar}") # result is "myvar = hello world"
set(myvar hello world)
message(STATUS "myvar = ${myvar}") # result is "myvar = hello;world"
變量與緩存
CMake 緩存
CMake 會(huì)自動(dòng)將一些編譯器和 C++ 特性等內(nèi)容檢測(cè)完后緩存到 CMakeCache.txt 文件中,這樣下次運(yùn)行 CMake 時(shí)就不用再檢測(cè)了,直接讀取緩存文件即可,這樣可以加快 CMake 的運(yùn)行速度。
前面用到的 find_package 命令就是一個(gè)例子,他會(huì)將檢測(cè)到的庫(kù)的路徑緩存到 CMakeCache.txt 文件中,這樣下次運(yùn)行 CMake 時(shí)就不用再檢測(cè)了。
緩存雖好,但是很多時(shí)候情況有變需要更新緩存,會(huì)導(dǎo)致很多 CMake 出錯(cuò)的情況,這時(shí)很多人會(huì)經(jīng)典的刪 build 大法,但是這樣需要完全重新構(gòu)建,非常耗時(shí),可以嘗試只刪除 CMakeCache.txt 文件,然后重新運(yùn)行 CMake -B build,這樣就可以更新緩存了。
緩存變量
可以用如下方式設(shè)置緩存變量
set(myvar "hello world" CACHE STRING "this is the docstring of myvar") # The last string is a docstring
message(STATUS "myvar = ${myvar}") # result is "myvar = hello world"
這里有一個(gè)坑點(diǎn),更新 CMakeCache.txt 文件后,CMakeLists.txt 中的變量并不會(huì)自動(dòng)更新,需要重新運(yùn)行 CMake 才會(huì)更新,這時(shí)可以使用 FORCE 參數(shù)來(lái)強(qiáng)制更新變量
sett(myvar "hello world" CACHE STRING "this is the docstring of myvar" FORCE) # The last string is a docstring
message(STATUS "myvar = ${myvar}") # result is "myvar = hello world"
其實(shí)也可以通過(guò)命令行參數(shù)來(lái)更新
cmake -B build -Dmyvar="goodbye world"
另外還可以通過(guò)圖形界面來(lái)編輯緩存變量,Linux 上可以使用 ccmake 命令,Windows 上可以使用 cmake-gui 命令。
最后還可以通過(guò)直接編輯 CMakeCache.txt 文件來(lái)更新緩存變量,該文件被設(shè)置為文本文件就是可供用戶手工編輯或者被第三方軟件打開(kāi)并解析的。
緩存變量類型
緩存變量由如下類型
- STRING 字符串,例如 “hello, world”
- FILEPATH 文件路徑,例如 “C:/vcpkg/scripts/buildsystems/vcpkg.cmake”
- PATH 目錄路徑,例如 “C:/Qt/Qt5.14.2/msvc2019_64/lib/cmake/”
- BOOL 布爾值,只有兩個(gè)取值:ON 或 OFF。
注意:TRUE 和 ON 等價(jià),F(xiàn)ALSE 和 OFF 等價(jià);YES 和 ON 等價(jià),NO 和 OFF 等價(jià)
CMake 對(duì) BOOL 類型的緩存變量的 set 指令提供了一個(gè)簡(jiǎn)寫(xiě) option,例如
add_executable(myapp main.cpp)
option(WITH_TBB "set to ON to enable TBB, OFF disale TBB" ON)
if (WITH_TBB)
find_package(TBB CONFIG REQUIRED)
target_link_libraries(myapp PRIVATE TBB::tbb)
target_compile_definitions(myapp PRIVATE WITH_TBB)
endif()
跟前面一樣,容易犯的一個(gè)經(jīng)典錯(cuò)誤就是直接改 CMakeLists.txt 文件,然后重新運(yùn)行 CMake,這樣是不會(huì)更新緩存變量的。官方推薦做法是使用 -DTBB:BOOL=ON/OFF 命令行參數(shù)來(lái)更新緩存變量。
繞開(kāi)緩存
既然緩存有這么多坑,那么我們就可以繞開(kāi)緩存,直接使用帶普通變量的默認(rèn)值來(lái)達(dá)到相同的效果,例如
if (NOT DEFINED WITH_TBB)
set(WITH_TBB ON)
endif()
一般來(lái)講,CMake 自帶的變量都會(huì)用這個(gè)方式來(lái)設(shè)置,但是就是會(huì)在 ccmake 中看不到。
跨平臺(tái)與編譯器
宏變量跨平臺(tái)
可以根據(jù)操作系統(tǒng)不同定義不同的宏變量,例如
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
target_compile_definitions(main PUBLIC MY_NAME="Bill Gates")
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
target_compile_definitions(main PUBLIC MY_NAME="Linus Torvalds")
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_compile_definitions(main PUBLIC MY_NAME="Steve Jobs")
endif()
這樣代碼中就可以
#include <cstdio>
int main() {
#ifdef MY_NAME
printf("Hello, %s!\n", MY_NAME);
#else
printf("Hello, world!\n");
#endif
}
前面的 CMake 代碼使用簡(jiǎn)寫(xiě)變量可讀性會(huì)稍微強(qiáng)一點(diǎn)
if (WIN32)
target_compile_definitions(main PUBLIC MY_NAME="Bill Gates")
elseif (UNIX AND NOT APPLE)
target_compile_definitions(main PUBLIC MY_NAME="Linus Torvalds")
elseif (APPLE)
target_compile_definitions(main PUBLIC MY_NAME="Steve Jobs")
endif()
還可以使用 生成器表達(dá)式 簡(jiǎn)化為一條語(yǔ)句
target_compile_definitions(main PUBLIC
$<$<PLATFORM_ID:Windows>:MY_NAME="Bill Gates">
$<$<PLATFORM_ID:Linux>:MY_NAME="Linus Torvalds">
$<$<PLATFORM_ID:Darwin>:MY_NAME="Steve Jobs">)
# using comma to separate
target_compile_definitions(main PUBLIC
$<$<PLATFORM_ID:Windows>:MY_NAME="DOS-like">,
$<$<PLATFORM_ID:Linux,Darwin,FreeBSD>:MY_NAME="Unix-like">
編譯器
判斷所使用的編譯器,如下
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
message(STATUS "using gcc")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
message(STATUS "using MSVC")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "using clang")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "NVIDIA")
message(STATUS "using nvcc")
endif()
從命令行指定編譯器
cmake -B build -DCMAKE_CXX_COMPILER="/usr/bin/clang++"
也可以通過(guò)環(huán)境變量指定編譯器
export CC=/usr/bin/clang
export CXX=/usr/bin/clang++
cmake -B build
條件與判斷
if 語(yǔ)句中的變量會(huì)自動(dòng)展開(kāi),無(wú)需添加 ${},例如
set(myvar "hello world")
if (myvar STREQUAL "hello world")
message(STATUS "myvar is hello world")
endif()
如果要加 ${},則需要用引號(hào)括起來(lái)(否則因?yàn)槠涮厥獾囊?guī)則會(huì)有一些其奇奇怪怪的問(wèn)題),例如
set(myvar "hello world")
if ("${myvar}" STREQUAL "hello world")
message(STATUS "myvar is hello world")
endif()
變量與作用域
變量的傳播
變量的傳播規(guī)則:父會(huì)傳遞給子,子不會(huì)傳遞給父,兄弟之間不會(huì)傳遞。
如果子模塊需要向父模塊傳遞變量,可以使用 set 命令的 PARENT_SCOPE 參數(shù),例如
set(myvar "hello world" PARENT_SCOPE)
緩存變量全局可見(jiàn),會(huì)傳播到整個(gè)項(xiàng)目中。
獨(dú)立作用域
- include 的 XXX.cmake 沒(méi)有獨(dú)立作用域
- add_subdirectory 的 CMakeLists.txt 有獨(dú)立作用域
- macro 沒(méi)有獨(dú)立作用域
- function 有獨(dú)立作用域(因此 PARENT_SCORE 也可以用于 function 的返回值)
更多內(nèi)容可以參考以下鏈接
https://cmake.org/cmake/help/v3.16/command/set.html
https://blog.csdn.net/Calvin_zhou/article/details/104060927
變量的訪問(wèn)
用 ${xx} 訪問(wèn)的是局部變量,局部變量服從剛剛所說(shuō)的父子模塊傳播規(guī)則。
用 $ENV{xx} 訪問(wèn)的是環(huán)境變量,環(huán)境變量是全局的,不服從父子模塊傳播規(guī)則。
message(STATUS "PATH = $ENV{PATH}")
用 $CACHE{xx} 訪問(wèn)的是緩存變量,緩存變量是全局的,不服從父子模塊傳播規(guī)則。
message(STATUS "CMAKE_CXX_COMPILER_ID = $CACHE{CMAKE_CXX_COMPILER_ID}")
${XX} 找不到局部變量時(shí)會(huì)去找緩存變量,如果緩存變量也找不到,就會(huì)報(bào)錯(cuò)。
if (DEFINED myvar) 會(huì)判斷 myvar 是否被定義,而 if (myvar) 會(huì)判斷 myvar 的值是否為空,前者即使 myvar 的值為空也會(huì)返回 true。
if (DEFINED ENV{XX}) 判斷環(huán)境變量是否被定義,if (DEFINED CACHE{XX}) 判斷緩存變量是否被定義。
其他小建議
CCache:編譯加速緩存
CCache 官方網(wǎng)站:https://ccache.dev/
將所有的編譯鏈接指令前加上 ccache 即可,例如
cmake_minimum_required(VERSION 3.15)
project(hellocmake)
find_program(CCACHE_PROGRAM ccache)
if (CCACHE_PROGRAM)
message(STATUS "Found ccache: ${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CCACHE_PROGRAM}")
endif()
添加 run 偽目標(biāo)
創(chuàng)建一個(gè) run 偽目標(biāo),執(zhí)行 main 的可執(zhí)行文件,例如
add_executable(main main.cpp)
add_custom_target(run
COMMAND $<TARGET_FILE:main>
WORKING_DIRECTORY ${CMAKE_PROJECT_DIR}
)
然后就可以通過(guò)以下命令運(yùn)行程序了
cmake --build build --target run
其最大的好處是跨平臺(tái),不用考慮在 Windows 上使用 build\main.exe 還是在 Linux 上使用 build/main,CMake 會(huì)自動(dòng)處理這個(gè)問(wèn)題。
添加 configure 偽目標(biāo)
創(chuàng)建一個(gè) configure 偽目標(biāo),可視化修改緩存,好處同樣是跨平臺(tái)。
if (CMAKE_EDIT_COMMAND)
add_custom_target(configure
COMMAND ${CMAKE_EDIT_COMMAND}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
endif()
總結(jié)
以上是生活随笔為你收集整理的CMake 学习笔记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: B612咔叽怎么开启镜像模式
- 下一篇: java信息管理系统总结_java实现科