cmake基础使用步骤

Posted on 2017-09-14 09:15  精神病了  阅读(262)  评论(0)    收藏  举报

下面是一个分步教程,介绍CMake有助于解决的常见构建系统问题。将这些主题中的许多主题引入到掌握CMake中,作为单独的问题,但是在一个示例项目中看到它们如何协同工作都是非常有帮助的。本教程可以在CMake源代码树Tests / Tutorial目录中找到每个步骤都有自己的子目录,其中包含该步骤的教程的完整副本

 

基本起点(Step1)

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

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

 

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

//一个计算数字平方根的简单程序
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc,char * argv [])
{
  if(argc <2)
    {
    fprintf(stdout,“Usage:%s number \ 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(VERSION 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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#include“TutorialConfig.h”
 
int main(int argc,char * argv [])
{
  if(argc <2)
    {
    fprintf(stdout,“%s版本%d。%d \ n”,
            的argv [0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,“Usage:%s number \ 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(MathFunction) 
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial MathFunctions)

现在让我们考虑使MathFunctions库可选。在本教程中,实际上没有任何理由这样做,但是使用更多的依赖第三方代码的库或库可能需要。第一步是向顶级的CMakeLists.txt文件添加一个选项。

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

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

#添加MathFunctions库?
if(USE_MYMATH)
  include_directories(“$ {PROJECT_SOURCE_DIR} / MathFunctions”)
  add_subdirectory(MathFunction)
  set(EXTRA_LIBS $ {EXTRA_LIBS} MathFunctions)
endif(USE_MYMATH)
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial $ {EXTRA_LIBS})

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

//一个计算数字平方根的简单程序
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include“TutorialConfig.h”
#ifdef USE_MYMATH
#include“MathFunctions.h”
#万一
 
int main(int argc,char * argv [])
{
  if(argc <2)
    {
    fprintf(stdout,“%s版本%d。%d \ n”,argv [0]
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,“Usage:%s number \ 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)
安装(FILES MathFunctions.h DESTINATION包括)

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

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

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

包括(CTEST)

#运行应用程序
add_test(TutorialRuns Tutorial 25)
#这是sqrt的25 add_test(TutorialComp25 Tutorial 25)
set_tests_properties(TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION“25 is 5”)
#它处理负数 add_test(TutorialNegative Tutorial -25) set_tests_properties(TutorialNegative PROPERTIES PASS_REGULAR_EXPRESSION“-25 is 0”)
#它处理小数字 add_test(TutorialSmall Tutorial 0.0001) set_tests_properties(TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION“0.0001 is 0.01”)
#使用消息是否正常工作?
add_test(TutorialUsage Tutorial) set_tests_properties(TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION“Usage:。* number”)

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

#定义一个宏以简化添加测试,然后使用它
宏(do_test arg结果)
  add_test(TutorialComp $ {arg} Tutorial $ {arg})
  set_tests_properties(TutorialComp $ {arg}
    属性PASS_REGULAR_EXPRESSION $ {result})
endmacro(do_test)
 
#做一堆基于结果的测试
do_test(25“25 is 5”)
do_test(-25“-25为0”)

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

添加系统内省(步骤4)

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

#这个系统是否提供log和exp函数?
包括(CheckFunctionExists)
check_function_exists(log HAVE_LOG)
check_function_exists(exp HAVE_EXP)

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

//平台是否提供exp和log函数?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP

对于TutorialConfig.h的configure_file命令,对log和exp的测试是很重要的。configure_file命令立即使用CMake中的当前设置配置文件。最后在mysqrt函数中,我们可以使用以下代码在系统上提供基于log和exp的替代实现:

//如果我们同时使用log和exp然后使用它们
#if定义(HAVE_LOG)&&定义(HAVE_EXP)
  result = exp(log(x)* 0.5);
#else //否则使用迭代方法

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

在本节中,我们将介绍如何将生成的源文件添加到应用程序的构建过程中。对于这个例子,我们将在构建过程的一部分中创建一个预计算平方根的表,然后将该表编译到我们的应用程序中。要完成这个,我们首先需要一个生成表的程序。在MathFunctions子目录中,一个名为MakeTable.cxx的新的源文件将这样做。

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

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

#首先我们添加生成表的可执行文件
add_executable(MakeTable MakeTable.cxx)
 
#添加生成源代码的命令
add_custom_command(
  OUTPUT $ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  COMMAND MakeTable $ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  DEPENDS 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的源的列表来完成。我们还必须将当前的二进制目录添加到include目录列表中,以便可以找到并包含mysqrt.cxx的Table.h。当该项目构建时,它将首先构建MakeTable可执行文件。然后它将运行MakeTable来生成Table.h。最后,它将编译mysqrt.cxx,其中包括Table.h来生成MathFunctions库。此时,我们添加的所有功能的顶级CMakeLists.txt文件如下所示:

cmake_minimum_required(VERSION 2.6)
项目(教程)
包括(CTEST)
 
#版本号。
设置(Tutorial_VERSION_MAJOR 1)
设置(Tutorial_VERSION_MINOR 0)
 
#这个系统是否提供log和exp函数?
include($ {CMAKE_ROOT} /Modules/CheckFunctionExists.cmake)
 
check_function_exists(log HAVE_LOG)
check_function_exists(exp HAVE_EXP)
 
#我们应该用自己的数学函数
选项(USE_MYMATH 
  “使用教程提供数学实现”ON)
 
#配置头文件以传递一些CMake设置
#到源代码
configure_file(
  “$ {} PROJECT_SOURCE_DIR /TutorialConfig.h.in”
  “$ {} PROJECT_BINARY_DIR /TutorialConfig.h”
 
#将二叉树添加到包含文件的搜索路径
#,以便我们找到TutorialConfig.h
include_directories(“$ {PROJECT_BINARY_DIR}”)
 
#添加MathFunctions库?
if(USE_MYMATH)
  include_directories(“$ {PROJECT_SOURCE_DIR} / MathFunctions”)
  add_subdirectory(MathFunction)
  set(EXTRA_LIBS $ {EXTRA_LIBS} MathFunctions)
endif(USE_MYMATH)
 
#添加可执行文件
add_executable(Tutorial tutorial.cxx)
target_link_libraries(Tutorial $ {EXTRA_LIBS})
 
#添加安装目标
安装(TARGETS教程DESTINATION bin)
安装(FILES“$ {PROJECT_BINARY_DIR} /TutorialConfig.h”        
         目的地包括)
 
#运行应用程序
add_test(TutorialRuns Tutorial 25)
 
#使用消息是否正常工作?
add_test(TutorialUsage Tutorial)
set_tests_properties(TutorialUsage
  性能 
  PASS_REGULAR_EXPRESSION“用法:。* number”
 
 
#定义一个宏以简化添加测试
宏(do_test arg结果)
  add_test(TutorialComp $ {arg} Tutorial $ {arg})
  set_tests_properties(TutorialComp $ {arg}
    属性PASS_REGULAR_EXPRESSION $ {result}
endmacro(do_test)
 
#做一堆基于结果的测试
do_test(4“4是2”)
do_test(9“9 is 3”)
do_test(5“5是2.236”)
do_test(7“7是2.645”)
do_test(25“25 is 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(
  OUTPUT $ {CMAKE_CURRENT_BINARY_DIR} /Table.h
  DEPENDS MakeTable
  COMMAND 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)
安装(FILES MathFunctions.h DESTINATION包括)

 

构建安装程序(步骤6)

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

构建一个CPack驱动的安装程序包
include(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)

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

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

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

设置(CTEST_PROJECT_NAME“教程”)

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