CMake学习

一、介绍

CMake是一个跨平台的源码构建工具,它使用统一的语法编写CMakeLists.txt文件,可以生成不同平台下的构建系统,然后再使用原生的开发工具(如VS/make等)进行编译安装。CMake可以看作是一门脚本语言,很多大型开源项目都在使用它,广泛用于C/C++项目中。

二、构建系统

构建系统(Buildsystem)就是指原始的项目工程文件,比如VS的.sln .vcxproj,各种Makefile等等

#查看CMake可以生成哪些构建系统
cmake -G

三、构建步骤

  • 通过CMakeLists.txt文件生成构建系统,这里会检查环境配置,比如编译器等,如果出错则直接退出。

        可以使用cmake命令或cmake-gui完成这一步,比如cmake命令: cmake  [选项]  <CMakeLists.txt路径>

  • 生成.vcxproj或Makefile之后就可以使用原生工具VS/nmake/make编译安装

常用选项:

-G:表示生成哪种构建系统

#一般性的构建步骤:
mkdir build    #在项目目录下创建build目录
cd build
cmake -G "Visual Studio 14 2015" ..    #生成VS2015工程文件,..表示CMakeLists.txt在上一级目录中
cmake -G "NMake Makefiles" ..          #生成Windows平台的Makefile
cmake -G "Unix Makefiles"  ..          #生成Linux/Unix平台下标准的Makefile
#根据不同平台选择相应的构建档,在CMake中称之为generator

#接下来使用VS/nmake/make等工具编译安装

-D:定义预处理变量,比如用CMAKE_BUILD_TYPE指定编译类型

cmake -DCMAKE_BUILD_TYPE=Debug|Release .. #生成Debug版或者Release版

-P: 执行CMake语言编写的脚本文件,通常以.cmake为后缀

cmake [{-D <var>=<value>}...] -P <script.cmake>

-S:顶层CMakeLists.txt目录

-B:指定构建目录,若不存在cmake创建它

cd 项目目录
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release #默认构建Debug版本
cmake --build build   #编译
cmake --install build #安装

四、CMake源文件

1、目录(CMakeLists.txt)

当CMake处理工程源码树的时候,入口点是顶层目录的CMakeLists.txt文件,文件中还可能使用add_subdirectory()命令添加子目录,每个子目录也必须有一个CMakeLists.txt

2、脚本(script.cmake)

通常<script>.cmake会使用macro()/function()命令定义一些公共的复用的函数(类似于c语言宏和函数的功能),脚本主要有下面两种执行方式:

  1. 通过cmake -P <script>.cmake单独解释执行,而不会生成构建系统,这种方式可以用来测试脚本文件中是否有语法错误
  2. 在CMakeLists.txt中使用include()命令加载并执行脚本文件中的代码,这样CMakeLists.txt后续的代码就可以直接调用这些宏和函数,通过CMAKE_MODULE_PATH内置变量指定脚本文件的查找路径,如果没找到则会搜索CMake安装目录

3、模块(module.cmake)

find_package命令用来查找项目中的依赖库,有可能是系统库(如OpenGL),也可能是第三方库(如OpenCV)

命令格式:
find_package(<PackageName> [version] [module] [REQUIRED] [[COMPONENTS] [components...]])

该命令首先会在CMAKE_MODULE_PATH指定的路径之中查找,然后在CMake的安装路径中查找并执行Find<PackageName>.cmake文件,在该文件中会搜索PackageName的相关信息,比如.h、.lib、.so的位置等

如果找到PackageName的这些信息,Find<PackageName>.cmake文件就会设置PackageName_FOUND、PackageName_INCLUDE_DIR、PackageName_LIBRARIES等变量,那么在CMakeLists.txt中就可以使用这些变量,比如用target_include_directories、target_link_libraries命令设置依赖库的头文件、库文件。

五、变量

CMake语言的变量都是字符串类型

1、变量范围

  • 函数范围

function()/endfunction()命令会创建一个新的变量范围,在函数中set/unset设定的变量都属于该范围,return之后这些变量就不可见了

  • 目录范围

function()/endfunction()命令之外set/unset的变量属于目录范围,在当前目录及其子目录的CMakeLists.txt都可见

注:add_subdirectory()添加的子目录的CMakeLists.txt会继承父目录CMakeLists.txt的变量

  • 持久缓存

CMake可以持久保存一些变量的值,这些变量每次构建都存在,使用set/unset的CACHE选项设置

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

  • variable:变量名称
  • value:变量值列表
  • CACHE:cache变量的标志
  • type:变量类型,取决于变量的值。类型分为:BOOL、FILEPATH、PATH、STRING、INTERNAL
  • docstring:必须是字符串,作为变量概要说明
  • FORCE:强制选项,强制修改变量值

2、变量引用

形式:${<variable>}

引用顺序:函数—>目录—>CACHE,如果没找到设置为空字符串

3、自定义变量

通过set/unset命令在函数范围目录范围定义/取消

4、内置变量

以CMAKE_开头的都属于内置变量,但也不限于CMAKE_开头,这里介绍最常用的几个

变量名含义
PROJECT_NAME由project指令设置的工程名
${ProName}_SOURCE_DIR工程(顶层)CMakeLists.txt所在的目录
PROJECT_SOURCE_DIR工程(顶层)CMakeLists.txt所在的目录,等同于${ProName}_SOURCE_DIR,推荐使用这个
${ProName}_BINARY_DIR 工程(顶层)CMakeLists.txt的构建目录,比如上面的build
PROJECT_BINARY_DIR工程(顶层)CMakeLists.txt的构建目录,等同于${ProName}_BINARY_DIR,推荐使用这个
CMAKE_CURRENT_SOURCE_DIR当前CMakeLists.txt所在的目录
CMAKE_CURRENT_BINARY_DIR当前CMakeLists.txt的构建目录
CMAKE_RUNTIME_OUTPUT_DIRECTORY指定exe的生成目录
CMAKE_LIBRARY_OUTPUT_DIRECTORY 指定dll/lib的生成目录

注意:PROJECT_SOURCE_DIR是固定不变的,就是指最顶层的CMakeLists.txt,包含project指令的那个

          CMAKE_CURRENT_SOURCE_DIR是变化的,指当前CMakeLists.txt,通过add_subdirectory()添加的每个子目录都有一个CMakeLists.txt

5、环境变量

环境变量的范围是全局的,每个CMakeLists.txt都可以引用,它不会被缓存。CMake进程启动时进行初始化,可以通过set/unset改变

引用:$ENV{variable}

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

           unset(ENV{<variable>})

六、平台相关变量

WIN32        如果是windows平台则为True

UNIX          如果是unix-like平台则为True

APPLE        如果是apple平台则为True

七、条件表达式

格式:
if(<condition>)
  <commands>
elseif(<condition>)
  <commands>
else()
  <commands>
endif()

条件判断关键字:

关键字意义
AND
OR
NOT
EXISTS文件或目录是否存在
COMMAND是否为命令
DEFINED变量是否定义
TARGET是否为目标,通过add_executable或add_library添加
MATCHES匹配正则表达式
IS_DIRECTORY是否为目录
IS_NEWER_THAN文件1是否比文件2新
EQUAL,LESS,LESS_EQUAL,GREATER,GREATER_EQUAL数字比较

STREQUAL,STRLESS,STRLESS_EQUAL

STRGREATER,STRGREATER_EQUAL

字符串比较

VERSION_EQUAL,VERSION_LESS,VERSION_LESS_EQUAL

VERSION_GREATER,VERSION_GREATER_EQUAL

版本比较

八、循环

  1. while(<condition>) #condition跟if的语法一样
      <commands>
    endwhile()
  2. foreach(<loop_var> <items>) #items是用分号或空格分隔的列表,loop_var每次迭代一个列表项
      <commands>
    endforeach()
  3. foreach(<loop_var> RANGE <start> <stop> [<step>]) #数字范围循环,start和step可以省略
  4. foreach(<loop_var> IN [LISTS [<lists>]] [ITEMS [<items>]]) #列表(项)循环

        注:break()/continue()相当于C语言的break和continue关键字

九、宏/函数

  1. macro(<name> [<arg1> ...])
      <commands>
    endmacro()
  2. function(<name> [<arg1> ...])
      <commands>
    endfunction()

        注:宏和函数一旦定义之后,就可以想普通命令一样去调用

十、生成二进制目标

1、生成可执行文件

命令格式:
add_executable(<name> [WIN32] [source1] [source2 ...])

选项:

WIN32:表示生成win32应用程序,即程序入口点为WinMain

source1 source2 ...指定要编译的源文件(.h,.c,.cpp,.ui,资源文件等)

2、生成链接库

命令格式:
add_library(<name> [STATIC | SHARED] [<source>...])

选项:

 STATIC:生成静态库

SHARED:生成动态库

3、源文件指定方式 

  • 通过set命令把要编译的文件放在变量中
  • 通过file(GLOB ...)命令生成源文件列表
  • add_executable/add_library命令中不指定源文件,通过target_source命令指定
命令格式:
file(GLOB <variable>
     [LIST_DIRECTORIES true|false] [RELATIVE <path>] [<globbing-expr>...])
file(GLOB_RECURSE <variable> 
     [LIST_DIRECTORIES true|false] [RELATIVE <path>] [<globbing-expr>...])

作用: 

根据globbing-expr表达式匹配目录下的文件,globbing-expr类似于正则表达式,常用的通配符有:

*  任意个任意字符

? 一个任意字符

[] 必有一个中括号的字符

示例:*.cpp,*.vt?,f[3-5].txt

选项: 

LIST_DIRECTORIES :是否列出目录,默认为true

RELATIVE:相对路径

GLOB_RECURSE模式会递归查找子目录

#头文件
set(HDR_LIST common.h)
#源文件
set(SRC_LIST main.cpp utils.cpp log.cpp)
#UI文件
set(UI_LIST frame.ui)
#资源文件
set(RC_LIST dolphin.rc)

add_executable(test ${HDR_LIST} ${SRC_LIST} ${UI_LIST} ${RC_LIST})

#通过下面两步设置与上面等价
#add_executable(test)
#target_source(test PRIVATE ${HDR_LIST} ${SRC_LIST} ${UI_LIST} ${RC_LIST})

#================================================================
file (GLOB SRCS LIST_DIRECTORIES false
            "${CMAKE_CURRENT_SOURCE_DIR}/*.h"
            "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp"
            "${CMAKE_CURRENT_SOURCE_DIR}/*.ui"
            "${CMAKE_CURRENT_SOURCE_DIR}/*.rc"
)
add_executable(test ${SRCS})

十一、指定头文件目录

target_include_directories(<target> [SYSTEM] <PUBLIC|PRIVATE> [items1...])

选项: PUBLIC:为当前target及其依赖目标(通过target_link_libraries命令指定)指定包含目录

            PRIVATE:只为当前target指定包含目录

注:老式的指定包含目录的命令为:include_directories([SYSTEM] dir1 [dir2 ...]),它会为当前CMakeLists.txt中的所有target都指定这个包含目录

十二、指定编译选项

target_compile_options(<target> <PUBLIC|PRIVATE> [items1...])

十三、指定链接库

target_link_libraries(<target> <PRIVATE|PUBLIC> <item>...)

例如:target_compile_options(<target> PRIVATE -Wextra -Werror)

十四、指定库文件路径

target_link_directories(<target> <PUBLIC|PRIVATE> [items1...])

十五、添加预处理宏

ADD_DEFINITIONS、ADD_COMPILE_DEFINITIONS预处理宏

set_target_properties(<target> PROPERTIES COMPILE_DEFINITIONS [items1...])

target_compile_definitions(<target> <PUBLIC|PRIVATE> [items1...]) 更推荐的用法

十六、安装

install(<TYPE> files... DESTINATION <dir>
        [PERMISSIONS permissions...]
        [CONFIGURATIONS [Debug|Release|...]]
        [COMPONENT <component>]
        [OPTIONAL] [NAMELINK_ONLY|NAMELINK_SKIP])

在这个结构中,我们可以看到以下几个关键部分:

  • <TYPE>:这是一个必选参数,它定义了我们要安装的内容的类型。这个参数可以是TARGETS(目标),FILES(文件),DIRECTORY(目录)等

  • files...:这是一个或多个我们要安装的文件或目标。对于TARGETS,这将是我们在add_executable或add_library中定义的目标名称。对于FILES和DIRECTORY,这将是文件或目录的路径

  • DESTINATION <dir>:这是一个必选参数,它定义了我们要将文件或目标安装到哪个目录

  • [PERMISSIONS permissions...]:这是一个可选参数,它允许我们定义安装的文件或目标的权限。如果我们不指定这个参数,CMake将使用默认的权限

  • [CONFIGURATIONS [Debug|Release|...]]:这是一个可选参数,它允许我们定义在哪些构建配置中执行安装命令。如果我们不指定这个参数,CMake将在所有的构建配置中执行安装命令

  • [COMPONENT <component>]:这是一个可选参数,它允许我们将安装的文件或目标分组到一个组件中。这个参数在创建安装包时非常有用

  • [OPTIONAL]:这是一个可选参数,它允许我们定义如果文件或目标不存在,CMake是否应该继续执行安装命令

  • [NAMELINK_ONLY|NAMELINK_SKIP]:这是一个可选参数,它只对库目标有效。它允许我们定义是否安装库的名字链接

安装路径:

CMAKE_INSTALL_PREFIX安装前缀(默认值为/usr/local)
CMAKE_INSTALL_LIBDIR安装库文件的目录名称(默认值为lib)
CMAKE_INSTALL_INCLUDEDIR头文件目录名称(默认值为include)
CMAKE_INSTALL_DATADIRSHARE目录名称(默认值为share)
CMAKE_INSTALL_SYSCONFDIR系统配置文件目录名称(默认值为/etc)
CMAKE_INSTALL_BINDIR可执行文件目录名称(默认值为bin)

十七、打包

在顶层CMakeLists.txt中通过设置CPack相关的变量来配置打包信息,添加include(CPack)引入CPack模块

string(TIMESTAMP PROJECT_BUILD_TIME "%Y%m%d_%H%M%S")
set(CPACK_GENERATOR "STGZ")
set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VERSION "0.1.1")
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${PROJECT_BUILD_TIME})
include(CPack)
#构建(编译)并安装,也可以分开两步执行
cmake --build build --target install
#构建(编译)并打包,也可以分开两步执行
cmake --build build --target package
#打包源码
cmake --build build --target package_source

单独执行打包过程

cd build
cpack -G STGZ -C Debug|Release --config CPackConfig.cmake
cpack -G TGZ  -C Debug|Release --config CPackSourceConfig.cmake

-G:指定生成器,即安装包格式,比如TGZ,STGZ、ZIP,DEB,RPM等

-C:指定安装包配置格式

--config:读取的配置文件,默认为CPackConfig.cmake

配置、编译、安装、打包分步执行

cd 项目目录
mkdir build
cd build
cmake ..
make
make install
make package
make package_source

STGZ包简介

 Linux下的STGZ包后缀通常是.sh或.run,是由一个shell脚本和tar.gz压缩包通过cat命令拼接在一起生成的。shell脚本中定义了安装软件包的一些操作,比如校验,解压,将文件拷贝到指定目录、设置环境变量等,可以根据需要进行定义。压缩包通常就是软件包的可执行文件和库文件等

CMake STGZ生成器默认的安装脚本为:{cmake安装路径/Modules/Internal/CPack/CPack.STGZ_Header.sh.in}

如果觉得默认的安装脚本比较简单,也可以自定义功能更丰富的脚本,并通过CPACK_STGZ_HEADER_FILE变量指定自定义脚本的位置

set(CPACK_GENERATOR "STGZ")
set(CPACK_STGZ_HEADER_FILE ${PROJECT_SOURCE_DIR}/etc/CPack.STGZ_Header.sh.in)

.in是CMake中的模板文件,文件中可以通过@CPACK_xxx@引用在CMakeLists.txt中设置的CPACK_xxx变量(注意:不能引用CMAKE_xxx类型变量)

十八、常用命令

1、字符串string

字符串查找:
string(FIND <string> <substring> <output_variable> [REVERSE])

   说明: 

        查找子字符串首次出现的位置,并把所有保存在output_variable中,若没找到则为-1

        REVERSE表示反向查找,即最后一次出现的位置

字符串替换:
string(REPLACE <match_str> <replace_str> <result_str> <src_str1> [<src_str2>...])

  说明: 

        在源字符串src_str中查找匹配的子字符串match_str,并替换为replace_str,替换后的字符串保存在result_str中,源字符串src_str保持不变

字符串拼接:
string(APPEND <string_variable> [<input>...])
string(PREPEND <string_variable> [<input>...])
string(CONCAT <output_variable> [<input>...])
string(JOIN <glue> <output_variable> [<input>...])

  说明:

APPEND在变量最后添加

PREPEND在变量前面添加

CONCAT把参数字符串直接连接在一起

JOIN把参数字符串用glue连接在一起

string(APPEND str1 "hello " "cmake")     #str1: hello cmake
string(PREPEND str2 "Lists.txt" "CMake") #str2: CMakeLists.txt
string(CONCAT str3 "Build" "System")     #str3: BuildSystem
string(JOIN "+" str4 "1" "2" "3" "4")    #str4: 1+2+3+4
大小写转换:
string(TOLOWER <string> <output_variable>)
string(TOUPPER <string> <output_variable>)
字符串长度:
string(LENGTH <string> <output_variable>)
子字符串提取:
string(SUBSTRING <string> <begin> <length> <output_variable>)
删除首尾空白:
string(STRIP <string> <output_variable>)
字符串比较:
string(COMPARE LESS <string1> <string2> <output_variable>)
string(COMPARE GREATER <string1> <string2> <output_variable>)
string(COMPARE EQUAL <string1> <string2> <output_variable>)
string(COMPARE NOTEQUAL <string1> <string2> <output_variable>)
string(COMPARE LESS_EQUAL <string1> <string2> <output_variable>)
string(COMPARE GREATER_EQUAL <string1> <string2> <output_variable>)

  说明:

        比较结果保存在变量output_variable,其值为true或false

2、列表list

3、信息打印message

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

常用的mode选项有:

  • FATAL_ERROR:致命错误,终止构建
  • SEND_ERROR:一般错误,继续构建
  • WARNING:警告信息
  • NOTICE:需要注意的信息
  • STATUS:一般输出信息
  • DEBUG:调试信息

4、文件file

file(COPY <file|dir> DESTINATION <dir>        复制文件或目录

5、源文件分组source_group

source_group(<name> [FILES <src>...] [REGULAR_EXPRESSION <regex>])

把源文件、头文件、ui文件等进行分组归类,在IDE中放在一起显示

FILES:要添加的文件

REGULAR_EXPRESSION:正则表达式匹配

<think>嗯,用户想要查找关于CMake学习资料,包括教程和资源。我需要先整理现有的引用内容,看看有哪些可用的信息。根据用户提供的引用[1],里面有提到一个详细的CMake教程、构建应用程序的经验、使用CPack打包软件,以及一个视频教程《Getting Started with CMake》。这些都是很好的资源。引用[2]提到在CentOS 7上更新CMake的方法,可能对用户的环境配置有帮助。 首先,用户可能需要基础教程,所以应该推荐官方文档和那个超详细的教程。然后,视频教程可能更适合视觉学习者,所以需要提到。接着,实战经验和小技巧也很重要,比如如何组织项目结构,处理依赖等,引用里的“cmake的一些小经验”应该包含这些。CPack打包可能属于进阶内容,但用户可能有兴趣,所以也要列出来。 用户可能在使用旧版系统,比如CentOS 7,所以需要提醒他们升级CMake的方法,如通过EPEL或源码编译,这点在引用[2]里提到了。此外,可能需要补充其他资源,比如书籍或在线课程,虽然引用里没有,但可以建议《CMake Cookbook》和Udemy的课程作为补充。 然后,生成相关问题的时候,要考虑用户可能遇到的后续问题,比如基础学习资料、项目构建方法、处理依赖、跨平台配置、调试技巧,以及推荐的书籍或课程。这样能帮助他们进一步深入学习。 最后,确保回答结构清晰,分点列出,每个部分有对应的引用,并在末尾添加相关问题。检查是否符合之前的格式要求,比如数学表达式是否正确,但这个问题可能不涉及数学内容,所以主要关注资源推荐和步骤。</think>以下是关于CMake学习教程和资源的整理,结合系统级要求和实际应用场景: ### 一、基础学习资源 1. **官方文档与教程** - CMake官方文档是最权威的参考资料,涵盖语法、命令和模块说明[^1]。 - 推荐教程:[Linux下CMake使用教程](https://example-link.com),详细讲解项目构建流程,适合入门[^1]。 2. **视频教程** - 《Getting Started with CMake》:适合零基础学习,涵盖构建、测试和安装流程[^1]。 3. **实战经验** - 参考《CMake的一些小经验》,学习项目结构优化和依赖管理技巧[^1]。 ### 二、进阶内容 1. **跨平台构建** - 使用`CMAKE_TOOLCHAIN_FILE`配置交叉编译,例如嵌入式开发或Windows/macOS兼容性设置。 2. **依赖管理** - 通过`find_package`或`FetchContent`管理第三方库,例如集成OpenCV或Boost。 3. **打包与分发** - 使用CPack生成DEB/RPM包或Windows安装程序,简化部署流程。 ### 三、环境配置注意事项 - **CentOS 7用户**:系统自带的CMake版本较旧,建议通过EPEL仓库或源码编译升级[^2]。 示例升级命令: ```bash # 通过EPEL安装 sudo yum install epel-release sudo yum install cmake3 # 或源码编译 wget https://cmake.org/files/v3.25/cmake-3.25.1.tar.gz tar xzf cmake-3.25.1.tar.gz cd cmake-3.25.1 ./bootstrap && make && sudo make install ``` ### 四、推荐工具与扩展 - **IDE支持**:CLion、VS Code + CMake插件 - **调试技巧**:使用`message()`命令输出变量值,或`--debug-output`参数运行CMake
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值