[翻译]CMAKE官方教程

本篇文章讲对官方教程进行翻译。本篇文章将构建完整的CMAKE项目分红了7个步骤。缓存

第一步

一个最基础的项目就是从源代码中构建一个可执行程序。对一个简单的项目来讲,CMakeLists.txt有两行代码是必需的。咱们的教程就从这里开始。CMakeLists.txt文件看上去是这样的:bash

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)
复制代码

咱们注意到,这个例子的指令都是小写的。实际上,CMAKE支持小写命令,大写命令或者大小写混用的命令。tutorial.cxx的将实现一个计算一个数字的平方根的功能,它的第一个版本是很是简单的:app

// A simple program that computes the square root of a number
#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]);
    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中会更加的灵活。为了增长版本号,咱们须要对CMakeLists.txt作一些改动:ide

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.h,内容以下:函数

// 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中获取值进行替换。 接着,咱们修改tutotial.cxx引用头文件,从而可使用版本号。源代码以下:工具

// A simple program that computes the square root of a number
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#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.h头文件和在使用信息中打印了版本号。测试

引用库(第二步)

如今,咱们将为项目添加库。这个库将包含咱们对计算平方根的实现。可执行文件将引用这个库用以取代编译器提供的标准平方根实现。在这个教程中,咱们会将库添加到名为MathFunctions的子路径。这在CMakeLists.txt须要一行指令:ui

add_library(MathFunctions mysqrt.cxx)
复制代码

mysqrt.cxx有一个名为mysqrt的函数,提供了与编译器提供平方根计算类似的功能。为了可以使用新的库,咱们在 CMakeLists.txt中添加了子路径,使得这个库可以被CMAKE编译。咱们也添加了其余的搜索目录,使得MathFunctions/MathFunctions.h头文件可以被引用。最后一个变化是添加了新的库到可执行文件中。CMakeLists.txt的最后几行是这样的:this

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中添加选项:spa

# should we use our own math functions?
option (USE_MYMATH 
        "Use tutorial provided math implementation" ON) 
复制代码

这么作,会让CMAKE GUI工具显示一个可供用户修改的选项,默认值是on。设置会被保存在缓存中,用户没必要每次都进行设置。下一个变化是条件编译连接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 <stdio.h>
#include <stdlib.h>
#include <math.h>
#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.在ToturialConfig.h头文件中新增如下代码,就能够为CMAKE提供该变量。

#cmakedefine USE_MYMATH
复制代码

安装和测试(第三步)

下一步咱们添加安装规则和为项目增长测试支持。安装规则至关的简单。对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)
复制代码

这就是所有了。在这个点上,你应该能够构建这个教程了,完成安装。这个项目会安装合适的头文件,依赖库和可执行文件。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运行测试。第一个测试简单的见证应用运行,没有段错误或者其余崩溃,而且返回0.这个是基本的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")
复制代码

添加系统感知(第四步)

接着,让咱们考虑添加一些平台相关的代码。在这个,i 子中,咱们会添加一些取决于平台是否具有logexp函数的代码。固然,全部平台都有这些函数,可是在这个教程中,让咱们设想如下他们是不常见的。若是一个平台有log,那么咱们就使用它来计算平方根。咱们首先测试一些这些函数是否可用,能够在CMakeLists.txt使用CheckFunctionExists.cmake中的宏定义:

# does this system provide the log and exp functions?
include (CheckFunctionExists)
check_function_exists (log HAVE_LOG)
check_function_exists (exp HAVE_EXP)
复制代码

接着,咱们修改TutorialConfig.h头文件的定义,定义是否生效取决于CMAKE是否在平台上找到来他们。

// does the platform provide exp and log functions?
#cmakedefine HAVE_LOG
#cmakedefine HAVE_EXP
复制代码

在配置前测试logexp很重要。配置文件指令会使用CMAKE的当前设置当即配置文件。最终,在平方根计算函数中,咱们基于logexp在当前系统是否可用决定使用哪一个实现。

// 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
  . . .
复制代码

添加动态文件生成和文件生成器(第五步)

在这一小节,咱们会展现你如何在应用构建流程中添加代码生成。在这个例子中,咱们会在构建过程当中建立来一个预先计算的品方跟表格,而后将表格编译到应用中去。为了实现这个功能,咱们首先须要一个生成表格的程序。在MathFucntions的子路径中,有一个名为MakeTable.cxx的源文件就是完成这个任务。

// A simple program that builds a sqrt table 
#include <stdio.h>
#include <stdlib.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;
}
复制代码

注意到,表格是经过合法的的C++产生的,而且输出的文件名是经过参数肯定的。因此下一步就是在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的可执行程序将做为独立的可执行程序。而后咱们添加一个自定义的指令定义如何运行MakeTable产生Table.h。再而后,咱们要让CMAKE知道mysqrt.cxx依赖于产生的Table.h。经过将生成的Table.h添加到MathFucntions的源代码列表中实现。另外咱们也要将当前的二进制目录添加到头文件搜索路径(including directories)中,这样Table.h才能被找到,而且才能被mysqrt.cxx引用到。当这个项目被构建时,首先构建MakeTable可执行程序,而后运行该程序生成Table.h.最后,编译已经引入来Table.hmysqrt.cxx生成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.h. 是这样的:

// 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
复制代码

MathFunctionsCMakeLists.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)
复制代码

建立安装器(第六步)

下一步,假设咱们须要将程序分发给其余人使用。咱们但愿根据不一样的平台同时提供二进制和源代码分发。这和咱们以前小节作的第三部略有不一样。在第三步的时候,咱们安装的是从过源代码构建出来的二进制。在这个例子中,咱们会构建支持二进制安装的安装包和支持诸如cygwin, debuan, RPMs的安装包。为来实现这个功能,咱们使用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的变量,定义来License的路径和版本信息。版本信息使用到咱们以前的定义。最后,咱们包含了CPACK模块。

接下来,是最多见的构造方式和运行方式。为了构建二进制包,你须要运行: cpack --config CPackConfig.cmake

为了建立源代码包,你须要运行:cpack --config CPackSourceConfig.cmake

提供仪表盘支持(第七步)

很容易的就能够提交咱们测试结果到仪表盘。在以前的步骤中,咱们已经定义了若干测试用例。咱们只须要运行这些用例而后提交便可。在根CMakeLists.txt中添加指令就能够增长仪表盘功能:

# enable dashboard scripting
include (CTest)
复制代码

咱们一样能够建立CTestConfig.cmake指定仪表盘名称。

set (CTEST_PROJECT_NAME "Tutorial")
复制代码

CTEST会读取文件中的配置而且运行。

相关文章
相关标签/搜索