CMake學(xué)習(xí)小結(jié)
參考資料
CGold的Cmake教程 -- 這個(gè)教程很新:
https://cgold.readthedocs.io/en/latest/overview.html
CMakeLists中的變量
常規(guī)變量
CMakeLists.txt中的常規(guī)變量有作用域的, 變量分以下兩種變量:
- 常規(guī)變量
set(abc "123")
- 緩存變量
set(abc "123" CACHE STRING "")
, 在CMakeLists.txt
構(gòu)造以后,會(huì)在CMakeCache.txt
中有所有緩存變量的記錄.
常規(guī)變量有如下特點(diǎn):
- 常規(guī)變量每次重新創(chuàng)建, 沒(méi)有緩存
- 常規(guī)變量使用
set(abc "123")
的方式創(chuàng)建 - 其他變量要引用這個(gè)創(chuàng)建的變量,需要用解引用方法
${}
, 例如:set(bcd "${abc}xyz")
, 此時(shí)bcd
的值是123xyz
- 常規(guī)變量受作用域影響, 作用域的變化會(huì)產(chǎn)生父作用域和子作用域的關(guān)系, 子作用域中會(huì)繼承所有的父作用的常規(guī)變量
-
function
和add_subdirectory
內(nèi)部會(huì)創(chuàng)建新的作用域!!!新作用域內(nèi)會(huì)繼承父作用域的變量, 改變子作用域中的變量的值,不會(huì)影響父作用域中變量的值 -
include
和macro
并不會(huì)創(chuàng)建新的作用域!!! 因此, 在這內(nèi)容中,修改變量會(huì)影響變量的值 - 如果在子作用域中, 如果想修改父作用域中的變量的值, 需要使用
PARENT_SCOPE
關(guān)鍵字 --set(abc "786" PARENT_SCOPE)
. 注意這里不會(huì)改變子作用域中的abc
的值, 只會(huì)修改父作用域中abc
的值
-
- 常規(guī)變量使用
unset(abc)
來(lái)重置某個(gè)變量在當(dāng)前作用域的值
變量以外,我們?cè)诿钚兄惺褂玫?code>-D參數(shù)也是聲明緩存變量的方式.
變量的引用于解引用${...}
:
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
set(a "xyz")
set(b "${a}_321")
set(${a}_1 "456")
set(variable_${a} "${a} + $臣咖 + 155")
message("b: '$'")
message("xyz_1: '${xyz_1}'")
message("variable_xyz: '${variable_xyz}'")
[usage-of-variables]> rm -rf _builds
[usage-of-variables]> cmake -Hdereference -B_builds
b: 'xyz_321'
xyz_1: '456'
variable_xyz: 'xyz + xyz_321 + 155'
-- Configuring done
-- Generating done
-- Build files have been written to: /.../usage-of-variables/_builds
嵌套使用${...}
:
cmake_minimum_required(VERSION 2.8)
project(foo)
foreach(lang C CXX)
message("Compiler for language ${lang}: ${CMAKE_${lang}_COMPILER}")
foreach(build_type DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
message("Flags for language ${lang} + build type ${build_type}: ${CMAKE_${lang}_FLAGS_${build_type}}")
endforeach()
endforeach()
Compiler for language C: /usr/bin/cc
Flags for language C + build type DEBUG: -g
Flags for language C + build type RELEASE: -O3 -DNDEBUG
Flags for language C + build type RELWITHDEBINFO: -O2 -g -DNDEBUG
Flags for language C + build type MINSIZEREL: -Os -DNDEBUG
Compiler for language CXX: /usr/bin/c++
Flags for language CXX + build type DEBUG: -g
Flags for language CXX + build type RELEASE: -O3 -DNDEBUG
Flags for language CXX + build type RELWITHDEBINFO: -O2 -g -DNDEBUG
Flags for language CXX + build type MINSIZEREL: -Os -DNDEBUG
其他的例如 list 列表,常規(guī)使用比較少, 可以自己已查資料
需要小結(jié)的是:
- All variables have a
string type
- List is nothing but
string
, elements of list separated by ; - The way how variables are interpreted depends on the command
- Do not give same names for cache and regular variables
-
add_subdirectory
andfunction
create new scope -
include
andmacro
work in the current scope
緩存變量(全局變量)
緩存變量由如下特點(diǎn):
- 緩存變量是全局作用域, 變量的值會(huì)從
CMakeCache.txt
中獲取,緩存變量在寫入CMakeCache.txt
以后, 每次都從這個(gè)緩存文件中讀取該值, 而不會(huì)重新調(diào)用set(... CACHE ...)
!!!(Cache變量只會(huì)初始化一次,在CMakeCache.txt
中存儲(chǔ)以后, 多次調(diào)用set(... CACHE ...)
不起作用!!!) - 緩存變量的常規(guī)初始化的方式是
set(abc "789" CACHE STRING "")
- 當(dāng)常規(guī)變量和緩存變量重名時(shí), 如果緩存變量聲明在后面, 會(huì)覆蓋常規(guī)變量
- 除了使用
set(... CACHE ...)
可以聲明緩存, 也可以在命令行使用-Dxxx=xxx
修改緩存變量的值!!! - 如果某個(gè)Cache變量已經(jīng)存在于
CMakeCache.txt
中, 代碼中的set(... CACHE ...)
將不會(huì)對(duì)這個(gè)值有影響, 如果我們?cè)诖a中需要強(qiáng)制修改Cache變量的值, 需要使用FORCE
字段 ---set(abc "123" CACHE STRING "" FORCE)
最佳實(shí)踐, 對(duì)于全局Cache變量太多,我們最好將他們放在一個(gè)單獨(dú)的文件, 然后在調(diào)用CMake .. -C cache.cmake
方法將這個(gè)文件加載進(jìn)去.
# cache.cmake
set(A "123" CACHE STRING "")
set(B "456" CACHE STRING "")
set(C "789" CACHE STRING "")
# CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
message("A: ${A}")
message("B: ${B}")
message("C: ${C}")
[usage-of-variables]> rm -rf _builds
[usage-of-variables]> cmake -C initial-cache/cache.cmake -Hinitial-cache -B_builds
loading initial cache file initial-cache/cache.cmake
A: 123
B: 456
C: 789
-- Configuring done
-- Generating done
-- Build files have been written to: /.../usage-of-variables/_builds
option() 命令創(chuàng)建的是一個(gè)緩存變量
最佳實(shí)踐, 由于Cache變量的全局性特點(diǎn), 用于做開(kāi)關(guān), 使用option
:
# top-level CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(zoo)
add_subdirectory(boo)
add_subdirectory(foo)
# foo/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(foo)
option(FOO_FEATURE_1 "Enable feature 1" OFF)
option(FOO_FEATURE_2 "Enable feature 2" OFF)
# boo/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(boo)
option(BOO_FEATURE_1 "Enable feature 1" ON)
option(BOO_FEATURE_2 "Enable feature 2" ON)
總結(jié):
- Use cache to set global variables
- Cache variables fits perfectly for expressing customized options: default value and respect user’s value
- Type of cache variable helps CMake-GUI users
- Prefixes should be used to avoid clashing because of the global nature of cache variables
環(huán)境變量 Environment variables
環(huán)境變量可以使用$ENV{...}
訪問(wèn):
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
message("Environment variable USERNAME: $ENV{USERNAME}")
[usage-of-variables]> rm -rf _builds
[usage-of-variables]> echo $USERNAME
ruslo
[usage-of-variables]> export USERNAME
[usage-of-variables]> cmake -Hread-env -B_builds
Environment variable USERNAME: ruslo
-- Configuring done
-- Generating done
-- Build files have been written to: /.../usage-of-variables/_builds
可以使用set(ENV{...})
來(lái)設(shè)置環(huán)境變量:
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
set(ENV{USERNAME} "Jane Doe")
message("Environment variable USERNAME: $ENV{USERNAME}")
[usage-of-variables]> rm -rf _builds
[usage-of-variables]> echo $USERNAME
ruslo
[usage-of-variables]> export USERNAME
[usage-of-variables]> cmake -Hset-env -B_builds
Environment variable USERNAME: Jane Doe
-- Configuring done
-- Generating done
-- Build files have been written to: /.../usage-of-variables/_builds
總結(jié):
- CMake can set, unset and read environment variables
- Check carefully configure-build steps where you set environment variables
- Child processes will inherit environment variables of parent
- Do not make your CMake code depends on environment variable if that variable may change
CMake的代碼執(zhí)行的過(guò)程
源碼樹(shù)(source-tree
):
CMakeLists.txt
一般會(huì)通過(guò)add_subdirectory
方法給source-tree
添加節(jié)點(diǎn), 而CMAKE_CURRENT_SOURCE_DIR
變量就是當(dāng)前處理節(jié)點(diǎn)的全路徑, 而整個(gè)source-tree
的根節(jié)點(diǎn)則是CMAKE_SOURCE_DIR
.
二進(jìn)制樹(shù)(binary-tree
):
在調(diào)用CMake
命令編譯以后,生成的文件夾是一個(gè)編譯結(jié)果二進(jìn)制樹(shù). 其中,CMAKE_BINARY_DIR
是二進(jìn)制樹(shù)的樹(shù)根, CMAKE_CURRENT_BINARY_DIR
是當(dāng)前處理的二進(jìn)制樹(shù)的節(jié)點(diǎn).
在CMakeLists.txt
的編寫過(guò)程中, 我們可以用CMake module
結(jié)合include()
命令來(lái)重用代碼. 常見(jiàn)的module是*.cmake
不要用
include
命令加載Find*.cmake
模塊!!!它有專用命令find_package
如果我們有一個(gè)專門存放的cmake module
的文件夾, 我們可以將它添加到CMAKE_MODULE_PATH
變量中, 最好使用list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
:
# Top level CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/modules")
include(MyModule)
# modules/MyModule.cmake
message("Hello from MyModule!")
除此之外, 一下是一些常用的變量:
-
CMAKE_CURRENT_LIST_DIR
和CMAKE_CURRENT_LIST_FILE
可以用下面一個(gè)實(shí)例展示:
# Top-level CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
include(mymodule)
# cmake/mymodule.cmake
message("Full path to module: ${CMAKE_CURRENT_LIST_FILE}")
message("Module located in directory: ${CMAKE_CURRENT_LIST_DIR}")
[cmake-sources]> rm -rf _builds
[cmake-sources]> cmake -Hpath-to-module -B_builds
Full path to module: /.../cmake-sources/path-to-module/cmake/mymodule.cmake
Module located in directory: /.../cmake-sources/path-to-module/cmake
-- Configuring done
-- Generating done
-- Build files have been written to: /.../cmake-sources/_builds
下面一個(gè)圖表現(xiàn)幾個(gè)常見(jiàn)的變量的關(guān)系:
-
CMAKE_SOURCE_DIR
/CMAKE_BINARY_DIR
these variables point to the root of thesource
/binary
trees. If your project will be added to another project as a subproject byadd_subdirectory
, the locations like${CMAKE_SOURCE_DIR}/my-resource.txt
will point to<top-level>/my-resource.txt
instead of<my-project>/my-resource.txt
-
PROJECT_SOURCE_DIR
/PROJECT_BINARY_DIR
these variables are better then previous but still have kind of a global nature. You should change all paths related toPROJECT_SOURCE_DIR
if you decide to move declaration of your project or decide to detach some part of the code and add new project command in the middle of the source tree. Consider using extra variable with clean separate purpose for such jobset(FOO_MY_RESOURCES "${CMAKE_CURRENT_LIST_DIR}/resources")
instead of referring to${PROJECT_SOURCE_DIR}/resources
. -
CMAKE_CURRENT_SOURCE_DIR
this is a directory withCMakeLists.txt
. If you’re using this variable internally you can substitute is withCMAKE_CURRENT_LIST_DIR
. In case you’re creatingmodule
for external usage consider moving all functionality to function.
add_executable
cmake_minimum_required(VERSION 2.8)
project(foo)
add_executable(foo main.cpp)
注意這里定義的 foo
是一個(gè)Target
, 名稱是全局范圍的變量, 并且唯一的!!!