【Tools】cmake 常用变量和常用环境变量查表手册---整理
原文鏈接:https://blog.csdn.net/gubenpeiyuan/article/details/8667279
一.cmake 變量引用的方式:
前面我們已經提到了,使用${}進行變量的引用。在IF等語句中,是直接使用變量名而不通過${}取值.
二.cmake 自定義變量的方式:
主要有隱式定義和顯式定義兩種,前面舉了一個隱式定義的例子,就是 PROJECT 指令,他會隱式的定義<projectname>_BINARY_DIR 和<projectname>_SOURCE_DIR 兩個變量。
顯式定義的例子我們前面也提到了,使用 SET 指令,就可以構建一個自定義變量了。
比如:
SET(HELLO_SRC main.SOURCE_PATHc),就 PROJECT_BINARY_DIR 可以通過${HELLO_SRC}來引用這個自定義變量了.
三.cmake 常用變量:
-
1.工程頂層目錄、工程編譯發生的目錄
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
<projectname>_BINARY_DIR
這三個變量指代的內容是一致的,如果是 in source 編譯,指得就是工程頂層目錄,如果是 out-of-source 編譯,指的是工程編譯發生的目錄。PROJECT_BINARY_DIR 跟其他指令稍有區別,現在,你可以理解為他們是一致的。 -
2.工程頂層目錄
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
<projectname>_SOURCE_DIR
這三個變量指代的內容是一致的,不論采用何種編譯方式,都是工程頂層目錄。
也就是在 in source 編譯時,他跟 CMAKE_BINARY_DIR 等變量一致。
PROJECT_SOURCE_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 <新路徑>)并不會對這個變量造成影響,它僅僅修改了最終目標文件存放的路徑。 -
5.CMAKE_CURRENT_LIST_FILE
輸出調用這個變量的 CMakeLists.txt 的完整路徑 -
6.CMAKE_CURRENT_LIST_LINE
輸出這個變量所在的行 -
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 指令定義的項目名稱。
四.cmake 調用環境變量的方式
使用$ENV{NAME}指令就可以調用系統的環境變量了。
比如
MESSAGE(STATUS “HOME dir: $ENV{HOME}”)
設置環境變量的方式是:
SET(ENV{變量名} 值)
-
1.CMAKE_INCLUDE_CURRENT_DIR
自動添加 CMAKE_CURRENT_BINARY_DIR和 CMAKE_CURRENT_SOURCE_DIR 到當前處理
的 CMakeLists.txt。相當于在每個CMakeLists.txt加入:
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}) -
2.CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE
將工程提供的頭文件目錄始終至于系統頭文件目錄的前面,當你定義的頭文件確實跟系統發生沖突時可以提供一些幫助。 -
3.CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH我們在上一節已經提及。
五.系統信息
- CMAKE_MAJOR_VERSION,CMAKE 主版本號,比如 2.4.6 中的 2
- 2.CMAKE_MINOR_VERSION,CMAKE 次版本號,比如 2.4.6 中的 4
- 3.CMAKE_PATCH_VERSION,CMAKE 補丁等級,比如 2.4.6 中的 6
- 4.CMAKE_SYSTEM,系統名稱,比如 Linux-2.6.22
- 5.CMAKE_SYSTEM_NAME,不包含版本的系統名,比如 Linux
- 6.CMAKE_SYSTEM_VERSION,系統版本,比如 2.6.22
- 7.CMAKE_SYSTEM_PROCESSOR,處理器名稱,比如 i686.
- 8.UNIX,在所有的類 UNIX 平臺為 TRUE,包括 OS X 和 cygwin
- 9.WIN32,在所有的 win32 平臺為 TRUE,包括 cygwin
六.主要的開關選項:
- 1.CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS,用來控制 IF ELSE 語句的書寫方式,在
下一節語法部分會講到。 - 2.BUILD_SHARED_LIBS
這個開關用來控制默認的庫編譯方式,如果不進行設置,使用 ADD_LIBRARY并沒有指定庫
類型的情況下,默認編譯生成的庫都是靜態庫。
如果 SET(BUILD_SHARED_LIBS ON)后,默認生成的為動態庫。 - 3.CMAKE_C_FLAGS
設置C編譯選項,也可以通過指令 ADD_DEFINITIONS()添加。 - 4.CMAKE_CXX_FLAGS
設置C++編譯選項,也可以通過指令ADD_DEFINITIONS()添加。
小結:
本章介紹了一些較常用的 cmake 變量,這些變量僅僅是所有 cmake 變量的很少一部分,目
前 cmake 的英文文檔也是比較缺乏的,如果需要了解更多的 cmake 變量,更好的方式是閱
讀一些成功項目的 cmake 工程文件,比如 KDE4 的代碼。
八.cmake 常用指令
前面我們講到了 cmake 常用的變量,相信“cmake 即編程”的感覺會越來越明顯,無論如何,我們仍然可以看到 cmake 比 autotools 要簡單很多。接下來我們就要集中的看一看cmake 所提供的常用指令。在前面的章節我們已經討論了很多指令的用法,如
PROJECT,ADD_EXECUTABLE,INSTALL,ADD_SUBDIRECTORY,SUBDIRS,
INCLUDE_DIRECTORIES,LINK_DIRECTORIES,TARGET_LINK_LIBRARIES,SET 等。
本節會引入更多的 cmake 指令,為了編寫的方便,我們將按照 cmake man page的順序來介紹各種指令,不再推薦使用的指令將不再介紹,INSTALL 系列指令在安裝部分已經做了非常詳細的說明,本節也不在提及。(你可以將本章理解成選擇性翻譯,但是會加入更多的個人理解)
8.1.基本指令
-
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 指令
文件操作指令,基本語法為:
這里的語法都比較簡單,不再展開介紹了,可參考這篇博客https://blog.csdn.net/gubenpeiyuan/article/details/51098116。
- 9.INCLUDE 指令,用來載入 CMakeLists.txt 文件,也用于載入預定義的 cmake 模塊.
INCLUDE(file1 [OPTIONAL])
INCLUDE(module [OPTIONAL])
[OPTIONAL] 參數的作用是文件不存在也不會產生錯誤。
你可以指定載入一個文件,如果定義的是一個模塊,那么將在CMAKE_MODULE_PATH中搜索這個模塊并載入。
載入的內容將在處理到 INCLUDE語句是直接執行。
8.2.INSTALL指令
INSTALL 系列指令已經在前面的章節有非常詳細的說明,這里不在贅述,可參考前面的安裝部分。
8.3.FIND_xxx指令
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)8.4.控制指令:
- 1.IF 指令,基本語法為:
另外一個指令是 ELSEIF,總體把握一個原則,凡是出現IF的地方一定要有對應的ENDIF.出現 ELSEIF 的地方,ENDIF是可選的。
表達式的使用方法如下:
IF(var),如果變量不是:空,0,N, NO, OFF, FALSE, NOTFOUND 或_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 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)之類的語句很容易引起歧義。
這就用到了我們在“常用變量”一節提到的 CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS開關。
可以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 指令,整個語句塊才會得到真正的執行。
小結:
本小節基本涵蓋了常用的 cmake 指令,包括基本指令、查找指令、安裝指令以及控制語句等,特別需要注意的是,在控制語句條件中使用變量,不能用${}引用,而是直接應用變量名。
掌握了以上的各種控制指令,你應該完全可以通過 cmake 管理復雜的程序了,下一節,我
們將介紹一個比較復雜的例子,通過他來演示本章的一些指令,并介紹模塊的概念。
九,復雜的例子:模塊的使用和自定義模塊
你現在還會覺得 cmake 簡單嗎?
本章我們將著重介紹系統預定義的 Find模塊的使用以及自己編寫 Find 模塊,系統中提供了其他各種模塊,一般情況需要使用 INCLUDE指令顯式的調用,FIND_PACKAGE指令是一個特例,可以直接調用預定義的模塊.
其實使用純粹依靠 cmake 本身提供的基本指令來管理工程是一件非常復雜的事情,所以,cmake 設計成了可擴展的架構,可以通過編寫一些通用的模塊來擴展 cmake.
在本章,我們準備首先介紹一下 cmake 提供的FindCURL 模塊的使用。然后,基于我們前面的 libhello共享庫,編寫一個FindHello.cmake模塊.
9.1使用 FindCURL模塊
在/backup/cmake 目錄建立 t5 目錄,用于存放我們的 CURL 的例子。
建立 src 目錄,并建立 src/main.c,內容如下:
這段代碼的作用是通過 curl取回 www.linux-ren.org 的首頁并寫入/tmp/curl-test文件中。
建立主工程文件 CMakeLists.txt
建立 src/CMakeLists.txt
ADD_EXECUTABLE(curltest main.c)
現在自然是沒辦法編譯的,我們需要添加 curl 的頭文件路徑和庫文件。
方法 1:
直接通過 INCLUDE_DIRECTORIES和 TARGET_LINK_LIBRARIES 指令添加:
我們可以直接在 src/CMakeLists.txt 中添加:
然后建立 build 目錄進行外部構建即可。
現在我們要探討的是使用 cmake 提供的 FindCURL 模塊。
方法 2,使用 FindCURL 模塊。
向src/CMakeLists.txt 中添加:
對于系統預定義的 Find<name>.cmake 模塊,使用方法一般如上例所示:
每一個模塊都會定義以下幾個變量
你可以通過<name>_FOUND來判斷模塊是否被找到,如果沒有找到,按照工程的需要關閉某些特性、給出提醒或者中止編譯,上面的例子就是報出致命錯誤并終止構建。
如果<name>_FOUND 為真,則將<name>_INCLUDE_DIR 加入 INCLUDE_DIRECTORIES,將<name>_LIBRARY加入 TARGET_LINK_LIBRARIES中。
我們再來看一個復雜的例子,通過<name>_FOUND來控制工程特性:
通過判斷系統是否提供了 JPEG 庫來決定程序是否支持 JPEG 功能。
9.2,編寫屬于自己的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)
9.3,使用自定義的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 ..
我們得到如下輸出:
因為找不到 libhello.x,所以,整個Makefile 生成過程被出錯中止。
小結:
在本節中,我們學習了如何使用系統提供的 Find<NAME>模塊并學習了自己編寫
Find<NAME>模塊以及如何在工程中使用這些模塊。
后面的章節,我們會逐漸學習更多的 cmake 模塊使用方法以及用 cmake 來管理 GTK 和 QT4工程。
總結
以上是生活随笔為你收集整理的【Tools】cmake 常用变量和常用环境变量查表手册---整理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【C++】43.使用【类对象】与 【类指
- 下一篇: 【C++】44.使用xx.so动态链接库