cmake教程

 

基本起点(第一步)

最基本的项目是从源代码文件构建的可执行文件。对于简单项目,只需要两行CMakeLists.txt文件。这将是本教程的起点。CMakeLists.txt文件如下所示:

cmake_minimum_required(版本2.6)
项目(教程)
add_executable(Tutorial tutorial.cxx)

 

请注意,此示例在CMakeLists.txt文件中使用小写命令。CMake支持大写,小写和大小写混合命令。tutorial.cxx的源代码将计算数字的平方根,并且其第一个版本非常简单,如下所示:

//一个简单的程序,用于计算数字的平方根
#include 
#include 
#include 
int main(int argc,char * argv [])
{
  如果(argc <2)
    {
    fprintf(stdout,“用法:%s号\ n”,argv [0]);
    返回1;
    }
  double inputValue = atof(argv [1]);
  double outputValue = sqrt(inputValue);
  fprintf(stdout,“%g的平方根为%g \ n”,
          inputValue,outputValue);
  返回0;
}

 

添加版本号和配置的头文件

我们将添加的第一个功能是为我们的可执行文件和项目提供版本号。虽然您可以在源代码中专门执行此操作,但是在CMakeLists.txt文件中执行此操作可以提供更大的灵活性。要添加版本号,我们修改CMakeLists.txt文件,如下所示:

cmake_minimum_required(版本2.6)
项目(教程)
#版本号。
设置(Tutorial_VERSION_MAJOR 1)
设置(Tutorial_VERSION_MINOR 0)
 
#配置头文件以传递某些CMake设置
#到源代码
configure_file(
  “ $ {PROJECT_SOURCE_DIR} /TutorialConfig.h.in”
  “ $ {PROJECT_BINARY_DIR} /TutorialConfig.h”
  )
 
#将二叉树添加到包含文件的搜索路径中
#,以便我们找到TutorialConfig.h
include_directories(“ $ {PROJECT_BINARY_DIR}”)
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)

 

由于已配置的文件将被写入二进制树,因此我们必须将该目录添加到路径列表中以搜索包含文件。然后,我们在源代码树中创建一个TutorialConfig.h.in文件,其内容如下:

//为教程配置的选项和设置
#define Tutorial_VERSION_MAJOR @ Tutorial_VERSION_MAJOR @
#define Tutorial_VERSION_MINOR @ Tutorial_VERSION_MINOR @

 

当CMake配置此头文件时,@ Tutorial_VERSION_MAJOR @和@ Tutorial_VERSION_MINOR @的值将被CMakeLists.txt文件中的值替换。接下来,我们修改tutorial.cxx以包括配置的头文件并利用版本号。产生的源代码在下面列出。

//一个简单的程序,用于计算数字的平方根
#include 
#include 
#include 
#include“ TutorialConfig.h”
 
int main(int argc,char * argv [])
{
  如果(argc <2)
    {
    fprintf(stdout,“%s版本%d。%d \ n”,
            argv [0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,“用法:%s号\ n”,argv [0]);
    返回1;
    }
  double inputValue = atof(argv [1]);
  double outputValue = sqrt(inputValue);
  fprintf(stdout,“%g的平方根为%g \ n”,
          inputValue,outputValue);
  返回0;
}

主要更改是包括TutorialConfig.h头文件,并打印出版本号作为用法消息的一部分。

 

添加库(步骤2)

现在,我们将库添加到我们的项目中。该库将包含我们自己的实现,用于计算数字的平方根。然后可执行文件可以使用此库,而不是使用编译器提供的标准平方根函数。在本教程中,我们将库放入名为MathFunctions的子目录中。它将具有以下一行CMakeLists.txt文件:

add_library(MathFunctions mysqrt.cxx)

源文件mysqrt.cxx具有一个名为mysqrt的函数,该函数提供与编译器的sqrt函数类似的功能。为了使用新库,我们在顶层CMakeLists.txt文件中添加了一个add_subdirectory调用,以便构建该库。我们还添加了另一个include目录,以便可以为函数原型找到MathFunctions / MathFunctions.h头文件。最后的更改是将新库添加到可执行文件。顶级CMakeLists.txt文件的最后几行现在看起来像:

include_directories(“ $ {PROJECT_SOURCE_DIR} / MathFunctions”)
add_subdirectory(MathFunctions) 
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(数学函数教程)

现在让我们考虑使MathFunctions库成为可选的。在本教程中,确实没有任何理由这样做,但是对于大型库或依赖第三方代码的库,您可能想要这样做。第一步是向顶层CMakeLists.txt文件添加一个选项。

#我们应该使用自己的数学函数吗?
选项(USE_MYMATH 
        “使用教程提供的数学实现”打开) 

这将显示在CMake GUI中,默认值为ON,用户可以根据需要进行更改。此设置将存储在缓存中,因此用户无需在每次在此项目上运行CMake时都保持设置。下一个更改是使MathFunctions库的建立和链接成为条件。为此,我们将顶级CMakeLists.txt文件的结尾更改为如下所示:

#添加MathFunctions库?
#
如果(USE_MYMATH)
  include_directories(“ $ {PROJECT_SOURCE_DIR} / MathFunctions”)
  add_subdirectory(MathFunctions)
  设置(EXTRA_LIBS $ {EXTRA_LIBS} MathFunctions)
endif(USE_MYMATH)
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(教程$ {EXTRA_LIBS})

这使用USE_MYMATH的设置来确定是否应编译和使用MathFunction。请注意,使用变量(在这种情况下为EXTRA_LIBS)收集所有可选库,以供以后链接到可执行文件中。这是用于保持大型项目和许多可选组件清洁的常用方法。对源代码的相应更改相当简单,让我们拥有:

//一个简单的程序,用于计算数字的平方根
#include 
#include 
#include 
#include“ TutorialConfig.h”
#ifdef USE_MYMATH
#include“ MathFunctions.h”
#万一
 
int main(int argc,char * argv [])
{
  如果(argc <2)
    {
    fprintf(stdout,“%s版本%d。%d \ n”,argv [0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,“用法:%s号\ n”,argv [0]);
    返回1;
    }
 
  double inputValue = atof(argv [1]);
 
#ifdef USE_MYMATH
  double outputValue = mysqrt(inputValue);
#其他
  double outputValue = sqrt(inputValue);
#万一
 
  fprintf(stdout,“%g的平方根为%g \ n”,
          inputValue,outputValue);
  返回0;
}

在源代码中,我们也使用USE_MYMATH。通过在配置文件中添加以下行,可以通过TutorialConfig.h.in配置文件将其从CMake提供给源代码:

#cmakedefine USE_MYMATH

 

安装和测试(步骤3)

下一步,我们将为我们的项目添加安装规则和测试支持。安装规则相当简单。对于MathFunctions库,我们通过将以下两行添加到MathFunctions的CMakeLists.txt文件中来设置要安装的库和头文件:

安装(TARGETS MathFunctions DESTINATION bin)
安装(包含文件MathFunctions.h目的地)

对于应用程序,将以下行添加到顶级CMakeLists.txt文件中,以安装可执行文件和配置的头文件:

#添加安装目标
安装(TARGETS教程的DESTINATION bin)
安装(文件“ $ {PROJECT_BINARY_DIR} /TutorialConfig.h”        
         目的地包括)

这就是全部。此时,您应该能够构建该教程,然后键入make install(或从IDE构建INSTALL目标),它将安装适当的头文件,库和可执行文件。CMake变量CMAKE_INSTALL_PREFIX用于确定文件的安装根目录。添加测试也是一个相当简单的过程。在顶级CMakeLists.txt文件的末尾,我们可以添加一些基本测试来验证应用程序是否正常运行。

包括(CTest)

#应用程序是否运行
add_test(TutorialRun教程25)
#25的平方根
add_test(TutorialComp25教程25)
set_tests_properties(TutorialComp25属性PASS_REGULAR_EXPRESSION“ 25为5”)
#是否处理负数
add_test(TutorialNegative Tutorial -25)
set_tests_properties(教程负属性PASS_REGULAR_EXPRESSION“ -25为0”)
#它处理小数字吗
add_test(教程小教程0.0001)
set_tests_properties(TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION“ 0.0001是0.01”)
#使用情况信息有效吗?
add_test(教程用法教程)
set_tests_properties(教程用法属性PASS_REGULAR_EXPRESSION“用法:。*数字”)

构建完成后,可以运行“ ctest”命令行工具来运行测试。第一个测试只是验证应用程序正在运行,没有段错误或其他崩溃,并且返回值为零。这是CTest测试的基本形式。接下来的几个测试都利用PASS_REGULAR_EXPRESSION测试属性来验证测试的输出是否包含某些字符串。在这种情况下,请验证计算出的平方根是否应该是正确的,并在提供错误数量的参数时打印用法消息。如果您想添加很多测试来测试不同的输入值,则可以考虑创建一个如下所示的宏:

#定义宏以简化添加测试,然后使用它
宏(do_test arg结果)
  add_test(TutorialComp $ {arg}教程$ {arg})
  set_tests_properties(TutorialComp $ {arg}
    PROPERTIES PASS_REGULAR_EXPRESSION $ {result})
endmacro(do_test)
 
#做一系列基于结果的测试
do_test(25“ 25是5”)
do_test(-25“ -25为0”)

对于do_test的每次调用,都会根据传递的参数将另一个测试以及其名称,输入和结果添加到项目中。

 

添加系统自省(步骤4)

接下来,让我们考虑将一些代码添加到我们的项目中,这取决于目标平台可能不具备的功能。对于此示例,我们将添加一些代码,具体取决于目标平台是否具有log和exp函数。当然,几乎每个平台都具有这些功能,但对于本教程而言,假定它们并不常见。如果平台有日志,那么我们将使用该日志来计算mysqrt函数中的平方根。我们首先使用顶级CMakeLists.txt文件中的CheckFunctionExists.cmake宏测试这些功能的可用性,如下所示:

#该系统是否提供日志和exp功能?
包括(CheckFunctionExists)
check_function_exists(记录HAVE_LOG)
check_function_exists(exp HAVE_EXP)

接下来,如果CMake在平台上找到它们,则修改TutorialConfig.h.in以定义这些值,如下所示:

//平台是否提供exp和log功能?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

在TutorialConfig.h的configure_file命令之前完成对log和exp的测试非常重要。configure_file命令使用CMake中的当前设置立即配置文件。最后,在mysqrt函数中,如果可以使用以下代码在系统上可用,则可以基于log和exp提供备用实现:

//如果我们同时拥有log和exp,则使用它们
#如果已定义(HAVE_LOG)&&已定义(HAVE_EXP)
  结果= exp(log(x)* 0.5);
#else //否则使用迭代方法
  。。。

 

添加生成的文件和生成器(步骤5)

在本节中,我们将展示如何将生成的源文件添加到应用程序的构建过程中。对于此示例,我们将在构建过程中创建一个预先计算的平方根表,然后将该表编译到我们的应用程序中。为此,我们首先需要一个将生成表的程序。在MathFunctions子目录中,一个名为MakeTable.cxx的新源文件将执行此操作。

//一个构建sqrt表的简单程序 
#include 
#include 
#include 
 
int main(int argc,char * argv [])
{
  我
  双重结果
 
  //确保我们有足够的参数
  如果(argc <2)
    {
    返回1;
    }
  
  //打开输出文件
  文件* fout = fopen(argv [1],“ w”);
  如果(!fout)
    {
    返回1;
    }
  
  //创建具有平方根表的源文件
  fprintf(fout,“ double sqrtTable [] = {\ n”);
  对于(i = 0; i <10; ++ i)
    {
    结果= sqrt(static_cast (i));
    fprintf(fout,“%g,\ n”,result);
    }
 
  //用零关闭表格
  fprintf(fout,“ 0}; \ n”);
  fclose(fout);
  返回0;
}

请注意,该表是作为有效的C ++代码生成的,用于将输出写入其中的文件名将作为参数传入。下一步是将适当的命令添加到MathFunctions的CMakeLists.txt文件中,以构建MakeTable可执行文件,然后在构建过程中运行它。需要一些命令来完成此操作,如下所示。

#首先,我们添加生成表的可执行文件
add_executable(MakeTable MakeTable.cxx)
 
#添加命令以生成源代码
add_custom_command(
  输出$ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  命令MakeTable $ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  依赖MakeTable
  )
 
#将二叉树目录添加到搜索路径 
#包含文件
include_directories($ {CMAKE_CURRENT_BINARY_DIR})
 
#添加主库
add_library(MathFunctions mysqrt.cxx $ {CMAKE_CURRENT_BINARY_DIR} /Table.h)

首先,添加MakeTable的可执行文件,就像添加任何其他可执行文件一样。然后,我们添加一个自定义命令,该命令指定如何通过运行MakeTable来产生Table.h。接下来,我们必须让CMake知道mysqrt.cxx取决于生成的文件Table.h。这是通过将生成的Table.h添加到库MathFunctions的源列表中来完成的。我们还必须将当前的二进制目录添加到包含目录列表中,以便mysqrt.cxx可以找到并包含Table.h。构建此项目时,它将首先构建MakeTable可执行文件。然后它将运行MakeTable来生成Table.h。最后,它将编译包括Table.h的mysqrt.cxx,以生成MathFunctions库。此时,具有我们添加的所有功能的顶级CMakeLists.txt文件如下所示:

cmake_minimum_required(版本2.6)
项目(教程)
包括(CTest)
 
#版本号。
设置(Tutorial_VERSION_MAJOR 1)
设置(Tutorial_VERSION_MINOR 0)
 
#该系统是否提供日志和exp功能?
包括($ {CMAKE_ROOT} /Modules/CheckFunctionExists.cmake)
 
check_function_exists(记录HAVE_LOG)
check_function_exists(exp HAVE_EXP)
 
#我们应该使用自己的数学函数
选项(USE_MYMATH 
  “使用教程提供的数学实现”打开)
 
#配置头文件以传递某些CMake设置
#到源代码
configure_file(
  “ $ {PROJECT_SOURCE_DIR} /TutorialConfig.h.in”
  “ $ {PROJECT_BINARY_DIR} /TutorialConfig.h”
  )
 
#将二叉树添加到包含文件的搜索路径中
#,以便我们找到TutorialConfig.h
include_directories(“ $ {PROJECT_BINARY_DIR}”)
 
#添加MathFunctions库?
如果(USE_MYMATH)
  include_directories(“ $ {PROJECT_SOURCE_DIR} / MathFunctions”)
  add_subdirectory(MathFunctions)
  设置(EXTRA_LIBS $ {EXTRA_LIBS} MathFunctions)
endif(USE_MYMATH)
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(教程$ {EXTRA_LIBS})
 
#添加安装目标
安装(TARGETS教程的DESTINATION bin)
安装(文件“ $ {PROJECT_BINARY_DIR} /TutorialConfig.h”        
         目的地包括)
 
#应用程序是否运行
add_test(TutorialRun教程25)
 
#使用情况信息有效吗?
add_test(教程用法教程)
set_tests_properties(TutorialUsage
  性质 
  PASS_REGULAR_EXPRESSION“用法:。*数字”
  )
 
 
#定义宏以简化添加测试
宏(do_test arg结果)
  add_test(TutorialComp $ {arg}教程$ {arg})
  set_tests_properties(TutorialComp $ {arg}
    PROPERTIES PASS_REGULAR_EXPRESSION $ {结果}
    )
endmacro(do_test)
 
#做一系列基于结果的测试
do_test(4“ 4是2”)
do_test(9“ 9是3”)
do_test(5“ 5是2.236”)
do_test(7“ 7是2.645”)
do_test(25“ 25是5”)
do_test(-25“ -25为0”)
do_test(0.0001“ 0.0001是0.01”)

TutorialConfig.h.in看起来像:

//为教程配置的选项和设置
#define Tutorial_VERSION_MAJOR @ Tutorial_VERSION_MAJOR @
#define Tutorial_VERSION_MINOR @ Tutorial_VERSION_MINOR @
#cmakedefine USE_MYMATH
 
//平台是否提供exp和log功能?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

MathFunctions的CMakeLists.txt文件如下所示:

#首先,我们添加生成表的可执行文件
add_executable(MakeTable MakeTable.cxx)
#添加命令以生成源代码
add_custom_command(
  输出$ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  依赖MakeTable
  命令MakeTable $ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  )
#将二叉树目录添加到搜索路径 
#包含文件
include_directories($ {CMAKE_CURRENT_BINARY_DIR})
 
#添加主库
add_library(MathFunctions mysqrt.cxx $ {CMAKE_CURRENT_BINARY_DIR} /Table.h)
 
安装(TARGETS MathFunctions DESTINATION bin)
安装(包含文件MathFunctions.h目的地)

 

 

生成安装程序(步骤6)

接下来,假设我们想将项目分发给其他人,以便他们可以使用它。我们希望在各种平台上提供二进制和源代码分发。这与我们先前在安装和测试(第3步)一节中所做的安装有些不同,在安装和测试(第3步)中,我们安装了从源代码构建的二进制文件。在此示例中,我们将构建支持二进制安装和cygwin,debian,RPM等中的软件包管理功能的安装软件包。为实现此目的,我们将使用CPack来创建平台特定的安装程序,如使用CPack打包一章中所述。具体来说,我们需要在顶级CMakeLists.txt文件的底部添加几行。

#构建一个CPack驱动的安装程序包
包括(InstallRequiredSystemLibraries)
设置(CPACK_RESOURCE_FILE_LICENSE  
     “ $ {CMAKE_CURRENT_SOURCE_DIR} /License.txt”)
设置(CPACK_PACKAGE_VERSION_MAJOR“ $ {Tutorial_VERSION_MAJOR}”)
设置(CPACK_PACKAGE_VERSION_MINOR“ $ {Tutorial_VERSION_MINOR}”)
包含(CPack)

这就是全部。我们首先包括InstallRequiredSystemLibraries。该模块将包括项目当前平台所需的任何运行时库。接下来,我们将一些CPack变量设置为存储该项目的许可证和版本信息的位置。版本信息利用了我们在本教程前面设置的变量。最后,我们包括CPack模块,该模块将使用这些变量和您要安装的系统的其他属性。

下一步是以常规方式构建项目,然后在其上运行CPack。要构建二进制发行版,请运行:

cpack --config CPackConfig.cmake

要创建源代码发行版,请输入

cpack --config CPackSourceConfig.cmake

 

添加对仪表板的支持(步骤7)

添加支持以将测试结果提交到仪表板非常容易。在本教程的较早步骤中,我们已经为项目定义了许多测试。我们只需要运行这些测试并将其提交到仪表板即可。为了包括对仪表板的支持,我们在顶层CMakeLists.txt文件中包含了CTest模块。

#启用仪表板脚本
包含(CTest)

我们还创建了一个CTestConfig.cmake文件,可以在其中为仪表板指定该项目的名称。

设置(CTEST_PROJECT_NAME“教程”)

CTest将在运行时读入该文件。要创建一个简单的仪表板,您可以在项目上运行CMake,将目录更改为二进制树,然后运行ctest –D实验。仪表板的结果将在此处上传到Kitware的公共仪表板。

你可能感兴趣的:(C++)