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

相关文章

Python基础文件操作方法超详细讲解(详解版)

《Python基础文件操作方法超详细讲解(详解版)》文件就是操作系统为用户或应用程序提供的一个读写硬盘的虚拟单位,文件的核心操作就是读和写,:本文主要介绍Python基础文件操作方法超详细讲解的相... 目录一、文件操作1. 文件打开与关闭1.1 打开文件1.2 关闭文件2. 访问模式及说明二、文件读写1.

前端高级CSS用法示例详解

《前端高级CSS用法示例详解》在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交互和动态效果的关键技术之一,随着前端技术的不断发展,CSS的用法也日益丰富和高级,本文将深... 前端高级css用法在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交

揭秘Python Socket网络编程的7种硬核用法

《揭秘PythonSocket网络编程的7种硬核用法》Socket不仅能做聊天室,还能干一大堆硬核操作,这篇文章就带大家看看Python网络编程的7种超实用玩法,感兴趣的小伙伴可以跟随小编一起... 目录1.端口扫描器:探测开放端口2.简易 HTTP 服务器:10 秒搭个网页3.局域网游戏:多人联机对战4.

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

C#基础之委托详解(Delegate)

《C#基础之委托详解(Delegate)》:本文主要介绍C#基础之委托(Delegate),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 委托定义2. 委托实例化3. 多播委托(Multicast Delegates)4. 委托的用途事件处理回调函数LINQ

JavaScript Array.from及其相关用法详解(示例演示)

《JavaScriptArray.from及其相关用法详解(示例演示)》Array.from方法是ES6引入的一个静态方法,用于从类数组对象或可迭代对象创建一个新的数组实例,本文将详细介绍Array... 目录一、Array.from 方法概述1. 方法介绍2. 示例演示二、结合实际场景的使用1. 初始化二

一文带你了解SpringBoot中启动参数的各种用法

《一文带你了解SpringBoot中启动参数的各种用法》在使用SpringBoot开发应用时,我们通常需要根据不同的环境或特定需求调整启动参数,那么,SpringBoot提供了哪些方式来配置这些启动参... 目录一、启动参数的常见传递方式二、通过命令行参数传递启动参数三、使用 application.pro

关于@RequestParam的主要用法详解

《关于@RequestParam的主要用法详解》:本文主要介绍关于@RequestParam的主要用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 基本用法2. 默认值3. 可选参数4. 绑定到对象5. 绑定到集合或数组6. 绑定到 Map7. 处理复杂类

SQL中的CASE WHEN用法小结

《SQL中的CASEWHEN用法小结》文章详细介绍了SQL中的CASEWHEN函数及其用法,包括简单CASEWHEN和CASEWHEN条件表达式两种形式,并通过多个实际场景展示了如何使用CASEWH... 目录一、简单CASE WHEN函数:二、CASE WHEN条件表达式函数三、常用场景场景1:不同状态展