ROS交流群
ROS Group
产品服务
Product Service
开源代码库
Github
官网
Official website
技术交流
Technological exchanges
激光雷达
LIDAR
ROS教程
ROS Tourials
深度学习
Deep Learning
机器视觉
Computer Vision

cmake的简单使用



  • 在用别人写的c或者c++程序的时候。最经常见到的就是cmakelist.txt这个文件了。通常想要编译程序的时候需要执行

    cmake
    make
    make install
    

    这里cmake就是根据cmakelist.txt文件生成编译时使用的makefile文件
    一个程序的编译过程需要经过两个主要的过程。一个是把各个文件编译成机械码,另一个是把编译出的各个文件的机械码链接成一个文件。而cmakelist.txt的作用就是告诉编译软件头文件和链接库在什么地方,以及添加一些编译的特殊参数。下面就简单介绍一下cmakelist.txt应该怎么写。这里只介绍基本的功能,目的是快速上手,所以算是一个 cmakelist.txt quick start guide.

    以下面这个文件为例

    cmake_minimum_required(VERSION 2.8.3)
    
    project(orb_slam2)
    find_package(catkin REQUIRED COMPONENTS
      roscpp
      sensor_msgs
      image_transport
      message_filters
      cv_bridge
      cmake_modules)
    
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall  -O3 -march=native ")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall  -O3 -march=native")
    
    # Check C++11 or C++0x support
    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")
       add_definitions(-DCOMPILEDWITHC11)
       message(STATUS "Using flag -std=c++11.")
    elseif(COMPILER_SUPPORTS_CXX0X)
       set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
       add_definitions(-DCOMPILEDWITHC0X)
       message(STATUS "Using flag -std=c++0x.")
    else()
       message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
    endif()
    
    LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../../cmake_modules)
    
    find_package(OpenCV 2.4.3 REQUIRED)
    find_package(Eigen3 3.1.0 REQUIRED)
    find_package(Pangolin REQUIRED)
    find_package(libmongocxx REQUIRED)
    find_package(libbsoncxx REQUIRED)
    
    
    catkin_package()
    
    include_directories(
    ${PROJECT_SOURCE_DIR}
    ${PROJECT_SOURCE_DIR}/../../../
    ${PROJECT_SOURCE_DIR}/../../../include
    ${Pangolin_INCLUDE_DIRS}
    ${LIBMONGOCXX_INCLUDE_DIRS}
    ${LIBBSONCXX_INCLUDE_DIRS}
    )
    
    set(LIBS
    ${catkin_LIBRARIES}
    ${OpenCV_LIBS}
    ${EIGEN3_LIBS}
    ${Pangolin_LIBRARIES}
    ${PROJECT_SOURCE_DIR}/../../../Thirdparty/DBoW2/lib/libDBoW2.so
    ${PROJECT_SOURCE_DIR}/../../../Thirdparty/g2o/lib/libg2o.so
    ${PROJECT_SOURCE_DIR}/../../../lib/libORB_SLAM2.so
    ${LIBMONGOCXX_LIBRARY_DIRS}/libmongocxx.so
    ${LIBBSONCXX_LIBRARY_DIRS}/libbsoncxx.so
    )
    
    # Node for monocular camera
    add_executable(mono
    src/ros_mono.cc
    )
    
    target_link_libraries(mono
    ${LIBS}
    )
    
    # Node for RGB-D camera
    add_executable(rgbd
    src/ros_rgbd.cc
    )
    
    target_link_libraries(rgbd
    ${LIBS}
    )
    
    # Node for stereo camera
    add_executable(stereo
    src/ros_stereo.cc
    )
    
    target_link_libraries(stereo
    ${LIBS}
    )
    

    首先是指定cmake的版本
    然后是工程的名字
    然后是find_package. 软件一般都是有依赖关系的,如果你依赖别的软件,就可以通过find_package来载入依赖的软件的相关信息,比如软件的头文件,软件的链接库位置。find_package会在特定的目录里去找对应的软件config文件。在cmake的安装路径下里面有各种已安装的软件包的cmake文件。find_package实际上就是在找这些文件,然后执行一遍。有时自己依赖的软件包并不提供这样的文件,这时候就要自己去写。
    alt text

    上图的libbsoncxx-config.cmake就是libbsoncxx这个软件包的cmake文件。在cmake文件中的CMAKE_MODULE_PATH变量就是find_package找的路径。可以在里面添加上自定义的路径。find_package还可以添加上 REQUIRED标记,表明这个软件包是必须的。如果find_package没有找到这个软件包,那么cmake就出报出没有找到这个软件包的错误。
    Set 可以用来设置环境变量的。
    然后在cmake文件里面是有简单的判断分支结构可以用的,简单的语法就是

    If(xxxx)
    elseif(xxxx)
    endif(xxxx)
    

    需要注意的是elseif里面并不是另一种条件,这里要和if里面写的一样。表示当if的条件未被满足时应该执行的语句。如果想要实现更加复杂的判断,就需要嵌套的if语句。
    最后就是 include_directories和 target_link_libraries。这两个的意义很明确,就是设置头文件的地址和链接库的位置。还有就是生成可执行程序的命令 add_executable, 第一个变量是生成的文件名字,之后是对应的源文件。如果不想生成可执行文件,只是想生成一个链接库供别人使用,那么可以用add_library这个命令。使用方法和 add_executable基本一致。下面是一个例子

        add_library(${PROJECT_NAME} SHARED
    src/System.cc
    src/Tracking.cc
    src/LocalMapping.cc
    src/LoopClosing.cc
    src/ORBextractor.cc
    src/ORBmatcher.cc
    src/FrameDrawer.cc
    src/Converter.cc
    src/MapPoint.cc
    src/KeyFrame.cc
    src/Map.cc
    src/MapDrawer.cc
    src/Optimizer.cc
    src/PnPsolver.cc
    src/Frame.cc
    src/KeyFrameDatabase.cc
    src/Sim3Solver.cc
    src/Initializer.cc
    src/Viewer.cc
    )
    

    SHARED是这个库的类型。动态链接库还是静态链接库。
    到这里基本上简单的cmakelist文件已经可以自己写了。下面说说软件库的cmake文件怎么写。就是find_package那个命令会用到的那个文件。

    我们还是以一个例子来做说明。

        # - Find MongoDB
    #  Find the MongoDB includes and client library
    #  This module defines
    #  MongoDB_INCLUDE_DIR, where to find mongo/client/dbclient.h
    #  MongoDB_LIBRARIES, the libraries needed to use MongoDB.
    #  MongoDB_FOUND, If false, do not try to use MongoDB.
    
    if(MongoDB_INCLUDE_DIR AND MongoDB_LIBRARIES)
       set(MongoDB_FOUND TRUE)
    
    else(MongoDB_INCLUDE_DIR AND MongoDB_LIBRARIES)
    
      find_path(MongoDB_INCLUDE_DIR bsoncxx/builder/basic/document.hpp
          /usr/local/include/mongocxx/v_noabi
          /usr/local/include/libbson-1.0
          /usr/local/include/libmongoc-1.0
          /usr/local/include/bsoncxx/v_noabi
          )
    
    if(WIN32)
      find_library(MongoDB_LIBRARIES NAMES mongoclient
          PATHS
          $ENV{ProgramFiles}/Mongo/*/lib
          $ENV{SystemDrive}/Mongo/*/lib
          )
    else(WIN32)
      find_library(MongoDB_LIBRARIES NAMES mongocxx bsoncxx
          PATHS
          /usr/lib
              /usr/lib64
          /usr/lib/mongo
          /usr/lib64/mongo
          /usr/local/lib
          /usr/local/lib64
          /usr/local/lib/mongo
          /usr/local/lib64/mongo
          /opt/mongo/lib
          /opt/mongo/lib64
          )
    endif(WIN32)
    
      if(MongoDB_INCLUDE_DIR AND MongoDB_LIBRARIES)
        set(MongoDB_FOUND TRUE)
        message(STATUS "Found MongoDB: ${MongoDB_INCLUDE_DIR}, ${MongoDB_LIBRARIES}")
      else(MongoDB_INCLUDE_DIR AND MongoDB_LIBRARIES)
        set(MongoDB_FOUND FALSE)
        if (MongoDB_FIND_REQUIRED)
    		message(FATAL_ERROR "MongoDB not found.")
    	else (MongoDB_FIND_REQUIRED)
    		message(STATUS "MongoDB not found.")
    	endif (MongoDB_FIND_REQUIRED)
      endif(MongoDB_INCLUDE_DIR AND MongoDB_LIBRARIES)
    
      mark_as_advanced(MongoDB_INCLUDE_DIR MongoDB_LIBRARIES)
    
    endif(MongoDB_INCLUDE_DIR AND MongoDB_LIBRARIES)
    

    这是一个叫做MongoDB的软件包的cmake文件。
    这个文件会通过设置MongoDB_FOUND变量来告诉cmake这个库有没有找到。这里需要注意的就是find_path这个指令。这个就是用来判断软件是否存在的关键指令。find_path的第一个参数接的是变量名字,之后是文件的名字。后面接一系列这个文件可能存在的路径。也就是软件的可能安装路径。如果在以下路径成功找到这个文件。第一个变量就会被赋值。反之如果找不到,那么这个变量就会被设成False。之后可以用if来判断不同条件下需要执行的语句。大致上就是如此。

    这里只是一些简单的介绍,如果想要更全面的更准确的了解cmake那么可以去看看cmake的官方文档。虽然我觉得那个并不是写出来给人看的。