日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > windows >内容正文

windows

CMake 学习笔记

發(fā)布時(shí)間:2024/1/3 windows 39 coder
生活随笔 收集整理的這篇文章主要介紹了 CMake 学习笔记 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

CMake 學(xué)習(xí)筆記

CMake 已經(jīng)是 C++ 構(gòu)建系統(tǒng)的事實(shí)標(biāo)準(zhǔn)。

主要是對(duì)小彭老師的 C++ 視頻課程中 CMake 相關(guān)部分的一些筆記和整理,視頻鏈接如下

  1. 學(xué) C++ 從 CMake 學(xué)起
  2. 現(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
      • 標(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é)起

基本的 C++ 編譯相關(guān)的命令

編譯單文件為可執(zhí)行文件

g++/clang++ main.cpp -o main.out

查看 binary 文件的反匯編代碼

objdump -D binary | less

查看 binary 文件的共享庫(kù)依賴

ldd binary

CMake 簡(jiǎn)介

  1. 構(gòu)建系統(tǒng)的構(gòu)建系統(tǒng)。
  2. 跨平臺(tái),只需要一份 CMakeLists.txt 文件就可以在不同的平臺(tái)上使用相應(yīng)的構(gòu)建系統(tǒng)來(lái)構(gòu)建項(xiàng)目。
  3. 自動(dòng)檢測(cè)源文件和頭文件之間的依賴關(guān)系,導(dǎo)出到 Makefile 里。
  4. 自動(dòng)檢測(cè)編譯器,使用對(duì)應(yīng)的 flag。

靜態(tài)庫(kù)和動(dòng)態(tài)庫(kù)

  1. 靜態(tài)庫(kù)相當(dāng)于直接把代碼插入到生成的可執(zhí)行文件中,會(huì)導(dǎo)致體積變大,但是只需要一個(gè)文件即可運(yùn)行
  2. 動(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_definitionsinclude_directorieslink_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)建分為兩步,

  1. 第一步是 cmake -B build,稱為配置階段(configure),這時(shí)只檢測(cè)環(huán)境并生成構(gòu)建規(guī)則
    會(huì)在 build 目錄下生成本地構(gòu)建系統(tǒng)能識(shí)別的項(xiàng)目文件(Makefile 或是 .sln)
  2. 第二步是 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)了,推薦使用。

添加源文件

  1. 直接在 add_executable 中添加源文件

    add_executable(myapp main.cpp hello.cpp)
    
  2. 先創(chuàng)建目標(biāo),然后添加源文件

    add_executable(myapp)
    target_sources(myapp PUBLIC main.cpp hello.cpp)
    
  3. 使用 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})
    
  4. 使用 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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。