一文搞定CMakeLists编写与库配置

描述

对目前看过的CMakeLists.txt中的命令进行解释,同时给出一些案例

一、模板

cmake_minimum_required(VERSION 3.1.0)  #定义cmake 支持的版本

project(sample)  #项目名

set(CMAKE_BUILD_TYPE "Release")

include_directories(
    ${
    
    PROJECT_SOURCE_DIR}/include
    # 需要添加的头文件路径
)

# 添加源文件路径
aux_source_directory(${
    
    PROJECT_SOURCE_DIR} SOURCE_LIST)
aux_source_directory(${
    
    PROJECT_SOURCE_DIR}/src SOURCE_LIST)

add_executable(sample main.cpp ${
    
    SOURCE_LIST})
target_link_libraries(sample -lpthread)

添加Opencv

find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
  include_directories(${
    
    OPENCV_INCLUDE_DIRS})
  target_link_libraries(sample ${
    
    OpenCV_LIBRARIES})
endif(OpenCV_FOUND)

添加PCL

find_package(PCL 1.12 REQUIRED)
if(PCL_FOUND)
  include_directories(${
    
    PCL_INCLUDE_DIRS})
  target_link_libraries(sample ${
    
    PCL_LIBRARIES})
endif(PCL_FOUND)

命令

mkdir build
cd build
cmake ..
make -j8

二、基本语法规则及关键语句

  1. 存放源代码子目录下要编写CMakeLists.txt
  2. CMake变量使用${}来取值,IF语句中直接使用变量名
  3. 指令(参数1 参数2),参数以空格或分号隔开
  4. 环境变量使用命令SET(ENV{VAR) VALUE)赋值,以$ENV{}取值

常用关键字

CMakeLists.txt中会涉及到一些预定义的变量

PROJECT_BINARY_DIR  # 工程的build路径
PROJECT_SOURCE_DIR  # 工程的根路径
EXECUTABLE_OUTPUT_PATH  # 可执行文件的输出路径

关键语句

2.1 加载外部库

2.1.1 格式

FIND_PACKAGE(<name> [version] [EXACT] [QUIET] [[REQUIRED|COMPONENTS] [ componets... ] ] )
  • version: 需要一个版本号,它是正在查找的包应该兼容的版本号。
  • EXACT 选项: 要求版本号必须精确匹配。如果在find-module内部对该命令的递归调用没有给定[version]参数,那么[version]和EXACT选项会自动地从外部调用前向继承。对版本的支持目前只存在于包和包之间。
    例子find_package(Boost ${boost_version} EXACT REQUIRED)
  • QUIET 参数:会禁掉包没有被发现时的警告信息。对应于Find.cmake模块中的 NAME_FIND_QUIETLY。
    例子find_package(Boost ${boost_version} EXACT QUIET)
  • REQUIRED 参数
    其含义是指是工程必须的,表示如果报没有找到的话,cmake的过程会终止,并输出警告信息。对应于Find.cmake模块中的 NAME_FIND_REQUIRED 变量。
    例子find_package(Boost REQUIRED COMPONENTS system)
  • COMPONENTS参数
    在REQUIRED选项之后,或者如果没有指定REQUIRED选项但是指定了COMPONENTS选项,在它们的后面可以列出一些与包相关(依赖)的部件清单(components list)
    例子find_package(Boost REQUIRED COMPONENTS system)

2.1.2 例子

这是查找pcl库1.12版本的一个例子

find_package(PCL 1.12 REQUIRED)
  1. find_package首先会在模块路径中寻找< name >.cmake
    这是查找库的一个典型方式,具体查找路径依次为CMake:
    变量${CMAKE_MODULE_PATH}中的所有目录。
    如果没有, 然后再查看它自己的模块目录: /share/cmake-x.y/Modules/ 。
    这称为模块模式。

  2. 如果没找到这样的文件:
    find_package()会在:~/.cmake/packages/或/usr/local/share/

    中的各个包目录中查找,寻找:<库名字的大写>Config.cmake 或 <库名字的小写>-config.cmake (包和包是不一样的,有时大写有时小写)

    (比如库Opencv,它会查找/usr/local/share/OpenCV中的OpenCVConfig.cmake或opencv-config.cmake)。**这称为配置模式。

  3. 不管使用哪一种模式,只要找到.cmake,.cmake里面都会定义下面这些变量:(不一定全部定义了,有些变量很可能是缺失的)

    扫描二维码关注公众号,回复: 14614476 查看本文章
    <NAME>_FOUND   # 01,代表没找到和找到
    <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES  # 头文件路径
    <NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS  # 库文件的枚举
    <NAME>_DEFINITIONS
    

    例如:当包的名字为OpenCV时,寻找的就是OpenCVConfig.cmake,OpenCVConfig.cmake之中定义的变量如下:

    OpenCV_FOUND
    OpenCV_INCLUDE_DIRS
    OpenCV_LIBRARIES
    OpenCV_LIBS # 与OpenCV_LIBRARIES一致
    

    (message打印一下,你会发现OPENCV_LIBS是不存在的,OpenCV_FOUND是1,OPENCV_FOUND是TRUE)
    因此,包的大小写要特别注意,很可能写错大小写,也会找不到包

2.1.3 使用

如果找到这个包,则可以通过在工程的顶层目录中的CMakeLists.txt 文件添加 include_directories(< NAME>_INCLUDE_DIRS) 来包含库的头文件,添加target_link_libraries(< NAME>_LIBRARIES)命令将源文件与库文件链接起来。

例如:仍旧以OpenCV举例

find_package(OpenCV REQUIRED)
if(OpenCV_FOUND)
	include_directories(${
    
    OPENCV_INCLUDE_DIRS})
	target_link_libraries (sample ${
    
    OpenCV_LIBRARIES})
endif(OpenCV_FOUND)

2.2 子路径编译

在这里插入图片描述假设我们构建这样一个工程
sample——需要的关键代码
add——基本核心的代码
test_api——用于测试功能的代码
main——主程序

我们要达到这样的效果:

  • main可以调用sample,add的代码
    sample可以调用add的代码
    test_api可以调用sample,add的代码,以保证可以测试main的功能
  • 在cmake_learn路径下build

我们可以在cmake_learn/CMakeLists.txt中,完成所有文件夹的编译。核心命令是add_subdirectory。一个小示例如下。

option(TEST "BUILD TEST" OFF)
if (TEST)
    message("build test!")
    add_subdirectory(${
    
    PROJECT_SOURCE_DIR}/test)
endif()

在add目录下的cmake_learn/add/CMakeLists.txt可以这么写,用于生成libadd.so的库

set(CMAKE_BUILD_TYPE "Release")
include_directories(
  ${
    
    PROJECT_SOURCE_DIR}/include
)
aux_source_directory(./src ADD)
add_library(add STATIC ${
    
    ADD})

而对于test目录下的cmake_learn/test/CMakeLists.txt,可以模仿cmake_learn/CMakeLists.txt来写。只不过注意${PROJECT_SOURCE_DIR}这时为cmake_learn/test/
因此

aux_source_directory(${
    
    PROJECT_SOURCE_DIR}/add/src SOURCE_LIST)

应该改为

aux_source_directory(${
    
    PROJECT_SOURCE_DIR}/../add/src SOURCE_LIST)

CMakeLists.txt中的${PROJECT_SOURCE_DIR}变量,等同于自身所在路径

三、常用命令

  1. 设定工程名字

    project(smaple)
    

    cmake预定义了两个变量<projectname>_BINARY_DIR<projectname>_SOURCE_DIR,它们等价于另外两个预定义的变量PROJECT_BINARY_DIRPROJECT_SOURCE_DIR,建议使用后两者,因为它们可以不受project名字的限制

  2. 设置cmake最低版本需求

    cmake_minimum_required(VERSION 3.1.0)
    

    这句话可以不写,但一些需要指定高版本cmake时要加上这句

  3. 设置工程构建类型
    常用的一般就是下面两个

    set(CMAKE_BUILD_TYPE "Release")  
    

    set(CMAKE_BUILD_TYPE "Debug")
    

    一般来说我使用Release,可执行文件的运行速度会快一些

  4. 添加头文件路径

    include_directories( 
    	${
          
          PROJECT_SOURCE_DIR}
    	${
          
          PROJECT_SOURCE_DIR}/include
    )
    
  5. 添加源文件路径

    aux_source_directory(${
          
          PROJECT_SOURCE_DIR} SOURCE_LIST)
    aux_source_directory(${
          
          PROJECT_SOURCE_DIR}/src SOURCE_LIST)
    

    得一行一行写,这样可以把需要的路径加到变量SOURCE_LIST中,SOURCE_LIST名字是自定义的,可以更改

  6. 生成可执行文件

    add_executable(sample ${
          
          SOURCE_LIST})
    

    build会生成名字为sample的可执行文件
    为了突出主要文件,也可以把主要文件写在这里

    add_executable(sample main.cpp ${
          
          SOURCE_LIST})
    
  7. 为可执行文件链接所需要的库文件

    target_link_libraries(sample 
    	${
          
          PCL_LIBRARIES} ${
          
          OpenCV_LIBS} -lpthread)
    

    命令中的PCL_LIBRARIESOpenCV_LIBS,分别代表了PCL库和Opencv库的库文件集合,LIBRARIESLIBS写哪个都行

  8. 更改可执行文件和生成库的输出路径
    格式

    set(EXECUTABLE_OUTPUT_PATH [path])
    set(LIBRARY_OUTPUT_PATH [path])
    

    例子

    set(EXECUTABLE_OUTPUT_PATH ${
          
          PROJECT_SOURCE_DIR})
    set(LIBRARY_OUTPUT_PATH ${
          
          PROJECT_SOURCE_DIR})
    

    add_executable之前添加上面一句话,会发现可执行文件和生成的库,都生成在了工程根目录下,而不是build下了

  9. 编译子目录

    add_subdirectory(${
          
          PROJECT_SOURCE_DIR}/test)
    

    完整格式

    add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
    

    选填的binary_dir,代表二进制生成文件会存放的位置,例如add_subdirectory(${PROJECT_SOURCE_DIR}/add ${PROJECT_SOURCE_DIR}/add/build),会在子文件夹add下生成build文件夹,将生成文件存在其中。
    可选参数EXCLUDE_FROM_ALL,代表将这个目录从编译过程中排除(一般不用),用了EXCLUDE_FROM_ALL参数后,该文件夹下的文件需要单独去构建

  10. 生成库

    add_library(XXX [SHARED|STATIC])
    

    让工程生成一个库文件,不填参数或者填参数STATIC,生成的是静态库libXXX.a,参数写SHARED,生成的是动态库libXXX.so
    编译子目录时如果使用add_subdirectory的参数指定了路径,库文件会生成在该路径;否则会生成在工程根目录的build下。

  11. 查找库

    find_library(XXX libYYY.so PPP)
    

    也可以这么写

    find_library(XXX NAMES libYYY PPP)
    

    在路径PPP下查找名字叫libYYY.so的库,将这个库命名为XXX

    之后链接这个库的时候,就可以替换成XXX这样写了target_link_libraries(${PROJECT_NAME} ${XXX})

  12. 设置变量

    格式

    SET(XXX YYY)
    

    举例子

    set(CMAKE_CXX_STANDARD 11)
    

    将YYY命名为XXX,也可以是将变量XXX的值设置为YYY。

  13. 选项
    例子

    option(TEST "BUILD TEST" OFF)
    if (TEST)
        message("build test!")
        add_subdirectory(${
          
          PROJECT_SOURCE_DIR}/test)
    endif()
    

    使用
    cmake .. -DTEST=ON,可以把TEST变量置为ON,从而编译子目录test

总结

cmake虽简单,功能如繁星,仍不足以全部说明,后期不断补充这篇文章吧

猜你喜欢

转载自blog.csdn.net/weixin_42156097/article/details/127651809