CMake使用介绍(1)

CMake历史及背景

CMake最早作为ITK(www.itk.org)组织中的项目,开始于1999年,该组织是由美国国家医学图书馆(US National library of medicine提供资金援助。ITK组织内部拥有大量的软件运行不同的平台,因此需要专门针对不同的平台编写不同的编译复杂构建系统,为了解决上述问题该组织开发出cmake构建工具。

cmake出现解决了两个问题:

1:开发者编写一种平台无关的CMakeList.txt文件定制整个软件编译流程,实现编译系统跨平台。

2:开发出了一系列命令,以简化整个编译系统,开发者只需要使用简单的命令就能构造编译系统,大大减少了代码量。

目前cmake的开发和维护工作由Kitware 公司负责,可以通过cmake --version可以查看到

CMake使用介绍(1)_第1张图片

CMake组成

cmake 主要是由三部分组成cmake, ctest,cpack

cmake:构建该软件的主要组成部分,包括大部分命令和功能。

ctest:cmake中的测试框架

cpack: cmake中的打包软件

cmake几乎支持市面上所有的操作系统,支持目前大部分编译器,主要支持C/C++语言。

Hello world用例

用cmake编写编译系统相比之前非常简单, 下面使用一个hello world用例简单说明

main.cpp的hello world用例:

#include 

int main()
{
   printf("hello world\n");
}

在代码源目录下创建一个CMakeLists.txt,代码仅需三行:

cmake_minimum_required(VERSION 3.10)

#set the project name
project(helloworld)

#add the executable
add_executable(helloworld main.cpp)
  • cmake_minimum_required为设置允许该工程最小的cmake版本,因为各个cmake版本之前略有差异,都是最小版本都是遵循向下兼容,需要设置满足该工程的最小版本。
  • project指令为设置该工程名
  • add_executable:将所需要编译成可执行文件的 cpp文件加入当中。

一般运行cmake之前创建一个build目录,build目录执行cmake ..命令生成相应的makefile文件,下面是最小生成的目录文件:

CMake使用介绍(1)_第2张图片

  • CMakeCache.txt为cache变量保存文件,相当于配置文件,里面保存当前工程中所有cache变量及其值
  • CMakeFiles: 该编译工程很多所需要的中间文件。
  • Makefile: 根目录makefle
  • cmake_install.cmake:编译之后的make install安装配置文件。

生成makefile之后,直接make可生成可执行文件。

整个用cmake构建的编译系统与之前相比比较简单。

CMake变量

变量在cmke中占重中之重,理解cmake变量基本能够看懂和编写cmake文件。

CMake变量划分

cmake变量主要涉及到三种类型

  • 自定义变量

自定义变量顾名思义是由开发人员定义编写变量,值可由开发人员在cmake文件中进行修改访问。

  • 内置变量

        内置变量也成为预留变量,由cmake内部进行创建保留,内置变量基本上都是cache变量,由于表示特殊意义。

  • 环境变量

环境变量即为操作系统环境变量,cmake可与之交互,获取到系统相互信息,cmake中由专有命令可与之交互。

 自定义变量

自定义变量按照定义方式分为:隐式定义和显示定义

隐式定义

隐式定义调用cmake的一些特殊命令之后,由cmake帮助开发人员定义的一些变量,省去开发人员定义繁琐。

支持隐式定义的命令由project 和enable_language命令。

project命令

project命令格式为:

      project(<projectname> [languageName1 languageName2 ... ] )

该命令字段主要是定义工程名,上述命令会隐式定义一些变量,以所定义的project name开头

隐式定义变量名

_BINARY_DIR

_DESCRIPTION

_HOMEPAGE_URL

_SOURCE_DIR

_VERSION

_VERSION_MAJOR

_VERSION_MINOR

_VERSION_PATCH

_VERSION_TWEAK

例如:

project(helloworld) 

上述指令会自动添加如下变量定义:

隐式定义变量名

helloworld_BINARY_DIR

helloworld_DESCRIPTION

helloworld_HOMEPAGE_URL

helloworld_SOURCE_DIR

helloworld_VERSION

helloworld_VERSION_MAJOR

helloworld_VERSION_MINOR

helloworld_VERSION_PATCH

helloworld_VERSION_TWEAK

        除了上述命令还有enable_language

 enable_language(<lang> [OPTIONAL] )

该 命令打开了 CMake 对参数中指定的语言的支持,支持CXX/C/Fortran/Asm,该命令会定义如下隐藏变量:

隐式定义变量名

CMAKE__COMPILER

CMAKE__CPPCHECK

CMAKE__CPPLINT

CMAKE__CREATE_SHARED_LIBRARY

CMAKE__CREATE_SHARED_MODULE

CMAKE__CREATE_STATIC_LIBRARY

CMAKE__FLAGS_DEBUG

CMAKE__FLAGS_RELEASE

CMAKE__COMPILER_AR

… … … …

显示定义

显示定义按照变量类型可以分为Normal变量和cache变量。

Normal变量定义

Nornal变量定义一般是采用set命令

set( ... [PARENT_SCOPE])

 将创建变量,并将 值设置为<value>,被设置之前, 会被展开。value值可以跟多个,如果是多个value,则variable变量以list形式进行保存。

PARENT_SCOPE:为变量名作用域,默认状况下变量默认作用域属于当前 CMakeLists.txt或者函数 ,如果添加PARENT_SCOPE字段命令则把一个变量的值设置到父路径或者调用函数中 。该句话貌似理解起来有点难度,下面以一个用例来说明。

Normal变量用例1

创建如下一个工程, 在父目录中创建一个CMakeLists.txt文件,同时创建一个src子目录且子目录中创建一个CMakeList.txt,如下图所示:

CMake使用介绍(1)_第3张图片

父目录中CMakeList.txt内容如下:

cmake_minimum_required(VERSION 3.10)
  
set(MY_VAL "666")
message("Parent dir, MY_VAL: ${MY_VAL}")
add_subdirectory(src)
message("After src, MY_VAL: ${MY_VAL}")

 父目录中创建一个MY_VAL变量值为666, 之后将添加子目录src, add_subdirectory(src)会自动搜索添加src目录中的CMakeList.txt。子目录CMakeList.txt的内容如下:

        set(MY_VAL "777")

将其变量MY_VAL的值为777,查看运行结果:

CMake使用介绍(1)_第4张图片

会发现在src目录中修改的MY_VAL值并没有在父目录中有效。

出现这样的原因:set变量的作用域为在当前 CMakeLists.txt或者函数内,在src目录中MY_VAL相当于只修改本CMakeList.txt中的变量,并不会影响父目录中的MY_VAL的值。

出现这一问题的根本原因是:CMake会为每个CMakeList.txt文件创建各自的实例,在根目录中的CMakeList.txt称之为全局实例,而在其他目录中的称之为本地实例。其实我们可以进一步发现子目录中的CMakeList.txt创建的实例会继承相应父目录中创建的变量,并复制一份给自己使用,这样修改MY_VAL值相当于修改的是自己复制的那一份,并不影响父目录中MY_VAL的值。

 Normal变量用例2

在用例1中,其实很多使用场景中子目录中的MY_VAL修改的值需要在父目录中生效,为了解决这一问题set命令添加了PARENT_SCOPE命令字段,将用例1中稍作改造,src目录中的CMakeLists.txt文件修改为:

set(MY_VAL "777" PARENT_SCOPE)

意思是MY_VAL这个变量来自于父目录中,修改的并不是本地文件中的MY_VAL。

运行结果:

CMake使用介绍(1)_第5张图片

可以看到上述结果修改MY_VAL值有效。

 Cache变量

cache变量使用如下命令定义:

  set( ... CACHE <docstring> [FORCE])  

<docstring>为必选项

       <type> 可以被 CMake GUI 用来选择一个窗口。

       FILEPATH  = 文件选择对话框。
           PATH        = 路径选择对话框。
          STRING    = 任意的字符串。
          BOOL       = 布尔值选择复选框。
         INTERNAL = 不需要 GUI 输入端。 (适用于永久保存的变量)  

<docstring>用于该变量解释说明一段文字。

[FORCE]选项将覆盖 cache ,常用于修改cache变量。

Cache变量作用相当于全局变量,即同一个CMake工程中所有CMakeLists.txt 都可以访问。 

 所有的 Cache 变量都会出现在 CMakeCache.txt 文件中。

optionfind_file命令同样可以创建cache变量。

设置Cache变量值方法:   

  • 定义初始化: set( ... CACHE <docstring>)         
  • 直接在终端中使用 cmake -D var=value ..或者通过cmake-gui
  • 使用FORCE命令字段修改已经定义Cache变量值:

                set( ... CACHE <docstring> FORCE)

               一般运行中间最好不要修改,会造成通过cmake-gui或者命令行设置的值无效         

Cache变量用例

将Normal变量用例进一步改进MY_VAL使用cache变量 ,则父目录中代码修改为:

cmake_minimum_required(VERSION 3.10)
  
set(MY_VAL "666" CACHE STRING INTERNAL )
message("Parent dir, MY_VAL: ${MY_VAL}")
add_subdirectory(src)
message("After srcr, MY_VAL: ${MY_VAL}")

    子目录中代码修改为:

set(MY_VAL "777" CACHE STRING INTERNAL FORCE)

运行结果:

CMake使用介绍(1)_第6张图片

 内置变量

内置变量为cmake保留变量,可以使用cmake --help-variable-list查看到所有的变量,常用变量如下:

CMAKE_SYSTEM

CMAKE_INCLUDE_PATH

CAMAKE_SYSTEM_NAME

CMAKE_LIBRARY_PATH

CMAKE_SYSTEM_VERSION

CMAKE_INSTALL_PREFIX

CMAKE_SYSTEM_PROCESSOR

PROJECT_BINARY_DIR

UNIX

PROJECT_SOURVE_DIR

WIN32

CMAKE_CURRENT_SOURCE_DIR

CMAKE_MAJOR_VERSION

CMAKE_CURRENT_BINARY_DIR

CMAKE_MINOR_VERSION

CMAKE_PREFIX_PATH

CMAKE_PATCH_VERSION

CMAKE_SHARED_LIBS

PROJECT_NAME

CMAKE_BUILD_TYPE

环境变量

环境变量主要涉及到cmake如何与其交互:

  • 读取环境变量:$ENV{NAME}
  • 设置环境变量:SET(ENV{ 变量名} ) 

CMake变量读取

cmake变量读取规则如下:

  • 获取变量值:${}
  • if 语句中直接使用变量名而不通过${}取值 

CMake变量打印

CMake变量打印主要使用message

message([] "message text" ...) 

mode为打印模式,针对不同的模式会有不同的动作:

mode支持打印级别

程序运行

FATTAL_ERROR

停止运行

SEND_ERROR

继续执行,但是会跳过生成的步骤

WARNING

继续运行

AUTHOR_WARNING

继续运行

DEPRECATION

继续运行

(noneor NOTICE

继续运行

STATUS

继续运行

VERBOSE

继续运行

DEBUG

继续运行

TRACE

继续运行

 参考资料

https://cmake.org/documentation/

https://gitlab.kitware.com/cmake/community/-/wikis/home

Mastering CMake》 

你可能感兴趣的:(cmake,cmake)