cmakelist 常见用法
各種目錄環境變量
https://www.cnblogs.com/xianghang123/p/3556425.html
https://blog.csdn.net/guoyajie1990/article/details/78138636
1,
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
這三個變量指代的內容是一致的,如果是 in source 編譯,指得就是工程頂層目錄,如果是 out-of-source 編譯,指的是工程編譯發生的目錄。PROJECT_BINARY_DIR 跟其他指令稍有區別,現在,你可以理解為他們是一致的。如下方的catkin 編譯例子(out of source)
CMAKE_BINARY_DIR /home/name/catkin_ws/build_isolated/package_dir
2,
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
這三個變量指代的內容是一致的,不論采用何種編譯方式,都是工程頂層目錄。
也就是在 in source 編譯時,他跟 CMAKE_BINARY_DIR 等變量一致。
PROJECT_SOURCE_DIR 跟其他指令稍有區別,現在,你可以理解為他們是一致的。下方的catkin 編譯例子
PROJECT_SOURCE_DIR /home/name/catkin_ws/src/package_dir
3,
CMAKE_CURRENT_SOURCE_DIR
指的是當前處理的 CMakeLists.txt 所在的路徑,比如上面我們提到的 src 子目錄。
4,
CMAKE_CURRRENT_BINARY_DIR
如果是 in-source 編譯,它跟 CMAKE_CURRENT_SOURCE_DIR 一致,如果是 out-of-source 編譯,他指的是 target 編譯目錄。
使用我們上面提到的 ADD_SUBDIRECTORY(src bin)可以更改這個變量的值。
使用 SET(EXECUTABLE_OUTPUT_PATH <新路徑>)并不會對這個變量造成影響,它僅僅修改了最終目標文件存放的路徑。
7,
CMAKE_MODULE_PATH
這個變量用來定義自己的 cmake 模塊所在的路徑。如果你的工程比較復雜,有可能會自己編寫一些 cmake 模塊,這些 cmake 模塊是隨你的工程發布的,為了讓 cmake 在處理CMakeLists.txt 時找到這些模塊,你需要通過 SET 指令,將自己的 cmake 模塊路徑設置一下。
比如
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
這時候你就可以通過 INCLUDE 指令來調用自己的模塊了。
8,
EXECUTABLE_OUTPUT_PATH 和 LIBRARY_OUTPUT_PATH
分別用來重新定義最終結果的存放目錄,前面我們已經提到了這兩個變量。
9,
PROJECT_NAME
返回通過 PROJECT 指令定義的項目名稱。
基本指令
1,ADD_DEFINITIONS
向 C/C++編譯器添加-D 定義,比如:
ADD_DEFINITIONS(-DENABLE_DEBUG -DABC),參數之間用空格分割。
如果你的代碼中定義了#ifdef ENABLE_DEBUG #endif,這個代碼塊就會生效。
如果要添加其他的編譯器開關,可以通過 CMAKE_C_FLAGS 變量和 CMAKE_CXX_FLAGS 變量設置。
2,ADD_DEPENDENCIES
定義 target 依賴的其他 target,確保在編譯本 target 之前,其他的 target 已經被構建。
ADD_DEPENDENCIES(target-name depend-target1
depend-target2 …)
3,ADD_EXECUTABLE、ADD_LIBRARY、ADD_SUBDIRECTORY
前面已經介紹過了,這里不再羅唆。
4,ADD_TEST 與 ENABLE_TESTING 指令。
ENABLE_TESTING 指令用來控制 Makefile 是否構建 test 目標,涉及工程所有目錄。語法很簡單,沒有任何參數,ENABLE_TESTING(),一般情況這個指令放在工程的主CMakeLists.txt 中.
ADD_TEST 指令的語法是:
ADD_TEST(testname Exename arg1 arg2 …)
testname 是自定義的 test 名稱,Exename 可以是構建的目標文件也可以是外部腳本等等。后面連接傳遞給可執行文件的參數。如果沒有在同一個 CMakeLists.txt 中打開ENABLE_TESTING()指令,任何 ADD_TEST 都是無效的。
比如我們前面的 Helloworld 例子,可以在工程主 CMakeLists.txt 中添加
ADD_TEST(mytest ${PROJECT_BINARY_DIR}/bin/main)
ENABLE_TESTING()
生成 Makefile 后,就可以運行 make test 來執行測試了。
5,AUX_SOURCE_DIRECTORY
基本語法是:
AUX_SOURCE_DIRECTORY(dir VARIABLE)
作用是發現一個目錄下所有的源代碼文件并將列表存儲在一個變量中,這個指令臨時被用來自動構建源文件列表。因為目前 cmake 還不能自動發現新添加的源文件。
比如
AUX_SOURCE_DIRECTORY(. SRC_LIST)
ADD_EXECUTABLE(main ${SRC_LIST})
你也可以通過后面提到的 FOREACH 指令來處理這個 LIST
6,CMAKE_MINIMUM_REQUIRED
其語法為 CMAKE_MINIMUM_REQUIRED(VERSION versionNumber [FATAL_ERROR])
比如 CMAKE_MINIMUM_REQUIRED(VERSION 2.5 FATAL_ERROR)
如果 cmake 版本小與 2.5,則出現嚴重錯誤,整個過程中止。
7,EXEC_PROGRAM
在 CMakeLists.txt 處理過程中執行命令,并不會在生成的 Makefile 中執行。
具體語法為:
EXEC_PROGRAM(Executable [directory in which to run] [ARGS <arguments to executable>] [OUTPUT_VARIABLE <var>] [RETURN_VALUE <var>])用于在指定的目錄運行某個程序,通過 ARGS 添加參數,如果要獲取輸出和返回值,可通過OUTPUT_VARIABLE 和 RETURN_VALUE 分別定義兩個變量.
這個指令可以幫助你在 CMakeLists.txt 處理過程中支持任何命令,比如根據系統情況去修改代碼文件等等。
舉個簡單的例子,我們要在 src 目錄執行 ls 命令,并把結果和返回值存下來。
可以直接在 src/CMakeLists.txt 中添加:
在 cmake 生成 Makefile 的過程中,就會執行 ls 命令,如果返回 0,則說明成功執行,那么就輸出 ls *.c 的結果。關于 IF 語句,后面的控制指令會提到。
8,FILE 指令
文件操作指令,基本語法為:
這里的語法都比較簡單,不在展開介紹了。
9,INCLUDE 指令,用來載入 CMakeLists.txt 文件,也用于載入預定義的 cmake 模塊.
INCLUDE(file1 [OPTIONAL]) INCLUDE(module [OPTIONAL])OPTIONAL 參數的作用是文件不存在也不會產生錯誤。
你可以指定載入一個文件,如果定義的是一個模塊,那么將在 CMAKE_MODULE_PATH 中搜索這個模塊并載入。
載入的內容將在處理到 INCLUDE 語句是直接執行。
FIND_指令
FIND_系列指令主要包含一下指令:
FIND_FILE(<VAR> name1 path1 path2 …)
VAR 變量代表找到的文件全路徑,包含文件名
FIND_LIBRARY(<VAR> name1 path1 path2 …)
VAR 變量表示找到的庫全路徑,包含庫文件名
FIND_PATH(<VAR> name1 path1 path2 …)
VAR 變量代表包含這個文件的路徑。
FIND_PROGRAM(<VAR> name1 path1 path2 …)
VAR 變量代表包含這個程序的全路徑。
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets…]])
用來調用預定義在 CMAKE_MODULE_PATH 下的 Find<name>.cmake 模塊,你也可以自己定義 Find<name>模塊,通過 SET(CMAKE_MODULE_PATH dir)將其放入工程的某個目錄中供工程使用,我們在后面的章節會詳細介紹 FIND_PACKAGE 的使用方法和 Find 模塊的編寫。
FIND_LIBRARY 示例:
FIND_LIBRARY(libX X11 /usr/lib) IF(NOT libX) MESSAGE(FATAL_ERROR “libX not found”) ENDIF(NOT libX)控制指令:
1,IF 指令,基本語法為:
IF(expression_r_r) # THEN section. COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ELSE(expression_r_r) # ELSE section. COMMAND1(ARGS ...) COMMAND2(ARGS ...) ... ENDIF(expression_r_r)另外一個指令是 ELSEIF,總體把握一個原則,凡是出現 IF 的地方一定要有對應的ENDIF.出現 ELSEIF 的地方,ENDIF 是可選的。
表達式的使用方法如下:
IF(var),如果變量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或<var>_NOTFOUND 時,表達式為真。
IF(NOT var ),與上述條件相反。
IF(var1 AND var2),當兩個變量都為真是為真。
IF(var1 OR var2),當兩個變量其中一個為真時為真。
IF(COMMAND cmd),當給定的 cmd 確實是命令并可以調用是為真。
IF(EXISTS dir)或者 IF(EXISTS file),當目錄名或者文件名存在時為真。
IF(file1 IS_NEWER_THAN file2),當 file1 比 file2 新,或者 file1/file2 其中有一個不存在時為真,文件名請使用完整路徑。
IF(IS_DIRECTORY dirname),當 dirname 是目錄時,為真。
IF(variable MATCHES regex)
IF(string MATCHES regex)
當給定的變量或者字符串能夠匹配正則表達式 regex 時為真。比如:
數字比較表達式
IF(variable LESS number) IF(string LESS number) IF(variable GREATER number) IF(string GREATER number) IF(variable EQUAL number) IF(string EQUAL number)按照字母序的排列進行比較.
IF(variable STRLESS string) IF(string STRLESS string) IF(variable STRGREATER string) IF(string STRGREATER string) IF(variable STREQUAL string) IF(string STREQUAL string)IF(DEFINED variable),如果變量被定義,為真。
一個小例子,用來判斷平臺差異:
上述代碼用來控制在不同的平臺進行不同的控制,但是,閱讀起來卻并不是那么舒服,
ELSE(WIN32)之類的語句很容易引起歧義。
可以 SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
這時候就可以寫成:
如果配合 ELSEIF 使用,可能的寫法是這樣:
IF(WIN32) #do something related to WIN32 ELSEIF(UNIX) #do something related to UNIX ELSEIF(APPLE) #do something related to APPLE ENDIF(WIN32)2,WHILE
WHILE 指令的語法是:
其真假判斷條件可以參考 IF 指令。
3,FOREACH
FOREACH 指令的使用方法有三種形式:
1,列表
像我們前面使用的 AUX_SOURCE_DIRECTORY 的例子
AUX_SOURCE_DIRECTORY(. SRC_LIST) FOREACH(F ${SRC_LIST}) MESSAGE(${F}) ENDFOREACH(F)2,范圍
FOREACH(loop_var RANGE total) ENDFOREACH(loop_var)從 0 到 total 以1為步進
舉例如下
最終得到的輸出是:
0
1
2
3
4
5
6
7
8
9
10
3,范圍和步進
FOREACH(loop_var RANGE start stop [step]) ENDFOREACH(loop_var)從 start 開始到 stop 結束,以 step 為步進,
舉例如下
最終得到的結果是:
5
8
11
14
這個指令需要注意的是,知道遇到 ENDFOREACH 指令,整個語句塊才會得到真正的執行。
install 指令
安裝的需要有兩種,一種是從代碼編譯后直接make 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安裝到CMAKEINSTALLPREFIX/lib目錄靜態庫libmystaticlib安裝到{CMAKE_INSTALL_PREFIX}/lib目錄 靜態庫libmystaticlib安裝到CMAKEI?NSTALLP?REFIX/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這個目錄的內容會被安裝在目標路徑下;
如果目錄名不以/結尾,那么這個目錄將被安裝為目標路徑下的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
OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ.
安裝時cmake腳本的執行
INSTALL([[SCRIPT <file>] [CODE <code>]] […])
SCRIPT參數用于在安裝時調用cmake腳本文件(也就是<abc>.cmake文件)
CODE參數用于執行CMAKE指令,必須以雙引號括起來。比如:
INSTALL(CODE “MESSAGE(“Sample install message.”)”)
示例:模塊的使用和自定義模塊
你現在還會覺得 cmake 簡單嗎?
本章我們將著重介紹系統預定義的 Find 模塊的使用以及自己編寫 Find 模塊,系統中提供了其他各種模塊,一般情況需要使用 INCLUDE 指令顯式的調用,FIND_PACKAGE 指令是一個特例,可以直接調用預定義的模塊.
其實使用純粹依靠 cmake 本身提供的基本指令來管理工程是一件非常復雜的事情,所以,cmake 設計成了可擴展的架構,可以通過編寫一些通用的模塊來擴展 cmake.
在本章,我們準備首先介紹一下 cmake 提供的 FindCURL 模塊的使用。然后,基于我們前面的 libhello 共享庫,編寫一個 FindHello.cmake 模塊.
一,使用 FindCURL 模塊
在/backup/cmake 目錄建立 t5 目錄,用于存放我們的 CURL 的例子。
建立 src 目錄,并建立 src/main.c,內容如下:
這段代碼的作用是通過 curl 取回 www.linux-ren.org 的首頁并寫入/tmp/curl-test文件中。
建立主工程文件 CMakeLists.txt
PROJECT(CURLTEST)
ADD_SUBDIRECTORY(src)
建立 src/CMakeLists.txt
ADD_EXECUTABLE(curltest main.c)
現在自然是沒辦法編譯的,我們需要添加 curl 的頭文件路徑和庫文件。
方法 1:
直接通過 INCLUDE_DIRECTORIES 和 TARGET_LINK_LIBRARIES 指令添加:
我們可以直接在 src/CMakeLists.txt 中添加:
INCLUDE_DIRECTORIES(/usr/include)
TARGET_LINK_LIBRARIES(curltest curl)
然后建立 build 目錄進行外部構建即可。
現在我們要探討的是使用 cmake 提供的 FindCURL 模塊。
方法 2,使用 FindCURL 模塊。
向src/CMakeLists.txt 中添加:
對于系統預定義的 Find.cmake 模塊,使用方法一般如上例所示:
每一個模塊都會定義以下幾個變量
<name>_FOUND
<name>_INCLUDE_DIR or <name>_INCLUDES
<name>_LIBRARY or <name>_LIBRARIES
你可以通過<name>_FOUND 來判斷模塊是否被找到,如果沒有找到,按照工程的需要關閉某些特性、給出提醒或者中止編譯,上面的例子就是報出致命錯誤并終止構建。
如果<name>_FOUND 為真,則將<name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,
將<name>_LIBRARY 加入 TARGET_LINK_LIBRARIES 中。
我們再來看一個復雜的例子,通過<name>_FOUND 來控制工程特性:
通過判斷系統是否提供了 JPEG 庫來決定程序是否支持 JPEG 功能。
二,編寫屬于自己的 FindHello 模塊。
我們在此前的 t3 實例中,演示了構建動態庫、靜態庫的過程并進行了安裝。
接下來,我們在 t6 示例中演示如何自定義 FindHELLO 模塊并使用這個模塊構建工程:
請在建立/backup/cmake/中建立 t6 目錄,并在其中建立 cmake 目錄用于存放我們自己定義的 FindHELLO.cmake 模塊,同時建立 src 目錄,用于存放我們的源文件。
1,定義 cmake/FindHELLO.cmake 模塊
FIND_PATH(HELLO_INCLUDE_DIR hello.h /usr/include/hello /usr/local/include/hello) FIND_LIBRARY(HELLO_LIBRARY NAMES hello PATH /usr/lib /usr/local/lib) IF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY)SET(HELLO_FOUND TRUE) ENDIF (HELLO_INCLUDE_DIR AND HELLO_LIBRARY) IF (HELLO_FOUND)IF (NOT HELLO_FIND_QUIETLY)MESSAGE(STATUS "Found Hello: ${HELLO_LIBRARY}")ENDIF (NOT HELLO_FIND_QUIETLY) ELSE (HELLO_FOUND)IF (HELLO_FIND_REQUIRED)MESSAGE(FATAL_ERROR "Could not find hello library")ENDIF (HELLO_FIND_REQUIRED) ENDIF (HELLO_FOUND)針對上面的模塊讓我們再來回顧一下 FIND_PACKAGE 指令:
FIND_PACKAGE(<name> [major.minor] [QUIET] [NO_MODULE]
[[REQUIRED|COMPONENTS] [componets…]])
前面的 CURL 例子中我們使用了最簡單的 FIND_PACKAGE 指令,其實他可以使用多種參數,
QUIET 參數,對應與我們編寫的 FindHELLO 中的 HELLO_FIND_QUIETLY,如果不指定這個參數,就會執行:
MESSAGE(STATUS “Found Hello: ${HELLO_LIBRARY}”)
REQUIRED 參數,其含義是指這個共享庫是否是工程必須的,如果使用了這個參數,說明這個鏈接庫是必備庫,如果找不到這個鏈接庫,則工程不能編譯。對應于
FindHELLO.cmake 模塊中的 HELLO_FIND_REQUIRED 變量。
同樣,我們在上面的模塊中定義了 HELLO_FOUND,
HELLO_INCLUDE_DIR,HELLO_LIBRARY 變量供開發者在 FIND_PACKAGE 指令中使用。
OK,下面建立 src/main.c,內容為:
建立 src/CMakeLists.txt 文件,內容如下:
FIND_PACKAGE(HELLO) IF(HELLO_FOUND)ADD_EXECUTABLE(hello main.c)INCLUDE_DIRECTORIES(${HELLO_INCLUDE_DIR})TARGET_LINK_LIBRARIES(hello ${HELLO_LIBRARY}) ENDIF(HELLO_FOUND)為了能夠讓工程找到 FindHELLO.cmake 模塊(存放在工程中的 cmake 目錄)我們在主工程文件 CMakeLists.txt 中加入:
SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
三,使用自定義的 FindHELLO 模塊構建工程
仍然采用外部編譯的方式,建立 build 目錄,進入目錄運行:
cmake …
我們可以從輸出中看到:
Found Hello: /usr/lib/libhello.so
如果我們把上面的 FIND_PACKAGE(HELLO)修改為 FIND_PACKAGE(HELLO QUIET),則不會看到上面的輸出。
接下來就可以使用 make 命令構建工程,運行:
./src/hello 可以得到輸出
Hello World。
說明工程成功構建。
四,如果沒有找到 hello library 呢?
我們可以嘗試將/usr/lib/libhello.x 移動到/tmp 目錄,這樣,按照 FindHELLO 模塊的定義,就找不到 hello library 了,我們再來看一下構建結果:
cmake …
仍然可以成功進行構建,但是這時候是沒有辦法編譯的。
修改 FIND_PACKAGE(HELLO)為 FIND_PACKAGE(HELLO REQUIRED),將 hello library 定義為工程必須的共享庫。
這時候再次運行 cmake …
我們得到如下輸出:
CMake Error: Could not find hello library.
因為找不到 libhello.x,所以,整個 Makefile 生成過程被出錯中止。
小結:
在本節中,我們學習了如何使用系統提供的 Find<NAME>模塊并學習了自己編寫
Find<NAME>模塊以及如何在工程中使用這些模塊。
總結
以上是生活随笔為你收集整理的cmakelist 常见用法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java调用python接口详解
- 下一篇: clion创建项目CMakeList.t