• 一文搞定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)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    添加Opencv

    find_package(OpenCV REQUIRED)
    if(OpenCV_FOUND)
      include_directories(${OPENCV_INCLUDE_DIRS})
      target_link_libraries(sample ${OpenCV_LIBRARIES})
    endif(OpenCV_FOUND)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    添加PCL

    find_package(PCL 1.12 REQUIRED)
    if(PCL_FOUND)
      include_directories(${PCL_INCLUDE_DIRS})
      target_link_libraries(sample ${PCL_LIBRARIES})
    endif(PCL_FOUND)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    命令

    mkdir build
    cd build
    cmake ..
    make -j8
    
    • 1
    • 2
    • 3
    • 4

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

    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  # 可执行文件的输出路径
    
    • 1
    • 2
    • 3

    关键语句

    2.1 加载外部库

    2.1.1 格式

    FIND_PACKAGE(<name> [version] [EXACT] [QUIET] [[REQUIRED|COMPONENTS] [ componets... ] ] )
    
    • 1
    • 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
    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里面都会定义下面这些变量:(不一定全部定义了,有些变量很可能是缺失的)

      <NAME>_FOUND   # 01,代表没找到和找到
      <NAME>_INCLUDE_DIRS or <NAME>_INCLUDES  # 头文件路径
      <NAME>_LIBRARIES or <NAME>_LIBRARIES or <NAME>_LIBS  # 库文件的枚举
      <NAME>_DEFINITIONS
      
      • 1
      • 2
      • 3
      • 4

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

      OpenCV_FOUND
      OpenCV_INCLUDE_DIRS
      OpenCV_LIBRARIES
      OpenCV_LIBS # 与OpenCV_LIBRARIES一致
      
      • 1
      • 2
      • 3
      • 4

      (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)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在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})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    而对于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)
    
    • 1

    应该改为

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

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

    三、常用命令

    1. 设定工程名字

      project(smaple)
      
      • 1

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

    2. 设置cmake最低版本需求

      cmake_minimum_required(VERSION 3.1.0)
      
      • 1

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

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

      set(CMAKE_BUILD_TYPE "Release")  
      
      • 1

      set(CMAKE_BUILD_TYPE "Debug")
      
      • 1

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

    4. 添加头文件路径

      include_directories( 
      	${PROJECT_SOURCE_DIR}
      	${PROJECT_SOURCE_DIR}/include
      )
      
      • 1
      • 2
      • 3
      • 4
    5. 添加源文件路径

      aux_source_directory(${PROJECT_SOURCE_DIR} SOURCE_LIST)
      aux_source_directory(${PROJECT_SOURCE_DIR}/src SOURCE_LIST)
      
      • 1
      • 2

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

    6. 生成可执行文件

      add_executable(sample ${SOURCE_LIST})
      
      • 1

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

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

      target_link_libraries(sample 
      	${PCL_LIBRARIES} ${OpenCV_LIBS} -lpthread)
      
      • 1
      • 2

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

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

      set(EXECUTABLE_OUTPUT_PATH [path])
      set(LIBRARY_OUTPUT_PATH [path])
      
      • 1
      • 2

      例子

      set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
      set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR})
      
      • 1
      • 2

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

    9. 编译子目录

      add_subdirectory(${PROJECT_SOURCE_DIR}/test)
      
      • 1

      完整格式

      add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
      
      • 1

      选填的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])
      
      • 1

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

    11. 查找库

      find_library(XXX libYYY.so PPP)
      
      • 1

      也可以这么写

      find_library(XXX NAMES libYYY PPP)
      
      • 1

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

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

    12. 设置变量

      格式

      SET(XXX YYY)
      
      • 1

      举例子

      set(CMAKE_CXX_STANDARD 11)
      
      • 1

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

    13. 选项
      例子

      option(TEST "BUILD TEST" OFF)
      if (TEST)
          message("build test!")
          add_subdirectory(${PROJECT_SOURCE_DIR}/test)
      endif()
      
      • 1
      • 2
      • 3
      • 4
      • 5

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

    总结

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

  • 相关阅读:
    【CloudCompare教程】001:CloudCompare中文版下载与安装图文教程
    杰理语音芯片ic玩具芯片ic的介绍_AD14NAD15N全系列开发
    Viewport的作用
    @ApiModel 和 @ApiModelProperty
    微信小程序的资源引用方式
    EI论文程序:Adaboost-BP神经网络的回归预测算法,可作为深度学习对比预测模型,丰富实验内容,自带数据集,直接运行!
    基于python+django的美食餐厅点餐订餐网站
    QT生成ICO文件
    kafka学习(七):消息队列与JMS
    用 TripletLoss 优化bert ranking
  • 原文地址:https://blog.csdn.net/weixin_42156097/article/details/127651809