CMake 基础用法,掌握 CMake 诀窍

2024-05-01 20:58

本文主要是介绍CMake 基础用法,掌握 CMake 诀窍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

CMake 是一个开源的、跨平台的工具系列,用于构建、测试和打包软件。CMake 用于使用简单的平台和编译器独立的配置文件(CMakeLists.txt)来控制软件编译过程,并生成可以在您选择的编译环境中使用的本地生成文件和工作空间。CMake 工具套件是由 Kitware 创建的,以响应对 ITK 和 VTK 等开源项目强大的跨平台构建环境的需求。

在这里插入图片描述

CMake 是一个元构建系统。它可以从抽象的文本配置生成真正的本地构建工具文件。通常这样的代码存在于 CMakeLists.txt 文件中。

一、Cmake 安装

以 Ubuntu 18.04 Server 安装为例进行介绍。

到 https://cmake.org/files/ 下载指定版本的 Cmake,这里下载当前最新版 https://cmake.org/files/v3.22/cmake-3.22.0-linux-x86_64.tar.gz

  1. 下载 cmake-3.22.0
wget https://cmake.org/files/v3.22/cmake-3.22.0-linux-x86_64.tar.gz
--2021-12-06 01:05:45--  https://cmake.org/files/v3.22/cmake-3.22.0-linux-x86_64.tar.gz
Resolving cmake.org (cmake.org)... 66.194.253.25
Connecting to cmake.org (cmake.org)|66.194.253.25|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 45044059 (43M) [application/x-gzip]
Saving to: ‘cmake-3.22.0-linux-x86_64.tar.gz’cmake-3.22.0-linux- 100%[===================>]  42.96M  24.0KB/s    in 18m 51s2021-12-06 01:24:37 (38.9 KB/s) - ‘cmake-3.22.0-linux-x86_64.tar.gz’ saved [45044059/45044059]
  1. 解压压缩包
tar zxvf cmake-3.22.0-linux-x86_64.tar.gz

列出 cmake-3.22.0-linux-x86_64 文件夹目录树,如果未安装 tree 命令支持,先安装。

tree 安装:

sudo apt  install tree
tree -L 2 cmake-3.22.0-linux-x86_64

cmake-3.22.0-linux-x86_64 目录树如下:

cmake-3.22.0-linux-x86_64
├── bin
│   ├── ccmake
│   ├── cmake
│   ├── cmake-gui
│   ├── cpack
│   └── ctest
├── doc
│   └── cmake
├── man
│   ├── man1
│   └── man7
└── share├── aclocal├── applications├── bash-completion├── cmake-3.22├── emacs├── icons├── mime└── vim
  1. 创建软链接

移动 cmake-3.22.0-linux-x86_64 到 /opt/cmake-3.22.0-linux-x86_64 文件夹下(文件路径是可以指定的, 一般选择在/opt 或 /usr 路径下)。

sudo mv cmake-3.22.0-linux-x86_64 /opt/cmake-3.22.0-linux-x86_64

将 cmake bin 下的文件创建软链接。

sudo ln -sf /opt/cmake-3.22.0-linux-x86_64/bin/* /usr/bin/
  1. 确认安装结果
cmake --version

输出如下,cmake 安装版本是 3.22.0。

cmake version 3.22.0

CMake suite maintained and supported by Kitware (kitware.com/cmake).

二、CMake Hello world

使用 CMake 前的准备工作。

mkdir test
cd test
touch main.cpp
touch CMakeLists.txt

main.cpp

#include<iostream>
using namespace std;
int main(){cout<<"hello world!"<<endl;return 0;
}

CMakeLists.txt

#cmake最小需要版本
cmake_minimum_required(VERSION 2.8)#项目名字
project(HELLOWORLD)#包含原程序,即把给定目录下的源程序复制给变量DIR_SRC
aux_source_directory(. DIR_SRC)#生成程序
add_executable(helloworld ${DIR_SRC})

创建 build 目录生成 Cmake 编译后的输出文件。

mkdir build
cd build
cmake ../

很明显报错了。

CMake Deprecation Warning at CMakeLists.txt:2 (cmake_minimum_required):Compatibility with CMake < 2.8.12 will be removed from a future version ofCMake.Update the VERSION argument <min> value or use a ...<max> suffix to tellCMake that the project does not need compatibility with older versions.CMake Error: CMake was unable to find a build program corresponding to "Unix Makefiles".  CMAKE_MAKE_PROGRAM is not set.  You probably need to select a different build tool.
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
See also "/home/snake/test/build/CMakeFiles/CMakeOutput.log".

兼容 CMake < 2.8.12 将从未来的版本中移除,需要更改 CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)

CMake无法找到对应于“Unix Makefiles”的构建程序。没有设置 CMAKE_MAKE_PROGRAM。您可能需要选择不同的构建工具。需要安装构建工具,这里选择安装 make。

sudo apt-get -y install make
make -v

make -v 输出如下:

GNU Make 4.1
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

CMAKE_C_COMPILER 和 CMAKE_CXX_COMPILER 未设置,安装 g++ 解决。

sudo apt install g++

从这里不难看出 Cmake 还是需要搭配 make 和 g++ 才可以编译。

再次进入 build 目录:

cmake ..

现在 cmake 工作正常了。

-- The C compiler identification is GNU 7.5.0
-- The CXX compiler identification is GNU 7.5.0
-- 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: /home/snake/test/build

重新列出 build 目录下的文件,Makefile 很熟悉吧,所以再次调用 make 命令就可以生成可执行 bin 文件了。cmake 原来只是生成了 Makefile,并没有直接生成最后的可运行二进制文件!

CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile

build 目录下执行 make 命令。

make

输出如下:

[ 50%] Building CXX object CMakeFiles/helloworld.dir/main.cpp.o
[100%] Linking CXX executable helloworld
[100%] Built target helloworld

helloworld bin 可执行文件已经生成。

build 目录运行 helloworld bin 程序。

./helloworld

输出如下:

hello world!

三、CMake 常用命令

确切的说,CMake 命令分为三种:脚本命令、项目命令、CTest 命令。

3.1 project

设置项目的名称,并将其存储在变量 PROJECT_NAME 中。当从顶级 CMakeLists.txt 中调用时,它还将项目名称存储在变量 CMAKE_PROJECT_NAME 中。

project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]][DESCRIPTION <project-description-string>][HOMEPAGE_URL <url-string>][LANGUAGES <language-name>...])

3.2 cmake_minimum_required

为一个项目设置最低需要的 cmake 版本,还更新策略设置。

cmake_minimum_required(VERSION <min>[...<policy_max>] [FATAL_ERROR])

<min> 和可选的 <policy_max> 都是 CMake 格式的版本:major.minor[.patch[.tweak]]

如果运行的 CMake 版本低于 <min> 需要的版本,它将停止处理项目并报告一个错误。可选的 <policy_max> 如果指定了版本,则必须至少为 <min> 版本,并影响策略设置,如策略设置中所述。如果运行版本的 CMake 是比 3.12 更老的,额外的…圆点将被视为版本组件分隔符,导致…部分被忽略,保留 3.12 之前的基于 <min> 的政策行为。

这个命令将 CMAKE_MINIMUM_REQUIRED_VERSION 变量的值设置为 。

FATAL_ERROR 选项会被 CMake 2.6 及更高版本所忽略。

策略设置

cmake_minimum_required(VERSION) 命令隐式地调用 cmake_policy(VERSION) 命令来指定当前项目代码是为给定的 CMake 版本范围编写的。所有 CMake 运行版本中已知并在 <min>(或 <max> 如果指定)版本或更早版本将被设置为使用 NEW 行为。后续版本中引入的所有策略都将被取消设置。这有效地请求首选行为作为一个给定的 CMake 版本,并告诉新的 CMake 版本警告他们的新策略。

当一个 高于 2.4 的版本被指定为该命令隐式调用的版本。

cmake_policy(VERSION <min>[...<max>])

3.3 aux_source_directory

找到一个目录中的所有源文件。收集指定目录下所有源文件的名称,并将列表存储在提供的 <variable> 变量中。此命令用于使用显式模板实例化的项目。模板实例化文件可以存储在 Templates 子目录中,并使用此命令自动收集,以避免手动列出所有实例化。

使用此命令可以避免为库或可执行目标编写源文件列表。虽然这看起来是可行的,但 CMake 无法生成一个知道何时添加了新源文件的构建系统。通常生成的构建系统知道什么时候需要重新运行 CMake,因为 CMakeLists.txt 文件被修改以添加一个新的源文件。当源代码只是添加到目录而没有修改该文件时,必须手动重新运行 CMake 来生成一个包含新文件的构建系统。

aux_source_directory(<dir> <variable>)

3.4 add_executable

添加一个名为 <name> 的可执行目标,从命令调用中列出的源文件中构建。<name> 对应于逻辑目标名称,并且在项目中必须是全局唯一的。所构建的可执行文件的实际文件名是根据本地平台的约定构造的(例如<name>.exe 或仅仅是<name>)。

add_executable(<name> [WIN32] [MACOSX_BUNDLE][EXCLUDE_FROM_ALL][source1] [source2 ...])

3.5 set

将普通变量、缓存变量或环境变量设置为给定值。

设置普通变量

在当前函数或目录范围内设置给定的 <variable> 变量。

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

设置缓存变量

设置给定的缓存 <variable> 变量(缓存条目)。由于缓存条目旨在提供用户可设置的值,因此默认情况下不会覆盖现有的缓存条目。使用 FORCE 选项覆盖现有条目。

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

设置环境变量

将环境变量设置为给定值。后续调用 $ENV{<variable>} 将返回这个新值。

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

3.6 add_subdirectory

将子目录添加到 build 中。source_dir 指定源 CMakeLists.txt 和代码文件所在的目录。如果它是一个相对路径,它将根据当前目录进行计算(这是典型用法),但它也可能是一个绝对路径。binary_dir 指定存放输出文件的目录。如果它是一个相对路径,它将根据当前输出目录求值,但它也可能是一个绝对路径。如果未指定 binary_dir,将在展开任何相对路径之前使用 source_dir 的值(典型用法)。CMake 将立即处理指定源目录中的 CMakeLists.txt 文件,然后继续处理当前输入文件。

如果提供了 EXCLUDE_FROM_ALL 参数,那么子目录中的目标将默认不包含在父目录的 ALL 目标中,并且将从 IDE 项目文件中排除。用户必须在子目录中显式地构建目标。这适用于子目录包含项目的一个单独的部分,这些部分是有用的,但不是必需的,比如一组示例。

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

3.7 find_library

该命令用于查找库。一个缓存条目,或者一个普通变量(如果指定了NO_CACHE),<VAR> 变量用于存储此命令的结果。如果找到库,则将结果存储在变量中,并且除非清除变量,否则不会重复搜索。如果没有找到任何东西,结果将是 <VAR>-NOTFOUND。

find_library (<VAR> name1 [path1 path2 ...])find_library (<VAR>name | NAMES name1 [name2 ...] [NAMES_PER_DIR][HINTS [path | ENV var]... ][PATHS [path | ENV var]... ][PATH_SUFFIXES suffix1 [suffix2 ...]][DOC "cache documentation string"][NO_CACHE][REQUIRED][NO_DEFAULT_PATH][NO_PACKAGE_ROOT_PATH][NO_CMAKE_PATH][NO_CMAKE_ENVIRONMENT_PATH][NO_SYSTEM_ENVIRONMENT_PATH][NO_CMAKE_SYSTEM_PATH][CMAKE_FIND_ROOT_PATH_BOTH |ONLY_CMAKE_FIND_ROOT_PATH |NO_CMAKE_FIND_ROOT_PATH])

3.8 file

这个命令专门用于需要访问文件系统的文件和路径操作。

Readingfile(READ <filename> <out-var> [...])file(STRINGS <filename> <out-var> [...])file(<HASH> <filename> <out-var>)file(TIMESTAMP <filename> <out-var> [...])file(GET_RUNTIME_DEPENDENCIES [...])Writingfile({WRITE | APPEND} <filename> <content>...)file({TOUCH | TOUCH_NOCREATE} [<file>...])file(GENERATE OUTPUT <output-file> [...])file(CONFIGURE OUTPUT <output-file> CONTENT <content> [...])Filesystemfile({GLOB | GLOB_RECURSE} <out-var> [...] [<globbing-expr>...])file(MAKE_DIRECTORY [<dir>...])file({REMOVE | REMOVE_RECURSE } [<files>...])file(RENAME <oldname> <newname> [...])file(COPY_FILE <oldname> <newname> [...])file({COPY | INSTALL} <file>... DESTINATION <dir> [...])file(SIZE <filename> <out-var>)file(READ_SYMLINK <linkname> <out-var>)file(CREATE_LINK <original> <linkname> [...])file(CHMOD <files>... <directories>... PERMISSIONS <permissions>... [...])file(CHMOD_RECURSE <files>... <directories>... PERMISSIONS <permissions>... [...])Path Conversionfile(REAL_PATH <path> <out-var> [BASE_DIRECTORY <dir>] [EXPAND_TILDE])file(RELATIVE_PATH <out-var> <directory> <file>)file({TO_CMAKE_PATH | TO_NATIVE_PATH} <path> <out-var>)Transferfile(DOWNLOAD <url> [<file>] [...])file(UPLOAD <file> <url> [...])Lockingfile(LOCK <path> [...])Archivingfile(ARCHIVE_CREATE OUTPUT <archive> PATHS <paths>... [...])file(ARCHIVE_EXTRACT INPUT <archive> [...])

3.9 add_definitions

在编译源文件时添加 -D define 标志。

add_definitions(-DFOO -DBAR ...)

3.10 add_library

使用指定的源文件向项目添加库。

add_library(<name> [STATIC | SHARED | MODULE][EXCLUDE_FROM_ALL][<source>...])add_library(<name> OBJECT [<source>...])   add_library(<name> INTERFACE)add_library(<name> <type> IMPORTED [GLOBAL])add_library(<name> ALIAS <target>)

3.11 target_include_directories

指定编译给定目标时要使用的 include 目录。<target> 必须由 add_executable() 或 add_library() 等命令创建,并且不能是 ALIAS 目标。

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]<INTERFACE|PUBLIC|PRIVATE> [items1...][<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

3.12 target_link_libraries

指定连接给定目标和/或其依赖项时要使用的库或标志。来自链接库目标的使用需求将被传播。目标依赖项的使用需求会影响其自身源的编译。

target_link_libraries(<target> ... <item>... ...)

3.13 set_target_properties

目标可以具有影响其构建方式的属性。设置目标的属性。该命令的语法是列出想要更改的所有目标,然后提供接下来想要设置的值。

set_target_properties(target1 target2 ...PROPERTIES prop1 value1prop2 value2 ...)

3.14 include_directories

将 include 目录添加到构建中。

include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

3.15 message

日志消息。

General messagesmessage([<mode>] "message text" ...)Reporting checksmessage(<checkState> "message text" ...)

四、CMake 实战 Live555 移植

写一份移植 Live555 到 Android 平台的 CMakeLists.txt。

cmake_minimum_required(VERSION 3.4.1)set(LIVE555_LIB_NAME Live555)file(GLOB BasicUsageEnvironmentFiles ${CMAKE_CURRENT_SOURCE_DIR}/BasicUsageEnvironment/*.cpp)
file(GLOB groupsockFiles ${CMAKE_CURRENT_SOURCE_DIR}/groupsock/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/groupsock/*.c)
file(GLOB liveMediaFiles ${CMAKE_CURRENT_SOURCE_DIR}/liveMedia/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/liveMedia/*.c)
file(GLOB UsageEnvironmentFiles ${CMAKE_CURRENT_SOURCE_DIR}/UsageEnvironment/*.cpp)include_directories(${CMAKE_CURRENT_SOURCE_DIR}/BasicUsageEnvironment/include/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/groupsock/include/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/liveMedia/include/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/UsageEnvironment/include/)add_definitions(-D LOCALE_NOT_USED)
# android 版本高于 24 才能使用 getifaddrs、freeifaddrs
add_definitions(-D NO_GETIFADDRS)
# bind() error (port number: 8554): Address already in use
add_definitions(-D ALLOW_SERVER_PORT_REUSE)set(OPENSSL_LIBS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../libs)message(STATUS "OPENSSL_LIBS_DIR = ${OPENSSL_LIBS_DIR}.")add_library(crypto STATIC IMPORTED)
set_target_properties(cryptoPROPERTIES IMPORTED_LOCATION ${OPENSSL_LIBS_DIR}/${ANDROID_ABI}/libcrypto.a)add_library(ssl STATIC IMPORTED)
set_target_properties(sslPROPERTIES IMPORTED_LOCATION ${OPENSSL_LIBS_DIR}/${ANDROID_ABI}/libssl.a)include_directories(${OPENSSL_LIBS_DIR}/include/)find_library(z-lib z)add_library( # Sets the name of the library.${LIVE555_LIB_NAME}# Sets the library as a shared library.SHARED# Provides a relative path to your source file(s).${BasicUsageEnvironmentFiles}${groupsockFiles}${UsageEnvironmentFiles}${liveMediaFiles})target_link_libraries( # Specifies the target library.${LIVE555_LIB_NAME}# Links the target library to the third library# included in the NDK.sslcrypto${z-lib})

编译后报错:invalid conversion from ‘int*’ to ‘socklen_t*’

修改 groupsock/include/NetCommon.h 123 行

#define SOCKLEN_T int 改为 #define SOCKLEN_T socklen_t

参考资料:

  1. https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html

这篇关于CMake 基础用法,掌握 CMake 诀窍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/952649

相关文章

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

AI基础 L9 Local Search II 局部搜索

Local Beam search 对于当前的所有k个状态,生成它们的所有可能后继状态。 检查生成的后继状态中是否有任何状态是解决方案。 如果所有后继状态都不是解决方案,则从所有后继状态中选择k个最佳状态。 当达到预设的迭代次数或满足某个终止条件时,算法停止。 — Choose k successors randomly, biased towards good ones — Close

bytes.split的用法和注意事项

当然,我很乐意详细介绍 bytes.Split 的用法和注意事项。这个函数是 Go 标准库中 bytes 包的一个重要组成部分,用于分割字节切片。 基本用法 bytes.Split 的函数签名如下: func Split(s, sep []byte) [][]byte s 是要分割的字节切片sep 是用作分隔符的字节切片返回值是一个二维字节切片,包含分割后的结果 基本使用示例: pa

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

c++基础版

c++基础版 Windows环境搭建第一个C++程序c++程序运行原理注释常亮字面常亮符号常亮 变量数据类型整型实型常量类型确定char类型字符串布尔类型 控制台输入随机数产生枚举定义数组数组便利 指针基础野指针空指针指针运算动态内存分配 结构体结构体默认值结构体数组结构体指针结构体指针数组函数无返回值函数和void类型地址传递函数传递数组 引用函数引用传参返回指针的正确写法函数返回数组

【QT】基础入门学习

文章目录 浅析Qt应用程序的主函数使用qDebug()函数常用快捷键Qt 编码风格信号槽连接模型实现方案 信号和槽的工作机制Qt对象树机制 浅析Qt应用程序的主函数 #include "mywindow.h"#include <QApplication>// 程序的入口int main(int argc, char *argv[]){// argc是命令行参数个数,argv是

【MRI基础】TR 和 TE 时间概念

重复时间 (TR) 磁共振成像 (MRI) 中的 TR(重复时间,repetition time)是施加于同一切片的连续脉冲序列之间的时间间隔。具体而言,TR 是施加一个 RF(射频)脉冲与施加下一个 RF 脉冲之间的持续时间。TR 以毫秒 (ms) 为单位,主要控制后续脉冲之前的纵向弛豫程度(T1 弛豫),使其成为显著影响 MRI 中的图像对比度和信号特性的重要参数。 回声时间 (TE)

生信代码入门:从零开始掌握生物信息学编程技能

少走弯路,高效分析;了解生信云,访问 【生信圆桌x生信专用云服务器】 : www.tebteb.cc 介绍 生物信息学是一个高度跨学科的领域,结合了生物学、计算机科学和统计学。随着高通量测序技术的发展,海量的生物数据需要通过编程来进行处理和分析。因此,掌握生信编程技能,成为每一个生物信息学研究者的必备能力。 生信代码入门,旨在帮助初学者从零开始学习生物信息学中的编程基础。通过学习常用