cMake简单示例

引言(Introduction)

根据百度百科的解释,CMake是跨平台的免费的开源的构建工具,可以用简单的语句来描述代码在所有平台的构建过程,实现代码自动构建(build automation)的功能。所谓自动构建是指软件自行构造的过程,包括编译计算机源代码为二进制代码,打包二进制代码以及执行自动测试的过程。CMake能够输出各种各样的makefile或者project文件,并且能够测试编译器所支持的C++特性,类似于UNIX下的automake。

需要注意的是,使用CMake构建程序需要经过两个步骤:1. 根据配置文件(CMakeLists.txt)产生标准的构建文件;2. 平台自带的构建工具根据构建文件执行真正的构建过程。从这里可以看出来,CMake真正发挥作用的是第一个步骤,并且其需要能够根据不同的平台,产生不同的构建文件,从而实现跨平台构建的功能。

所以真正需要学习的是CMakeLists.txt文件的编写,该文件也被称为组态档,并且该文件是用CMake language编写的。所以下面首先介绍CMakeLists.txt的一些特性,随后根据一个简单例子介绍CMakeLists.txt编写的要点。

1. CMakeLists.txt

CMakeLists.txt是CMake的核心内容,其内包含cmake的版本信息以及编译器需要的各种信息。CMakeLists.txt是根据一个相对简单,可解释性强的脚本语言进行编写的。该语言支持变量,字符串操作,数组,函数声明以及模块导入的功能。其命令的形式主要如下:

COMMAND(argument ...)

其中参数之间使用空格分开。

变量的取值使用 , 但 是 在 I F 控 制 语 句 中 则 是 直 接 使 用 变 量 名 。 环 境 变 量 使 用 {},但是在IF控制语句中则是直接使用变量名。环境变量使用 IF使使ENV{}方式取值,使用SET(ENV{VAR} VALUE)赋值。

需要注意的就这些,如果后续笔者看到还有别的需要注意的点,笔者会持续进行更新。

2. An Example

本例子来自知乎up主(被包养的程序员丶)的文章,感谢其分享。后续实验是根据官网上进行复现的,官网的tutorial也有Github

2.1 基础

  1. 准备计算平方根的代码,命名为calculatesqrt.cpp;

    // ./calculatesqrt.cpp
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <math.h> 
    int main(int argc, char* argv[]){ 
        if(argc<2){ 
            fprintf(stdout, "Uage: %s number\n", argv[0]); 
            return 1; 
        } 
        double inputValue = atof(argv[1]); 
        double outputValue = sqrt(inputValue); 
        fprintf(stdout, "The square root of %g is %g\n",inputValue, outputValue); 
        return 0; 
    } 
    
  2. 编写相应的CMakeLists.txt文件;

    # ./CMakeLists.txt
    # specify the minimum cmake version
    cmake_minimum_required(VERSION 3.10)
    
    # set the project name
    project(CalculateSqrt)
    
    # add the executable
    add_executable(CalculateSqrt calculatesqrt.cpp)
    
  3. 创建build目录。
    此时得到的目录树如下:
    在这里插入图片描述
    完成上面三步后,进入build目录,执行cmake命令:
    在这里插入图片描述
    这时得到的文件目录如下:
    在这里插入图片描述
    到这一步就已经完成CMake的工作了,但要生成可执行文件还得执行make命令:
    在这里插入图片描述此时得到的目录树如下:
    在这里插入图片描述
    上图中,CalculateSqrt就是得到的可执行文件。执行可执行文件CalculateSqrt可以得到:
    在这里插入图片描述

2.2 为项目添加版本号和可执行文件

在进行这一步前,可以把build目录下面的所有文件都删除,并保留build目录。

  1. 在源代码目录中,添加输入文件CalculateSqrtConfig.h.in作为头文件的配置文件:

    // ./CalculateSqrtConfig.h.in
    // the configured options and settings for CalculateSqrt,
    #define CalculateSqrt_VERSION_MAJOR @CalculateSqrt_VERSION_MAJOR@ 
    #define CalculateSqrt_VERSION_MINOR @CalculateSqrt_VERSION_MINOR@ 
    
  2. 修改calculatesqrt.cpp:

    // ./calculatesqrt.cpp
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string> 
    #include <iostream> 
    #include <math.h> 
    #include "CalculateSqrtConfig.h" 
    
    int main(int argc, char* argv[]){ 
        if(argc<2){ 
            std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." 
                  << CalculateSqrt_VERSION_MINOR << std::endl; 
            fprintf(stdout, "Uage: %s number\n", argv[0]); 
            return 1; 
        } 
        double inputValue = atof(argv[1]); 
        double outputValue = sqrt(inputValue); 
        fprintf(stdout, "The square root of %g is %g\n",inputValue, outputValue); 
        return 0; 
    } 
    
  3. 修改CMakeLists.txt:

    # ./CMakeLists.txt
    # specify the minimum cmake version
    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(CalculateSqrt VERSION 1.0)
    
    # copy a file to another location and modify its contents
    # https://cmake.org/cmake/help/latest/command/configure_file.html
    configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h)
    
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    # add the executable
    add_executable(CalculateSqrt calculatesqrt.cpp)
    
    # add include directories to a target
    target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}")
    
  4. 进入build目录,执行cmake命令,可以生成得到MakeFile文件与CalculateSqrtConfig.h文件:

    自动生成的CalculateSqrtConfig.h文件内容如下:

    // ./build/CalculateSqrtConfig.h
    // the configured options and settings for CalculateSqrt,
    #define CalculateSqrt_VERSION_MAJOR 1 
    #define CalculateSqrt_VERSION_MINOR 0 
    

    可见其是把CMake里面的变量取了出来。
    这一步如果出现

    Cannot specify include directories for target "CalculateSqrt" which is not built by this project.
    

    那么需要修改包含头文件目录的命令为:

    # target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}")
    set_target_properties(CalculateSqrt PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}")
    
  5. 执行make命令得到可执行文件:
    在这里插入图片描述

  6. 执行CalculateSqrt可以得到输出:在这里插入图片描述

    ???这输出不对劲呀,不是cpp文件的内容。贼迷。

2.3 为工程添加链接库lib

在进行这一步前,可以把build目录下面的所有文件都删除,并保留build目录。这一步是链接已有的函数到工程。

  1. 在源代码目录下创建MathFunctions目录,并在其下面定义三个文件MathFunctions.h,mysqrt.cpp,CMakeLists.txt,其内容分别如下:

    // ./MathFunctions/MathFunctions.h
    #include <stdio.h> 
    #include <math.h> 
    
    // declare your sqrt function
    double mysqrt(double);
    

    mysqrt.cpp内容如下

    // ./MathFunctions/mysqrt.cpp
    #include "MathFunctions.h"
    
    double mysqrt(double inputValue){
        return sqrt(inputValue);
    }
    

    CMakeLists.txt如下:

    # ./MathFunctions/CMakeLists.txt
    add_library(MathFunctions mysqrt.cpp) 
    
  2. 修改源代码目录下的CMakeLists.txt文件为:

    # CMakeLists.txt
    # specify the minimum cmake version
    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(CalculateSqrt VERSION 1.0)
    
    # copy a file to another location and modify its contents
    # https://cmake.org/cmake/help/latest/command/configure_file.html
    configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h)
    
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    add_subdirectory(MathFunctions)
    
    # add the executable
    add_executable(CalculateSqrt calculatesqrt.cpp)
    
    # specify libraries of flags to use when linking a given target and/or its dependents
    # https://cmake.org/cmake/help/latest/command/target_link_libraries.html
    target_link_libraries(CalculateSqrt PUBLIC MathFunctions) 
    
    # add include directories to a target
    target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions")
    
  3. 进入build目录下,执行cmake命令可以得到:在这里插入图片描述

    这时候CMakeLists.txt文件生成成功。

  4. 使用make构建可执行文件:在这里插入图片描述

    这时候文件目录的结构为:在这里插入图片描述

  5. 执行可执行文件可以得到正常的结果了:在这里插入图片描述

  6. Plus:为了让MathFunctions库变为可选的,即通过简单修改CMakeLists.txt的文件,达到变更使用MathFunctions的效果,这在大项目里面是非常有用的。
    为了实现lib变为可选的功能,只需要修改源代码目录下的calculatesqrt.cpp与CMakeLists.txt文件即可。最终得到的CMakeLists.txt文件内容如下:

    # ./CMakeLists.txt
    # specify the minimum cmake version
    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(CalculateSqrt VERSION 1.0)
    
    # make the MathFunctions library optional
    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    
    # copy a file to another location and modify its contents
    # https://cmake.org/cmake/help/latest/command/configure_file.html
    configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h)
    
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    if(USE_MYMATH)
      add_subdirectory(MathFunctions)
      # use the variable EXTRA_LIBS to collect up any optional libraries
      list(APPEND EXTRA_LIBS MathFunctions)
      # use the variable EXTRA_INCLUDES to collect up any optional header files
      list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
    endif()
    
    # add the executable
    add_executable(CalculateSqrt calculatesqrt.cpp)
    
    # specify libraries of flags to use when linking a given target and/or its dependents
    # https://cmake.org/cmake/help/latest/command/target_link_libraries.html
    target_link_libraries(CalculateSqrt PUBLIC MathFunctions) 
    
    # add include directories to a target
    target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/MathFunctions")
    

    calculatesqrt.cpp的内容如下:

    // ./calculatesqrt.cpp
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string> 
    #include <iostream> 
    #include <math.h> 
    #include "CalculateSqrtConfig.h" 
    
    #ifdef USE_MYMATH
        #include "MathFunctions.h"
    #endif
    
    int main(int argc, char* argv[]){ 
        if(argc<2){ 
            std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." 
                  << CalculateSqrt_VERSION_MINOR << std::endl; 
            fprintf(stdout, "Uage: %s number\n", argv[0]); 
            return 1; 
        } 
        double inputValue = atof(argv[1]); 
        #ifdef USE_MYMATH
            double outputValue = mysqrt(inputValue); 
            std::cout << "The square root of " << inputValue << " is " 
                << outputValue << " with mysqrt function" << std::endl; 
        #else
            double outputValue = sqrt(inputValue);
            std::cout << "The square root of " << inputValue << " is " 
                << outputValue << std::endl;
        #endif
        return 0; 
    } 
    

2.4 为库添加使用需求

可以通过修改CMakeLists.txt文件来满足库的不同的使用需求,cmake中控制不同库的使用需求的命令为:

下面将从最简单的接口功能介绍这一部分。接口(INTERFACE)是生产者为消费者提供的函数入口,而消费者并不需要知道其内部是如何实现的。这一个功能的控制需要使用到target_include_directories()的命令。

这一部分只需要修改两个文件:"./CMakeLists.txt"与"./MathFunctions/CMakeLists.txt"。

  1. 修改"./MathFunctions/CMakeLists.txt",提供接口:

    # ./MathFunctions/CMakeLists.txt
    add_library(MathFunctions mysqrt.cpp)
    
    target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
    
  2. 修改"./CMakeLists.txt",添加库:

    # ./CMakeLists.txt
    # specify the minimum cmake version
    cmake_minimum_required(VERSION 3.10)
    
    # set the project name and version
    project(CalculateSqrt VERSION 1.0)
    
    # make the MathFunctions library optional
    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    
    # copy a file to another location and modify its contents
    # https://cmake.org/cmake/help/latest/command/configure_file.html
    configure_file(CalculateSqrtConfig.h.in CalculateSqrtConfig.h)
    
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    
    if(USE_MYMATH)
      add_subdirectory(MathFunctions)
      list(APPEND EXTRA_LIBS MathFunctions)
      list(APPEND EXTRA_INCLUDES "${PROJECT_BINARY_DIR}/MathFunctions")
    endif()
    
    # add the executable
    add_executable(CalculateSqrt calculatesqrt.cpp)
    
    # specify libraries of flags to use when linking a given target and/or its dependents
    # https://cmake.org/cmake/help/latest/command/target_link_libraries.html
    target_link_libraries(CalculateSqrt PUBLIC ${EXTRA_LIBS}) 
    
    # add include directories to a target
    target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES})
    
  3. 再回到build目录下执行cmake即可。

2.5 安装与测试

为了把撰写的程序安装到指明的路径中,例如安装到"/usr/local/bin/"目录下,即可以在任意路径下输入脚本文件名执行命令,不需要指明脚本的路径(当然,你可以通过修改bash的环境变量也能实现相同的功能),可以使用make的安装命令。这个实现需要分别在两个CMakeLists.txt文件后面指明工程生成的目的地。

  1. 修改"./MathFunctions/CMakeLists.txt",指明目的地:

    # ./MathFunctions/CMakeLists.txt
    add_library(MathFunctions mysqrt.cpp)
    
    target_include_directories(MathFunctions INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
    
    install(TARGETS MathFunctions DESTINATION lib)
    install(FILES MathFunctions.h DESTINATION include)
    
  2. 修改"./CMakeLists.txt",指明目的地:

    # ./CMakeLists.txt
    # .
    # .
    # .
    # add include directories to a target
    target_include_directories(CalculateSqrt PUBLIC "${PROJECT_BINARY_DIR}" ${EXTRA_INCLUDES})
    
    # add install rule
    install(TARGETS CalculateSqrt DESTINATION bin)
    install(FILES "${PROJECT_BINARY_DIR}/CalculateSqrtConfig.h"
      DESTINATION include
      )
    
  3. 回到build目录下,依次执行build, make, make install。最后make install输出的结果依次为:在这里插入图片描述

以上即实现了安装的功能。为了测试编写文件是否能够运行成功,可以在CMakeLists.txt中指明测试的场景。测试这一部分只需要在"./CMakeLists.txt"添加测试命令即可。

# ./CMakeLists.txt
# .
# .
# .
# enable testing
enable_testing()

# does the application run
add_test(NAME Runs COMMAND CalculateSqrt 25)

# does the usage message work?
add_test(NAME Usage COMMAND CalculateSqrt)
set_tests_properties(Usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
  )

# define a function to simplify adding tests
# do_test: funciton name, target: test command(CalculateSqrt)
# arg: input args, result: error message
function(do_test target arg result)
  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
  set_tests_properties(Comp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endfunction(do_test)

# do a bunch of result based tests
do_test(CalculateSqrt 4 "4 is 2")
do_test(CalculateSqrt 9 "9 is 3")
do_test(CalculateSqrt 5 "5 is 2.236")
do_test(CalculateSqrt 7 "7 is 2.645")
do_test(CalculateSqrt 25 "25 is 5")
do_test(CalculateSqrt -25 "-25 is [-nan|nan|0]")
do_test(CalculateSqrt 0.0001 "0.0001 is 0.01")

然后重新构建项目,执行make生成二进制文件。然后使用ctest命令进行测试:
在这里插入图片描述

这里一共添加了9个测试示例,前两个测试的命令分别为“CalculateSqrt 25”与"CalculateSqrt"。当测试示例的返回值为0时,该示例通过。所以这9个示例中,只有第二个示例是没有通过的。

注意:为了完成后续实验,要记得把"/usr/local/include/"的“CalculateSqrtConfig.h”与“/usr/local/bin/”的"CalculateSqrt"给删除,如果不删除的话,后续会一直使用这两个文件。

2.6 系统自检

本实验是为了测试系统中是否自带某个函数,如本例实验中测试“math.h”中是否具有"log"与"exp"函数。这需要修改“./CMakeLists.txt”文件,和头文件的配置文件"CalculateSqrtConfig.h.in",以及自己定义的"mysqrt.cpp"。

  1. 在“./CMakeLists.txt”添加检查语句:

    # ./CMakeLists.txt
    # adding system introspection
    include(CheckSymbolExists)
    set(CMAKE_REQUIRED_LIBRARIES "m")
    # HAVE_LOG = 1 if log in file "math.h"
    check_symbol_exists(log "math.h" HAVE_LOG)
    check_symbol_exists(exp "math.h" HAVE_EXP)
    
  2. 在"./CalculateSqrtConfig.h.in"添加包含语句:

    // ./CalculateSqrtConfig.h.in
    // does the platform provide exp and log functions?
    #cmakedefine HAVE_LOG
    #cmakedefine HAVE_EXP
    
  3. 在"./MathFunctions/mysqrt.cpp"包含生成的头文件"CalculateSqrtConfig.h":

    // ./MathFunctions/mysqrt.cpp
    #include "CalculateSqrtConfig.h"
    

然后重新cmake,make以及执行,发现平台并没有包含log与exp函数。虽然它们确实存在于"math.h"文件中。查看头文件"CalculateSqrtConfig.h",发现确实没有定义:

// ./build/CalculateSqrtConfig.h
#define USE_MYMATH

// does the platform provide exp and log functions?
/* #undef HAVE_LOG */
/* #undef HAVE_EXP */

Plus:如果不想在"./MathFunctions/mysqrt.cpp"包含生成的头文件"CalculateSqrtConfig.h",可以在“./CMakeLists.txt”添加以下语句,将定义的宏"HAVE_LOG"和"HAVE_EXP"直接生成到目标"MathFunctions"中。记住,这一段要加在"add_subdirectory(MathFunctions)"语句之后,否则cmake是看不到"MathFunctions"这个项目的:

# ./CMakeLists.txt
if(HAVE_LOG AND HAVE_EXP)
  target_compile_definitions(MathFunctions
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()

2.7 增加用户命令并产生文件

这个实验中,添加一个"./MathFunctions/MakeTable.cpp"文件,用于生成一个记录19的平方根的表格。当输入的数位于19时,会根据该表格计算平方根,否则调用sqrt函数。

在本实验开始之前,一样需要先删除"./build"目录下的所有文件,可以在"./MathFunctions/CMakeLists.txt"与"./MathFunctions/mysqrt.cpp"中删除检查"log"与"exp"符号的命令。

  1. 创建"./MathFunctions/MakeTable.cpp"文件,其内容如下:

    // ./MathFunctions/MakeTable.cpp
    // A simple program that builds a sqrt table
    #include <stdio.h>
    #include <math.h>
    
    int main (int argc, char *argv[])
    {
      int i;
      double result;
    
      // make sure we have enough arguments
      if (argc < 2)
        {
        return 1;
        }
    
      // open the output file
      FILE *fout = fopen(argv[1],"w");
      if (!fout)
        {
        return 1;
        }
    
      // create a source file with a table of square roots
      fprintf(fout,"double sqrtTable[] = {\n");
      for (i = 0; i < 10; ++i)
        {
        result = sqrt(static_cast<double>(i));
        fprintf(fout,"%g,\n",result);
        }
    
      // close the table with a zero
      fprintf(fout,"0};\n");
      fclose(fout);
      return 0;
    }
    
  2. 在"./MathFunctions/CMakeLists.txt"中添加生成可执行文件"MakeTable":

    # ./MathFunctions/CMakeLists.txt
    # add MakeTable as another executable
    add_executable(MakeTable MakeTable.cxx)
    
  3. 在"./MathFunctions/CMakeLists.txt"中添加通过执行"MakeTable"生成“Table.h”的命令:

    # ./MathFunctions/CMakeLists.txt
    # add custom command to produce Table.h
    add_custom_command(
      OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
      COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
      DEPENDS MakeTable)
    
  4. 为了告诉CMake,"./MathFunctions/mysqrt.cpp"依赖生成的"Table.h"文件,需要"./MathFunctions/CMakeLists.txt"添加library:

    # ./MathFunctions/CMakeLists.txt
    # add library path
    add_library(MathFunctions mysqrt.cpp ${CMAKE_CURRENT_BINARY_DIR}/Table.h)
    
  5. 为了让"./MathFunctions/mysqrt.cpp"能看到生成的"Table.h"文件,需要把当前二进制路径加入到include目录中:

    # ./MathFunctions/CMakeLists.txt
    target_include_directories(MathFunctions
        INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
        PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
    
  6. 修改"./calculatesqrt.cpp"文件,使用生成的"sqrtTable"表格:

    // ./calculatesqrt.cpp
    int main(int argc, char* argv[]){ 
        if(argc<2){ 
            std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." 
                  << CalculateSqrt_VERSION_MINOR << std::endl; 
            fprintf(stdout, "Uage: %s number\n", argv[0]); 
            return 1; 
        } 
        double inputValue = atof(argv[1]); 
        #ifdef USE_MYMATH
            double outputValue = mysqrt(inputValue); 
            std::cout << "The square root of " << inputValue << " is " 
                << outputValue << " with mysqrt function" << std::endl; 
        #else
            double outputValue = sqrt(inputValue);
            std::cout << "The square root of " << inputValue << " is " 
                << outputValue << std::endl;
        #endif
        return 0; 
    }
    
  7. 回到build目录下,执行cmake,make即可。最终得到的文件目录如下:在这里插入图片描述

2.8 构建安装程序

当需要把项目打包分享给其他人时,可以同时提供不同平台的二进制文件与源文件。这与2.5的构建是不同的,在2.5中,我们是将可执行文件安装到指定路径,并将生成的头文件复制到指定路径;而在这个实验中,则是构建支持二进制安装与包管理的安装包(installation packages that support binary installations and package management features)。为了实现这一点,需要使用CPack。

  1. 使用CPack只需要在"./CMakeLists.txt"文件的底部加上:

    # ./CMakeLists.txt"
    # include any runtime libraries that are needed by the project for the current platform
    include(InstallRequiredSystemLibraries)
    # set some CPack variables to where we have stored the license and version information for this project.
    set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
    set(CPACK_PACKAGE_VERSION_MAJOR "${CalculateSqrt_VERSION_MAJOR}")
    set(CPACK_PACKAGE_VERSION_MINOR "${CalculateSqrt_VERSION_MINOR}")
    include(CPack)
    
  2. 执行cmake,然后执行cpack构建二进制版本,生成的结果如下:在这里插入图片描述

    红色方块的文件代表执行cpack打包生成的文件。

2.9 添加可视化仪表

本实验将测试用例的结果用可视化仪表来展示。

  1. 修改"./CMakeLists.txt"中的"enable_testing()“为"include(CTest)”:

    # ./CMakeLists.txt"
    # enable testing
    # enable_testing()
    # enable dashboard scripting
    include(CTest)
    
  2. 创建"./CTestConfig.cmake"文件:

    # ./CTestConfig.cmake
    set(CTEST_PROJECT_NAME "CalculateSqrt")
    set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")
    
    set(CTEST_DROP_METHOD "http")
    set(CTEST_DROP_SITE "my.cdash.org")
    set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
    set(CTEST_DROP_SITE_CDASH TRUE)
    

    这里官网提供了dash的脚本。

  3. 执行build,然后执行以下命令:

    ctest [-VV] -C Debug -D Experimental
    

    可以得到结果如下:
    在这里插入图片描述

    转到官网提供的网站可以看到:

在这里插入图片描述

2.10 构建共享库

本实验中,主要介绍使用CMake生成共享链接库。这主要通过设置camke的变量"BUILD_SHARED_LIBS"来设置库的类型。

  1. 修改"./CMakeLists.txt"文件,添加"BUILD_SHARED_LIBS"变量:

    # ./CMakeLists.txt
    # ...
    set(CMAKE_CXX_STANDARD 11)
    set(CMAKE_CXX_STANDARD_REQUIRED True)
    # control where the static and shared libraries are built so that on windows
    # we don't need to tinker with the path to run the executable
    set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
    set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
    set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
    
    option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
    # ...
    
  2. 因为"./mysqrt.cpp"中经常使用到"MathFunctions",所以可以将其生成一个静态库"SqrtLibrary",修改"./MathFunctions/CMakeLists.txt":

    # ./MathFunctions/CMakeLists.txt
    # add the library that runs
    add_library(MathFunctions mysqrt.cpp)
    
    # state that anybody linking to us needs to include the current source dir
    # to find MathFunctions.h, while we don't.
    target_include_directories(MathFunctions
                               INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                               )
    
    # should we use our own math functions
    option(USE_MYMATH "Use tutorial provided math implementation" ON)
    if(USE_MYMATH)
    
      target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")
    
      # first we add the executable that generates the table
      add_executable(MakeTable MakeTable.cpp)
    
      # add the command to generate the source code
      add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
        COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
        DEPENDS MakeTable
        )
    
      # library that just does sqrt
      add_library(SqrtLibrary STATIC
                  mysqrt.cpp
                  ${CMAKE_CURRENT_BINARY_DIR}/Table.h
                  )
    
      # state that we depend on our binary dir to find Table.h
      target_include_directories(SqrtLibrary PRIVATE
                                 ${CMAKE_CURRENT_BINARY_DIR}
                                 )
    
      # state that SqrtLibrary need PIC when the default is shared libraries
      set_target_properties(SqrtLibrary PROPERTIES
      POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
      )
      
      target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
    endif()
    
    # define the symbol stating we are using the declspec(dllexport) when
    # building on windows
    target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")
    
    # install rules
    install(TARGETS MathFunctions DESTINATION lib)
    install(FILES MathFunctions.h DESTINATION include)
    
  3. 更新"./MathFunctions/mysqrt.cpp"文件,使用"mathfunctions"与"deltail"命名空间:

    // ./MathFunctions/mysqrt.cpp
    #include <iostream>
    #include <cmath>
    
    #include "MathFunctions.h"
    
    // include the generated table
    #include "Table.h"
    
    namespace mathfunctions {
    namespace detail {
    // a hack square root calculation using simple operations
    double mysqrt(double x)
    {
      if (x <= 0) {
        return 0;
      }
    
      // use the table to help find an initial value
      double result = x;
      if (x >= 1 && x < 10) {
        std::cout << "Use the table to help find an initial value " << std::endl;
        result = sqrtTable[static_cast<int>(x)];
      }else{
            return sqrt(x);
      }
      // do ten iterations
      for (int i = 0; i < 10; ++i) {
        if (result <= 0) {
          result = 0.1;
        }
        double delta = x - (result * result);
        result = result + 0.5 * delta / result;
        std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
      }
    
      return result;
    }
    }
    }
    
  4. 修改"./calculatesqrt.cpp"文件,使其包含"MathFunctions.h"并使用"mathfunctions::mysqrt()":

    // ./calculatesqrt.cpp
    #include "MathFunctions.h"
    
    int main(int argc, char* argv[]){ 
        if(argc<2){ 
            std::cout << argv[0] << " Version " << CalculateSqrt_VERSION_MAJOR << "." 
                  << CalculateSqrt_VERSION_MINOR << std::endl; 
            fprintf(stdout, "Uage: %s number\n", argv[0]); 
            return 1; 
        } 
        double inputValue = atof(argv[1]); 
        double outputValue = mathfunctions::mysqrt(inputValue); 
        std::cout << "The square root of " << inputValue << " is " 
            << outputValue << " with mysqrt function" << std::endl; 
        return 0; 
    }
    
  5. 最终更新"./MathFunctions/MathFunctions.h"文件,定义DECLSPEC:

    // ./MathFunctions/MathFunctions.h
    #if defined(_WIN32)
    #  if defined(EXPORTING_MYMATH)
    #    define DECLSPEC __declspec(dllexport)
    #  else
    #    define DECLSPEC __declspec(dllimport)
    #  endif
    #else // non windows
    #  define DECLSPEC
    #endif
    
    namespace mathfunctions {
    double DECLSPEC sqrt(double x);
    }
    

总结(Conclusion)

本文是关于CMake教程的一个学习笔记。较大的项目往往包含众多模块,如果要自己写makefile,那么各个模块之间的编译与包含关系足够让我们头疼的。所以借助CMake较为简单的写法,可以帮助我们生成跨平台的makefile文件,使得工程能够在不同的平台上编译。笔者对于cmake认知尚浅,所以文中可能有所纰漏,望体谅。

参考资料(Reference)

CMake-Wikipedia

Build automation-Wikipedia

cmake和CMakeLists.txt的学习

“轻松搞定CMake”系列之CMakeLists文件编写语法规则详解

CMake教程(一)

CMake Tutorial

`

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值