03 最小CMake项目

所有CMake项目都从一个CMakeLists.txt文件开始,此文件应该放在源代码树的最顶层目录下。可以将CMakeLists.txt想象成CMake项目文件,定义了从源和目标的构建到测试、打包和其他自定义任务的一切事物。他可以仅有简单的几行,也可以相当的复杂并且从其他目录中导入更多的文件。CMakeLists.txt只是一个普通的文本文件,通常像项目中其他源文件一样直接编辑。

和其他源文件的语言规范类似,CMake也定义了自己的语言规范,其中有许多程序员熟悉的东西,例如变量、函数、宏、条件逻辑、循环、注释等。这些概念和功能将在后续章节进行介绍。但目前,我们从一个简单的构建开始。如下是一个极小的,格式良好的CMakeLists.txt文件,用于生成一个简单可执行文件:

cmake_minimum_required(VERSION 3.2)
project(MyApp)
add_executable(myExe main.cpp)

示例中的每一行都执行了一个CMake内建命令。在CMake中,命令和其他语言的函数调用类似,只是虽然它们支持参数,但是不能直接返回值(后续章节显示如何以其他方式将值返回给调用者),参数之间用空格分隔,可以跨多行分隔:

add_executable(myExe
main.cpp
src1.cpp
src2.cpp)

命令的名字是大小写不敏感的,但如今更常见的是命令名称全使用小写(CMake官方文档也是如此)。

3.1 管理CMake版本

CMake一直在更新并新增对其他工具、平台和特性的支持。CMake开发人员保证了每个新版本的向后兼容性,因此当用户更新到较新版本的CMake时,项目可以继续像以前一样构建。有时,CMake在新的版本中会更改自身的某一行为,或者引入更严格的检查和警告,CMake提供了策略(policy)机制,允许项目不立即处理此类问题,而是声明其“像CMake版本X.Y.Z一样执行”。这允许CMake在内部修复错误并引入新功能,但仍可以保持与任何过去的版本的行为一致。

项目指定其预期的CMake版本行为的主要方式是使用cmake_minimum_required()命令。这个命令应该是CMakeLists.txt文件的第一行,以便在进行任何其他行为之前进行检查和配置,这个命令执行两件事:

  • 它指定项目所需的CMake的最低版本。如果CMakeLists.txt文件使用比指定版本更老的CMake来处理,则它会立即报错并停止运行。这可确保在CMake执行之前,所需的最小功能集可用。
  • 它强制使CMake的行为与指定版本一致。

此命令十分重要,如果CMakeLists.txt的第一条命令不是cmake_minimum_required()命令,则CMake会发出警告,因为CMake需要知道如何为后续的命令设置策略行为(policy behavior)。对于大多数项目,将cmake_minimum_required()简单地视为指定所需的最低CMake版本就足够了,而另一个作用,将CMake行为设置为与特定版本相同,可以被认为是附带的,在12 策略TODO中更详细的讨论了策略的设置并解释了如何根据需要定制此行为。

cmake_minimum_required()命令的格式很直观:

cmake_minimum_required(VERSIOn major.minor[.patch[.tweak]])

VERSION关键字必须始终存在,并且提供的版本详细信息中有major.minor部分。在大多数项目中,不需要patchtweak部分,这些通常只出现在CMake的次要更新版本中。只有在需要修复CMake的特定的错误时,项目才应该指定patch部分,而由于CMake 3.x系列中没有使用tweak,项目也不应该使用此部分。

开发人员应该仔细考虑它们的项目所需的CMake最低版本,3.2版本可能是现在项目所考虑的最低的版本,因为它为现代CMake提供了相当完整的功能集。2.8.12版本缺乏许多有用的功能,但它可能适用于较老的项目。如果需要在iOS等一些fast-moving平台中开发,可能需要较新版本的CMake才能支持新的操作系统版本。

作为一般经验法则,使用最新的CMake版本,不会给项目的构建带来重大的问题。通常项目中最大的困难是需要支持较老的平台,而这些平台提供的CMake版本可能相当旧,对于这种情况,开发人员应该尽可能考虑安装一个更新的版本,而不是将自己限制在非常旧的CMake版本上。另一方面,如果项目本身是其他项目的依赖项,那么使用较新版本的CMake可能会给适配带来障碍,在这种情况下,使用能满足所需的CMake功能的较低的版本,如何可以则使用高版本的功能可能更好(12 策略TODO中介绍了实现此目的的方法)。这种方式防止了其他项目被迫使用比目标环境所提供的版本更高的CMake。使用旧版本的主要缺点是它可能会导致更多的告警,CMake鼓励使用较新的版本。

3.2 project()命令

每个CMake项目都应该包含一个project()命令,并且应该紧随在cmake_minimum_required()命令之后。这个命令的常用调用形式如下:

project(projectName
[VERSION major[.minor[.patch[.tweak]]]]
[LANGUAGES languageName ...])

其中projectName是必填项,只能包含字母、数字、下划线(_)和连字符(-),在实践中通常只使用字母和下划线。由于不允许空格,所以项目名称不必用引号包围。此名称可以用于具有某些项目生成器(例如Xcode和Visual Studio)的顶层项目中,也可用于项目的其他部分,例如作为打包和文档元数据的默认值,用来提供项目特定变量等。项目名称是project()命令的唯一必须参数。

可选项VERSION仅在CMake 3.0及之后支持,与projectName一样,版本详细信息被CMake用来填充一些变量或者作为默认的包元数据,除此之外没有其他意义。尽管如此,指定项目的版本是一个好的习惯,以便于项目的其他部分可以引用它,19 指定版本细节TODO中深入介绍了这一点,并说明了如何在CMakeLists.txt文件中引用此版本信息。

可选项LANGUAGES定义项目启用的编程语言,支持的值包括C、CXX、Fortran、ASM、Java等,如果指定多种语言,使用空格分隔它们。在某些特殊情况下,项目可能声明无需任何语言,此时可以使用LANGUAGES NONE来实现,后面章节介绍的技术利用了这种特殊的形式。如果没有提供LANGUAGES选项,则CMake默认使用C和CXX作为LANGUAGES的值。CMake 3.0版本之前不支持LANGUAGES关键字,但仍可以指定语言,只需要按照旧版本的形式,在项目名称后面追加:

project(myProj C CXX)

新的项目鼓励指定3.0及以上的CMake版本,并使用带有LANGUAGES关键字的形式。

project()命令不仅填充几个变量,它的重要职责之一是检查每种启用的语言的编译器,以确保它们能成功编译和链接,可以尽早发现编译器和链接器的问题。当检查通过后,CMake将设置一些变量和属性,这些变量和属性控制着启用的语言的构建过程。如果CMakeLists.txt文件没有调用project()或没有尽早调用,那么CMake将在内部隐式地使用默认语言C和CXX来调用它,确保正确设置编译器和链接器,保证依赖于它们的命令的正确性。后续章节将详细介绍如何设置工具链,并演示如何查询和修改编译器标志、编译器位置等内容。

当CMake检查编译器和链接器成功时,检查结果将缓存下载,以便在后续CMake运行时不重复检查,这些缓存的详细信息存储在构建目录的CMakeCache.txt文件中,有关检查的详细信息可以在构建区域的子目录中找到,但开发人员通常不关注那里,除非在使用不熟悉的编译器或设置交叉编译工具链文件时。

3.3 构建基本可执行文件

为了完成我们的最小示例,使用add_executable()命令告诉CMake使用一组源文件来创建可执行文件,此命令的基本形式为:

add_executable(targetName source1 [source2 ...])

此命令将创建一个可执行文件,该可执行文件在CMake中使用targetName来引用。此名称可以包含字母、数字、下划线和连字符。项目构建时,将在构建目录生成一个可执行文件,文件的名字基于目标名称且与平台有关。考虑如下示例:

add_executable(myApp main.cpp)

默认情况下,Windows平台中可执行文件名称为myApp.exe,而在基于Unix的平台上则为myApp。可以通过目标属性来自定义可执行文件的名称,将在09_属性TODO中介绍。可以使用多个不同目标名称的add_executable()命令来创建多个可执行文件,如果多个add_executable()命令使用了相同的目标名称,CMake将报错并结束运行。

3.4 注释

在本章结束之前,需要学习一下CMakeLists.txt文件的注释。CMake的注释和Unix shell脚本格式类似,任何以#开头的行都被视为注释,除了在以引号包围的字符串中,文本行中#之后的任何内容也被视为注释。如下显示了一些注释示例,并汇集了本章中介绍的概念:

cmake_minimum_required(VERSION3.2)

# 我们不使用C++编译器,所以project()命令无需测试
# 平台C++编译器是否可用
project(MyApp VERSION 4.7.2 LANGUAGES C) add_executable(mainToo
main.c
debug.c) add_executable(testTool testTool.c)

3.5 建议

确保每个CMake项目在顶级CMakeLists.txt文件的第一行中包含cmake_minimum_required()命令。在决定所需的最小版本号时,要记住版本越高,项目能够使用的CMake功能就越多,这意味着项目能适配新的平台或操作系统,但是也不可避免的引入了新的需要构建系统处理的问题。相反,如果创建一个为操作系统本身的一部分进行构建和分发的项目(常见于Linux),则最低CMake版本可能由系统本身的CMake版本决定。

如果项目需要CMake 3.0或更高版本,应该尽早将项目版本号写入project()命令中。如果在项目生命周期的后期,要克服现有流程的惯性来改变项目的版本号,可能会很困难。

github地址:https://github.com/theArgs/CMake-Guide

03 最小CMake项目的更多相关文章

  1. 直接在CMake项目中编译GoogleTest和GoogleMock作为项目的一部分

    直接在CMake项目中编译GoogleTest和GoogleMock作为项目的一部分 本文是关于如何将GoogleTest和GoogleMock在没有预先编译安装在机器的情况下,直接在项目中作为项目的 ...

  2. 通过例子进阶学习C++(七)CMake项目通过模板库实现约瑟夫环

    本文是通过例子学习C++的第七篇,通过这个例子可以快速入门c++相关的语法. 1.问题描述 回顾一下约瑟夫环问题:n 个人围坐在一个圆桌周围,现在从第 s 个人开始报数,数到第 m 个人,让他出局:然 ...

  3. Visual Studio CMake 项目和 WSL

    Visual Studio CMake 项目和 WSL https://devblogs.microsoft.com/cppblog/c-with-visual-studio-2019-and-win ...

  4. Flutter 即学即用系列博客——03 在旧有项目引入 Flutter

    前言 其实如果打算在实际项目中引入 Flutter,完全将旧有项目改造成纯 Flutter 项目的可能性比较小,更多的是在旧有项目引入 Flutter. 因此本篇我们就说一说如何在旧有项目引入 Flu ...

  5. hge引擎示例教程cmake项目

    hge引擎的示例代码在vs2017不能很好的运行,需要调不少东西,所以我将其重新整理成cmake的项目. 所有示例均在vs2017 msvc 下测试可以正常运行. 由于缺少libhgehelp.a所以 ...

  6. Qt Creator 源码学习笔记03,大型项目如何管理工程

    阅读本文大概需要 6 分钟 一个项目随着功能开发越来越多,项目必然越来越大,工程管理成本也越来越高,后期维护成本更高.如何更好的组织管理工程,是非常重要的 今天我们来学习下 Qt Creator 是如 ...

  7. 「Python实用秘技03」导出项目的极简环境依赖

    本文完整示例代码及文件已上传至我的Github仓库https://github.com/CNFeffery/PythonPracticalSkills 这是我的系列文章「Python实用秘技」的第3期 ...

  8. ASP.NET Core Web API 最小化项目

    ASP.NET Core中默认的ASP.NET Core 模板中有Web API 模板可以创建Web API项目. 有时,只需要创建一个API,不需要关心Razor,本地化或XML序列化.通过删除无用 ...

  9. Qt Creator下应用CMake项目调试mex文件

    网上可以找到很多应用Visual Studio编写.编译mex文件,并与MATLAB联合调试的文章.但这只限于Win平台,网上许多源码都是.mexa64的文件,它们的作者是怎么调试的呢?这里我介绍一下 ...

随机推荐

  1. 不要让Microsoft edge 打开IE浏览器的设置(兼容性问题)

    1打开Microsoft edge 2 打开设置 3 搜索栏搜索IE,打开即可

  2. NC202498 货物种类

    NC202498 货物种类 题目 题目描述 某电商平台有 \(n\) 个仓库,编号从 \(1\) 到 \(n\) . 当购进某种货物的时候,商家会把货物分散的放在编号相邻的几个仓库中. 我们暂时不考虑 ...

  3. charles(CA证书)的app端安装

    在使用charles进行的app抓包的时候势必需要对他进行配置: 1. pc端: 第一步: http请求接收charles > proxy > proxy setting > por ...

  4. 如何准备论文线上Presentation视频录制教程(Summary of Video Recording)

    0:前言 由于国外的疫情严重,目前大多数学术会议都是线上举办,因此往往需要制作presentation的视频录制.由于各种软件横飞,有的需要会员并且不熟悉操作,特别浪费时间.因此,我将这次的操作和遇到 ...

  5. centos7 nginx 域名能ping通,但无法打开网页

    方法一:关闭防火墙 sudo systemctl stop firewalld.service 方法二:容许80端口访问 vim打开iptables, 命令如下: #vim /etc/sysconfi ...

  6. python测试开发django-197.django-celery-beat 定时任务

    前言 django-celery-beat 可以支持定时任务,把定时任务写到数据库. 接着前面这篇写python测试开发django-196.python3.8+django2+celery5.2.7 ...

  7. 【docker专栏6】详解docker容器状态转换管理命令

    docker容器有三种状态运行.停止.暂停,镜像可以创建.运行容器,镜像和容器也可以转换成tar压缩包进行存储.本文为大家介绍容器的状态转换命令及镜像创建运行容器.tar包导入导出相关的命令及使用场景 ...

  8. [ZJCTF 2019]NiZhuanSiWei 1

    考察知识点:反序列化.php伪协议 1.打开之后获得源码信息,如下: <?php $text = $_GET["text"]; $file = $_GET["fil ...

  9. 无需CORS,用nginx解决跨域问题,轻松实现低代码开发的前后端分离

    近年来,前后端分离已经成为中大型软件项目开发的最佳实践. 在技术层面,前后端分离指在同一个Web系统中,前端服务器和后端服务器采用不同的技术栈,利用标准的WebAPI完成协同工作.这种前后端分离的&q ...

  10. 时间模块之datatime模块、os模块、sys模块、json模块、json模块实操

    目录 一.模块的绝对导入和相对导入 二.包的概念 三.编程思想的转变 四.软件开发目录规范 五.常见的内置模块 一.时间模块之datatime模块 1.datetime.datetime.today( ...