CMake

作者: 徐凯_xp | 来源:发表于2019-01-02 10:43 被阅读48次

    理论上来说,任意一个c++程序都可以使用g++来编译,但当程序规模越来越大时,一个工程可能有许多的文件夹和源文件,这时输入的编译命令将会越来越长。因此,对于C++使用一些工程管理工具会更加高效。这里我们使用CMake。
    在一个CMake工程中,我们会使用cmake命令生成一个Makefile文件,然后,用make命令根据这个 makefile文件的内容来编译整个工程。

    入门案例:单个源文件
    1. 编写 CMakeLists.txt
      首先编写 CMakeLists.txt 文件,并保存在与 main.cc 源文件同个目录下:
    # 声明要求的cmake最低版本
    cmake_minimum_required (VERSION 2.8)
    # 声明一个cmake工程
    project (HelloSLAM)
    # 添加一个可执行程序
    #语法 :add_executable(程序名 源代码文件)
    add_executable(Demo main.cc)
    

    CMakeLists.txt 文件用于告诉cmake我们要对这个目录下的文件做什么事情,CmakeLists.txt 文件的内容需要遵守cmake的语法。

    编译项目

    在当前目录执行 cmake . ,得到 Makefile 后再使用 make 命令编译得到 Demo1 可执行文件。

    cmake .
    

    得到Makefile后再使用make命令编译得到Demo1可执行文件

    多个源文件

    上面的例子只有单个源文件,现在假如把power函数单独写进一个名为MathFunctions的源文件里,使得这个工程变成如下形式:

    ./Demo2
        |
        +--- main.cc
        |
        +--- MathFunctions.cc
        |
        +--- MathFunctions.h
    

    这个时候,CMakeLists.txt 可以改成如下的形式:

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    # 项目信息
    project (Demo2)
    # 指定生成目标
    add_executable(Demo main.cc MathFunctions.cc)
    

    唯一的改动只是在 add_executable命令中增加了一个 MathFunctions.cc源文件。这样写当然没什么问题,但是如果源文件很多,把所有源文件的名字都加进去将是一件烦人的工作。更省事的方法是使用aux_source_directory命令,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:

    aux_source_directory(<dir> <variable>)
    

    因此,可以修改 CMakeLists.txt 如下:

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    # 项目信息
    project (Demo2)
    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)
    # 指定生成目标
    add_executable(Demo ${DIR_SRCS})
    

    这样,CMake 会将当前目录所有源文件的文件名赋值给变量DIR_SRCS ,再指示变量 DIR_SRCS中的源文件需要编译成一个名称为Demo 的可执行文件。

    多个目录,多个源文件

    现在进一步将 MathFunctions.hMathFunctions.cc文件移动到 math 目录下。

    ./Demo3
        |
        +--- main.cc
        |
        +--- math/
              |
              +--- MathFunctions.cc
              |
              +--- MathFunctions.h
    

    对于这种情况,需要分别在项目根目录 Demo3 和 math 目录里各编写一个 CMakeLists.txt 文件。为了方便,我们可以先将 math 目录里的文件编译成静态库再由 main 函数调用。
    根目录中的 CMakeLists.txt :

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    # 项目信息
    project(Demo3)
    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(.DIR_SRCS)
    # 添加 math 子目录
    add_subdirectory(math)
    # 指定生成目标 
    add_executable(Demo main.cc)
    # 添加链接库
    target_link_libraries(Demo MathFunctions)
    

    该文件添加了下面的内容: 第3行,使用命令 add_subdirectory指明本项目包含一个子目录 math,这样 math 目录下的CMakeLists.txt文件和源代码也会被处理 。第6行,使用命令 target_link_libraries 指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库 。

    子目录中的 CMakeLists.txt:

    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_LIB_SRCS 变量
    aux_source_directory(. DIR_LIB_SRCS)
    # 生成链接库
    add_library (MathFunctions ${DIR_LIB_SRCS})
    

    在该文件中使用命令 add_librarysrc 目录中的源文件编译为静态链接库。

    自定义编译选项

    CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。

    例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。
    修改 CMakeLists 文件
    第一步是在顶层的 CMakeLists.txt 文件中添加该选项:

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    # 项目信息
    project (Demo4)
    # 加入一个配置头文件,用于处理 CMake 对源码的设置
    configure_file (
      "${PROJECT_SOURCE_DIR}/config.h.in"
      "${PROJECT_BINARY_DIR}/config.h"
      )
    # 是否使用自己的 MathFunctions 库
    option (USE_MYMATH
           "Use provided math implementation" ON)
    # 是否加入 MathFunctions 库
    if (USE_MYMATH)
      include_directories ("${PROJECT_SOURCE_DIR}/math")
      add_subdirectory (math)  
      set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
    endif (USE_MYMATH)
    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)
    # 指定生成目标
    add_executable(Demo ${DIR_SRCS})
    target_link_libraries (Demo  ${EXTRA_LIBS})
    

    其中:

    1. 第7行的 configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。
    2. 第13行的 option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON
    3. 第17行根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库。

    修改 main.cc文件

    之后修改 main.cc]文件,让其根据 USE_MYMATH 的预定义值来决定是否调用标准库还是 MathFunctions 库:

    #include 
    #include 
    #include "config.h"
    #ifdef USE_MYMATH
      #include "math/MathFunctions.h"
    #else
      #include 
    #endif
    int main(int argc, char *argv[])
    {
        if (argc < 3){
            printf("Usage: %s base exponent \n", argv[0]);
            return 1;
        }
        double base = atof(argv[1]);
        int exponent = atoi(argv[2]);
        
    #ifdef USE_MYMATH
        printf("Now we use our own Math library. \n");
        double result = power(base, exponent);
    #else
        printf("Now we use the standard library. \n");
        double result = pow(base, exponent);
    #endif
        printf("%g ^ %d is %g\n", base, exponent, result);
        return 0;
    }
    

    编写 config.h.in 文件

    上面的程序值得注意的是第2行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in文件,内容如下:

    #cmakedefine USE_MYMATH
    

    这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件

    编译项目
    现在编译一下这个项目,为了便于交互式的选择该变量的值,可以使用 ccmake 命令

    相关文章

      网友评论

        本文标题:CMake

        本文链接:https://www.haomeiwen.com/subject/quhslqtx.html