CMake Tutorial翻译

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


另请参阅cmake buildsystem (7)和cmake 语言 (7)手册页的介绍部分, 以了解 cmake 概念和源树组织的概述。

一个基本的起点 (Step1)
最基本的项目是一个由源代码文件生成的可执行程序。对于简单项目, 需要使用两行 CMakeLists.txt 文件。这将是我们教程的出发点。CMakeLists.txt 文件如下所示:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
 
请注意, 本例使用 CMakeLists.txt 文件中的小写命令。CMake 支持上、下和混合 case 命令。教程的源代码. cxx 将计算一个数字的平方根, 它的第一个版本非常简单, 如下所示:

// A simple program that computes the square root of a number
#include
#include
#include
int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"Usage: %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;
}
添加版本号和已配置的头文件
我们将添加的第一个功能是为我们的可执行文件和项目提供一个版本号。虽然您可以在源代码中完全执行此操作, 但在 CMakeLists.txt 文件中执行它可提供更大的灵活性。要添加版本号, 我们将修改 CMakeLists.txt 文件, 如下所示:

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
 
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )
 
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories("${PROJECT_BINARY_DIR}")
 
# add the executable
add_executable(Tutorial tutorial.cxx)
 


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


// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
 


当 CMake 配置此头文件时, @Tutorial_VERSION_MAJOR @ 和 @Tutorial_VERSION_MINOR @ 的值将由 CMakeLists.txt 文件中的值替换。接下来, 我们修改教程. cxx 以包括已配置的头文件和使用版本号。生成的源代码如下所示。


// A simple program that computes the square root of a number
#include
#include
#include
#include "TutorialConfig.h"
 
int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n",
            argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %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;
}
主要的变化是包含 TutorialConfig 头文件, 并打印出一个版本号作为使用信息的一部分。




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


add_library(MathFunctions mysqrt.cxx)
源文件 mysqrt cxx 有一个称为 mysqrt 的函数, 它为编译器的 sqrt 函数提供了类似的功能。为了使用新的库, 我们在顶层 CMakeLists.txt 文件中添加一个 add_subdirectory 调用, 这样库就会被生成。我们还添加了另一个包含目录, 以便可以为函数原型找到 MathFunctions/MathFunctions 头文件。最后的更改是将新库添加到可执行文件中。顶层 CMakeLists.txt 文件的最后几行现在看起来像:


include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
add_subdirectory (MathFunctions) 
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial MathFunctions)
现在让我们考虑使 MathFunctions 库可选。在本教程中, 真的没有任何理由这样做, 但与更大的库或库, 依赖于第三方代码, 你可能想。第一步是向顶层 CMakeLists.txt 文件中添加选项。


# should we use our own math functions?
option (USE_MYMATH 
        "Use tutorial provided math implementation" ON) 
这将显示在 CMake GUI 中, 默认值为 ON, 用户可以根据需要进行更改。此设置将存储在缓存中, 以便用户每次在该项目上运行 CMake 时都不需要继续设置它。下一个更改是使 MathFunctions 库的生成和链接成为条件。为此, 我们将顶层 CMakeLists.txt 文件的末尾更改为如下所示:


# add the MathFunctions library?
#
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})
这将使用 USE_MYMATH 的设置来确定是否应编译和使用 MathFunctions。注意使用变量 (在本例中为 EXTRA_LIBS) 来收集任何可选库, 以便以后链接到可执行文件中。这是一个常见的方法, 用于保持较大的项目与许多可选组件清洁。对源代码的相应更改是相当直接的, 并留给我们:


// A simple program that computes the square root of a number
#include
#include
#include
#include "TutorialConfig.h"
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif
 
int main (int argc, char *argv[])
{
  if (argc < 2)
    {
    fprintf(stdout,"%s Version %d.%d\n", argv[0],
            Tutorial_VERSION_MAJOR,
            Tutorial_VERSION_MINOR);
    fprintf(stdout,"Usage: %s number\n",argv[0]);
    return 1;
    }
 
  double inputValue = atof(argv[1]);
 
#ifdef USE_MYMATH
  double outputValue = mysqrt(inputValue);
#else
  double outputValue = sqrt(inputValue);
#endif
 
  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}


在源代码中, 我们也使用了 USE_MYMATH。通过在配置文件中添加以下行, 可以从 CMake 到源代码通过 TutorialConfig。


#cmakedefine USE_MYMATH


安装和测试 (步骤 3)
在下一步, 我们将添加安装规则和测试支持我们的项目。安装规则是相当直接的向前。对于 MathFunctions 库, 我们通过将以下两行添加到 MathFunctions ' CMakeLists.txt 文件中来安装库和头文件:


install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)
对于应用程序, 将以下行添加到顶层 CMakeLists.txt 文件中以安装可执行文件和配置的头文件名:


# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"        
         DESTINATION include)
这就是一切。此时, 您应该能够生成教程, 然后键入 "安装" (或从 IDE 生成安装目标), 然后安装相应的头文件、库和可执行文件。CMake 变量 CMAKE_INSTALL_PREFIX 用于确定文件安装位置的根。添加测试也是一个相当直接的向前过程。在顶层 CMakeLists.txt 文件的末尾, 我们可以添加一些基本测试, 以验证应用程序是否工作正常。


include(CTest)


# does the application run
add_test (TutorialRuns Tutorial 25)
# does it sqrt of 25
add_test (TutorialComp25 Tutorial 25)
set_tests_properties (TutorialComp25 PROPERTIES PASS_REGULAR_EXPRESSION "25 is 5")
# does it handle negative numbers
add_test (TutorialNegative Tutorial -25)
set_tests_properties (TutorialNegative PROPERTIES PASS_REGULAR_EXPRESSION "-25 is 0")
# does it handle small numbers
add_test (TutorialSmall Tutorial 0.0001)
set_tests_properties (TutorialSmall PROPERTIES PASS_REGULAR_EXPRESSION "0.0001 is 0.01")
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number")


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


#define a macro to simplify adding tests, then use it
macro (do_test arg result)
  add_test (TutorialComp${arg} Tutorial ${arg})
  set_tests_properties (TutorialComp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result})
endmacro (do_test)
 
# do a bunch of result based tests
do_test (25 "25 is 5")
do_test (-25 "-25 is 0")
对于 do_test 的每次调用, 将根据传递的参数将另一个测试添加到项目中, 其中有名称、输入和结果。




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


# does this system provide the log and exp functions?
include (CheckFunctionExists)
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
接下来, 我们修改 TutorialConfig 在定义这些值, 如果 CMake 在平台上发现它们, 如下所示:


// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
重要的是, 对日志和 exp 的测试是在 TutorialConfig configure_file 命令之前完成的。configure_file 命令立即使用 CMake 中的当前设置配置文件。最后在 mysqrt 函数中, 如果使用以下代码在系统上可用, 我们可以提供基于日志和 exp 的备用实现:


// if we have both log and exp then use them
#if defined (HAVE_LOG) && defined (HAVE_EXP)
  result = exp(log(x)*0.5);
#else // otherwise use an iterative approach
  . . .




添加生成的文件和生成器 (步骤 5)
在本节中, 我们将演示如何将生成的源文件添加到应用程序的生成过程中。对于本示例, 我们将创建一个预计算平方根表作为生成过程的一部分, 然后将该表编译到我们的应用程序中。为了实现这一点, 我们首先需要一个程序, 将生成表。在 MathFunctions 子目录中, 一个新的源文件名为 MakeTable. cxx 会这样做的。


// A simple program that builds a sqrt table 
#include
#include
#include
 
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(i));
    fprintf(fout,"%g,\n",result);
    }
 
  // close the table with a zero
  fprintf(fout,"0};\n");
  fclose(fout);
  return 0;
}


请注意, 该表是作为有效的 c++ 代码生成的, 并且要将输出写入的文件的名称作为参数传入。下一步是将相应的命令添加到 MathFunctions ' CMakeLists.txt 文件中, 以生成 MakeTable 可执行程序, 然后将其作为生成过程的一部分运行。完成此任务需要一些命令, 如下所示。


# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
 
# 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
  )
 
# add the binary tree directory to the search path for 
# include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
 
# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h  )


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


cmake_minimum_required (VERSION 2.6)
project (Tutorial)
include(CTest)
 
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
 
# does this system provide the log and exp functions?
include (${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
 
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
 
# should we use our own math functions
option(USE_MYMATH 
  "Use tutorial provided math implementation" ON)
 
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
  "${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
  "${PROJECT_BINARY_DIR}/TutorialConfig.h"
  )
 
# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
include_directories ("${PROJECT_BINARY_DIR}")
 
# add the MathFunctions library?
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/MathFunctions")
  add_subdirectory (MathFunctions)
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)
 
# add the executable
add_executable (Tutorial tutorial.cxx)
target_link_libraries (Tutorial  ${EXTRA_LIBS})
 
# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"        
         DESTINATION include)
 
# does the application run
add_test (TutorialRuns Tutorial 25)
 
# does the usage message work?
add_test (TutorialUsage Tutorial)
set_tests_properties (TutorialUsage
  PROPERTIES 
  PASS_REGULAR_EXPRESSION "Usage:.*number"
  )
 
 
#define a macro to simplify adding tests
macro (do_test arg result)
  add_test (TutorialComp${arg} Tutorial ${arg})
  set_tests_properties (TutorialComp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endmacro (do_test)
 
# do a bunch of result based tests
do_test (4 "4 is 2")
do_test (9 "9 is 3")
do_test (5 "5 is 2.236")
do_test (7 "7 is 2.645")
do_test (25 "25 is 5")
do_test (-25 "-25 is 0")
do_test (0.0001 "0.0001 is 0.01")


TutorialConfig 的样子:


// the configured options and settings for Tutorial
#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
#cmakedefine USE_MYMATH
 
// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP


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


# first we add the executable that generates the table
add_executable(MakeTable MakeTable.cxx)
# add the command to generate the source code
add_custom_command (
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  )
# add the binary tree directory to the search path 
# for include files
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
 
# add the main library
add_library(MathFunctions mysqrt.cxx ${CMAKE_CURRENT_BINARY_DIR}/Table.h)
 
install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)




 


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


# build a CPack driven installer package
include (InstallRequiredSystemLibraries)
set (CPACK_RESOURCE_FILE_LICENSE  
     "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
set (CPACK_PACKAGE_VERSION_MAJOR "${Tutorial_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${Tutorial_VERSION_MINOR}")
include (CPack)
这就是一切。我们从包括 InstallRequiredSystemLibraries 开始。此模块将包括当前平台项目所需的任何运行库。接下来, 我们将一些 CPack 变量设置为存储此项目的许可证和版本信息的位置。版本信息使用我们在本教程前面设置的变量。最后, 我们包括 CPack 模块, 它将使用这些变量和系统的一些其他属性来设置安装程序。


下一步是以通常的方式构建项目, 然后运行 CPack。要生成二进制分布, 请执行以下操作:


cpack --config CPackConfig.cmake
要创建要键入的源分发


cpack --config CPackSourceConfig.cmake


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


# enable dashboard scripting
include (CTest)
我们还创建了一个 CTestConfig cmake 文件, 在这里我们可以为仪表板指定此项目的名称。


set (CTEST_PROJECT_NAME "Tutorial")
CTest 将在该文件运行时读取。要创建一个简单的仪表板, 您可以在项目上运行 CMake, 将目录更改为二进制树, 然后运行 ctest –D 实验。

你可能感兴趣的:(CMake,CMake)