本文主要是介绍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
- 下载 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]
- 解压压缩包
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
- 创建软链接
移动 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/
- 确认安装结果
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
参考资料:
- https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html
这篇关于CMake 基础用法,掌握 CMake 诀窍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!