CMake簡介
-
CMake 是做什么的?
CMake是一套類似於automake的跨平台輔助項目編譯的工具。 我覺得語法更加簡單易用。 -
CMake的工作流程
CMake處理頂級目錄的CMakeLists.txt
(CMake的配置文件,配置了子目錄,編譯目標,編譯依賴等等),最后根據配置生成相應的MakeFile。使用make命令既可以進行編譯。
CMake 基本語法
CMake定義了一套領域編程語言或者說腳本,稱為CMake語言,支持變量定義、流程控制、函數、預制函數。
文件組織
CMake能夠處理cmake語言源碼。
在一個項目中,cmake語言源碼存在的位置分為以下兩種。
文件夾 (CMakeLists.txt),
script腳本 (<script>.cmake,后綴不重要)
-
Directories
在項目中,項目根目錄的CMakeLists.txt是CMake的入口點,也就是說CMake命令先找CMakeL
ists.txt,並執行內部的命令,生成構建系統。CMakeLists.txt應該定義了所有的編譯控制。
並用add_subdirectory()指定要處理的子文件夾(子項目),子文件夾內部也要有
CMakeLists.txt文件,在CMake執行到add_subdirectory()時,CMake會進入到指定的子文
件夾,然后在子文件夾內部尋找CMakeLists.txt執行,生成子文件夾的構建系統。子文件夾的
源碼的構建工作目錄就是在子文件夾內。 -
Scripts
stripts腳本如果要單獨執行,需要cmake -P xxx.cmake。stripts腳本不會生成構建系統,
因為在stripts腳本中,不允許指定構建目標。
command
CMake代碼由一系列command的調用組成。包括if else 都屬於command。
類似於下面這個命令
\# 添加可執行目標hello, 參數為world.c
add_executable(hello world.c)
command調用語法為
identifier(以空格隔開的參數表)
參數可以用()括起來,表示這個單個參數。
如if(TRUE OR (TRUE AND FALSE))
注意:command名大小寫不敏感
參數類型有
方括號形式
[={len}[
內部隨便寫點文本,cmake不會內部的變量引用或者換行進行處理。可以保持文本原始樣子。${variable}
\-escape
]=]
方括號不允許嵌套
={len}
的意思:len
表示結束符的=個數。當[=2]
時, ]==] 才是結束符。
例子:
message([=3[
This is the first line in a bracket argument with bracket length 1.
No \-escape sequences or ${variable} references are evaluated.
This is always one argument even though it contains a ; character.
The text does not end on a closing bracket of length 0 like ]].
It does end in a closing bracket of length 1.
]===])
引號形式
引號形式的就是放在""內部的參數,""會被當成一個參數傳進函數。""內部的變量引用或者轉義會
被解析。可以用\表示字符串還沒有結束。
message("This is a quoted argument containing multiple lines.
This is always one argument even though it contains a ; character.
Both \\-escape sequences and ${variable} references are evaluated.
The text does not end on an escaped double-quote like \".
It does end in an unescaped double quote.
")
無引號形式
CMake支持參數不帶任何引號,因為所有值都會轉換成String。所有的參數會被封裝成List。
List的分隔符為;,所以參數列表內如果一個字符串用;分割,;兩邊會被當成兩個參數。
\#這里有四個參數
commandName(arg arg2 arg3;arg4)
foreach(arg
NoSpace
Escaped\ Space
This;Divides;Into;Five;Arguments
Escaped\;Semicolon
)
message("${arg}")
endforeach()
輸出
NoSpace
Escaped Space
This
Divides
Into
Five
Arguments
Escaped;Semicolon
注釋
注釋分為行注釋和塊注釋
-
行注釋
# 行注釋,只能寫一行內容
-
塊注釋
塊注釋有結尾有開頭,可以寫多行注釋。用[[]]括起來,注意[要緊跟#。#[[這是多
行注釋]]
流程控制
條件控制
if(condition)
elseif(condition)
else()
endif()
如
if(VAR1 MATCHES "Hello")
message("this is hello")
message("this is hello2")
elseif(VAR1 MATCHES "world")
message("this is world")
message("this is world2")
endif()
循環
for循環
語法為
foreach(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endforeach(loop_var)
示例
set(mylist "a" "b" c "d")
foreach(_var ${mylist})
message("當前變量是:${_var}")
endforeach()
上面是最簡單的用法,還有一個foreach(loop_var RANGE start stop [step]) 的用法。
set(result 0)
foreach(_var RANGE 0 100)
math(EXPR result "${result}+${_var}")
endforeach()
message("from 0 plus to 100 is:${result}")
在foreach循環中,支持break()和continue()。
while循環
while(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endwhile(condition)
自定義command
CMake系統內置了一批command,https://cmake.org/cmake/help/v3.7/manual/cmake-commands.7.html
但是開發者仍然能夠自定義command。
function
function(<name> [arg1 [arg2 [arg3 ...]]])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endfunction(<name>)
在function內可以使用一些變量取得傳入的參數信息。
變量名 | 意義 |
---|---|
ARGC | 參數個數 |
ARGV | 參數列表 |
ARGV0 | 參數0 |
ARGV1 | 參數1 |
ARGV2 | 參數2 |
ARGN | 超出最后一個預期參數的參數列表 |
函數原型聲明時,只接受一個參數,那么調用函數時傳遞給函數的參數列表中,
從第二個參數(如果有的話)開始就會保存到ARGN。
例如
function (argument_tester arg)
message(STATUS "ARGN: ${ARGN}")
message(STATUS "ARGC: ${ARGC}")
message(STATUS "ARGV: ${ARGV}")
message(STATUS "ARGV0: ${ARGV0}")
message(STATUS "ARGV0: ${arg}")
list(LENGTH ARGV argv_len)
message(STATUS "length of ARGV: ${argv_len}")
set(i 0)
while( i LESS ${argv_len})
list(GET ARGV ${i} argv_value)
message(STATUS "argv${i}: ${argv_value}")
math(EXPR i "${i} + 1")
endwhile()
endfunction ()
argument_tester(arg0 arg1 arg2 arg3)
macro
宏和function的作用是一樣的,但是宏只是對字符串的簡單替換。和define類似。
macro(
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
endmacro(
下面簡單的用一個實例區分兩者的區別
set(var "ABC")
macro(Moo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})
function(Foo arg)
message("arg = ${arg}")
set(arg "abc")
message("# After change the value of arg.")
message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})
結果為:
=== Call macro ===
arg = ABC
\# After change the value of arg.
arg = ABC
=== Call function ===
arg = ABC
\# After change the value of arg.
arg = abc
變量定義和引用
CMake中,變量的值要么是String要么是String組成的List。
CMake沒有用=賦值的操作,只有通過set,option來定義變量。
option只能定義OFF,ON的變量。
變量定義
set
set分為兩種
- set普通變量
set(<variable> <value>... [PARENT_SCOPE])
例如
//VA=a;b, VA是一個字符串list
set(VA a b)
//VA=a,VA是一個字符串
set(VB a)
- set CACHE變量
CACHE變量會自動保存到CMakeCache.txt中,上次的結果下次繼續用。
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
示例
set(ICD_LIBRARY "${PROJECT_BINARY_DIR}/lib" CACHE INTERNAL "ICD Library location" )
option
option(<option_variable> "help string describing option"
[initial value])
變量引用
可以使用${variable_name} 。如果變量沒有定義,返回空. 變量引用可以嵌套,變量引用的值從內往外計算。
如
${outer_${inner_variable}_variable}.
CMake系統內置了一堆的變量,可以查閱
https://cmake.org/cmake/help/v3.7/manual/cmake-variables.7.html
環境變量的訪問
$ENV{VAR}
變量只有string類型。變量名字大小寫敏感,並且可以包含任意字符。
采用set()/unset()定義和取消定義
變量作用域存在於set的當前作用域
變量作用域:
-
Function Scope
在函數內部set的變量,作用域作用於當前函數及其調用的函數內。return 后就沒了。 -
Directory Scope
再CMakeLists.txt定義的變量(非function內部),作用域在當前Directory及其子Directory中。 -
Persistent Cache
持久緩存。變量值會緩存到CMakeCache.txt中,下次運行,會使用CMakeCache中的值。
采用set(variable value CACHE <type> "")
方式設置。
如
set(ICD_LIBRARY "${PROJECT_BINARY_DIR}/lib" CACHE INTERNAL "ICD Library location" )
CMake將會自動把find_path和option的值放到CMakeCache中。
Lists
在CMake中,所有的值都會被當成string來存儲,但是在某些情況下, 多個string可以組成list。
例如在無""參數,多個字符串中間加了一個;。可以使用循環來遍歷List
set(srcs a.c b.c c.c) #sets "srcs" to "a.c;b.c;c.c"
CMake專門提供了一個內置command來處理list
list(LENGTH <list> <output variable>) //獲得list長度
list(GET <list> <element index> [<element index> ...]
<output variable>) //獲得list的某個位置元素
list(APPEND <list> [<element> ...])//add
list(FILTER <list> <INCLUDE|EXCLUDE> REGEX <regular_expression>)//清理
list(FIND <list> <value> <output variable>) //查找
list(INSERT <list> <element_index> <element> [<element> ...])
list(REMOVE_ITEM <list> <value> [<value> ...])
list(REMOVE_AT <list> <index> [<index> ...])
list(REMOVE_DUPLICATES <list>)
list(REVERSE <list>)
list(SORT <list>)