前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >C/C++生态工具链——编译构建工具CMake/CMakeList初探

C/C++生态工具链——编译构建工具CMake/CMakeList初探

作者头像
Coder-ZZ
发布于 2023-02-23 08:43:43
发布于 2023-02-23 08:43:43
2.8K00
代码可运行
举报
文章被收录于专栏:C/C++进阶专栏C/C++进阶专栏
运行总次数:0
代码可运行

一,CMake简介

CMake的全称是Cross-platform Make。我第一次参与Linux C++开发时使用的工具是Make,而后开始切换到CMake,一开始以为CMake是和C语言有关,原来开头的C表示它可以跨平台。

CMake的使用场景:

跨平台编译运行,交叉编译。一般基于CMakeLists.txt文件定义的编译构建规则来生成目标文件和目标库。

CMakeLists.txt样例如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#cmake最低版本需求
cmake_minimum_required(VERSION 3.13)

#项目名称
project(cmake_study)

#相关设置用set函数
set(CMAKE_CXX_STANDARD 11)

#生成的可执行文件的名称
add_executable(cmake_study src/main.cc)

在Linux环境使用CMake的构建和编译流程如下:

step1. 编写CMake的配置文件——CMakeLists.txt。

step2. 执行命令 cmake PATH 或者 ccmake PATH 构建生成 Makefile配置文件。PATH为CMakeLists.txt所在的目录。

step3. 在Makefile文件所在的路径,执行make命令进行编译。

一般使用过程如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ mkdir build
$ cd build/
$ cmake ..
$ make

* 为了不让编译产生的中间文件污染项目的文件结构,专门创建build文件夹进行编译构建。

二,CMake与Make的区别

CMake并不直接参与软件的构建和编译,而是生成用于构建的Makefile等配置文件。因此在完成同样的编译任务时,CMake比Make的用法更容易,且屏蔽了Makefile中的很多复杂的语法点。

三,CMakeLists.txt语法

cmake的语法由函数名和参数构成,参数区分大小写,函数名不区分大小写(这个依据个人喜好,笔者习惯用小写,大写有点费眼睛+_+)。


(1) cmake_minimum_required

含义:设置项目所需的最低cmake版本以及更新策略

语法:

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

使用样例:

cmake_minimum_required(VERSION 2.8.0)


(2) project

含义:设置项目的名称、版本、编程语言等信息

语法:

project(<PROJECT-NAME>

[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]

[LANGUAGES <language-name>...])

使用样例:

project(Demo)


(3) set

含义:设置普通变量、缓存或环境变量的值

语法:

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

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

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

使用样例:

set(CMAKE_CXX_COMPILER D:/MinGW/bin/g++)


(4) file

含义:定义对文件系统的文件和路径的操作,可以结合Linux指令对文件的操作去理解。

语法:

file(READ <filename> <out-var> [...])

file({WRITE | APPEND} <filename> <content>...)

file(MAKE_DIRECTORY [<dir>...])

使用样例:

file(WRITE test.txt "Test Write\n" )


(5) option

含义:提供用户可以选择的布尔选项。

语法:

option(<variable> "<help_text>" [value])

使用样例:

option(TEST_DEBUG "option for debug" OFF)


(6) if…else[if]…endif

含义:这个不用详细介绍了,用法同编程语言中的控制语句

语法:

if/else([<condition>])

使用样例:

if(WIN32)

message(STATUS "in Windows System")

elseif(UNIX)

message(STATUS "in Unix System")

endif()


(7) include_directories

含义:将指定目录添加到编译器的头文件搜索范围

语法:

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

使用样例:

include_directories(../src/com/include)


(8) link_directories

含义:添加需要链接的共享库(动态链接库)文件路径,相当于g++命令的-L参数,也相当于Linux环境变量设置LD_LIBRARY_PATH

语法:

link_directories([AFTER|BEFORE] directory1 [directory2 ...])

使用样例:

link_directories(${SOURCE_DIR}/lib)


(9) aux_source_directory

含义:查找指定目录中的所有源文件,将结果存进指定变量名

语法:

aux_source_directory(<dir> <variable>)

使用样例:

aux_source_directory(../src DIR_SRCS)


(10) add_custom_command

含义:添加自定义构建规则

语法:

add_custom_command(OUTPUT output1 [output2 ...]

COMMAND command1 [ARGS] [args1...]

[COMMAND command2 [ARGS] [args2...] ...])

使用样例:

add_custom_command(

TARGET ${_target}

POST_BUILD

COMMAND echo ${_command}

VERBATIM)


(11) add_compile_options

含义:设置编译选项

语法:

add_compile_options(<option> ...)

使用样例:

add_compile_options(-std=c++11)


(12) add_subdirectory

含义:将子目录添加到构建范围

语法:

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

使用样例:

add_subdirectory(utils)


(13) add_executable

含义:使用指定的源文件来生成目标可执行文件

语法:

add_executable(<name> [WIN32] [MACOSX_BUNDLE]

[EXCLUDE_FROM_ALL]

[source1] [source2 ...])

使用样例:

add_executable(main main.cpp)


(14) add_dependencies

含义:给编译目标添加依赖的target

语法:

add_dependencies(<target> [<target-dependency>]...)

使用样例:

add_dependencies(log com_log)


(15) add_library

含义:添加一个库到工程中,指定这个库的源文件

语法:

add_library(<name> [STATIC | SHARED | MODULE]

[EXCLUDE_FROM_ALL]

[<source>...])

使用样例:

add_library(opencv_core SHARED IMPORTED)


(16) configure_file

含义:将文件复制到另一个位置并修改其内容。

语法:

configure_file(<input> <output>

[NO_SOURCE_PERMISSIONS | USE_SOURCE_PERMISSIONS |

FILE_PERMISSIONS <permissions>...])

使用样例:

configure_file(CMakeLists.txt.in download/CMakeLists.txt)


(17) find_package

含义:查找依赖的包名

语法:

find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE]

[REQUIRED] [[COMPONENTS] [components...]])

使用样例:

find_package(OpenSSL REQUIRED)


(18) find_library

含义:查找依赖的库

语法:

find_library (<VAR> name1 [path1 path2 ...])

使用样例:

find_library(LOG_LIB log)


(19) find_path

含义:搜索包含指定文件名的路径

语法:

find_path (<VAR> name1 [path1 path2 ...])

使用样例:

find_path(_ZeroMQ_ROOT NAMES include/zmq.h)


(20) target_link_libraries

含义:将之前打包的库链接到生成的目标上

语法:

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

使用样例:

target_link_libraries(${THREAD_LIB_NAME} pthread)


(21) target_include_directories

含义:指定编译生成目标时,需要使用的目录

语法:

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]

<INTERFACE|PUBLIC|PRIVATE> [items1...]

[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

使用样例:

target_include_directories(hello_library

PUBLIC

${PROJECT_SOURCE_DIR}/include)


(22) target_sources

含义:指定构建目标或其依赖项时要使用的源文件

语法:

target_sources(<target>

<INTERFACE|PUBLIC|PRIVATE> [items1...]

[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

使用样例:

target_sources(main PRIVATE main.cpp)


(23) target_compile_definitions

含义:在编译目标文件时,指定要用到的编译选项

语法:

target_compile_definitions(<target>

<INTERFACE|PUBLIC|PRIVATE> [items1...]

[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

使用样例:

target_compile_definitions(${PROJECTNAME} PUBLIC ARM7)


(24) message

含义:编译过程添加日志消息

语法:

message([<mode>] "message text" ...)

使用样例:

message(STATUS "sources into a library? ${LIBRARY}")


四,CMake常用的环境变量


--CMAKE_C_COMPILER

指定C编译器


--CMAKE_CXX_COMPILER

指定C++编译器


--CMAKE_BUILD_TYPE

指定构建类型,例如Debug, Release


--CMAKE_C_FLAGS

指定C编译器配置


--CMAKE_CXX_FLAGS

指定C++编译器配置


--CMAKE_INSTALL_PREFIX

指定安装的路径前缀


--CMAKE_EXE_LINKER_FLAGS

创建可执行文件时,定义链接器的配置


--CMAKE_MODULE_LINKER_FLAGS

创建模块时,定义链接器的配置


--CMAKE_BINARY_DIR

构建树顶层的完整路径


--PROJECT_BINARY_DIR

构建项目的完整路径


--CMAKE_SOURCE_DIR

源代码树顶层的完整路径


--PROJECT_SOURCE_DIR

当前项目的顶级源目录


--CMAKE_CURRENT_SOURCE_DIR

cmake 当前正在处理的源目录的完整路径


--EXECUTABLE_OUTPUT_PATH

生成的可执行文件路径


--LIBRARY_OUTPUT_PATH

生成的库路径


--BUILD_SHARED_LIBS

通过add_library构建“STATIC/SHARED”库


--CMAKE_CURRENT_LIST_FILE

当前正在处理的文件列表的完整路径


--CMAKE_CURRENT_LIST_LINE

当前正在处理的文件的行号


--CMAKE_MODULE_PATH

提供find_package搜索第三方库时使用的路径


五,开发场景中常见的CMakeList样例

场景一,简单应用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cmake_minimum_required(VERSION 3.1...3.24)

#项目声明:项目名/版本号/编码语言
project(
  ModernCMakeExample
  VERSION 1.0
  LANGUAGES C++)

#把源代码添加进构建的目标库
add_library(MyLibExample simple_lib.cpp simple_lib.hpp)

#生成可执行文件
add_executable(MyExample simple_example.cpp)

#设置链接生成的库文件的名称
target_link_libraries(MyExample PRIVATE MyLibExample)

场景二,复杂工程--基于开源项目libjsonutils

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cmake_minimum_required(VERSION 3.13...3.19 FATAL_ERROR)
project(libjsonutils VERSION 1.0.0 LANGUAGES CXX)

#Make sure that custom modules like FindRapidJSON are found
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake)

# Find system dependencies
set(MIN_BOOST_VERSION 1.65)
find_package(Boost ${MIN_BOOST_VERSION} REQUIRED COMPONENTS regex)

set(MIN_RapidJSON_VERSION 1.1)
find_package(RapidJSON ${MIN_RapidJSON_VERSION} REQUIRED)

# Create target and set properties
add_library(jsonutils
    src/json_utils.cpp
    src/file_utils.h
)

#Add an alias so that library can be used inside the build tree, e.g. when testing
add_library(JSONUtils::jsonutils ALIAS jsonutils)

#Set target properties
target_include_directories(jsonutils
    PUBLIC
        $<INSTALL_INTERFACE:include>
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
)

target_compile_features(jsonutils PRIVATE cxx_auto_type)
target_compile_options(jsonutils PRIVATE
    $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
    -Wall -Wextra -Wpedantic>)

target_link_libraries(jsonutils
    PUBLIC
        Boost::headers RapidJSON::RapidJSON
    PRIVATE
        Boost::regex
)

场景三,交叉编译,嵌入式场景用的比较多,通过编写toolchain.cmake指定编译时的工具链

toolchain.cmake样例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#设定目标操作系统的名称
set(CMAKE_SYSTEM_NAME Windows)

#设定编译器
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)

#调整find命令的运行模式:在目标环境中搜索头文件和库
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

#在宿主机环境中搜索程序
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

学习CMake时,直接对着语法看最枯燥且收获最小。CMake由于足够灵活,带来的问题就是晦涩难懂,笔者发现身边很多经验丰富的开发者在编写CMakeList.txt时一样头疼。所以不能指望像学习脚本语言一样看完一遍便可熟悉,而是应该像查字典一样在开发中学习。可以把一些开源项目下载到自己的编译环境,查看项目中的CMakeList的写法,然后尝试自己编译和修改,可以加深对CMake用法的理解。

参考教程:

《CMake Cookbook》

https://www.hahack.com/codes/cmake/

https://doc.embedfire.com/linux/

https://github.com/Kitware/CMake

https://github.com/pabloariasal/modern-cmake-sample

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-09-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员与背包客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
cmake 简介2021-10-03
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
用户3519280
2023/07/08
3920
Cmake的使用
有了Cmake以后,只需要编写一个CmakeLists文件就可以对应将一个C++工程不通操作系统
全栈程序员站长
2022/09/22
1.5K0
Cmake的使用
使用Cmake生成跨平台项目编译解决方案
    项目最近有需求在windows下面运行,我花了几周时间将linux的服务器移植到windows下面,目前已经能够正常运行服务器,目前又有了新需求,两边的代码结构和组织是分开的,因此为了两边能够同步维护,需要一个能够跨平台的项目编译解决方案,经过调研之后,选择了使用cmake这个工具,本文主要讲述,使用cmake的生产项目的一些基础知识。
帘卷西风
2018/08/03
5.5K0
使用Cmake生成跨平台项目编译解决方案
cmake使用
CMake是一个跨平台的安装编译工具,可以用简单的语句来描述所有平台的安装编译过程。
李小白是一只喵
2020/10/28
2K0
cmake使用
【CMake】CMake 引入 ( Android NDK 构建脚本 | CMake 命令手册 )
该系列博客的应用场景是 Android Studio 下 NDK 编程 , 使用 CMake 构建 C/C++ 工程 ;
韩曙亮
2023/03/28
3.9K0
CMake---优雅的构建C/C++软件项目实践(1)
首先说明的是本篇文章不从cmake的整个语法上去讲述,而是从一个实际项目的构建上入手,去了解如何优雅的去构建一个软件项目,搭建一个C/C++软件项目基本的依赖组件,最后形成一个构建C/C++软件项目的模板,方便后面新项目的重复使用。相信对我们日常的软件项目构建都会有很好的收获。废话不都说,开始。
别打名名
2020/03/31
8.5K0
[082]破局Cmake中的PRIVATE,PUBLIC,INTERFACE
最近看了很多项目的代码,代码是用cmake编译的,由于各种库之间链接关系错综复杂,加上PRIVATE,PUBLIC,INTERFACE属性值,我在添加代码的时候总会遇到稀奇古怪的编译的问题,网上看了很多文章,写的都不是很靠谱,正好看到一个b站视频讲的不错,解决了我很多疑惑,我又有了新的疑惑,折腾了一晚上终于把这个搞明白了,分享给大家。
王小二
2023/08/16
1.3K0
[082]破局Cmake中的PRIVATE,PUBLIC,INTERFACE
CMake学习笔记(三)——以笔者的Robosub竞赛为例
CMake学习笔记(三)——以笔者的Robosub竞赛为例 继笔者认真学习了CMake语法之后,便开始尝试自己用CMake将以前用Qt写的软件框架程序改编为CMake指令生成模式。现已成功,在此奉上一
剑影啸清寒
2018/01/02
1.3K0
CMake学习笔记(三)——以笔者的Robosub竞赛为例
CMake常用命令的一些整理
CMake 是什么我就不用再多说什么了,相信大家都有接触才会看一篇文章。对于不太熟悉的开发人员可以把这篇文章当个查找手册。
混说Linux
2022/11/18
1.6K0
CMake 秘籍(五)
每个项目都必须处理依赖关系,而 CMake 使得在配置项目的系统上查找这些依赖关系变得相对容易。第三章,检测外部库和程序,展示了如何在系统上找到已安装的依赖项,并且到目前为止我们一直使用相同的模式。然而,如果依赖关系未得到满足,我们最多只能导致配置失败并告知用户失败的原因。但是,使用 CMake,我们可以组织项目,以便在系统上找不到依赖项时自动获取和构建它们。本章将介绍和分析ExternalProject.cmake和FetchContent.cmake标准模块以及它们在超级构建模式中的使用。前者允许我们在构建时间获取项目的依赖项,并且长期以来一直是 CMake 的一部分。后者模块是在 CMake 3.11 版本中添加的,允许我们在配置时间获取依赖项。通过超级构建模式,我们可以有效地利用 CMake 作为高级包管理器:在您的项目中,您将以相同的方式处理依赖项,无论它们是否已经在系统上可用,或者它们是否需要从头开始构建。接下来的五个示例将引导您了解该模式,并展示如何使用它来获取和构建几乎任何依赖项。
ApacheCN_飞龙
2024/05/16
8490
CMake 基础学习
使用 {}进行变量的引用。例如:message({Hello_VERSION}), Hello为工程名。CMake提供了很多有用的变量。以下仅列举常用的变量:
艳龙
2021/12/16
1.8K0
cmake基本使用
CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项
aruba
2020/07/02
1.6K0
ubuntu下CMake编译生成动态库和静态库,以OpenTLD为例。
opencv2.4.8,下载:Here. ubuntu下CMake编译生成动态库(.so)和静态库(.a),以OpenTLD为例。 直接看CMakeLists.txt吧。 cmake_minimum_required( VERSION 2.8 ) set(PROJECT_NAME OpenTLD) project(${PROJECT_NAME}) configure_file(h2.mp4 h2.mp4 COPYONLY) configure_file(parameters.ym
MachineLP
2018/01/09
2K0
CMake简介及使用实例
CMake是一个跨平台的建构系统的工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的构建文档makefile或者project文件,描述系统建构的过程。还能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CmakeLists.txt。CMake并不直接建构出最终的软件,而是产生标准的建构档(如 Unix的 Makefile或 Windows Visual C++的 projects/workspaces),然后再依一般的构建方式使用。
恋喵大鲤鱼
2018/08/03
2.7K0
如何用cmake编译
CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译源码生成可执行程序或共享库(so(shared object))。因此CMake的编译基本就两个步骤:
机智的程序员小熊
2019/03/10
4.2K0
如何用cmake编译
从零开始编写一个cmake构建脚本
PROJECT_NAME工程的版本号时 0.0.0,该版本号会被三个cmake内置变量所继承,例如主版本号PROJECT_VERSION_MAJOR=0,次版本号PROJECT_VERSION_MINOR=0,补丁版本号PROJECT_VERSION_PATCH=0,后续可以直接使用这三个内置变量来使用库的版本号
小帅聊鸿蒙
2024/08/26
1660
从零开始编写一个cmake构建脚本
CMake,大型项目采用的构建工具
本篇文章主要描述CMake的基本用法。在之前的文件中我对Makefile,Autotools这两个构建工具。相关文章如下:
Rice加饭
2022/05/10
1.1K0
QT的cmake项目工程配置
QT的项目工程默认使用的是qmake,这本来也没什么问题。但是由于要用到vcpkg这个工具来管理第三方库,好像这个 vcpkg工具只能在cmake的项目中才能使用。
杨永贞
2022/04/13
3.6K0
C++中使用CMake编译管理项目
CMake是一个跨平台的Makefile生成工具,可以根据特定的规则生成相应的Makefile文件,并对C/C++源代码进行编译和管理。 有一篇博客介绍CMake的使用,比较通俗易懂,链接地址是:Cmake 详解 CMake的官方下载地址为:https://cmake.org/download/ 官方文档地址为:CMake 3.16 Documentation 官方的CMake指南地址为:CMake Tutorial
ccf19881030
2019/11/03
4K0
CMake基础
该命令会调用编译器程序g++,让他读取main.cpp中的字符串(称为源码),并根据C++标准生成相应的机器指令码,输出到a.out这个文件中,(称为可执行文件)
用户9645905
2023/03/19
2K0
CMake基础
相关推荐
cmake 简介2021-10-03
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档