21、linux筆記--CMake
簡介
CMake is an open-source, cross-platform family of tools designed to build, test and package software. CMake is used to control the software compilation process using simple platform and compiler independent configuration files, and generate native makefiles and workspaces that can be used in the compiler environment of your choice. The suite of CMake tools were created by Kitware in response to the need for a powerful, cross-platform build environment for open-source projects such as ITK and VTK.
CMake is part of Kitware’s collection of commercially supported open-source platforms for software development.
特點
- 在每個原始碼目錄下都有一個 CMakeLists.txt.
- CMake 語句不區分大小寫。一句一行,無行結束符號,註釋用#
- CMake 實際也是一種程式語言。CMake 根據 CMakeLists.txt 自動生成 Makefile.。
- CMake 比 Autotools 更簡單明瞭。
- 開放原始碼。
- 跨平臺,並可生成native編譯配置檔案,在Linux/Unix 平臺,生成 makefile,在蘋果平臺,可以生成xcode,在 Windows 平臺,可以生成 MSVC 的工程檔案。
- 能夠管理大型專案。
- 可擴充套件,可以為cmake編寫特定功能的模組,擴充cmake 功能。
- 注:如果你的工程只有幾個檔案,直接編寫Makefile 是最好的選擇。
語法
1、變數使用${}方式取值,但是在 IF 語句中是直接使用變數名取值
MESSAGE(STATUS “This is bin dir” $(PROJECT_BINARY_DIR))
MESSAGE(STATUS “This is bin dir $(PROJECT_BINARY_DIR)”)
上面兩句等效。
2、指令(引數 1 引數 2 …),引數之間用空格或分號隔開
SET( SRC_LIST main.cpp hello.cpp)
SET(SRC_LIST “main.cpp” “hello.cpp”)
SET(SRC_LIST “main.cpp”;“hello.cpp”)
3、內部構建和外部構建:在哪個目錄下執行 cmake 命令,則在哪個目錄構建
In-source:編譯過程檔案和原始碼檔案在同一目錄下面(在工程目錄下 cmake)
Out-of-sourc:將編譯目錄和原始碼目錄分割開(在非工程目錄下 cmake)。
4、常用變數及指令
(1)CMake變數
序號 | 語句 | 註釋 |
---|---|---|
1 | PROJECT_BINARY_DIR 、PROJECT_SOURCE_DIR、CMAKE_BINARY_DIR 、CMAKE_SOURCE_DIR | 工程目標檔案目錄、工程原始檔目錄 |
2 | CMAKE_CURRENT_BINARY_DIR、CMAKE_CURRENT_SOURCE_DIR | 指當前處理的 CMakeLists.txt 所在的路徑 |
3 | CMAKE_CURRENT_LIST_FILE CMAKE_CURRENT_LIST_LINE | 輸出呼叫這個變數的 CMakeLists.txt |
4 | < project name>_BINARY_DIR < project name>_SOURCE_DIR | project name 工程目標檔案 project name 源目標檔案 |
5 | EXECUTABLE_OUTPUT_PATH | 最終目標二進位制檔案存放目錄 |
6 | LIBRARY_OUT_PATH | 最終目標庫檔案存放目錄 |
7 | CMAKE_INSTALL_PREFIX | 目標檔案安裝目錄 ,預設目錄為/usr/local/bin |
8 | CMAKE_MODULE_PATH | 定義自己的 CMake模組所在的路徑 |
9 | PROJECT_NAME | 返回通過 PROJECT 指令定義的值 |
10 | CMAKE_INCLUDE_CURRENT_DIR | 自動新增 CMAKE_CURRENT_BINARY_DIR 和CMAKE_CURRENT_SOURCE_DIR 到當前 CMakeLists.txt 處理。 |
11 | CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFO RE | 將工程提供的標頭檔案目錄 始終至於系統標頭檔案目錄前面 |
12 | CMAKE_MAJOR_VERSION CMAKE_MINOR_VERSION CMAKE_PATCH_VERSION | CMake 主版本號,2.4.6 中的 2 CMake 次版本號,2.4.6 中的 4 CMake 的補丁等級,2.4.6 中的 6 |
13 | CMAKE_SYSTEM CMAKE_SYSTEM_NAME CMAKE_SYSTEM_VERSION CMAKE_SYSTEM_PROCESSOR | 系統名稱,如 Linux-2.6.26 Linux 2.6.26 I386 |
14 | UNIX WIN32 | 在所有的類 UNIX 平臺值為 TRUE, 包括 MacOS 和 和 Cygwin 在所有的 WIN32 平臺值為 TRUE, 包括 Cygwin |
15 | CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS | 開關選項,用來控制 if else 的書寫方式 |
16 | BUILD_SHARED_LIBS | 開關,預設為靜態庫 |
17 | CMAKE_C_FLAGS CMAKE_CXX_FLAGS | 設定 C 編譯選項 設定 C++ 編譯選項 |
(2)CMake 指令([] 表示可選引數)
序號 | 語句 | 註釋 |
---|---|---|
1 | PROJECT(project name[CXX][C][Java]) | 定義工程名稱( 工程名與生成的 目標檔名稱是沒有任何關係的) 。 此條指令隱含了兩個變數 < project name>_BINARY_DIR < project name>_SOURCE_DIR |
2 | SET(var [value] [cache type docstring[force]]) | 自定義變數指令 set( SRC_LIST main.cpp hello.cpp) set(SRC_LIST “main.cpp” “test.cpp”) |
3 | MASSEGE([SEND_ERROR|STATUS|FATAL_ERROR] “massage to display” …) | SEND_ERROR: 產生錯誤,生成過程被跳過
STATUS: 輸出字首為--- 的資訊 FATAL_ERROR: 立即終止所有 CMake 過程 |
4 | ADD_EXECUTABLE(target source_file…) | 增加可執行目標檔案, target 由 source_file生成 |
5 | ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE FROM ALL]) | 增加子目錄 |
6 | SUBDIRS(dir1 dir2 …) | 一次新增多個目錄, 即使外部編譯, 子目錄體系仍然會被儲存 |
7 | INSTALL(TARGETS targets [ [ARCHIVE|LIBRARY|RUNTIME] [DESTINATION< dir>] [PERMISSIONS permissions…] [CONFIGGURATIONS [Debug|Release|…]] [COMPONENT ] [OPTIONAL]][…]) | 安裝目標檔案 ARCHIVE 靜態庫檔案 LIBRARY 動態庫檔案 RUNTIME 可執行檔案 DESTINATION 定義安裝路徑, 如果是絕對路徑則覆蓋了 CMAKE_INSTALL_PREFIX, 否則是指 相對 CMAKE_INSTALL_PREFIX 的相對路徑 |
8 | INSTALL(FILES files [ [DESTINATION < dir>] [PERMISSIONS permissions…] [CONFIGGURATIONS [Debug|Release|…]] [COMPONENT ] [OPTIONAL] ][…]) | 安裝普通檔案 可以指定許可權,如果不指定, 則預設是 644 |
9 | INSTALL(DIRECTORY dirs [ [DESTINATION < dir>] [FILE_PERMISSIONS permissions…] [DIRECTORY_PERMISSIONS permissions…] [USE_SOURCE_PERMISSIONS permissions…] [CONFIGGURATIONS [Debug|Release|…]] [COMPONENT ] [[PATTERN | REGEX ] [EXCLUDE] [PERMISSIONS permissions…]] […]) | 安裝目錄 可以指定許可權,如果不指定, 則默 認是 644 |
10 | ADD_LIBRARY(libname
[SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1source2 … sourceN) | MODULE: 在使用 dydl 的系統有效, 如果支援 dydl, 則預設為 SHARED |
11 | SET_TARGET_PROPERTIES(target1 target2 … PROPERTIES prop1 value prop2 value2 …) | 設定目標輸出的名字 及屬性 由於 TARGET 名字不能有重複, 所以需在生成庫檔案再改為需要的名 字, 這時就要用到這個指令了。 相關變數: OUTPUT_NAME, 輸出名字( 庫,可執行檔名字,可以不用加字尾) OUTPUT_VALUE CLEAN_DIRECT_OUTPUT VERSION SOVERSION |
12 | GET_TARGET_PROPERTIES(VAR target property) | 獲取目標的屬性 |
13 | $ENV{NAME} | 呼叫系統環境變數 |
14 | SET(ENV{ 變數名} 值) | 設定環境變數值 |
15 | ADD_DEFINITIONS 例:ADD_DEFINITIONS(-DENABLE_DEBUG) | 向編譯器新增-D定義 |
16 | ADD_DEPENDENCIES(target_name depend_target1 depend_target) | 定義 target 依賴的其他 target |
17 | ADD_TEST(testname program arg1 arg2) | 在打開了 ENABEL_TESTING 後有效 |
18 | ENABEL_TESTING | 不帶任何引數, 控制 Makefile 是否構建 test 目標, 一般用在工程主CMakeList.txt |
19 | AUX_SOURCE_DIRECTORY(dir VARIABLE) 例 AUX_SOURCE_DIRECTORY(. SRC_LIST), 將當前目錄下原始檔名賦給變數 SRC_LIST | 自動構建原始檔列表 |
20 | CMAKE_MINIMUM_REQUIRED(VERSION version_num [FATAL_ERROR]) | 檢查 CMake 版本,若不滿足, 產生錯誤提示或退出 |
21 | EXEC_PROGRAM(program [ARGS args] [OUTPUT_VARIABLE var] [RETURN_VALUE value]) | ARGS 用於新增引數 OUTPUT_VARIABLE 用於獲取命令輸出 RETURN_VALUE 用於獲取返回值 |
22 | FILE 指令 FILE(WRITE filename “message” …) FILE(APPEND filename “message” …) FILE(READ filename variable) FILE(GLOB variable [RELATIVE path] [globing expressions]…) FILE(GLOB_RECURSE variable [RELATIVE path] [globing expressions]…) FILE(REMOVE [directory]…) FILE(REMOVE_RECURSE [directory]…) FILE(MAKE_DIRECTORY [directory]…) FILE(RELATIVE_PATH variable directory file) FILE(TO_CMAKE_PATH path result) FILE(TO_NATIVE_PATH path result) | 寫檔案 新增內容到檔案 讀檔案 移除目錄 遞迴移除目錄 建立目錄 |
23 | INCLUDE(file [OPTIONAL])
INCLUDE(module [OPTIONAL]) | 用來載入 CMakeLists.txt 檔案 或者CMake |
24 | FIND 指令 FIND_FILE(< VAR>name1 path1 path2 …) FIND_LIBRARY(< VAR>name1 path1 path2 …) FIND_PATH(< VAR>name1 path1 path2 …) FIND_PROGRAM(< VAR>name1 path1 path2 …) FIND_PACKAGE(< name> [major.minor] [QUITE] [NO_MODULE] [[REQUIRED|COMPONENTS] [components…]]) | VAR 變數 name1 代表找到的檔案全路徑, 包含檔名VAR 變數 name2 代表找到的檔案全路徑, 包含庫檔名 VAR 變數代表包含這個檔案的路徑 VAR 變數代表包含這個程式的全路徑 |
5、判斷語句
IF指令,基本語法為:
IF(expression)
#THEN section
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ELSE(expression)
#ELSE section
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ENDIF(expression)
另外一個指令是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_VEWER_THAN file2),當file1比file2新,或者file1/file2其中有一個不存在時為真,檔名請使用完整路徑。
IF(IS_DIRECTORY dirname),當dirname是目錄時,為真。
IF(variable MATCHES regex)
IF(string MATCHES regex)
當給定的變數或者字串能夠匹配正則表示式regex時為真。比如:
IF(“hello” MATCHES “ell”)
MESSAGE(“true”)
ENDIF(“hello” MATCHES “ell”)
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 number)
IF(string STRLESS number)
IF(variable STRGREATER number)
IF(string STRGREATER number)
IF(variable STREQUAL number)
IF(string STREQUAL number)
按照字母序的排列進行比較
IF(DEFINED variable),如果變數被定義,為真
一個小例子,用來判斷平臺差異:
IF(WIN32)
MESSAGE(STATUS “This is windows”)
#做一些Windows相關的操作
ELSE(WIN32)
MESSAGE(STATUS “This is windows”)
#做一些非Windows相關的操作
ENDIF(WIN32)
上述程式碼用來控制在不同的平臺進行不同的控制,但是,閱讀起來卻並不是那麼舒服
ENDIF(WIN32)之類的語句很容易引起歧義。
這就用到了我們在“常用變數”一節提到的CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS開關。
可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
這時候就可以寫成:
IF(WIN32)
WLSE()
ENDIF()
如果配合ELSEIF使用,可能的寫法是這樣:
IF(WIN32)
#do something related to WIN32
ELSEIF(UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ELSEIF(WIN32)
6、迴圈語句
(1)WHILE
WHILE指令的語法是:
WHILE(condition)
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ENDWHILE(condition)
其真假判斷條件可以參考IF指令。
(2)FOREACH
FOREACH指令的使用方法有三種形式:
<1>列表
FOREACH(loop_var_ arg1 arg2 …)
COMMAND1(ARGS …)
COMMAND2(ARGS …)
…
ENDFOREACH(loop_var)
像我們前面使用的AUX_SOURCE_DIRECTORY的例子
AUX_SOURCE_DIRECTORY( SRC_LIST)
FOREACH(F {F})
ENDFOREACH(F)
<2>範圍
FOREACH(loop_var RANGE total)
ENDFOREACH(loop_var)
從0到total以1為步進
舉例如下:
FOREACH(VAR RANGE 10)
MESSAGE($ {VAR})
ENDFOREACH(VAR)
最終得到的輸出是:
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為步進。
舉例如下:
FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
最終得到的結果是:
5
8
11
14
這個指令需要注意的是,知道遇到ENDFOREACH指令,整個語句塊才會得到真正的執行。
7、模組的使用與編寫
其實使用純粹依靠cmake本身提供的基本指令來管理工程是一件非常複雜的事情,所以,cmake設計成了可擴充套件的架構,可以通過編寫一些通用的模組來擴充套件cmake。
接下來首先介紹cmake提供的FindCURL模組的使用。然後,基於我們前面的libhello共享庫,編寫一個FindHello.cmake模組。