“轻松搞定CMake”系列之CMakeLists文件编写语法规则详解

2023-05-16

文章目录

    • 基本语法规则
    • 常见CMakeLists.txt中指令剖析
    • 从VS项目配置过程理解CMakeLists内容
    • CMake中常用变量汇总
    • 常用CMakeLists文件模板
      • 基础模板
      • 使用OpenCV库CMakeLists文件模板
      • 使用PCL库CMakeLists文件模板
      • 使用Eigen库CMakeLists文件模板

基本语法规则

CMake要求要求工程主目录和所有存放源代码子目录下都要编写CMakeLists.txt文件,注意大小写(CM 大写,Lists中L要大写且不要落下s)。
CMake变量使用${}方式取值,但是在IF控制语句中是直接使用变量名
环境变量使用$ENV{}方式取值,使用SET(ENV{VAR} VALUE)赋值

指令(参数1 参数2…)

参数使用括弧括起,参数之间使用空格或分号分开。

ADD_EXECUTABLE指令为例:

ADD_EXECUTABLE(hello main.c func.c)

或者

ADD_EXECUTABLE(hello main.c;func.c)

注:指令是大小写无关的,参数和变量是大小写相关的。我一般习惯用小写。

常见CMakeLists.txt中指令剖析

  • cmake_minimum_required(VERSION 2.6)命令
    规定cmake软件的最低版本。这行命令是可选的,可以不写这句话,但在有些情况下,如果CMakeLists.txt文件中使用了一些高版本cmake特有的一些命令时,就需要加上这一行,提醒用户升级到该版本之后再执行cmake。

  • project(<projectname>)命令
    语法:project(projectname [cxx] [c] [java])
    可以指定工程采用的语言,选项分别表示:C++, C, java, 如不指定默认支持所有语言。
    指定项目的名称,在生成VS项目下即指明了生成的.sln项目的文件名。项目最终编译生成的可执行文件不一定是这个项目名称,而是由另一条命令(add_executable)指定的。

    通过project命令,为整个代码项目定义了一个管理架构,这点可以通过在VS下建立项目的结构层次来帮助理解。VS下所有的项目都是在解决方案(.sln文件)下进行管理的,一个解决方案可以包含多个项目。cmake中project命令正是定义了解决方案的名称,add_executableadd_library命令都会生成一个项目,cmake会自动为每个项目(包括生成可执行文件的项目和生成链接库的项目)创建对应的文件夹,存储编译中间文件。

    但是这个项目名称还是很必要的,在cmake中有两个预定义的变量:<projectname>_BINARY_DIR<projectname>_SOURCE_DIR ,即一旦使用了project指明了一个项目名称,则同时隐式定义了这两个预定义的变量。在内部编译情况下,这两个变量的含义相同,而在外部编译下,两者指代的内容会有所不同。要理解这两个变量的含义,我们首先需要了解什么是外部构建(out of source build),下面会介绍什么叫外部构建。

    同时cmake还预定义了PROJECT_BINARY_DIRPROJECT_SOURCE_DIR变量,其中PROJECT_BINARY_DIR就等同于<projectname>_BINARY_DIR,PROJECT_SOURCE_DIR等同于<projectname>_SOURCE_DIR。因此在实际应用中,强烈推荐使用PROJECT_BINARY_DIRPROJECT_SOURCE_DIR变量,这样即使项目名称发生变化也不会影响CMakeLists.txt文件。

    外部构建的含义:
    外部构建和内部构建最大的不同在于执行cmake的工作路径不同,在我们使用cmake构建项目时,首先要求指定**“Where is the source code",然后要求指定"Where to build the binaries"。此时如果这两个路径相同的话,cmake生成的所有中间文件和可执行文件都会存放在项目目录(即所谓的根目录)中;而如果我们在项目目录下先新建一个build文件夹,然后将"Where to build the binaries"指定为该build文件夹的路径,此时中间文件和可执行文件都会存放在build目录下。第二种方法的优点显而易见,它最大限度的保持了代码目录的整洁,同时由于第二种方法的生成、编译和安装时发生在不同于项目目录的其他目录中,所以第二种方法就叫做“外部构建”**。
    回到project命令下的问题,在外部构建的情况下,PROJECT_SOURCE_DIR指向 的目录同内部构建一样,仍然是项目目录,而PROJECT_BINARY_DIR则有所不同,指向的是<项目目录>/build目录。cmake强烈推荐使用外部构建的方法。

  • ADD_SUBDIRECTORY命令
    语法: ADD_SUBDIRECTORY(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    该命令告诉CMake去子目录中查看可用的CMakeLists.txt文件
    指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。 EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除。比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建。
    在我们的项目中,我们添加了src目录到项目中,而把对应于src目录生成的中间文件和目标文件存放到bin目录下,在上一节举例中“外部构建”的情况下,中间文件和目标文件将存放在build/srcobj目录下

  • ADD_EXECUTABLE命令
    告诉工程生成一个可执行文件。该命令定义了工程最终生成的可执行文件的文件名以及参与编译的头文件和cpp文件。
    如果想指定生成的可执行文件的存放路径,可以设置cmake中预定义变量EXECUTABLE_OUTPUT_PATH 的值。例如,将生成的可执行文件放置在cmake编译路径的bin文件夹下可用:SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)

  • ADD_LIBRARY命令
    语法:ADD_LIBRARY(libname [SHARED|STATIC]
    告诉工程生成一个库文件

  • FIND_LIBRARY命令
    查找库所在目录,语法如下:

find_library (
          <VAR>
          name | NAMES name1 [name2 ...] [NAMES_PER_DIR]
          [HINTS path1 [path2 ... ENV var]]
          [PATHS path1 [path2 ... ENV var]]
          [PATH_SUFFIXES suffix1 [suffix2 ...]]
          [DOC "cache documentation string"]
          [NO_DEFAULT_PATH]
          [NO_CMAKE_ENVIRONMENT_PATH]
          [NO_CMAKE_PATH]
          [NO_SYSTEM_ENVIRONMENT_PATH]
          [NO_CMAKE_SYSTEM_PATH]
          [CMAKE_FIND_ROOT_PATH_BOTH |
           ONLY_CMAKE_FIND_ROOT_PATH |
           NO_CMAKE_FIND_ROOT_PATH]
         )

举例:

FIND_LIBRARY(RUNTIME_LIB rt /usr/lib  /usr/local/lib NO_DEFAULT_PATH)

cmake会在目录中查找,如果所有目录中都没有,值RUNTIME_LIB就会被赋为NO_DEFAULT_PATH

  • SET命令——用于设置变量,相当于为变量取别名
    SET(CMAKE_BUILE_TYPE DEBUG) 设置编译类型debug 或者release。 debug 版会生成相关调试信息,可以使用GDB 进行调试;release不会生成调试信息。当无法进行调试时查看此处是否设置为debug.
    SET(CMAKE_C_FLAGS_DEBUG “-g -Wall”) 设置编译器的类型
    CMAKE_C_FLAGS_DEBUG ---- C 编译器
    CMAKE_CXX_FLAGS_DEBUG ---- C++ 编译器

  • CMAKE_MODULE_PATH变量——定义cmake模块所在路径
    如果你的工程比较复杂,有可能会自己编写一些 cmake 模块(所谓cmake模块指的是在该文件内部定义了一些变量,这些变量指明了一些头文件路径和库文件路径等有用信息)。这些 cmake 模块是随你的工程发布的,为了让 cmake 在处理CMakeLists.txt 时找到这些模块,你需要通过 SET 指令,将自己的 cmake 模块路径设置一下。
    比如
    SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
    这时候你就可以通过 INCLUDE 指令来调用自己的模块了

  • EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH变量
    我们可以通过 SET 指令重新定义 EXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATH 变量来指定最终的目标二进制的位置(指最终生成的CRNode可执行文件或者最终的共享库,而不包含编译生成的中间文件)。
    命令如下:
    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    需要注意的是,在哪里 ADD_EXECUTABLEADD_LIBRARY,如果需要改变目标存放路径,就在哪里加入上述的定义。

从VS项目配置过程理解CMakeLists内容

由于在之前学习C++编程一直是在VS中进行,当时就涉及到了众多有关头文件、链接库的配置过程,还有很多编译选项的设置,而CMake则相当于用自己的语法规则配置了整个项目,控制编译流程,因此可以从VS的配置过程来对照学习CMake中一些基本语法到底在做什么事。

采用外部构建项目时候编写的CMakeLists.txt:
1、在项目文件夹下新建一个CMakeLists.txt
同时新建一个文件夹build在此文件夹中执行cmake …即可进行项目构建;(前提是项目文件夹下源文件已经准备好)
set(TARGET_NAME rimeserver);设置项目的变量名字;
PROJECT(${TARGET_NAME})
cmake_minimum_required(VERSION 2.8.12)
2、CMakeLists.txt文件中依赖库及文件的设置

  1. include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
    包含CMakeLists.txt文件同级目录的include文件夹,相当于在VS中设置包含文件
    2)add_executable(${TARGET_NAME} ${SOURCES})
    ${TARGET_NAME}代表你设置的项目名称变量;
    ${SOURCES}代表你的源文件变量;
    add_executable代表项目生成一个可执行文件;
    相当于在VS中新建一个项目时一步步填写的项目名称等。
    3)add_dependencies(${TARGET_NAME} deploy)
    deploy代表你项目依赖的项目,保证deploy目标在其他的目标之前被构建;
    4)target_link_libraries(${TARGET_NAME}
    指定项目依赖的lib文件,相当于在VS中设置链接器下面的链接库
    5)set_target_properties(${TARGET_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
    设置项目属性,相当于VS中项目属性设置页中的各种条目。

CMake中常用变量汇总

CMake变量查询网站:https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/Useful-Variables

说明:

  • 引用变量内容需要用${变量名}格式
  • 可以使用message命令查看变量具体内容
    message(STATUS "The CMAKE_VERSION  is ${CMAKE_VERSION}")
    message(STATUS "The CMAKE_SOURCE_DIR  is ${CMAKE_SOURCE_DIR}")
    
  • 可以在CMakeLists.txt文件中使用set命令设置某些变量值
    set(CMAKE_BUILD_TYPE "Release")
  • 可以通过cmake命令行参数设置变量值
    cmake -DCMAKE_BUILD_TYPE=Release,这种方式会被CMakeLists.txt中set命令设置的变量值覆盖

常用变量:

变量名含义
PROJECT_NAMEproject命令中写的项目名
CMAKE_VERSION当前使用CMake的版本
CMAKE_SOURCE_DIR工程顶层目录,即入口CMakeLists文件所在路径
PROJECT_SOURCE_DIRCMAKE_SOURCE_DIR
CMAKE_BINARY_DIR工程编译发生的目录,即执行cmake命令进行项目配置的目录,一般为build
PROJECT_BINARY_DIRCMAKE_BINARY_DIR
CMAKE_CURRENT_SOURCE_DIR当前处理的CMakeLists.txt所在的路径
CMAKE_CURRRENT_BINARY_DIR当前处理的CMakeLists.txt中生成目标文件所在编译目录
CMAKE_CURRENT_LIST_FILE输出调用这个变量的CMakeLists.txt文件的完整路径
CMAKE_CURRENT_LIST_DIR当前处理的CMakeLists.txt文件所在目录的路径
CMAKE_INSTALL_PREFIX指定make install命令执行时包安装路径
CMAKE_MODULE_PATHfind_package命令搜索包路径之一,默认为空

编译配置相关变量:

变量名含义
CMAKE_BUILD_TYPE编译选项,Release或者Debug,如set(CMAKE_BUILD_TYPE "Release")
CMAKE_CXX_FLAGS编译标志,设置C++11编译,set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
CMAKE_CXX_STANDARD也可以设置C++11编译,set(CMAKE_CXX_STANDARD 11)

常用CMakeLists文件模板

基础模板

## This is a standard CMakeLists.txt template, including
## how to enable C++11 compiler option and how to set 'Release'
## target compile option

cmake_minimum_required(VERSION 2.8)
project(cmake_template)

############# Use message function to print information ############
message(STATUS "CMake version: ${CMAKE_VERSION}")

############ enable C++11 compiler###############
## Method 1
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
    message(WARNING "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support.")
    message(STATUS "Please use a different C++ compiler.")
endif()
## Method 2
set(CMAKE_CXX_STANDARD 11)


############ #enable warnings##################
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall")


#############Set cmake compiler options##############
## Method 1
set(CMAKE_BUILD_TYPE "Release")

## Method 2
IF(NOT CMAKE_BUILD_TYPE)
  SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING
      "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel."
      FORCE)
ENDIF(NOT CMAKE_BUILD_TYPE)

################## Some useful arguments ##########################
set(CMAKE_INCLUDE_CURRENT_DIR ON)

##########
##Build###
##########
add_library(lib hello.cpp)

add_executable(main main.cpp)
target_link_libraries(main ${LINK_LIBRARIES})

使用OpenCV库CMakeLists文件模板

cmake_minimum_required(VERSION 2.8.12)
project(OpenCV_test)

#find packages
find_package(OpenCV REQUIRED)

#include headers
include_directories(${OpenCV_INCLUDE_DIRS})

#target generation
add_executable(OpenCV_test main.cpp)

#link libraries
target_link_libraries(OpenCV_test ${OpenCV_LIBS})

使用PCL库CMakeLists文件模板

cmake_minimum_required(VERSION 2.8.12)
project(PCL_test)

#find packages
find_package(PCL 1.7 REQUIRED)
add_definitions(${PCL_DEFINITIONS})

#include headers
include_directories(${PCL_INCLUDE_DIRS})

#target generation
add_executable(PCL_test main.cpp)

#link libraries
target_link_libraries(PCL_test ${PCL_LIBRARIES})

使用Eigen库CMakeLists文件模板

cmake_minimum_required(VERSION 2.8.12)
project(Eigen_test)

#find packages
find_package(Eigen3 REQUIRED)

#include headers
include_directories(${EIGEN3_INCLUDE_DIR})

#target generation
add_executable(Eigen_test main.cpp)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“轻松搞定CMake”系列之CMakeLists文件编写语法规则详解 的相关文章

随机推荐

  • FPS游戏-罗技鼠标-通用的压枪宏

    郑重提示 这个宏不支持G HUB xff0c 只支持老款的鼠标驱动 xff08 因为当时G HUB刚出来全是bug就没用G HUB搞 xff0c 现在水平上去了也不想更新了哈哈 xff09 介绍 技术不行 xff0c 啥都白扯 本宏的开发用
  • 特殊字符对照表

    新的特殊字符编码对照表渲染后的字符编码十六进制渲染的十六进制编码十进制编码Java描述备注 amp x20 amp x20 amp 32 39 u0020 39 ordinary space amp xa2 amp xa2 amp 162
  • USB设备的bulk-only批量传输

    任何USB传输只能由主机发起 主机发送IN或OUT的请求只需一个令牌包 xff0c 这个包含在USBH BulkSendData 和USBH BulkReceiveData 函数中 xff0c 不需要编程者操心 为了详细一点的说明这些 xf
  • 基于stm32f407通过USB配置CH340

    1 在设备枚举的case ENUM SET CONFIGURATION 设置配置 状态后 xff0c 配置CH340的波特率 xff0c 然后跳过CBW和CSW协议 xff1b 直接采用批量传输的方式进行数据传输
  • FreeRTOS的多任务处理

    本文转载的原文路径为 xff1a https www cnblogs com yangguang it p 7233591 html 1 FreeRTOS 多任务调度支持 时间片段 xff0c 抢占式 和 和做式 任务侁先级可以在调度器启动
  • 单片机产生随机数

    在C语言中 为了生成随机数 xff0c 可以使用如下两个函数 xff1a void srand unsigned int seed int rand void 利用srand和rand 配合使用产生伪随机数序列 根据随机数种子rand 函数
  • C语言中 结构体成员冒号和的库函数

    C语言中的结构题成员 用冒号 xff1a struct sha204h temp key uint8 t value 32 unsigned int key id 4 unsigned int source flag 1 unsigned
  • 关于ATSHA204a的使用流程

    ATSHA204含有一个EEPROM 和 一个 SRAM EEPROM 包含 1个 Data Zone 512Bytes xff1a 一般用来存储密钥的 1个 configration Zone 88Bytes xff1a 用来设置Data
  • stm32L476RG,通过串口打印信息

    调试STM32L476RG时 xff0c 想要通过串口打印信息方法 xff1a 1 通过官方的历程 xff0c 移植USART的程序 xff0c 位置STM32Cube FW L4 V1 13 0 Projects NUCLEO L476R
  • k8s 1.18.20版本部署

    身为k8s初学者 xff0c 在掌握k8s理论知识的同时 xff0c 也需要掌握一下实际部署k8s的过程 xff0c 对于理论的学习起到一定的帮助作用 罗列了一下相关步骤 xff0c 请各位参考 xff1a 一 环境准备 三台虚机 xff1
  • java后端对接外部系统(HttpClient HttpPost)

    前言 最近遇到一个需求对接外部系统 xff0c 我们自己的系统发送请求 xff0c 根据请求内容的不同调用不同的外部系统 举例 xff1a 我们是做互联网医院的 xff0c 根据医生开处方选择药店的不同 xff0c 调用各药店自己的系统 x
  • “轻松搞定CMake”系列之find_package用法详解

    本文是 轻松搞定CMake 系列博客中的一篇 xff0c 该篇文章的主要目的是详细讲解一下CMake中搜包命令find package的使用和原理 其他更多文章请参考 xff1a 轻松搞定CMake 系列博客概述 文章目录 1 find p
  • 一遍成功的ROS主从机详细配置

    ROS支持多机互通 xff0c 可以设置一台主机 xff0c 多台从机 xff0c 主机中运行roscore xff0c 启动master节点 xff0c 从机直接运行其他节点 xff0c 在配置好的机器之间就可以互相通信了 xff0c 就
  • pip install列出待安装包的所有版本

    问题描述 一般我们在使用pip Install命令安装Python软件包的时候 xff0c 会想知道能安装的包版本的是多少 xff0c 这样方便自己选择可能的兼容版本进行安装 xff0c 在pip某个版本之前获取待安装包的所有版本命令比较简
  • ImportError with scipy.misc cannot import toimage错误解决

    问题描述 在使用如下代码时出现错误 xff1a span class token keyword import span scipy span class token punctuation span misc scipy span cla
  • AttributeError: module ‘requests‘ has no attribute ‘get‘

    问题描述 执行python程序时报错 xff1a AttributeError module span class token string 39 requests 39 span has no attribute span class t
  • Ubuntu下dpkg -i 安装deb报错 trying to overwrite shared

    问题表述 Ubuntu 22 04下 xff0c 执行下述命令时报错 xff1a 命令 xff1a span class token function sudo span dpkg i libglapi mesa 22 0 5 0ubunt
  • 4k高分屏Windows10下软件字体过小解决办法

    在Windows10下使用Matlab xff0c 由于是4k显示屏 xff0c 本来以为Matlab会出现字体过小的情况 xff0c 结果并没有出现什么问题 字体显示一切正常 xff0c 然而在打开Simulink时 xff0c Matl
  • Eigen矩阵运算开源库使用完全指南

    Eigen库是一个开源的矩阵运算库 xff0c 其利用C 43 43 模板编程的思想 xff0c 构造所有矩阵通过传递模板参数形式完成 由于模板类不支持库链接方式编译 xff0c 而且模板类要求全部写在头文件中 xff0c 从而导致导致Ei
  • “轻松搞定CMake”系列之CMakeLists文件编写语法规则详解

    文章目录 基本语法规则常见CMakeLists txt中指令剖析从VS项目配置过程理解CMakeLists内容CMake中常用变量汇总常用CMakeLists文件模板基础模板使用OpenCV库CMakeLists文件模板使用PCL库CMak