CMake历史及背景

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

cmake出现解决了两个问题:

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

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

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

CMake组成

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

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

ctest:cmake中的测试框架

cpack: cmake中的打包软件

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

Hello world用例

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

main.cpp的hello world用例:

#include <stdio.h>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文件,下面是最小生成的目录文件:

  • 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开头

隐式定义变量名

<PROJECT-NAME>_BINARY_DIR

<PROJECT-NAME>_DESCRIPTION

<PROJECT-NAME>_HOMEPAGE_URL

<PROJECT-NAME>_SOURCE_DIR

<PROJECT-NAME>_VERSION

<PROJECT-NAME>_VERSION_MAJOR

<PROJECT-NAME>_VERSION_MINOR

<PROJECT-NAME>_VERSION_PATCH

<PROJECT-NAME>_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_<LANG>_COMPILER

CMAKE_<LANG>_CPPCHECK

CMAKE_<LANG>_CPPLINT

CMAKE_<LANG>_CREATE_SHARED_LIBRARY

CMAKE_<LANG>_CREATE_SHARED_MODULE

CMAKE_<LANG>_CREATE_STATIC_LIBRARY

CMAKE_<LANG>_FLAGS_DEBUG

CMAKE_<LANG>_FLAGS_RELEASE

CMAKE_<LANG>_COMPILER_AR

… … … …

显示定义

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

Normal变量定义

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

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

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

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

Normal变量用例1

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

父目录中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,查看运行结果:

会发现在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。

运行结果:

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

Cache变量

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

set(<variable> <value>... CACHE <type> <docstring> [FORCE])

<type> <docstring>为必选项

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

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

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

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

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

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

option、find_file命令同样可以创建cache变量。

设置Cache变量值方法:

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

set(<variable> <value>... CACHE <type> <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保留变量,可以使用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([<mode>] "message text" ...)

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

mode支持打印级别

程序运行

FATTAL_ERROR

停止运行

SEND_ERROR

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

WARNING

继续运行

AUTHOR_WARNING

继续运行

DEPRECATION

继续运行

(none)or NOTICE

继续运行

STATUS

继续运行

VERBOSE

继续运行

DEBUG

继续运行

TRACE

继续运行

参考资料

https://cmake.org/documentation/

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

《Mastering CMake》

CMake使用介绍(1)相关推荐

  1. Cmake的介绍和使用 Cmake实践 - 吾尝终日而思矣 不如须臾之所学也 - C++博客

    Cmake的介绍和使用 Cmake实践 - 吾尝终日而思矣 不如须臾之所学也 - C++博客 Cmake的介绍和使用 Cmake实践 - 吾尝终日而思矣 不如须臾之所学也 - C++博客 Cmake的 ...

  2. CMake使用介绍(2)

    如何设置编译器 实际使用过程中,尤其是嵌入式开发中,经常需要使用到交叉编译,这时就要将系统默认编译器切换到相对应的使用到的编译器,常用的编译器设置主要由一下几种方法: 1:修改系统环境变量,修改默认编 ...

  3. make和cmake简要介绍

    GCC GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言译器.GNU编译器套件包括C.C++. Objective-C. Fortran.Java.A ...

  4. make、makefile和cmake简单介绍

    make.makefile和cmake的关系 cmake安装 cmake简单示例 make.makefile和cmake的关系 提到make,makefile就不得不提GNU,GNU是"GN ...

  5. 三、安装cmake,安装resin ,tars服务,mysql 安装介绍,安装jdk,安装maven,c++ 开发环境安装...

    三.安装cmake,安装resin 2018年07月01日 21:32:05 youz1976 阅读数:308 开发环境说明: centos7.2 ,最低配置:1核cpu,2G内存,1M带宽 1.安装 ...

  6. cmake使用总结(转)---工程主目录CMakeList文件编写

    在linux 下进行开发很多人选择编写makefile 文件进行项目环境搭建,而makefile 文件依赖关系复杂,工作量很大,搞的人头很大.采用自动化的项目构建工具cmake 可以将程序员从复杂的m ...

  7. 熟悉 CMake(二)—— 以一个实例说明 CMakeLists.txt 文件的编写

    原文请见 cmake使用总结(转)-工程主目录CMakeList文件编写 在 Linux 下进行开发很多人选择编写 makefile 文件进行项目环境搭建,而makefile 文件依赖关系复杂,工作量 ...

  8. CMake Tutorial

    CMake Tutorial 介绍 CMake教程提供了一个循序渐进的指南,涵盖了CMake帮助解决的常见的系统构建问题.了解示例项目中各种主题是如何一起工作的会非常有帮助.教程文档和示例源代码可以在 ...

  9. CMake | include_guard命令详解

    目录 一.命令详解 二.简单使用 1.CMakeLists.txt 2.cmake/custom.cmake 3.配置 三.为低于3.10的版本自定义"包含保护"机制 1.cmak ...

最新文章

  1. linux学习-awk工具
  2. chrome浏览器上传文件延迟_扫描识别工具Dynamic Web TWAIN使用教程:移动浏览器捕获(下)...
  3. telnet或SQLplus下命令输入错误如何删掉重新输入
  4. linux 配置思科路由器,将配置文件导入cisco2611路由器步骤
  5. python线程池模块第三方包_python线程池(threadpool)模块使用笔记详解
  6. confirm的意思中文翻译_confirm的中文意思
  7. Ubuntu20.04安装eBPF BCC
  8. 深入浅出 SSL 管理配置实战
  9. 摄影测量学知识点总结(万字长文警告)
  10. 有关微信小程序云数据库修改数据的坑
  11. Failed to load C:\SDK\android-sdk-windows\build-tools\27.0.2\lib\dx.jar
  12. 职场“巨婴”,毁人不倦
  13. 一分钟集成类似抖音、头条、腾讯视频、网易新闻、飞猪、咸鱼等常用标题栏
  14. C/C++基础查漏补缺(一)----------寒假学习笔记(一)
  15. Cat.1、Cat.4、4G之间的区别
  16. 了解一下winsock
  17. 磁共振功能成像BOLD-fMRI原理
  18. LeetCode 796. 旋转字符串
  19. 铁道部新客票系统设计(一)
  20. Linux信号量与互斥锁解决生产者与消费者问题

热门文章

  1. Istio微服务平台集成实践
  2. MiniDao_1.6-SNAPSHOT 版本发布,轻量级Java持久化框架
  3. 微信公众号开发--微信JS-SDK扫一扫功能
  4. JEECG开源社区招收学生说明
  5. seasar一般性配置
  6. 标签管理体系之业务应用
  7. SpringBoot2 整合MinIO中间件,实现文件便捷管理
  8. 怎么把VSCode加入右键功能菜单中
  9. CH2401 送礼物(双向dfs)
  10. Java千百问_06数据结构(014)_java数组如何存储在内存中