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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

现代 CMake 模块化项目管理指南

發(fā)布時間:2024/1/8 windows 38 coder
生活随笔 收集整理的這篇文章主要介紹了 现代 CMake 模块化项目管理指南 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

現(xiàn)代 CMake 模塊化項目管理指南

參考小彭老師的視頻教程整理筆記,學(xué)習(xí)同時方便快速查閱,視頻鏈接如下

【公開課】現(xiàn)代 CMake 模塊化項目管理指南【C/C++】

對應(yīng)課程 PPT 和源碼見

https://github.com/parallel101/course

文件/目錄組織規(guī)范

完整案例參考源碼倉庫
https://github.com/parallel101/course/tree/master/16/00

推薦的目錄組織方式

.
├── biology
│?? ├── CMakeLists.txt
│?? ├── include
│?? │?? └── biology
│?? │??     └── Animal.h
│?? └── src
│??     └── Animal.cpp
├── CMakeLists.txt
└── pybmain
    ├── CMakeLists.txt
    ├── include
    │?? └── pybmain
    │??     └── myutils.h
    └── src
        └── main.cpp

第一點,劃分了 biology 和 pybmain 兩個子項目,每個子項目在各自目錄下各有自己的 CMakeLists.txt 文件。

第二點,所有子項目中都被劃分為了 include 和 src 兩個子目錄,分別用來放頭文件和源碼文件,而其中 include 目錄又套了一層項目名,這樣可以避免頭文件名稱沖突。子項目的 CMakeLists.txt 文件中需要使用

target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)

源碼文件中這樣寫,

#include <biology/Animal.h>
#include <pybmain/myutils.h>

第三點,推薦每個模塊都有自己的命名空間,頭文件中需要

#pragma once
namespace biology {
class Animal {
//...
};
}

源碼文件中需要

#include <biology/Animal.h>

namespace biology {
//...
};

根項目的 CMakeLists.txt 配置

cmake_minimum_required(VERSION 3.15)

if (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

set(CMake_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

project(CppCMakeDemo LANGUAGES CXX)

add_subdirectory(biology)
add_subdirectory(pybmain)

最后兩行是關(guān)鍵,用來添加子項目,會調(diào)用子項目的 CMakeLists.txt 文件。

子項目的 CMakeLists.txt 配置

庫文件的配置

# biology/CMakeLists.txt
file(GLOB_RECURSE srcs CONFIGURE_DEPENDS src/*.cpp include/*.h)
add_library(biology STATIC ${srcs})
target_include_directories(biology PUBLIC include)

首先使用 GLOB_RECURSE 命令獲取所有源碼文件,然后使用 add_library 命令添加靜態(tài)庫,最后使用 target_include_directories 命令添加頭文件搜索路徑。

  1. PUBLIC 修飾符表示這個頭文件搜索路徑會被暴露給其他依賴這個庫的項目,鏈接了 biology 庫的 pybmain 項目也可以共享這個路徑。
  2. 注意到我們將 .h 文件也一并添加到了 add_library 命令中,這樣可以確保 .h 文件也會被添加到 IDE 中,方便查看。
  3. GLOB_RECURSE 相比 GLOB 允許匹配嵌套的目錄。
  4. CONFIGURE_DEPENDS 選項表示如果源碼文件發(fā)生變化,cmake --build build 會檢測目錄是否更新,有新文件則自動重新運行 cmake -B build 重新配置項目。

可執(zhí)行文件的配置

# pybmain/CMakeLists.txt
file(GLOB_RECURSE srcs CONFIGURE_DEPENDS src/*.cpp include/*.h)
add_executable(pybmain ${srcs})
target_include_directories(pybmain PUBLIC include)

target_link_libraries(pybmain PUBLIC biology)

基本和庫文件的配置一致,只是使用 add_executable 命令添加可執(zhí)行文件,使用 target_link_libraries 命令鏈接庫文件。

CMake 的 include 功能

和 C/C++ 的 #include 一樣,CMake 也有一個 include 命令,CMake 會在 CMAKE_MODULE_PATH 中搜索相應(yīng)的 XXX.cmake 文件。

# ./CMakeLists.txt
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

project(CppCMakeDemo LANGUAGES CXX)

include(MyUsefulFuncs)
# cmake/MyUsefulFuncs.cmake
macro (my_add_target name type)
    # 用法:my_add_target(pybmain EXECUTABLE)
    file(GLOB_RECURSE srcs CONFIGURE_DEPENDS src/*.cpp src/*.h)
    if ("${type}" MATCHES "EXECUTABLE")
        add_executable(${name} ${srcs})
    else()
        add_library(${name} ${type} ${srcs})
    endif()
    target_include_directories(${name} PUBLIC include)
endmacro()

set(SOME_USEFUL_GLOBAL_VAR    ON)
set(ANOTHER_USEFUL_GLOBAL_VAR OFF)

macro 和 function 的區(qū)別

  • macro 相當于直接把代碼粘貼過去,直接訪問調(diào)用者的作用域。這里寫的相對路徑 include 和 src,是基于調(diào)用者所在路徑。
  • function 則是會創(chuàng)建一個閉包,優(yōu)先訪問定義者的作用域。這里寫的相對路徑 include 和 src,則是基于定義者所在路徑。

include 和 add_subdirectory 的區(qū)別

  • include 相當于直接把代碼粘貼過去,直接訪問調(diào)用者的作用域。這里創(chuàng)建的變量和外面共享,直接 set(key val) 則調(diào)用者也有 ${key} 這個變量了。
  • function 中則是基于定義者所在路徑,優(yōu)先訪問定義者的作用域。這里需要 set(key val PARENT_SCOPE) 才能修改到外面的變量。

第三方庫/依賴項配置

主要講解 find_package 命令,其官方文檔為
https://cmake.org/cmake/help/latest/command/find_package.html

用法舉例

# 查找名為 OpenCV 的包,找不到不報錯,事后可以通過 ${OpenCV_FOUND} 查詢是否找到。
find_package(OpenCV)
# 查找名為 OpenCV 的包,找不到不報錯,也不打印任何信息。
find_package(OpenCV QUIET)
# 查找名為 OpenCV 的包,找不到就報錯(并終止 cmake 進程,不再繼續(xù)往下執(zhí)行)。
find_package(OpenCV REQUIRED)    # 最常見用法
# 查找名為 OpenCV 的包,找不到就報錯,且必須具有 OpenCV::core 和 OpenCV::videoio 這兩個組件,如果沒有這兩個組件也會報錯。
find_package(OpenCV REQUIRED COMPONENTS core videoio)
# 查找名為 OpenCV 的包,找不到就報錯,可具有 OpenCV::core 和 OpenCV::videoio 這兩個組件,沒有這兩組件不會報錯,通過 ${OpenCV_core_FOUND} 查詢是否找到 core 組件。
find_package(OpenCV REQUIRED OPTIONAL_COMPONENTS core videoio)

find_package 原理

實際上 find_package(OpenCV) 是在找一個叫做 OpenCVConfig.cmake 的文件,這個文件是 OpenCV 項目提供的,用來告訴 cmake OpenCV 的安裝路徑和組件信息。

這個包配置文件由第三方庫作者提供,在安裝這個包時一并安裝到系統(tǒng)中的,一般的約定是將其安裝到 /usr/lib/cmake/XXX/ 目錄下,其中 XXX 為包名。

Windows 系統(tǒng)下的搜索路徑

<prefix>/
<prefix>/cmake/
<prefix>/<name>*/
<prefix>/<name>*/cmake/
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/cmake/

其中 <prefix> 是變量 ${CMAKE_PREFIX_PATH},Windows 平臺默認為 C:/Program Files,<name> 是在 find_package(<name> REQUIRED) 命令中指定的包名,<arch> 是系統(tǒng)的架構(gòu)名。

Unix 系統(tǒng)下的搜索路徑

<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/(lib/<arch>|lib*|share)/<name>*/
<prefix>/(lib/<arch>|lib*|share)/<name>*/cmake/
<prefix>/<name>*/(lib/<arch>|lib*|share)/cmake/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/
<prefix>/<name>*/(lib/<arch>|lib*|share)/<name>*/cmake/

其中 <prefix> 是變量 ${CMAKE_PREFIX_PATH},Unix 平臺默認為 /usr,<name> 是你在 find_package(<name> REQUIRED) 命令中指定的包名,<arch> 是系統(tǒng)的架構(gòu),例如 x86_64-linux-gnui386-linux-gnu(用于伺候 Ubuntu 喜歡把庫文件套娃在 /usr/lib/x86_64-linux-gnu 目錄下)。

另外 <name> 可以有額外后綴,例如 find_package(Qt5) 可以在 Qt5.12.1/cmake 或者 Qt5xxx/cmake 目錄下找到 Qt5Config.cmake 文件。

非標準路徑安裝的庫

想讓 CMake 找到非標準路徑安裝的庫,本質(zhì)上是定義好 NAME_DIR 變量,告訴 CMake 庫文件的路徑,然后再調(diào)用 find_package 命令。

例如可以直接在 CMakeLists.txt 中定義該變量,

# pybmain/CMakeLists.txt
set(OpenCV_DIR "/home/pyb/opencv/build")
find_package(OpenCV REQUIRED)

這種方式對該項目有效,但是不便于開源分發(fā),因其路徑直接寫死在 CMakeLists.txt 中,其他人要用的時候就會找不到該路徑下的 OpenCVConfig.cmake 文件。

也可以設(shè)置環(huán)境變量

export OpenCV_DIR="/home/pyb/opencv/build"

這種方式全局有效,但是不便于多版本共存,因為環(huán)境變量是全局的,如果要切換到其他版本的 OpenCV,就需要重新設(shè)置環(huán)境變量。

最好的方式是在調(diào)用 cmake 命令時定義該變量,例如

cmake -B build -DOpenCV_DIR="/home/pyb/opencv/build"

雖然每次需要在命令行中輸入,但是這種方式既不會污染全局環(huán)境,也不會污染項目的 CMakeLists.txt,而且可以方便的切換版本。并且 CMake 本身有緩存功能,只要沒有刪除 build 目錄下的 CMakeCache.txt 文件,下次再運行 cmake -B build 時不輸入該變量 CMake 也會自動讀取緩存中的值。

未提供 Config 文件的第三方庫

有一些庫非常熱門,但是并未提供 Config 文件,例如 Python,CUDA,Jemalloc 等,這時候就需要我們自己寫 FindXXX.cmake 文件來查找該庫,幸運的是 CMake 已經(jīng)為我們提供了這些庫的 FindXXX.cmake 文件,可以在 CMake 安裝目錄下的 share/cmake/Modules/ 目錄下找到。

另外有一些庫沒有那么熱門,CMake 也沒有為我們提供 FindXXX.cmake 文件,這時候需要我們自己編寫相應(yīng)的 Find 文件,但是往往網(wǎng)上已經(jīng)有人寫過了,只需要搜索一下就可以找到,下面的鏈接是 Jemalloc 的 Find 文件

https://github.com/AcademySoftwareFoundation/openvdb/blob/master/cmake/FindJemalloc.cmake

這些文件有些使用的是古代 CMake 風(fēng)格,有些是現(xiàn)代 CMake 風(fēng)格,命名也不盡統(tǒng)一,但是一般都會有相應(yīng)的說明文檔,可以參考著使用。

指定 find_package 模式

find_package 命令有兩種模式,一種是 MODULE 模式,一種是 CONFIG 模式。

  • MODULE 模式,只會尋找 FindTBB.cmake 文件,而不會尋找 TBBConfig.cmake 文件,這種模式下,find_package 命令會在 CMAKE_MODULE_PATH(默認為 /usr/share/cmake/Modules)中搜索 FindTBB.cmake 文件

    find_package(TBB MODULE REQUIRED)
    
  • CONFIG 模式,只會尋找 TBBConfig.cmake 文件,而不會尋找 FindTBB.cmake 文件,這種模式下,find_package 命令會在 ${CMAKE_PREFIX_PATH}/lib/cmake/TBB(默認為 /usr/lib/cmake/TBB)、${TBB_DIR}$ENV{TBB_DIR} 中搜索 TBBConfig.cmake 文件

    find_package(TBB CONFIG REQUIRED)
    

不指定則兩者都會嘗試,默認先查找 Find 文件,如果找不到再查找 Config 文件。

直接作為子模塊引用

有些庫并不是通過 find_package 命令來引用的,而是直接將其作為子模塊引入項目中,例如

add_subdirectory(spdlog)
target_link_libraries(myapp PUBLIC spdlog)

總結(jié)

以上是生活随笔為你收集整理的现代 CMake 模块化项目管理指南的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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