从零开始 CMake 学习笔记 (F)Build Type
开始前先默念三遍口诀:
- Declare a target
- Declare target’s traits
- It’s all about targets
本系列主要根据GitHub上的 cmake-examples 项目进行翻译总结,同时对于不清晰的概念及函数进行查阅理解记录形成。
文章目录
- 从零开始 CMake 学习笔记 (F)Build Type
- 1 介绍
-
- 2 概念解析
- 2.1 编译类型
- 2.2 设置当前次的编译类型
- 2.2.1 CMake图形界面
- 2.2.2 CMake命令行
- 2.3 设置默认的编译类型
- 2.3.1 set() 拓展
- 2.3.1.1 设置普通变量
- 2.3.1.2 设置缓存变量
- 2.3.1.3 设置环境变量
- 3 运行结果
1 介绍
本示例展示了如何在生成可执行二进制文件中设置编译类型,这些类型可以指定项目的优化级别以及是否在二进制文件中包含调试信息。
1.1 文件树
F-build-type $ tree
.
├── CMakeLists.txt
├── main.cpp
1.2 文件简介
- CMakeLists.txt - 包含了你希望运行的 CMake 命令
cmake_minimum_required(VERSION 3.5)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message("Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()
project (build_type)
add_executable(cmake_examples_build_type main.cpp)
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "Hello Build Type!" << std::endl;
return 0;
}
2 概念解析
2.1 编译类型
CMake具有许多内置的构建配置,可用于编译工程。 这些配置指定了代码优化的级别,以及调试信息是否包含在二进制文件中。主要有下面四种:
-
Release - 发行版。顾名思义,程序完成开发后的发布版本,对代码做了优化,速度非常快,但无法跟踪代码,不能打断点调试。可以在编译器追加后: -O3 -DNDEBUG
选择此版本。
-
Debug - 调试版。对代码不做任何优化,可以 debug 项目中的任意文件。一般速度相对会慢,而且体积更大。可以在编译器追加后: -g
选择此版本。
-
RelWithDebInfo - 非常近似与Release版本,代码也是经过优化的,同时还支持添加断点进行调试。可以在编译器追加后: -Os -DNDEBUG
选择此版本。
-
MinSizeRel - 最小体积版本。类似与Release版本,但更偏重于优化文件大小,而不是运行速度。可以在编译器追加后: -O2 -g -DNDEBUG
选择此版本。
2.2 设置当前次的编译类型
2.2.1 CMake图形界面
2.2.2 CMake命令行
CMake命令行中,同配置其他参数一致,使用 -D 选项
cmake .. -DCMAKE_BUILD_TYPE=Release
2.3 设置默认的编译类型
CMake提供的默认编译类型时不包含优化的。因此,如果我们想要将设置的编译类型应用于所有用户的各个子项目,那我们可以考虑直接更改默认的编译类型。你可以直接在项目顶层的 CMakeLists.txt 文件中设置如下:
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message("Setting build type to 'RelWithDebInfo' as none was specified.")
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
# 当使用cmake-gui的时候,设置编译类型的四个可选项
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()
2.3.1 set() 拓展
set() 命令可以为普通变量、缓存变量、环境变量赋值。下面分别对三种变量的设置进行说明。
2.3.1.1 设置普通变量
命令格式:set(<variable> <value>... [PARENT_SCOPE])
命令含义:将变量variable
设置为值<value>...
,变量variable
的 作用域 为调用set
命令的函数或者当前目录,如果使用了PARENT_SCOPE
选项,意味着该变量的作用域会传递到上一层(也就是上一层目录或者当前函数的调用者,如果是函数则传递到函数的调用者,如果是目录则传递到上一层目录),并且在当前作用域该变量不受带PARENT_SCOPE选项的set命令的影响(如果变量之前没有定义,那么在当前作用域仍然是无定义的;如果之前有定义值,那么值和之前定义的值保持一致,类似于之前target_include_directories
的 INTERFACE
命令,我定义但是我不用)。
关于变量的作用域:每一个新的目录或者函数都会创建一个新的作用域,普通变量的作用域,如果不使用PARENT_SCOPE选项,只能从外层往内层传递。
案例1:
最常用的用法示例如下:
cmake_minimum_required(VERSION 3.5)
project (set_test)
set (normal_var a)
message (">>> value = ${normal_var}")
输出为:
>>> value = a
案例2:
设置变量为多个给定的值:
cmake_minimum_required(VERSION 3.5)
project (set_test)
set (normal_var a b c)
message (">>> value = ${normal_var}")
输出为:
>>> value = a;b;c
案例3:
设置变量为空:
cmake_minimum_required(VERSION 3.5)
project (set_test)
set (normal_var a b c)
message (">>> value = ${normal_var}")
set (normal_var) # 设置变量为空
message (">>> value = ${normal_var}")
输出为:
>>> value = a;b;c
>>> value =
案例4:
在函数内使用选项PARENT_SCOPE
定义变量,在函数定义的文件中(非另一个函数中)使用该变量。
cmake_minimum_required(VERSION 3.5)
project (set_test)
set (normal_var_in_fn aaa PARENT_SCOPE)
message (">>> value = ${normal_var_fn}")
输出为:
# 输出
>>> value =
原本为空,在设置的时候用了PARENT_SCOPE
关键字,那么就代表,我定义但我不用。此时该变量的值依然为空,而当前函数或者库的调用者则可以使用这个变量。
案例5:
在一个函数内使用选项PARENT_SCOPE
定义变量,在另一个函数(调用者,示例中为test_fn_parent)内调用该函数。
cmake_minimum_required(VERSION 3.5)
project (set_test)
function (test_fn arg1)
set (normal_var_in_fn nohello)
set (normal_var_in_fn ${arg1} PARENT_SCOPE)
message (">>> in function, value = ${normal_var_in_fn}")
endfunction (test_fn)
function (test_fn_parent arg1)
test_fn (${arg1})
message (">>> in parent function, value = ${normal_var_in_fn}")
endfunction (test_fn_parent)
test_fn_parent (hello)
输出为:
# 输出
>>> in function, value = nohello
>>> in parent function, value = hello
由结果可以看到,在定义该变量的函数中,后面一句 set (normal_var_in_fn ${arg1} PARENT_SCOPE)
紧接着打印输出,而输出的值依然为之前设置的 nohello 。而在调用这个函数的函数中,输出的值变为了 hello。 这就是 我定义但我不用。
2.3.1.2 设置缓存变量
命令格式:set(<variable> <value>... CACHE <type> <docstring> [FORCE])
命令含义:将缓存条目variable
设置为值<value>...
,除非用户进行设置或使用了选项 FORCE ,默认情况下缓存变量的值不会被覆盖。缓存变量可以通过 cmake-gui 界面的add entry
按钮来增加。缓存变量的本质为可以跨层级进行传递的变量,类似于全局变量。
缓存变量的<type
>主要有以下几类:
BOOL
:布尔值ON/OFF
。如果是 cmake-gui 的话会对此类缓存变量提供一个选择框。FILEPATH
:文件路径。 如果是 cmake-gui 的话会对此类缓存变量提供一个文件选择框。PATH
:目录路径。 如果是 cmake-gui 的话会对此类缓存变量提供一个目录选择框。STRING / STRINGS
:文本行。 如果是 cmake-gui 的话会对此类缓存变量提供一个文本框。INTERNAL
:文本行。但是只用于内部,不对外呈现。主要用于运行过程中存储变量,因此使用该type
意味着使用FORCE
。
注意事项:
- 如果变量先前未定义或者使用了
FORCE
选项,则缓存变量会直接被强行赋值。 - 可以在使用cmake构建的使用通过 -D 选项来给缓存变量赋值,这样 CMakeLists.txt 内的
set
命令只会为缓存变量添加类型。 - 如果变量类型是目录或者文件路径,通过 -D 选项传入的若只是相对路径,那么
set
会给这个相对路径前添加当前的工作目录以变成绝对路径(如果已经是绝对路径则不会处理)。
2.3.1.3 设置环境变量
命令格式:set(ENV{<variable>} [<value>])
命令含义:将环境变量设置为值<value>
(注意没有…,不可为多个值),接着使用$ENV{<variable>}
会得到新的值。
注意事项:
- 该命令设置的环境变量只在当前的 cmake 进程生效,既不会影响调用者的环境变量,也不会影响系统环境变量。
- 如果
<value>
值为空或者 ENV{<variable>}
后没有参数,则该命令会清除掉当前环境变量的值。 <value>
后的参数会被忽略。
案例1
先设置变量,然后不跟参数。
cmake_minimum_required(VERSION 3.5)
project (set_test)
message (">>> value = $ENV{CMAKE_PREFIX_PATH}")
set (ENV{CMAKE_PREFIX_PATH} "/test/sub")
message (">>> value = $ENV{CMAKE_PREFIX_PATH}")
set (ENV{CMAKE_PREFIX_PATH})
message (">>> value = $ENV{CMAKE_PREFIX_PATH}")
输出为:
>>> value =
>>> value = /test/sub
>>> value =
案例2
在后面不跟参数也可以,在后面设置为 ""空值
cmake_minimum_required(VERSION 3.5)
project (set_test)
message (">>> value = $ENV{CMAKE_PREFIX_PATH}")
set (ENV{CMAKE_PREFIX_PATH} "/test/sub")
message (">>> value = $ENV{CMAKE_PREFIX_PATH}")
set (ENV{CMAKE_PREFIX_PATH} "")
message (">>> value = $ENV{CMAKE_PREFIX_PATH}")
输出为:
>>> value =
>>> value = /test/sub
>>> value =
3 运行结果
构建此示例的标准输出如下所示:
F-build-type $ mkdir build
F-build-type $ cd build/
F-build-type $ cmake ..
Setting build type to 'RelWithDebInfo' as none was specified.
-- The C compiler identification is GNU 4.8.4
-- The CXX compiler identification is GNU 4.8.4
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build
F-build-type $ make VERBOSE=1
/usr/bin/cmake -H/home/matrim/workspace/cmake-examples/01-basic/F-build-type -B/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory `/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build'
make -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/depend
make[2]: Entering directory `/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build'
cd /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/matrim/workspace/cmake-examples/01-basic/F-build-type /home/matrim/workspace/cmake-examples/01-basic/F-build-type /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake --color=
Dependee "/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake" is newer than depender "/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles/cmake_examples_build_type.dir/depend.internal".
Dependee "/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles/cmake_examples_build_type.dir/depend.internal".
Scanning dependencies of target cmake_examples_build_type
make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build'
make -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/build
make[2]: Entering directory `/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build'
/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles 1
[100%] Building CXX object CMakeFiles/cmake_examples_build_type.dir/main.cpp.o
/usr/bin/c++ -O2 -g -DNDEBUG -o CMakeFiles/cmake_examples_build_type.dir/main.cpp.o -c /home/matrim/workspace/cmake-examples/01-basic/F-build-type/main.cpp
Linking CXX executable cmake_examples_build_type
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmake_examples_build_type.dir/link.txt --verbose=1
/usr/bin/c++ -O2 -g -DNDEBUG CMakeFiles/cmake_examples_build_type.dir/main.cpp.o -o cmake_examples_build_type -rdynamic
make[2]: Leaving directory `/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build'
/usr/bin/cmake -E cmake_progress_report /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles 1
[100%] Built target cmake_examples_build_type
make[1]: Leaving directory `/home/matrim/workspace/cmake-examples/01-basic/F-build-type/build'
/usr/bin/cmake -E cmake_progress_start /home/matrim/workspace/cmake-examples/01-basic/F-build-type/build/CMakeFiles 0
F-build-type $ ./cmake_example_build_type
Hello Build Type!
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)