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

一,CMake简介

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

CMake的使用场景:

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

CMakeLists.txt样例如下:

bash 复制代码
#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命令进行编译。

一般使用过程如下:

bash 复制代码
$ 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 \\[.\\[.\\[.\\]\]\]

LANGUAGES \...\]) **使用样例:** project(Demo) *** ** * ** *** **(3) set** **含义:**设置普通变量、缓存或环境变量的值 **语法:** set(\ \... \[PARENT_SCOPE\]) set(\ \... CACHE \ \ \[FORCE\]) set(ENV{\} \[\\]) **使用样例:** set(CMAKE_CXX_COMPILER D:/MinGW/bin/g++) *** ** * ** *** **(4) file** **含义:**定义对文件系统的文件和路径的操作,可以结合Linux指令对文件的操作去理解。 **语法:** file(READ \ \ \[...\]) file({WRITE \| APPEND} \ \...) file(MAKE_DIRECTORY \[\...\]) **使用样例:** file(WRITE test.txt "Test Write\\n" ) *** ** * ** *** **(5) option** **含义:**提供用户可以选择的布尔选项。 **语法:** option(\ "\" \[value\]) **使用样例:** option(TEST_DEBUG "option for debug" OFF) *** ** * ** *** **(6) if...else\[if\]...endif** **含义:**这个不用详细介绍了,用法同编程语言中的控制语句 **语法:** if/else(\[\\]) **使用样例:** 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(\ \) **使用样例:** 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(\ ...) **使用样例:** 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(\ \[WIN32\] \[MACOSX_BUNDLE

EXCLUDE_FROM_ALL

source1\] \[source2 ...\]) **使用样例:** add_executable(main main.cpp) *** ** * ** *** **(14) add_dependencies** **含义:**给编译目标添加依赖的target **语法:** add_dependencies(\ \[\\]...) **使用样例:** add_dependencies(log com_log) *** ** * ** *** **(15) add_library** **含义:**添加一个库到工程中,指定这个库的源文件 **语法:** add_library(\ \[STATIC \| SHARED \| MODULE

EXCLUDE_FROM_ALL

\...\]) **使用样例:** add_library(opencv_core SHARED IMPORTED) *** ** * ** *** **(16) configure_file** **含义:**将文件复制到另一个位置并修改其内容。 **语法:** configure_file(\ \ \[NO_SOURCE_PERMISSIONS \| USE_SOURCE_PERMISSIONS \| FILE_PERMISSIONS \...\]) **使用样例:** configure_file(CMakeLists.txt.in download/CMakeLists.txt) *** ** * ** *** **(17) find_package** **含义:**查找依赖的包名 **语法:** find_package(\ \[version\] \[EXACT\] \[QUIET\] \[MODULE

REQUIRED\] \[\[COMPONENTS\] \[components...\]\]) **使用样例:** find_package(OpenSSL REQUIRED) *** ** * ** *** **(18) find_library** **含义:**查找依赖的库 **语法:** find_library (\ name1 \[path1 path2 ...\]) **使用样例:** find_library(LOG_LIB log) *** ** * ** *** **(19) find_path** **含义:**搜索包含指定文件名的路径 **语法:** find_path (\ name1 \[path1 path2 ...\]) **使用样例:** find_path(_ZeroMQ_ROOT NAMES include/zmq.h) *** ** * ** *** **(20) target_link_libraries** **含义:**将之前打包的库链接到生成的目标上 **语法:** target_link_libraries(\ ... \... ...) **使用样例:** target_link_libraries(${THREAD_LIB_NAME} pthread) *** ** * ** *** **(21) target_include_directories** **含义:**指定编译生成目标时,需要使用的目录 **语法:** target_include_directories(\ \[SYSTEM\] \[AFTER\|BEFORE

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

\ \[items2...\] ...\]) **使用样例:** target_include_directories(hello_library PUBLIC ${PROJECT_SOURCE_DIR}/include) *** ** * ** *** **(22) target_sources** **含义:**指定构建目标或其依赖项时要使用的源文件 **语法:** target_sources(\ \ \[items1...

\ \[items2...\] ...\]) **使用样例:** target_sources(main PRIVATE main.cpp) *** ** * ** *** **(23) target_compile_definitions** **含义:**在编译目标文件时,指定要用到的编译选项 **语法:** target_compile_definitions(\ \ \[items1...

\ \[items2...\] ...\]) **使用样例:** target_compile_definitions(${PROJECTNAME} PUBLIC ARM7) *** ** * ** *** **(24) message** **含义:**编译过程添加日志消息 **语法:** message(\[\\] "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样例 **场景一,简单应用** ```bash 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** ```bash 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 $ $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_compile_features(jsonutils PRIVATE cxx_auto_type) target_compile_options(jsonutils PRIVATE $<$,$,$>: -Wall -Wextra -Wpedantic>) target_link_libraries(jsonutils PUBLIC Boost::headers RapidJSON::RapidJSON PRIVATE Boost::regex ) ``` **场景三,交叉编译,嵌入式场景用的比较多,通过编写toolchain.cmake指定编译时的工具链** toolchain.cmake样例 ```bash #设定目标操作系统的名称 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

相关推荐
Y.O.U..7 分钟前
今日八股——C++
开发语言·c++·面试
weixin_3077791316 分钟前
使用C#实现从Hive的CREATE TABLE语句中提取分区字段名和数据类型
开发语言·数据仓库·hive·c#
Xiaok101824 分钟前
解决 Hugging Face SentenceTransformer 下载失败的完整指南:ProxyError、SSLError与手动下载方案
开发语言·神经网络·php
绿草在线26 分钟前
Mock.js虚拟接口
开发语言·javascript·ecmascript
go_bai37 分钟前
Linux环境基础开发工具——(2)vim
linux·开发语言·经验分享·笔记·vim·学习方法
小郝 小郝38 分钟前
【C语言】strstr查找字符串函数
c语言·开发语言
yinhezhanshen44 分钟前
理解rust里面的copy和clone
开发语言·后端·rust
Zhichao_9744 分钟前
【UE5 C++课程系列笔记】33——商业化Json读写
c++·ue5
Jtti1 小时前
PHP在Debian环境上的并发处理能力如何
开发语言·debian·php