Cmake中find_package命令的搜索模式之模塊模式(Module mode)

一、模塊模式簡(jiǎn)介

??前面介紹了find_package有兩種搜索包的模式(參考find_package介紹),本篇文章介紹其中的一種:模塊模式(Module Mode)蝴韭。在這種模式下攒驰,當(dāng)調(diào)用find_package命令查找<PackageName>包的時(shí)候,實(shí)際上會(huì)去查找一個(gè)名為Find<PackageName>.cmake的文件啊易,這個(gè)文件的主要任務(wù)就是確定一個(gè)包是否可用田弥,查找的結(jié)果會(huì)反映在變量<PackageName>_FOUND上供find_package的調(diào)用者使用。當(dāng)找到可用的包陌兑,同時(shí)也會(huì)提供使用這個(gè)包所需要的變量沈跨、宏和導(dǎo)入目標(biāo)(例如庫(kù)文件)。
??前面已經(jīng)介紹過(guò)使用系統(tǒng)提供的FindLibLZMA.cmake來(lái)查找LibLZMA庫(kù)兔综,這個(gè)文件是CMake在安裝的時(shí)候就提供的饿凛,位于CMake的安裝目錄之下。因此對(duì)于未提供該文件的第三方庫(kù)软驰,我們可以通過(guò)自己生成Find<PackageName>.cmake來(lái)供CMake使用涧窒,接下來(lái)將介紹如何利用自己生成的.cmake文件找到自己編寫的庫(kù)。
??模塊模式依賴另一個(gè)程序:pkg-config锭亏,在繼續(xù)往下之前纠吴,請(qǐng)參考pkg-config用法詳解了解這個(gè)命令。

二慧瘤、標(biāo)準(zhǔn)變量名稱

??Find<PackageName>.cmake文件承擔(dān)了定義<PackageName>包相關(guān)的變量的作用戴已,這些變量稱作"標(biāo)準(zhǔn)變量"。一旦find_package調(diào)用成功碑隆,這些變量將返回給調(diào)用者使用恭陡,為了保證不同的包之間返回的變量不沖突,對(duì)編寫的Find<PackageName>.cmake返回的標(biāo)準(zhǔn)變量名稱有如下約束:所有的變量都是以PackageName_開頭上煤,PackageName就是文件Find<PackageName>.cmake中的<PackageName>休玩,必須完全一致,大小寫敏感劫狠。簡(jiǎn)單的列舉幾個(gè)變量定義如下拴疤,更多的變量定義見本文的四、對(duì)標(biāo)準(zhǔn)變量名稱的更多說(shuō)明

  • PackageName_INCLUDE_DIRS:使用包需要包含的頭文件独泞。
  • PackageName_LIBRARIES:使用包所需要的庫(kù)文件呐矾,是全路徑或者鏈接器能在庫(kù)搜索目錄下找到的庫(kù)文件名稱。
  • PackageName_DEFINITIONS:使用包所需要的編譯選項(xiàng)懦砂。
  • PackageName_LIBRARY:庫(kù)的路徑蜒犯,只有當(dāng)包提供的是單個(gè)庫(kù)的時(shí)候才能使用這形式。
  • PackageName_INCLUDE_DIR:使用包所需要包含的頭文件目錄荞膘,只能在單個(gè)庫(kù)的使用罚随,使用者需要將該路徑加入到搜索路徑中。

三羽资、編寫.cmake文件

??接下來(lái)我們來(lái)寫一個(gè).cmake文件淘菩,假設(shè)我們的包名為mymath,該包提供一個(gè)libmymath.a的庫(kù)屠升,其中包含一個(gè)add接口潮改,簡(jiǎn)單計(jì)算兩個(gè)整數(shù)的和并打印出結(jié)果(對(duì)這個(gè)庫(kù)的更多信息可以參考pkg-config用法詳解4.1~4.3小節(jié))狭郑。在編寫Findmymath.cmake之前,我們先來(lái)看下.cmake文件的格式汇在。

3.1 .cmake文件的格式

  • 文件開頭是license信息
  • 接著是一個(gè)CMake支持的多行注釋(單行注釋以#開頭翰萨;多行注釋是指:以#[開頭,緊接著跟著0個(gè)或多個(gè)=趾疚,之后是[,接下來(lái)就是注釋內(nèi)容缨历,注釋可以跨越多行,然后以]糙麦、0個(gè)或多個(gè)=]組成結(jié)束丛肮,開頭的=個(gè)數(shù)要和結(jié)尾的=個(gè)數(shù)相等)赡磅,.cmake要求注釋以.rst:開頭:
# 多行注釋,0個(gè)=的情況
#[[
...
中間的內(nèi)容都是注釋
...
]]
# 多行注釋宝与,多個(gè)=的情況,開始的=和結(jié)尾的=要保持?jǐn)?shù)量一致习劫,此例子中為5個(gè)=
#[=====[
...
中間的內(nèi)容都是注釋
...
]=====]
  • 接下來(lái)在注釋中間申明find_package的標(biāo)準(zhǔn)變量及相關(guān)說(shuō)明咆瘟。
    1)首先是包的名字,分為兩行诽里,第一行是包名字袒餐,第二行是包名字下方的下劃線---(與包名字等長(zhǎng)度)。
    2)接著是對(duì)包的一個(gè)簡(jiǎn)要描述谤狡,這個(gè)沒(méi)有特殊要求灸眼。
    3)接下來(lái)分為幾個(gè)部分,主要是作用對(duì)幾類變量的申明(導(dǎo)入變量墓懂、結(jié)果變量焰宣、緩存變量),格式都是一致的:首先是變量類型的說(shuō)明捕仔,并在其下方以等長(zhǎng)^^^^標(biāo)識(shí)匕积,接著是一段文件對(duì)變量?jī)?nèi)容的簡(jiǎn)要描述,接著是我們要定義的標(biāo)準(zhǔn)變量了榜跌,變量以``標(biāo)準(zhǔn)變量``標(biāo)識(shí)(兩對(duì)``符號(hào)闪唆,中間是變量名稱),每個(gè)變量下可以對(duì)變量做一個(gè)簡(jiǎn)短的描述說(shuō)明斜做。
  • 注釋部分到此結(jié)束苞氮,接下來(lái)是對(duì)庫(kù)進(jìn)行真正查找,并把注釋部分申明的變量進(jìn)行賦值的過(guò)程瓤逼。
    1)先嘗試使用pkg-config來(lái)找到真正的庫(kù)笼吟,pkg-config是系統(tǒng)提供的命令用于找系統(tǒng)中是否存在相關(guān)的庫(kù)(參考pkg-config用法詳解)库物,在CMake中使用如下兩條,CMake會(huì)從<PackageName>.pc文件中讀取對(duì)應(yīng)的變量贷帮。
    find_package(PkgConfig)
    pkg_check_modules(PC_mymath QUIET mymath)
    2)如果能找到庫(kù)戚揭,那么變量PC_mymath_FOUND存在,并且可以得到mymath相關(guān)的頭文件和庫(kù)目錄撵枢,并且是存儲(chǔ)在以PC_mymath_XXX開頭的變量中民晒,例如PC_mymath_INCLUDE_DIRS(頭文件目錄)、PC_mymath_LIBRARY_DIRS(庫(kù)文件目錄)锄禽、PC_mymath_LIBRARIES(庫(kù)名稱)等等(具體有哪些變量可以參考man pkg-config或者在CMakeCache.txt中過(guò)濾PC_mymath查看)潜必。
  • 上一步利用了pkg-config獲得的變量還不是最終要給find_package返回的變量,我們要對(duì)返回的變量做正確的賦值沃但,并最終調(diào)用include(FindPackageHandleStandardArgs)find_package_handle_standard_args將變量返回給find_package調(diào)用處磁滚。
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mymath
FOUND_VAR mymath_FOUND
REQUIRED_VARS
 mymath_LIBRARY
 mymath_INCLUDE_DIR
VERSION_VAR mymath_VERSION
)

3.2 編寫自己的Findmymath.cmake文件

??假定我們使用的庫(kù)mymath已經(jīng)提供了.pc文件,并能夠通過(guò)pkg-config方式找到它(可以通過(guò)pkg-onfig --list-all查看到mymath庫(kù)宵晚,參考pkg-config用法詳解)垂攘。

??接下來(lái)我們來(lái)編寫庫(kù)mymathFindmymath.cmake文件,參照前面.cmake文件說(shuō)明淤刃,內(nèi)容如下晒他,示例只提供了庫(kù)目錄、庫(kù)文件逸贾、頭文件等少量變量信息:

# Findmymath.cmake

#[============[.rst:
Findmymath
----------

對(duì)這個(gè)文件的描述:查找mymath庫(kù)

Imported Targets
^^^^^^^^^^^^^^^^

如果提供導(dǎo)出可執(zhí)行目標(biāo)陨仅,可以在下面進(jìn)行定義:
``mymath::mymath``
    導(dǎo)出mymath::mymath可執(zhí)行目標(biāo),我們的測(cè)試庫(kù)并未提供耕陷,該處只是一個(gè)示意

Result Variables
^^^^^^^^^^^^^^^^

可以在下面定義一些普通變量:

``mymath_FOUND``
    如果找到mymath庫(kù)掂名,該變量值為True.
``mymath_VERSION``
    mymath庫(kù)的版本.
``mymath_INCLUDE_DIRS``
    使用mymath庫(kù)需要包含的頭文件.
``mymath_LIBRARIES``
    使用mymath庫(kù)需要用到的庫(kù)文件.

Cache Variables
^^^^^^^^^^^^^^^

可以在下面定義一些緩存變量:

``mymath_INCLUDE_DIR``
    包含mymath.h頭文件的目錄.
``mymath_LIBRARY``
    mymath庫(kù)所在的目錄.
]============]

find_package(PkgConfig)
pkg_check_modules(PC_mymath QUIET mymath)

find_path(mymath_INCLUDE_DIR
    NAMES mymath.h
    PATHS ${PC_mymath_INCLUDE_DIRS}
    PATH_SUFFIXES mymath)

find_library(mymath_LIBRARY
    NAMES mymath
    PATHS ${PC_mymath_LIBRARY_DIRS})

set(mymath_VERSION ${PC_mymath_VERSION})
set(mymath_INCLUDE_DIRS "/just/for/include/test") # 只是為了測(cè)試用,沒(méi)有實(shí)際作用
set(mymath_LIBRARY_DIRS "/just/for/library/test") # 只是為了測(cè)試用哟沫,沒(méi)有實(shí)際作用

# 將變量導(dǎo)出給調(diào)用者使用
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mymath
  FOUND_VAR mymath_FOUND
  REQUIRED_VARS
    mymath_LIBRARY
    mymath_INCLUDE_DIR
    mymath_INCLUDE_DIRS
    mymath_LIBRARY_DIRS
  VERSION_VAR mymath_VERSION
)

??我們的測(cè)試文件test.cpp如下:

// test.cpp
#include "mymath.h"
int main(int argc, char** argv)
{
    mymath::add(1, 2);
    return 0;
}

??最后饺蔑,在我們的CMakeLists.txt來(lái)使用find_package來(lái)找到并使用mymath庫(kù)

# CMakeLists.txt,和測(cè)試文件test.cpp在同個(gè)目錄
cmake_minimum_required(VERSION 3.10.2)
project(find_package_test)
message("Find path: ${CMAKE_MODULE_PATH}")
find_package(mymath)
if (mymath_FOUND)
    add_executable(test test.cpp)
    include_directories(${mymath_INCLUDE_DIR})
    target_link_libraries(test ${mymath_LIBRARY})
    set_target_properties(test PROPERTIES
        INTERFACE_COMPILE_OPTIONS "${PC_mymath_CFLAGS_OTHER}") # 這個(gè)語(yǔ)句在本例中不是必須嗜诀,只是說(shuō)明可以通過(guò)這種方式獲取編譯mymath庫(kù)需要使用的編譯選項(xiàng)
endif()

??在CMakeLists.txt下執(zhí)行cmake命令猾警,此處為了演示,直接指定CMAKE_MODULE_PATH的值為當(dāng)前的.cmake所在路徑隆敢,這樣find_package命令會(huì)直接找到我們編寫的.cmake文件并讀取其中的內(nèi)容发皿,編譯并運(yùn)行最終程序(只摘取了我們關(guān)注的顯示信息):

# 執(zhí)行cmake
cmake . -DCMAKE_MODULE_PATH=./

# 運(yùn)行結(jié)果,首次會(huì)打印出來(lái)找到的庫(kù)以及所在的全路徑拂蝎、版本號(hào)等信息
......
-- Found PkgConfig: /usr/local/bin/pkg-config (found version "0.29.2")
-- Found mymath: /XXX/mymath/lib/libmymath.a (found version "1.0")
......

# 執(zhí)行make
make

# 運(yùn)行程序
./test

# 運(yùn)行結(jié)果
Add 1 and 2 is 3

四穴墅、對(duì)標(biāo)準(zhǔn)變量名稱的更多說(shuō)明

  • PackageName_INCLUDE_DIRS:使用包需要包含的頭文件。

  • PackageName_LIBRARIES:使用包所需要的庫(kù)文件,是全路徑或者鏈接器能在庫(kù)搜索目錄下找到的庫(kù)文件名稱玄货。

  • PackageName_DEFINITIONS:使用包所需要的編譯選項(xiàng)皇钞。

  • PackageName_EXECUTABLE:可執(zhí)行文件的全路徑。

  • PackageName_YYY_EXECUTABLE:與上面類似松捉,不過(guò)此處的YYY表示包中提供的其他可執(zhí)行文件的名字夹界,通常是全大寫,可以使用這種方式避免名稱沖突隘世】墒粒可以用于包提供多個(gè)可執(zhí)行文件的場(chǎng)景。

  • PackageName_LIBRARY_DIRS:可選丙者,庫(kù)目錄复斥。

  • PackageName_ROOT_DIR:包查找的根目錄

  • PackageName_VERSION_VV:指定模塊的版本號(hào)是VV,但是要注意蔓钟,一個(gè)模塊可能有多個(gè)歷史的版本永票,只能有一個(gè)版本的變量設(shè)置成true,例如模塊mymath有三個(gè)版本滥沫,分別是Version 1Version 2键俱、Version 3兰绣,那么mymath_VERSION_1mymath_VERSION_2编振、mymath_VERSION_3這三個(gè)變量只能有一個(gè)設(shè)置為true缀辩,否則會(huì)報(bào)錯(cuò)。

  • PackageName_WRAP_YY:當(dāng)該變量設(shè)置為false踪央,表明相關(guān)的包裝命令無(wú)法使用臀玄。

  • PackageName_Yy_FOUND:此處的Yy表示包的組件,必須跟find_package命令提供的組件名稱完全匹配畅蹂,如果該變量設(shè)置為false健无,表明組件未找到或者不可用∫盒保可用于檢查哪些組件是可用的累贤。

  • PackageName_FOUND:如果能找到模塊,那么該變量會(huì)置為true少漆。

  • PackageName_NOT_FOUND_MESSAGE:未找到時(shí)候的消息提示臼膏。

  • PackageName_RUNTIME_LIBRARY_DIRS:可選,運(yùn)行庫(kù)的搜索路徑示损,當(dāng)可執(zhí)行文件需要鏈接到共享庫(kù)的時(shí)候需要指定該變量渗磅,以讓鏈接器能找到對(duì)應(yīng)的共享庫(kù)路徑。在windows下,會(huì)將該變量?jī)?nèi)容加入到PATH始鱼,Linux下會(huì)加入到LD_LIBRARY_PATH仔掸。

  • PackageName_VERSION:找到模塊的全版本字符串,值得注意的是當(dāng)前許多存在的模塊使用的是PackageName_VERSION_STRING替代风响。

  • PackageName_VERSION_MAJOR:主版本號(hào)

  • PackageName_VERSION_MINOR:次版本號(hào)

  • PackageName_VERSION_PATCH:補(bǔ)丁版本號(hào)

  • PackageName_LIBRARY:庫(kù)路徑嘉汰,只有當(dāng)模塊提供的是單個(gè)庫(kù)的時(shí)候才能使用這形式,多個(gè)庫(kù)可以參考下面的命令状勤。

  • PackageName_Yy_LIBRARY:模塊PackageName提供的庫(kù)Yy的路徑鞋怀,當(dāng)一個(gè)包下有多個(gè)庫(kù)、或者其他包有同名的庫(kù)時(shí)(也就是不同包之間庫(kù)重名的時(shí))使用持搜。

  • PackageName_INCLUDE_DIR:使用庫(kù)需要包含的頭文件目錄密似,只能在單個(gè)庫(kù)的使用。使用者需要將該路徑加入到搜索路徑中葫盼。

  • PackageName_Yy_INCLUDE_DIR:多個(gè)庫(kù)時(shí)候残腌,指定使用庫(kù)Yy需要包含的頭文件。


附錄:參考文檔

  1. https://cmake.org/cmake/help/v3.0/module/FindPkgConfig.html
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贫导,一起剝皮案震驚了整個(gè)濱河市抛猫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌孩灯,老刑警劉巖闺金,帶你破解...
    沈念sama閱讀 222,252評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異峰档,居然都是意外死亡败匹,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門讥巡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掀亩,“玉大人,你說(shuō)我怎么就攤上這事欢顷〔酃鳎” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵吱涉,是天一觀的道長(zhǎng)刹泄。 經(jīng)常有香客問(wèn)我,道長(zhǎng)怎爵,這世上最難降的妖魔是什么特石? 我笑而不...
    開封第一講書人閱讀 59,869評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮鳖链,結(jié)果婚禮上姆蘸,老公的妹妹穿的比我還像新娘墩莫。我一直安慰自己,他們只是感情好逞敷,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評(píng)論 6 398
  • 文/花漫 我一把揭開白布狂秦。 她就那樣靜靜地躺著,像睡著了一般推捐。 火紅的嫁衣襯著肌膚如雪裂问。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評(píng)論 1 312
  • 那天牛柒,我揣著相機(jī)與錄音堪簿,去河邊找鬼。 笑死皮壁,一個(gè)胖子當(dāng)著我的面吹牛椭更,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛾魄,決...
    沈念sama閱讀 41,010評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼虑瀑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了滴须?” 一聲冷哼從身側(cè)響起舌狗,我...
    開封第一講書人閱讀 39,924評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎扔水,沒(méi)想到半個(gè)月后把夸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,469評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡铭污,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了膀篮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘹狞。...
    茶點(diǎn)故事閱讀 40,680評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖誓竿,靈堂內(nèi)的尸體忽然破棺而出磅网,到底是詐尸還是另有隱情,我是刑警寧澤筷屡,帶...
    沈念sama閱讀 36,362評(píng)論 5 351
  • 正文 年R本政府宣布涧偷,位于F島的核電站,受9級(jí)特大地震影響毙死,放射性物質(zhì)發(fā)生泄漏燎潮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評(píng)論 3 335
  • 文/蒙蒙 一扼倘、第九天 我趴在偏房一處隱蔽的房頂上張望确封。 院中可真熱鬧,春花似錦、人聲如沸爪喘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)秉剑。三九已至泛豪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侦鹏,已是汗流浹背诡曙。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留种柑,地道東北人岗仑。 一個(gè)月前我還...
    沈念sama閱讀 49,099評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像聚请,于是被迫代替她去往敵國(guó)和親荠雕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容