1 基本指令
1,ADD_DEFINITIONS
向 C/C++編譯器添加-D 定義,比如:
DD_DEFINITIONS(-DENABLE_DEBUG -DABC),參數之間用空格分割。
如果你的代碼中定義了#ifdef ENABLE_DEBUG #endif,這個代碼塊就會生效。
2,ADD_DEPENDENCIES
ADD_DEPENDENCIES(target-name depend-target1 depend-target2 ...)
如果兩個targets有依賴關系(通過target_link_libraries解決)並且依賴庫也是通過編譯源碼產生的。這時候一句 add_dependencies 可以在直接編譯上層target時,自動檢查下層依賴庫是否已經生成。沒有的話先編譯下層依賴庫,然后再編譯上層target,最后 link depend target。若只有一個targets有依賴關系,一般選擇使用 target_link_libraries。
3,ADD_EXECUTABLE
ADD_EXECUTABLE(hello main.cpp)
定義了這個工程會生成一個文件名為 hello 的可執行文件,相關的源文件是mian.cpp。
4,ADD_LIBRARY
該指令的主要作用就是將指定的源文件生成鏈接庫文件,然后添加到工程中去。語法如下:
add_library(<name> [STATIC | SHARED | MODULE] [EXCLUDE_FROM_ALL] [source1] [source2] [...])
其中<name>表示鏈接庫文件的名字,該鏈接庫文件會根據命令里列出的源文件來創建。而 STATIC、SHARED 和 MODULE 的作用是指定生成的鏈接庫文件的類型,STATIC 為靜態鏈接庫,SHARED 為動態鏈接庫,而 MODULE,在使用 dyld 的系統有效,如果不支持 dyld,則被當作 SHARED 對待。 EXCLUDE_FROM_ALL 參數的含義是將這個target排除在all target列表之外,這樣當執行make時,這個target就不會被編譯。而語法中的source1 source2分別表示各個源文件。
5,ADD_SUBDIRECTORY
ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
這個指令用於向當前工程添加存放源文件的子目錄,並可以指定中間二進制和目標二進制存放的位置。EXCLUDE_FROM_ALL 參數的含義是將這個子目錄的所有target排除在all target列表之外,這樣當執行make時,這個子目錄的所有target就不會被編譯。
6,CMAKE_MINIMUM_REQUIRED
CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
檢查cmake的版本,要求至少為versionNumber。例如 CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR) ,如果 cmake 版本小於 2.5,則出現嚴重錯誤,整個過程中止。
7,INCLUDE_DIRECTORIES
INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
指定頭文件的搜索路徑。例如我現在想要#include "cv.h",但是這個cv.h的路徑是 /usr/local/include/opencv,那么總不能在主函數頭前寫#include “/usr/local/include/opencv/cv.h” 吧,這個時候就用到include_directories了,它提供了一個搜索頭文件暫時的根目錄,即你可以在 CMakeLists.txt 中寫上 include_directories(/usr/local/include) 來讓庫文件搜索以 /usr/local/include 為基礎,然后在main函數前寫上 #include “opencv/cv.h" 即可。
8,LINK_DIRECTORIES
LINK_DIRECTORIES(directory1 directory2 ...)
添加需要鏈接的庫文件目錄,相當於g++命令的-L選項的作用。 該指令有時候不一定需要,因為find_package和find_library指令可以得到庫文件的絕對路徑。不過你自己寫的動態庫文件放在自己新建的目錄下時,可以用該指令指定該目錄的路徑以便工程能夠找到。例子如下:
LINK_DIRECTORIES("/opt/MATLAB/R2012a/bin/glnxa64")
9,LINK_LIBRARIES
LINK_LIBRARIES(library1 library2 ...)
添加需要鏈接的庫文件路徑,注意這里是全路徑,要用在add_executable之前。例子如下:
LINK_LIBRARIES("/opt/MATLAB/R2012a/bin/glnxa64/libeng.so")
10,TARGET_LINK_LIBRARIES
TARGET_LINK_LIBRARIES(target library1 <debug | optimized> library2 ...)
為庫或二進制可執行文件添加庫鏈接,要用在add_executable之后。 上述指令中的target是指通過add_executable()和add_library()指令生成已經創建的目標文件。例子如下:
TARGET_LINK_LIBRARIES(myProject hello),連接libhello.so庫
TARGET_LINK_LIBRARIES(myProject libhello.a)
11,PKG_CHECK_MODULES
pkg_check_modules(<PREFIX> [REQUIRED] [QUIET]
[NO_CMAKE_PATH] [NO_CMAKE_ENVIRONMENT_PATH]
<MODULE> [<MODULE>]*)
pkg_check_modules 是 CMake 自己的 pkg-config 模塊 的一個用來簡化的封裝:你不用再檢查 CMake 的版本,加載合適的模塊,檢查是否被加載,等等,參數和傳給 find_package 的一樣:先是待返回變量的前綴,然后是包名(pkg-config 的)。這樣就定義了<prefix>_INCLUDE_DIRS
和其他的這類變量,后續的用法就與 find_package 一致。
2 find_package 指令
2.1 原理
語法如下:
find_package(<package> [version] [EXACT] [QUIET]
[REQUIRED] [[COMPONENTS] [components...]]
[NO_POLICY_SCOPE])
功能:采用兩種模式搜索外部庫。
[version]參數需要一個版本號,它是正在查找的包應該兼容的版本號(格式是major[.minor[.patch[.tweak]]])。EXACT選項要求該版本號必須精確匹配。QUIET選項將會禁掉包沒有被發現時的警告信息。REQUIRED選項表示如果包沒有找到的話,cmake的過程會終止,並輸出警告信息。在REQUIRED選項之后,或者如果沒有指定REQUIRED選項但是指定了COMPONENTS選項,在它們的后面可以列出一些與包相關的部件清單(components list)。
首先明確一點,cmake本身不提供任何搜索庫的便捷方法,所有搜索庫並給變量賦值的操作必須由cmake代碼完成,比如下面將要提到的FindXXX.cmake和XXXConfig.cmake。只不過,庫的作者通常會提供這兩個文件,以方便使用者調用。
搜索有以下兩種模式:
- Module模式:搜索CMAKE_MODULE_PATH指定路徑下的FindXXX.cmake文件,執行該文件從而找到XXX庫。其中,具體查找庫並給XXX_INCLUDE_DIRS和XXX_LIBRARIES兩個變量賦值的操作由FindXXX.cmake模塊完成。
- Config模式:搜索XXX_DIR指定路徑下的XXXConfig.cmake文件,執行該文件從而找到XXX庫。其中具體查找庫並給XXX_INCLUDE_DIRS和XXX_LIBRARIES兩個變量賦值的操作由XXXConfig.cmake模塊完成。
兩種模式看起來似乎差不多,不過cmake默認采取Module模式,如果Module模式未找到庫,才會采取Config模式。如果XXX_DIR路徑下找不到XXXConfig.cmake文件,則會找/usr/local/lib/cmake/XXX/中的XXXConfig.cmake文件。總之,Config模式是一個備選策略。通常,庫安裝時會拷貝一份XXXConfig.cmake到系統目錄中,因此在沒有顯式指定搜索路徑時也可以順利找到。
若XXX安裝時沒有安裝到系統目錄,因此無法自動找到XXXConfig.cmake,可以在CMakeLists.txt最前面添加XXX的搜索路徑。
set(XXX_DIR /home/wjg/projects/XXX/build) #添加CaffeConfig.cmake的搜索路徑
2.2 使用
當編譯一個需要使用第三方庫的軟件時,我們需要知道:
去哪兒找頭文件 .h | 對比GCC的 -I 參數 |
---|---|
去哪兒找庫文件 (.so/.dll/.lib/.dylib/…) | 對比GCC的 -L 參數 |
需要鏈接的庫文件的名字 | 對比GCC的 -l 參數 |
比如說,我們需要一個第三方庫 curl,那么我們的 CMakeLists.txt 需要指定頭文件目錄和庫文件,類似:
include_directiories(/usr/include/curl)
target_link_libraries(myprogram path/curl.so)
如果借助於cmake提供的finder會怎么樣呢?使用cmake的Modules目錄下的FindCURL.cmake,相應的CMakeList.txt 文件:
find_package(CURL REQUIRED)
include_directories(${CURL_INCLUDE_DIR})
target_link_libraries(curltest ${CURL_LIBRARY})
為了能支持各種常見的庫和包,CMake自帶了很多模塊。可以通過命令 cmake –help-module-list (輸入cmake –help,然后雙擊Tab會有命令提示)得到你的CMake支持的模塊的列表:直接查看模塊路徑。比如Ubuntu linux上,模塊的路徑是 ls /usr/share/cmake/Modules/:
ll -th /usr/share/cmake-3.5/Modules/
......
-rw-r--r-- 1 root root 76K Sep 27 2016 FindBoost.cmake
-rw-r--r-- 1 root root 2.7K Mar 24 2016 FindCoin3D.cmake
-rw-r--r-- 1 root root 77K Mar 24 2016 FindCUDA.cmake
-rw-r--r-- 1 root root 3.1K Mar 24 2016 FindCups.cmake
-rw-r--r-- 1 root root 2.4K Mar 24 2016 FindCURL.cmake
........
讓我們以bzip2庫為例。CMake中有個 FindBZip2.cmake 模塊。只要使用 find_package(BZip2) 調用這個模塊,cmake會自動給一些變量賦值,然后就可以在CMake腳本中使用它們了。變量的列表可以查看cmake模塊文件,或者使用命令:
root@xy:~/cmake_practice/cmake_build/build_demo10# cmake --help-module FindBZip2
FindBZip2
---------
Try to find BZip2
Once done this will define
::
BZIP2_FOUND - system has BZip2
BZIP2_INCLUDE_DIR - the BZip2 include directory
BZIP2_LIBRARIES - Link these to use BZip2
BZIP2_NEED_PREFIX - this is set if the functions are prefixed with BZ2_
BZIP2_VERSION_STRING - the version of BZip2 found (since CMake 2.8.8)
cmake 會將路徑賦值給對應的變量,我們以curl的cmake為例,其部分內容如下:
find_path(CURL_INCLUDE_DIR NAMES curl/curl.h)
mark_as_advanced(CURL_INCLUDE_DIR)
# Look for the library (sorted from most current/relevant entry to least).
find_library(CURL_LIBRARY NAMES
curl
# Windows MSVC prebuilts:
curllib
libcurl_imp
curllib_static
# Windows older "Win32 - MSVC" prebuilts (libcurl.lib, e.g. libcurl-7.15.5-win32-msvc.zip):
libcurl
)
比如一個使用bzip2的簡單程序,編譯器需要知道 bzlib.h 的位置,鏈接器需要找到bzip2庫。(動態鏈接的話,Unix上是 libbz2.so 類似的文件,Windows上是 libbz2.dll )
project(helloworld)
add_executable(helloworld hello.c)
find_package (BZip2)
if (BZIP2_FOUND)
include_directories(${BZIP_INCLUDE_DIRS})
target_link_libraries (helloworld ${BZIP2_LIBRARIES})
endif (BZIP2_FOUND)
3 INSTALL 指令
安裝的方法有兩種,一種是從代碼編譯后直接make install安裝,一種是打包時的指定目錄安裝(INSTALL)。
這里需要引入一個新的cmake 指令 INSTALL和一個非常有用的變量CMAKE_INSTALL_PREFIX。 CMAKE_INSTALL_PREFIX變量類似於configure腳本的 –prefix,常見的使用方法看 起來是這個樣子:
cmake -DCMAKE_INSTALL_PREFIX=/usr .
INSTALL指令用於定義安裝規則,安裝的內容可以包括目標二進制、動態庫、靜態庫以及 文件、目錄、腳本等。
INSTALL指令包含了各種安裝類型,我們需要一個個分開解釋:
目標文件的安裝
INSTALL(TARGETS targets...
[[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION <dir>]
[PERMISSIONS permissions...]
[CONFIGURATIONS
[Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL]
] [...])
參數中的TARGETS后面跟的就是我們通過ADD_EXECUTABLE或者ADD_LIBRARY定義的目標文件,可能是可執行二進制、動態庫、靜態庫。
目標類型也就相對應的有三種,ARCHIVE特指靜態庫,LIBRARY特指動態庫,RUNTIME特指可執行目標二進制。
DESTINATION定義了安裝的路徑,如果路徑以/開頭,那么指的是絕對路徑,這時候 CMAKE_INSTALL_PREFIX其實就無效了。如果你希望使用CMAKE_INSTALL_PREFIX來定義安裝路徑,就要寫成相對路徑,即不要以/開頭,那么安裝后的路徑就是${CMAKE_INSTALL_PREFIX}/<DESTINATION定義的路徑>。
舉個簡單的例子:
INSTALL(TARGETS myrun mylib mystaticlib
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION libstatic
)
上面的例子會將:
可執行二進制myrun安裝到${CMAKE_INSTALL_PREFIX}/bin目錄。
動態庫libmylib安裝到${CMAKE_INSTALL_PREFIX}/lib目錄。
靜態庫libmystaticlib安裝到${CMAKE_INSTALL_PREFIX}/libstatic目錄。
特別注意的是你不需要關心TARGETS具體生成的路徑,只需要寫上TARGETS名稱就可以了。
普通文件的安裝
INSTALL(FILES files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
#可用於安裝一般文件,並可以指定訪問權限,文件名是此指令所在路徑下的相對路徑。
#如果默認不定義權限PERMISSIONS,安裝后的權限為,OWNER_WRITE,OWNER_READ,
#GROUP_READ,和WORLD_READ,即644權限。
非目標文件的可執行程序安裝(比如腳本之類)
INSTALL(PROGRAMS files... DESTINATION <dir>
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[RENAME <name>] [OPTIONAL])
跟上面的FILES指令使用方法一樣,唯一的不同是安裝后權限為:
OWNER_EXECUTE, GROUP_EXECUTE, 和WORLD_EXECUTE,即755權限
目錄的安裝
INSTALL(DIRECTORY dirs... DESTINATION <dir>
[FILE_PERMISSIONS permissions...]
[DIRECTORY_PERMISSIONS permissions...]
[USE_SOURCE_PERMISSIONS]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[[PATTERN <pattern> | REGEX <regex>]
[EXCLUDE] [PERMISSIONS permissions...]] [...])
這里主要介紹其中的DIRECTORY、PATTERN以及PERMISSIONS參數。
DIRECTORY后面連接的是所在Source目錄的相對路徑,但務必注意:
abc和abc/有很大的區別。 abc意味着abc這個目錄會安裝在目標路徑下;abc/意味着abc這個目錄的內容會被安裝在目標路徑下。PATTERN用於使用正則表達式進行過濾, PERMISSIONS用於指定PATTERN過濾后的文件權限。
我們來看一個例子:
INSTALL(DIRECTORY icons scripts/ DESTINATION share/myproj
PATTERN "CVS" EXCLUDE
PATTERN "scripts/*"
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ
GROUP_EXECUTE GROUP_READ)
這條指令的執行結果是:
將icons目錄安裝到 <prefix>/share/myproj,將scripts/中的內容安裝到 <prefix>/share/myproj。
不包含目錄名為CVS的目錄,對於scripts/*文件指定權限為 OWNER_EXECUTE 不包含目錄名為CVS的目錄,對於scripts/*文件指定權限為 OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ。
參考:
CSDN FishBear_move_on-cmake教程4(find_package使用)
《CMake實踐》