跳到主要内容
avatar logoavatar logo
主页资料关于导航
ctrlK
avatar logoavatar logo
  • 手册列表
  • 📊 嵌入式札记
  • ☁️ 云原生
  • 💻 边缘计算
  • 🤖 神经网络
  • 📝 小芝士
    • 🌏代理
    • 🌐设置静态IP/hostname
    • 📊python数据可视化
    • ⚓Git
    • CMake
  • 安装以及使用介绍
  • 基本语法规则
  • cmake中一些预定义变量
  • cmake中如何生成动态库和静态库
  • cmake中如何使用动态库和静态库(查找库的路径)
  • cmake中如何指定生成文件的输出路径
  • cmake中如何增加编译选项
  • cmake中如何增加头文件路径
  • cmake中如何在屏幕上打印信息
  • cmake中如何给变量赋值
  • 部分常用命令
  • 常用配置

CMake

info

快速入门简介

VSCode搭建多语言环境

安装以及使用介绍​

在ubuntu系统下的安装命令如下:

sudo apt install cmake

查看cmake版本:

cmake -version
  • 为什么用cmake

理论上说,任意一个 C++ 程序都可以用 g++ 来编译。但当程序规模越来越大时,一个工程可能有许多个文件夹和里边的源文件,这时输入的编译命令将越来越长。如果仅靠 g++ 命令,我们需要输入大量的编译指令,整个编译过程会变得异常繁琐。在历史上工程师们曾使用 makefile 进行自动编译,但下面要谈的 cmake 比它更加方便。在一个 cmake 工程中,我们会用 cmake 命令生成一个 makefile 文件,然后,用 make命令,根据这个 makefile 文件的内容,编译整个工程。

  • 使用步骤

在CMakeLists.txt同一目录下新建build目录,使用步骤类似于下列命令。

mkdir build
cd build
cmake .. # 在该目录下生成makefile文件
make -j4 # 编译
./YourExecutable #运行

上面的cmake后面接了两个点,表示CMakeLists.txt在上一个目录。也可以不添加build目录,CMakeLists.txt同一目录使用cmake .进行编译,只不过生成的内容太多,影响美观。

这一步骤类似于使用源码安装软件,./YourExecutable运行命令,修改为下列命令:

sudo make install

基本语法规则​

  • 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)
  • 如果文件名或者路径名包含了空格,可以使用双引号将它括起来(不包含空格也可以使用双引号):
SET(SRC_LIST “fu nc.c”)

cmake中一些预定义变量​

  • PROJECT_BINARY_DIR:运行cmake命令的目录,通常是${PROJECT_SOURCE_DIR}/build

  • EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置

  • LIBRARY_OUTPUT_PATH 重新定义目标链接库文件的存放位置

    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)#设定可执行文件地址
    SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)#设定输出库地址
  • PROJECT_SOURCE_DIR:工程的根目录

  • CMAKE_CURRENT_SOURCE_DIR:当前处理的CMakeLists.txt所在的目录

  • CMAKE_MAJOR_VERSION:cmake主版本号,如2.8.6中的2

  • CMAKE_MINOR_VERSION:cmake次版本号,如2.8.6中的8

  • CMAKE_PATCH_VERSION:cmake补丁等级,如2.8.6中的6

  • CMAKE_SYSTEM:系统名称,例如Linux-2.6.22

  • CAMKE_SYSTEM_NAME:不包含版本的系统名,如Linux

  • CMAKE_SYSTEM_VERSION:系统版本,如2.6.22

  • CMAKE_SYSTEM_PROCESSOR:处理器名称,如i686

  • UNIX:在所有的类UNIX平台为TRUE,包括OS X和cygwin

  • WIN32:在所有的win32平台为TRUE,包括cygwin

  • BUILD_SHARED_LIBS:控制默认的库编译方式。如果未进行设置,使用ADD_LIBRARY时又没有指定库类型,默认编译生成的库都是静态库。

  • CMAKE_C_FLAGS:设置C编译选项

  • CMAKE_CXX_FLAGS:设置C++编译选项

  • PROJECT_NAME: 工程名。

cmake中如何生成动态库和静态库​

参考ADD_LIBRARY和SET_TARGET_PROPERTIES用法,t3示例展示了相关用法

cmake中如何使用动态库和静态库(查找库的路径)​

参考INCLUDE_DIRECTORIES, LINK_DIRECTORIES, TARGET_LINK_LIBRARIES用法

  • t4示例使用动态库或静态库
  • t5示例如何使用cmake预定义的cmake模块(以FindCURL.cmake为例演示)
  • t6示例如何使用自定义的cmake模块(编写了自定义的FindHELLO.cmake)

cmake中如何指定生成文件的输出路径​

  • 如上ADD_SUBDIRECTORY的时候指定目标二进制文件输出路径(推荐使用下面这种)
  • 使用SET命令重新定义EXECUTABLE_OUTPUT_PATH和LIBRARY_OUTPUT_PATH变量来指定最终的二进制文件的位置
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)

cmake中如何增加编译选项​

  • 使用变量CMAKE_C_FLAGS添加C编译选项
  • 使用变量CMAKE_CXX_FLAGS添加C++编译选项
  • 使用ADD_DEFINITION添加

cmake中如何增加头文件路径​

  • 参考INCLUDE_DIRECTORIES命令用法

cmake中如何在屏幕上打印信息​

  • 参考MESSAGE用法

cmake中如何给变量赋值​

  • 参考SET和AUX_SOURCE_DIRECTORY用法

部分常用命令​

  • PROJECT:指定工程名称,还可指定工程支持的语言(可忽略)
  • SET:定义变量,可定义多个变量,如下:
SET(SRC_LIST main.c util.c reactor.c))
  • MESSAGE:向终端输出用户定义的信息或变量的值
MESSAGE([SEND_ERROR | STATUS | FATAL_ERROR] “message to display” …)
  • SEND_ERROR, 产生错误,生成过程被跳过
  • STATUS, 输出前缀为—的信息
  • FATAL_ERROR, 立即终止所有cmake过程
  • ADD_EXECUTABLE:生成可执行文件
  • ADD_LIBRARY:生成动态库或静态库
ADD_LIBRARY(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] SRC_LIST) #生成动态、静态库,module等同于shared
# EXCLUDE_FROM_ALL表示该库不会被默认构建
  • SET_TARGET_PROPERTIES:设置动态库的版本和API版本
SET_TARGET_PROPERTIES(hello_static PROPERTIES OUTPUT_NAME "hello")#生成的库名字
# 设置动态库的版本号,这里设置了两个版本号
SET_TARGET_PROPERTIES(hello_shared PROPERTIES VERSION 1.2 SOVERSION 1)
  • CMAKE_MINIMUM_REQUIRED:声明CMake的版本要求
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
  • ADD_SUBDIRECTORY:向当前工程添加存放源文件的子目录
  • SUBDIRS:deprecated,不再推荐使用
  • INCLUDE_DIRECTORIES:向工程添加多个特定的头文件搜索路径
  • LINK_DIRECTORIES:添加非标准的共享库搜索路径
  • TARGET_LINK_LIBRARIES:为target添加需要链接的共享库
  • ADD_DEFINITIONS:向C/C++编译器添加-D定义
  • ADD_DEPENDENCIES:定义target依赖的其他target
  • AUX_SOURCE_DIRECTORY:发现一个目录下所有的源代码文件并将列表存储在一个变量中
  • EXEC_PROGRAM:用于在指定目录运行某个程序(默认为当前CMakeLists.txt所在目录)
  • INCLUDE:载入CmakeList.txt文件或者预定义的cmake模块
INCLUDE(file [OPTIONAL]) #用来载入CMakeLists.txt文件
INCLUDE(module [OPTIONAL])#用来载入预定义的cmake模块
  • FIND_
  • FIND_FILE( name path1 path2 …)

VAR变量代表找到的文件全路径,包含文件名

  • FIND_LIBRARY( name path1 path2 …)

VAR变量代表找到的库全路径,包含库文件名

FIND_LIBRARY(libX X11 /usr/lib)
IF (NOT libx)
MESSAGE(FATAL_ERROR "libX not found")
ENDIF(NOT libX)
  • FIND_PATH( name path1 path2 …)

VAR变量代表包含这个文件的路径

  • FIND_PROGRAM( name path1 path2 …)

VAR变量代表包含这个程序的全路径

  • FIND_PACKAGE( [major.minor][QUIET] [NO_MODULE][REQUIRED | COMPONENTS][componets …]])

用来调用预定义在CMAKE_MODULE_PATH下的Find.cmake模块,你也可以自己定义Find 模块,通过SET(CMAKE_MODULE_PATH dir)将其放入工程的某个目录供工程使用

  • IF语法:
IF (expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ELSE (expression)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDIF (expression) # 一定要有ENDIF与IF对应
IF (expression), #expression不为:空,0,N,NO,OFF,FALSE,NOTFOUND或<var>_NOTFOUND,为真
IF (not exp), #与上面相反
IF (var1 AND var2)
IF (var1 OR var2)
IF (COMMAND cmd) #如果cmd确实是命令并可调用,为真
IF (EXISTS dir) #如果目录,为真
IF (EXISTS file) #如果文件存在,为真
IF (file1 IS_NEWER_THAN file2),#当file1比file2新,或file1/file2中有一个不存在时为真,文件名需使用全路径
IF (IS_DIRECTORY dir) #当dir是目录时,为真
IF (DEFINED var) #如果变量被定义,为真
IF (var MATCHES regex) #此处var可以用var名,也可以用${var}
IF (string MATCHES regex)
#当给定的变量或者字符串能够匹配正则表达式regex时为真。比如:
IF ("hello" MATCHES "ell")
MESSAGE("true")
ENDIF ("hello" MATCHES "ell")

数字比较表达式:

IF (variable LESS number)
IF (string LESS number)
IF (variable GREATER number)
IF (string GREATER number)
IF (variable EQUAL number)
IF (string EQUAL number)

按照字母表顺序进行比较:

IF (variable STRLESS string)
IF (string STRLESS string)
IF (variable STRGREATER string)
IF (string STRGREATER string)
IF (variable STREQUAL string)
IF (string STREQUAL string)
#一个小例子,用来判断平台差异:
IF (WIN32)
MESSAGE(STATUS “This is windows.”)
ELSE (WIN32)
MESSAGE(STATUS “This is not windows”)
ENDIF (WIN32)
#上述代码用来控制在不同的平台进行不同的控制,但是,阅读起来却并不是那么舒服,ELSE(WIN32)之类的语句很容易引起歧义。
#可以SET(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
#这时候就可以写成:
IF (WIN32)
ELSE ()
ENDIF ()
#配合ELSEIF使用,可能的写法是这样:
IF (WIN32)
#do something related to WIN32
ELSEIF (UNIX)
#do something related to UNIX
ELSEIF(APPLE)
#do something related to APPLE
ENDIF (WIN32)
  • WHILE语法:其真假判断条件可以参考IF指令
WHILE(condition)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDWHILE(condition)
  • FOREACH:FOREACH指令的使用方法有三种形式

形式一:列表

FOREACH(loop_var arg1 arg2 ...)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)

示例:

AUX_SOURCE_DIRECTORY(. SRC_LIST)
FOREACH(F ${SRC_LIST})
MESSAGE(${F})
ENDFOREACH(F)

形式二:范围

FOREACH(loop_var RANGE total)
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)
#从0到total以1为步进
FOREACH(VAR RANGE 10)
MESSAGE(${VAR})
ENDFOREACH(VAR)
#输出:
012345678910

形式三:范围和步进

FOREACH(loop_var RANGE start stop [step])
COMMAND1(ARGS ...)
COMMAND2(ARGS ...)
...
ENDFOREACH(loop_var)

从start开始到stop结束,以step为步进。注意:直到遇到ENDFOREACH指令,整个语句块才会得到真正的执行。

FOREACH(A RANGE 5 15 3)
MESSAGE(${A})
ENDFOREACH(A)
输出:
581114

常用配置​

这里介绍一些常用的配置,也作为以后的参考。

###############################################################
# 基础配置
cmake_minimum_required(VERSION 2.8)# # 声明要求的 cmake 最低版本
project(YourProjectName) # # 声明一个 cmake 工程
set(CMAKE_BUILD_TYPE Release) # 设置编译模式
set(CMAKE_CXX_FLAGS "-std=c++14 -O3") # C++支持的版本
set(OpenCV_DIR /opt/ros/kinetic/share/OpenCV-3.3.1-dev)#手动定义变量,find_package没找到的情况下可以使用

###############################################################
#典型配置
include_directories(/usr/local/include)#找根目录


# OpenCV
find_package(OpenCV REQUIRED)#寻找OpenCV.CMakeLists,以此找到包,并赋值各库相关变量
include_directories(${OpenCV_INCLUDE_DIRS})#添加头文件搜索路径,这样可以在代码中的#include做根目录
#OpenCV_INCLUDE_DIRS是关于find_package的变量

# Ceres
find_package(Ceres REQUIRED)
include_directories(${CERES_INCLUDE_DIRS})

# g2o
find_package(G2O REQUIRED)
include_directories(${G2O_INCLUDE_DIRS})

# Eigen
include_directories("/usr/include/eigen3")

# pcl
find_package(PCL 1.2 REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS}) #库地址
add_definitions(${PCL_DEFINITIONS})

add_executable(gaussNewton gaussNewton.cpp)#添加对主函数的可执行文件
target_link_libraries(gaussNewton ${OpenCV_LIBS}) # 添加OpenCV的库,目标链接库

add_executable(ceresCurveFitting ceresCurveFitting.cpp)
target_link_libraries(ceresCurveFitting ${OpenCV_LIBS} ${CERES_LIBRARIES})

add_executable(g2oCurveFitting g2oCurveFitting.cpp)
target_link_libraries(g2oCurveFitting ${OpenCV_LIBS} ${G2O_CORE_LIBRARY} ${G2O_STUFF_LIBRARY})
⚓Git
  • 安装以及使用介绍
  • 基本语法规则
  • cmake中一些预定义变量
  • cmake中如何生成动态库和静态库
  • cmake中如何使用动态库和静态库(查找库的路径)
  • cmake中如何指定生成文件的输出路径
  • cmake中如何增加编译选项
  • cmake中如何增加头文件路径
  • cmake中如何在屏幕上打印信息
  • cmake中如何给变量赋值
  • 部分常用命令
  • 常用配置