一、模塊模式簡(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ù)mymath
的Findmymath.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 1
、Version 2
键俱、Version 3
兰绣,那么mymath_VERSION_1
、mymath_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
需要包含的頭文件。