1. 程式人生 > >21、linux筆記--CMake

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.

特點

  1. 在每個原始碼目錄下都有一個 CMakeLists.txt.
  2. CMake 語句不區分大小寫。一句一行,無行結束符號,註釋用#
  3. CMake 實際也是一種程式語言。CMake 根據 CMakeLists.txt 自動生成 Makefile.。
  4. CMake 比 Autotools 更簡單明瞭。
  5. 開放原始碼。
  6. 跨平臺,並可生成native編譯配置檔案,在Linux/Unix 平臺,生成 makefile,在蘋果平臺,可以生成xcode,在 Windows 平臺,可以生成 MSVC 的工程檔案。
  7. 能夠管理大型專案。
  8. 可擴充套件,可以為cmake編寫特定功能的模組,擴充cmake 功能。
  9. 注:如果你的工程只有幾個檔案,直接編寫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變數

序號語句註釋
1PROJECT_BINARY_DIR 、PROJECT_SOURCE_DIR、CMAKE_BINARY_DIR 、CMAKE_SOURCE_DIR工程目標檔案目錄、工程原始檔目錄
2CMAKE_CURRENT_BINARY_DIR、CMAKE_CURRENT_SOURCE_DIR指當前處理的 CMakeLists.txt 所在的路徑
3CMAKE_CURRENT_LIST_FILE CMAKE_CURRENT_LIST_LINE輸出呼叫這個變數的 CMakeLists.txt
4< project name>_BINARY_DIR
< project name>_SOURCE_DIR
project name 工程目標檔案
project name 源目標檔案
5EXECUTABLE_OUTPUT_PATH最終目標二進位制檔案存放目錄
6LIBRARY_OUT_PATH最終目標庫檔案存放目錄
7CMAKE_INSTALL_PREFIX目標檔案安裝目錄 ,預設目錄為/usr/local/bin
8CMAKE_MODULE_PATH定義自己的 CMake模組所在的路徑
9PROJECT_NAME返回通過 PROJECT 指令定義的值
10CMAKE_INCLUDE_CURRENT_DIR 自動新增
CMAKE_CURRENT_BINARY_DIR
和CMAKE_CURRENT_SOURCE_DIR
到當前 CMakeLists.txt 處理。
11CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFO RE將工程提供的標頭檔案目錄
始終至於系統標頭檔案目錄前面
12CMAKE_MAJOR_VERSION
CMAKE_MINOR_VERSION
CMAKE_PATCH_VERSION
CMake 主版本號,2.4.6 中的 2
CMake 次版本號,2.4.6 中的 4
CMake 的補丁等級,2.4.6 中的 6
13CMAKE_SYSTEM
CMAKE_SYSTEM_NAME
CMAKE_SYSTEM_VERSION
CMAKE_SYSTEM_PROCESSOR
系統名稱,如 Linux-2.6.26
Linux
2.6.26
I386
14UNIX

WIN32
在所有的類 UNIX 平臺值為 TRUE,
包括 MacOS 和 和 Cygwin
在所有的 WIN32 平臺值為 TRUE,
包括 Cygwin
15CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS開關選項,用來控制 if else 的書寫方式
16BUILD_SHARED_LIBS開關,預設為靜態庫
17CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
設定 C 編譯選項
設定 C++ 編譯選項

(2)CMake 指令([] 表示可選引數)

序號語句註釋
1PROJECT(project name[CXX][C][Java]) 定義工程名稱( 工程名與生成的
目標檔名稱是沒有任何關係的) 。
此條指令隱含了兩個變數
< project name>_BINARY_DIR
< project name>_SOURCE_DIR
2SET(var [value] [cache type docstring[force]])自定義變數指令
set( SRC_LIST main.cpp hello.cpp)
set(SRC_LIST “main.cpp” “test.cpp”)
3MASSEGE([SEND_ERROR|STATUS|FATAL_ERROR] “massage to display” …)SEND_ERROR: 產生錯誤,生成過程被跳過 STATUS: 輸出字首為--- 的資訊
FATAL_ERROR: 立即終止所有 CMake 過程
4ADD_EXECUTABLE(target source_file…) 增加可執行目標檔案,
target 由 source_file生成
5ADD_SUBDIRECTORY(source_dir [binary_dir]
[EXCLUDE FROM ALL])
增加子目錄
6SUBDIRS(dir1 dir2 …) 一次新增多個目錄, 即使外部編譯,
子目錄體系仍然會被儲存
7INSTALL(TARGETS targets [
[ARCHIVE|LIBRARY|RUNTIME]
[DESTINATION< dir>]
[PERMISSIONS permissions…]
[CONFIGGURATIONS [Debug|Release|…]]
[COMPONENT ]
[OPTIONAL]][…])
安裝目標檔案
ARCHIVE 靜態庫檔案
LIBRARY 動態庫檔案
RUNTIME 可執行檔案
DESTINATION 定義安裝路徑,
如果是絕對路徑則覆蓋了
CMAKE_INSTALL_PREFIX, 否則是指
相對 CMAKE_INSTALL_PREFIX 的相對路徑
8INSTALL(FILES files [
[DESTINATION < dir>]
[PERMISSIONS permissions…]
[CONFIGGURATIONS [Debug|Release|…]]
[COMPONENT ]
[OPTIONAL]
][…])
安裝普通檔案
可以指定許可權,如果不指定,
則預設是 644
9INSTALL(DIRECTORY dirs [
[DESTINATION < dir>]
[FILE_PERMISSIONS permissions…]
[DIRECTORY_PERMISSIONS permissions…]
[USE_SOURCE_PERMISSIONS permissions…]
[CONFIGGURATIONS [Debug|Release|…]]
[COMPONENT ]
[[PATTERN | REGEX ]
[EXCLUDE] [PERMISSIONS permissions…]]
[…])
安裝目錄
可以指定許可權,如果不指定,
則默 認是 644
10ADD_LIBRARY(libname [SHARED|STATIC|MODULE]
[EXCLUDE_FROM_ALL] source1source2 … sourceN)
MODULE: 在使用 dydl 的系統有效,
如果支援 dydl, 則預設為 SHARED
11SET_TARGET_PROPERTIES(target1 target2 …
PROPERTIES prop1 value prop2 value2 …)
設定目標輸出的名字 及屬性
由於 TARGET 名字不能有重複,
所以需在生成庫檔案再改為需要的名 字,
這時就要用到這個指令了。
相關變數:
OUTPUT_NAME, 輸出名字( 庫,可執行檔名字,可以不用加字尾)
OUTPUT_VALUE
CLEAN_DIRECT_OUTPUT
VERSION
SOVERSION
12GET_TARGET_PROPERTIES(VAR target property) 獲取目標的屬性
13$ENV{NAME} 呼叫系統環境變數
14SET(ENV{ 變數名} 值) 設定環境變數值
15ADD_DEFINITIONS
例:ADD_DEFINITIONS(-DENABLE_DEBUG)
向編譯器新增-D定義
16ADD_DEPENDENCIES(target_name
depend_target1 depend_target)
定義 target 依賴的其他 target
17ADD_TEST(testname program arg1 arg2) 在打開了 ENABEL_TESTING 後有效
18ENABEL_TESTING 不帶任何引數,
控制 Makefile 是否構建 test 目標,
一般用在工程主CMakeList.txt
19AUX_SOURCE_DIRECTORY(dir VARIABLE)
例 AUX_SOURCE_DIRECTORY(. SRC_LIST),
將當前目錄下原始檔名賦給變數 SRC_LIST
自動構建原始檔列表
20CMAKE_MINIMUM_REQUIRED(VERSION
version_num [FATAL_ERROR])
檢查 CMake 版本,若不滿足,
產生錯誤提示或退出
21EXEC_PROGRAM(program
[ARGS args]
[OUTPUT_VARIABLE var]
[RETURN_VALUE value])
ARGS 用於新增引數
OUTPUT_VARIABLE 用於獲取命令輸出
RETURN_VALUE 用於獲取返回值
22FILE 指令
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)
寫檔案
新增內容到檔案
讀檔案





移除目錄
遞迴移除目錄
建立目錄
23INCLUDE(file [OPTIONAL])
INCLUDE(module [OPTIONAL])
用來載入 CMakeLists.txt 檔案
或者CMake
24FIND 指令
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 SRCLIST)MESSAGE({SRC_LIST}) MESSAGE({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模組。