CMake学习小结
参考资料
CGold的Cmake教程 -- 这个教程很新:
https://cgold.readthedocs.io/en/latest/overview.html
CMakeLists中的变量
常规变量
CMakeLists.txt中的常规变量有作用域的, 变量分以下两种变量:
- 常规变量
set(abc "123")
- 缓存变量
set(abc "123" CACHE STRING "")
, 在CMakeLists.txt
构造以后,会在CMakeCache.txt
中有所有缓存变量的记录.
常规变量有如下特点:
- 常规变量每次重新创建, 没有缓存
- 常规变量使用
set(abc "123")
的方式创建 - 其他变量要引用这个创建的变量,需要用解引用方法
${}
, 例如:set(bcd "${abc}xyz")
, 此时bcd
的值是123xyz
- 常规变量受作用域影响, 作用域的变化会产生父作用域和子作用域的关系, 子作用域中会继承所有的父作用的常规变量
-
function
和add_subdirectory
内部会创建新的作用域!!!新作用域内会继承父作用域的变量, 改变子作用域中的变量的值,不会影响父作用域中变量的值 -
include
和macro
并不会创建新的作用域!!! 因此, 在这内容中,修改变量会影响变量的值 - 如果在子作用域中, 如果想修改父作用域中的变量的值, 需要使用
PARENT_SCOPE
关键字 --set(abc "786" PARENT_SCOPE)
. 注意这里不会改变子作用域中的abc
的值, 只会修改父作用域中abc
的值
-
- 常规变量使用
unset(abc)
来重置某个变量在当前作用域的值
变量以外,我们在命令行中使用的-D
参数也是声明缓存变量的方式.
变量的引用于解引用${...}
:
cmake_minimum_required(VERSION 2.8)
project(foo NONE)
set(a "xyz")
set(b "${a}_321")
set(${a}_1 "456")
set(variable_${a} "${a} + ${b} + 155")
message("b: '${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 列表,常规使用比较少, 可以自己已查资料
需要小结的是:
- 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
缓存变量(全局变量)
缓存变量由如下特点:
- 缓存变量是全局作用域, 变量的值会从
CMakeCache.txt
中获取,缓存变量在写入CMakeCache.txt
以后, 每次都从这个缓存文件中读取该值, 而不会重新调用set(... CACHE ...)
!!!(Cache变量只会初始化一次,在CMakeCache.txt
中存储以后, 多次调用set(... CACHE ...)
不起作用!!!) - 缓存变量的常规初始化的方式是
set(abc "789" CACHE STRING "")
- 当常规变量和缓存变量重名时, 如果缓存变量声明在后面, 会覆盖常规变量
- 除了使用
set(... CACHE ...)
可以声明缓存, 也可以在命令行使用-Dxxx=xxx
修改缓存变量的值!!! - 如果某个Cache变量已经存在于
CMakeCache.txt
中, 代码中的set(... CACHE ...)
将不会对这个值有影响, 如果我们在代码中需要强制修改Cache变量的值, 需要使用FORCE
字段 ---set(abc "123" CACHE STRING "" FORCE)
最佳实践, 对于全局Cache变量太多,我们最好将他们放在一个单独的文件, 然后在调用CMake .. -C cache.cmake
方法将这个文件加载进去.
# 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() 命令创建的是一个缓存变量
最佳实践, 由于Cache变量的全局性特点, 用于做开关, 使用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)
总结:
- 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
环境变量 Environment variables
环境变量可以使用$ENV{...}
访问:
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{...})
来设置环境变量:
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
总结:
- 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的代码执行的过程
源码树(source-tree
):
CMakeLists.txt
一般会通过add_subdirectory
方法给source-tree
添加节点, 而CMAKE_CURRENT_SOURCE_DIR
变量就是当前处理节点的全路径, 而整个source-tree
的根节点则是CMAKE_SOURCE_DIR
.
二进制树(binary-tree
):
在调用CMake
命令编译以后,生成的文件夹是一个编译结果二进制树. 其中,CMAKE_BINARY_DIR
是二进制树的树根, CMAKE_CURRENT_BINARY_DIR
是当前处理的二进制树的节点.
在CMakeLists.txt
的编写过程中, 我们可以用CMake module
结合include()
命令来重用代码. 常见的module是*.cmake
不要用
include
命令加载Find*.cmake
模块!!!它有专用命令find_package
如果我们有一个专门存放的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
可以用下面一个实例展示:
# 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
下面一个图表现几个常见的变量的关系:
关系-
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
是一个Target
, 名称是全局范围的变量, 并且唯一的!!!
网友评论