一、文件数

  1. ├── CMakeLists.txt
  2. ├── main.cpp

1.1 main.cpp

#include <iostream>

int main(int argc, char *argv[])
{
   std::cout << "Hello Build Type!" << std::endl;
   return 0;
}

1.2 CMakeLists.txt

cmake_minimum_required(VERSION 3.5)
project(build_type)

# 如果没有指定则设置默认编译方式
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message("Setting build type to 'RelWithDebInfo' as none was specified.")    # 命令行输出message里的信息

    set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE                                     # 不管CACHE是否设置过CMAKE_BUILD_TYPE这个
        STRING "Choose the type of build." FORCE)                                # 变量,强制赋值为RelWithDebInfo

    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug"                 # 当使用cmake-gui的时候,设置构建级别的四个选项
                "Release" "MinSizeRel" "RelWithDebInfo")
endif()

add_executable(cmake_examples_build_type main.cpp)

二、CMake具体解析

2.1 构建级别

CMake具有许多内置的构建配置,可用于编译工程。 这些配置指定了代码优化的级别,以及调试信息是否包含在二进制文件中。

这些优化级别,主要有:

  • Release —— 不可以打断点调试,程序开发完成后发行使用的版本,占的体积小。 它对代码做了优化,因此速度会非常快,
    在编译器中使用命令: -O3 -DNDEBUG 可选择此版本。
  • Debug ——调试的版本,体积大。
    在编译器中使用命令: -g 可选择此版本。
  • MinSizeRel—— 最小体积版本
    在编译器中使用命令:-Os -DNDEBUG可选择此版本。
  • RelWithDebInfo—— 既优化又能调试。
    在编译器中使用命令:-O2 -g -DNDEBUG可选择此版本。

2.2 设置级别的方式

2.2.1 CMake图形界面

§ 5. build type - 图1

2.2.2 CMake命令行中

在命令行运行 CMake 的时候, 使用 cmake 命令行的 -D 选项配置编译类型

cmake .. -DCMAKE_BUILD_TYPE=Release

2.2.3 CMake中设置默认的构建级别

CMake 提供的默认构建类型是不进行优化的构建级别。 对于某些项目,需要自己设置默认的构建类型,以便不必记住进行设置。

具体语法接下来介绍

2.2.4 set()命令

该命令可以为普通变量、缓存变量、环境变量赋值。

处可以设置零个或多个参数。多个参数将以分号分隔的列表形式加入,以形成要设置的实际变量值。零参数将导致未设置普通变量。见 [unset()](https://cmake.org/cmake/help/latest/command/unset.html#command:unset) 命令显式取消设置变量。

所以此处学习 SET 命令需要分为设置普通变量,缓存变量以及环境变量三种类别来学习。

§ 5. build type - 图2 正常变量

set(<variable <value>... [PARENT_SCOPE])

设置的变量值作用域属于整个 CMakeLists.txt 文件(一个工程可能有多个 CMakeLists.txt)。

当这个语句加入 PARENT_SCOPE 后,表示要设置的变量是父目录中的 CMakeLists.txt 已经设置的变量。

比如,有如下目录树

├── CMakeLists.txt
└── src
    └── CMakeLists.txt

并且在顶层的 CMakeLists.txt 中包含了 src 目录:add_subdirectory(src),那么顶层的 CMakeLists.txt 就是父目录。

如果父目录中有变量 Bang,在子目录中就可以直接使用(比如,用 message() 输出 Bang,值就是父目录中设置的值)。并且利用 set() 修改该变量 Bang() 的值,但是如果希望在子目录中的 CMakeLists.txt 中对变量的修改能够得以保留的话,就需要在 set() 命令中加入 Parent scoope 这个变量。

当然,如果父目录中本身中没有这个变量,子目录中仍然使用了 parent scope,那么出了这个作用域后,该变量仍然不会存在。

§ 5. build type - 图3 CACHE 变量

完整语句如下:

set(<variable><value>... CACHE<type><docstring>[FORCE])
  • 首先,什么是 CACHE 变量,就是在运行 CMake 的时候,变量的值可能会被缓存到一份文件里,即 build 文件夹下的 CMakeCache.txt 文件,当你重新运行 CMake 的时候,那些变量会默认使用这个缓存里面的值。这个值是全局变量,整个 CMake 工程都可以使用该变量。
  • 在 build 这个文件夹里,只要运行 cmake .. 命令就会自动出现一些值,比如 CMAKE_INSATLL_PREFIX。如果设置 set(CMAKE_INSTALL_PREFIX"/usr“),虽然 CACHE 缓存文件里还有这个 CMAKE_INSTALL_PREFIX 变量,但是因为我们显示地设置了一个名为 CMAKE_INSTALL_PREFIX 的正常变量,所以之后使用 CMAKE_INSTALL_PREFIX,值就是我们设置的正常变量的值。
  • 如果加上 CACHE 关键字,则设置的这个变量会被写入缓存文件中(但是如果本身缓存文件中有这个变量,则不会覆盖缓存中的变量)。只有加上 FORCE 关键字,这个被写入文件的值会覆盖之前文件中存在的同名变量。
  • 加上 CACHE 关键字,和是必须的。

CMake GUI 中通过 Add Entry 让用户设置值。可以有 5 种选项(我这里是 4 种),弹出提示消息

§ 5. build type - 图4

  • BOOL:则为布尔ON/OFF值。 cmake-gui 提供一个复选框。
  • FILEPATH:则为磁盘上文件的路径。 cmake-gui 提供一个文件对话框。
  • PATH:则为磁盘上目录的路径。 cmake-gui 提供一个文件对话框。
  • STRING:则为一行文字。 cmake-gui 提供文本字段或下拉选择(如果 STRINGS设置了缓存条目属性。)
  • INTERNAL:则为一行文字。 cmake-gui 不显示内部条目。它们可用于在运行之间持久存储变量。使用此类型暗含FORCE

比如

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")

这句话,就是强制在缓存文件中覆盖 CMAKE_BUILD_TYPE 这个变量,将这个变量设置为 RelWithDebInfo。而STRING "Choose the type of build."参数在使用 cmake-gui 的时候起作用,在界面上会出现一个下拉框供给用户选择来设置 CMAKE_BUILD_TYPE 变量。里的一行文字作为提示。

但是这个下拉框里的内容,需要使用随后的set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")这个命令来设置。也就是所谓的设置 string 缓存条目属性。

添加后的界面如下图所示:

§ 5. build type - 图5

§ 5. build type - 图6 环境变量

set(ENV{<variable>} [<value>])

设置一个 Environment Variable 到给定值,随后的调用 $ENV{<variable>} 将返回此新值。此命令仅仅影响当前的 CMake 进程,不影响调用 CMake 的进程,也不影响整个系统环境,也不影响后续构建或测试过程的环境。

如果在空字符串之后 ENV{}或如果没有参数,则此命令将清除环境变量的任何现有值。之后``的参数将被忽略。如果发现其他参数,则会发出作者警告。

三、CMake构建

$ cmake ..
Setting build type to 'RelWithDebInfo' as none was specified.
-- The C compiler identification is GNU 8.4.1
-- The CXX compiler identification is GNU 8.4.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /root/CmakeTest/build

$ make VERBOSE=1
/usr/bin/cmake -S/root/CmakeTest -B/root/CmakeTest/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /root/CmakeTest/build/CMakeFiles /root/CmakeTest/build//CMakeFiles/progress.marks
make  -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/root/CmakeTest/build'
make  -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/depend
make[2]: Entering directory '/root/CmakeTest/build'
cd /root/CmakeTest/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /root/CmakeTest /root/CmakeTest /root/CmakeTest/build /root/CmakeTest/build /root/CmakeTest/build/CMakeFiles/cmake_examples_build_type.dir/DependInfo.cmake --color=
make[2]: Leaving directory '/root/CmakeTest/build'
make  -f CMakeFiles/cmake_examples_build_type.dir/build.make CMakeFiles/cmake_examples_build_type.dir/build
make[2]: Entering directory '/root/CmakeTest/build'
[ 50%] Building CXX object CMakeFiles/cmake_examples_build_type.dir/main.cpp.o
/usr/bin/c++   -O2 -g -DNDEBUG -MD -MT CMakeFiles/cmake_examples_build_type.dir/main.cpp.o -MF CMakeFiles/cmake_examples_build_type.dir/main.cpp.o.d -o CMakeFiles/cmake_examples_build_type.dir/main.cpp.o -c /root/CmakeTest/main.cpp
[100%] 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 
make[2]: Leaving directory '/root/CmakeTest/build'
[100%] Built target cmake_examples_build_type
make[1]: Leaving directory '/root/CmakeTest/build'
/usr/bin/cmake -E cmake_progress_start /root/CmakeTest/build/CMakeFiles 0

$ ls
Consolidate compiler generated dependencies of target cmake_examples_build_type

$ ./cmake_examples_build_type
Hello Build Type!