cmake 教程

  1. 一个基本的开始

最基本的工程是将源码文件编译成一个可执行程序。对于一个很简单的工程,CMakeLists.txt只须要以下几行文件便可。这将是咱们教程的开始。CMakeLists.txt文件就像这样。c++

cmake_minimum_required (VERSION 2.6)
project (Tutorial)
add_executable(Tutorial tutorial.cxx)

上面的cmake是采用小写写成的。cmake支持采用大写,也能够采用小写或者混合大小写的命令来。这个源码将会计算一个数的平方根。初版是比较简单的。as follow: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;
}

Adding a Version Number and Configured Header File

第一个特征将会提供给可执行程序一个版本号。你能够将这个版本号提供在程序里。固然也能够写在cmake里。写在cmake里面会更灵活一些。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)

由于配置文件会被写入binary tree。咱们必须添加目录到头文件的搜索目录。咱们在源码路径下建立一个TutorialConfig.h.in的文件。函数

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

When CMake configures this header file the values for @Tutorial_VERSION_MAJOR@ and @Tutorial_VERSION_MINOR@ will be replaced by the values from the CMakeLists.txt file. Next we modify tutorial.cxx to include the configured header file and to make use of the version numbers. The resulting source code is listed below.测试

当cmake的配置这些头文件的值时,@Tutorial_VERSION_MAJOR@ 和@Tutorial_VERSION_MINOR@将会把cmakelists.txt的值替换调。如今咱们修改tutorial.cxx的文件以下所示。ui

// 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;
}

step2.添加运行时库this

如今咱们将添加一个库到咱们的工程中。这个工程将包含咱们本身实现的计算一个书数的平方根。这个程序可以利用库去替换编译器提供的标准求根函数。对于这个教程咱们将把这个库放在一个叫mathfunctions的子目录下。cmakelists.file是以下所示。spa

add_library(MathFunctions mysqrt.cxx)

源码文件有一个叫mysqrt的函数提供相似编译器的求根函数。确保利用咱们的新库。咱们把add_subdirectory放在了CMakeLists.txt放在了库的前面。这样库将会被构建。咱们也添加了另一个include directory ,添加MathFunctions/MathFunctions.h 的头文件/这样可以找到函数的原型。最后要连接函数库到咱们的可执行程序里。code

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 文件的顶层添加一个选项。orm

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

这个会显示在CMake的GUI里,默认是ON,使用者能够按须要改变 。设置后会被保存在cache里,客户每次cmake工程的时候,都不须要去设置。下一个改变是构建和连接MathFunctions。

# 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去决定是否编译和连接它。注意变量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。这是设置给CMake的,经过配置文件TutorialConfig.h.in来设置。

#cmakedefine USE_MYMATH

Installing and Testing (Step 3)

接下来,咱们会添加安装规则和测试支持咱们的项目。安装的规则是至关直白的。对于MathFunctions库,咱们安装头文件和库添加以下如何两行到MathFunctions的CMakeLists.txt文件:

install (TARGETS MathFunctions DESTINATION bin)
install (FILES MathFunctions.h DESTINATION include)

For the application,这两行添加顶层的CMakeLists.txt,去安装可执行程序和配置文件

# add the install targets
install (TARGETS Tutorial DESTINATION bin)
install (FILES "${PROJECT_BINARY_DIR}/TutorialConfig.h"        
         DESTINATION include)

下载你就能够构建tutorial,而后输入make install (或者构建IDE中的INSTALL目标),它将会将头文件和库和可执行程序安装在合适的位置。CMake的变量CMAKE_INSTALL_PREFIX是用于决定文件安装的根目录。添加测试也是至关简单地一项工程。在顶层的CMakeLists.txt文件底部咱们能够添加a number of basic tests去验证应用是否正确。

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命令运行测试。第一个测试是简单地验证应用运行,没有段错误或其余的崩溃问题(does not segfault or otherwise crash),而且有一个0的返回值。或者有一个返回值是0。这是CTest的基础测试。下面的测试使用了PASS_REGULAR_EXPRESSION来测试输出信息是否包含有字符串。在这个案例里验证计算平方根是不然正确,在提供错误的参数时,打印信息是否正确。若是你想添加大量的测试去验证不一样的输入参数,你能够考虑建立 macro.

Adding System Introspection (Step 4)

接下来咱们考虑添加一些代码到咱们的工程里,在有些平台里可能没有的特性。在这个例子里咱们根据目标平台是否有log和exp函数添加一些代码。固然差很少每一个平台都有这些函数,但对于这个教程咱们假设他们是很不普通的。若是平台里有log函数,那咱们将优先使用它。咱们首先利用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.in定义宏,cmake可以发现他们在各个平台下面。

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

在使用配置文件TutorialConfig.h前,测试log和exp。配置文件会当即使用cmake当中的设置。最终在mysqrt函数咱们里咱们提供了两种实现方式。

// 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
  . . .

Adding a Generated File and Generator (Step 5)

在这节中,咱们添加一个生成的源文件到应用程序的构建中去。咱们建立一个预先生成的平方根表做为构建过程的一部分,编译它到application。为了达到这一点,咱们须要一个程序去生成这个表。在MathFunctions 的目录里添加一个新文件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++代码产生的,输出文件名是一个输入参数。下一步咱们要在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  )

首先,MakTable就像其余可执行程序同样是被视为可执行程序。而后咱们添加一个自定义命令来指定如何生。 接下来,咱们须要让cmake知道mysqrt.cxx依赖Table.h。这是经过往MahFuncitons库添加生成的文件Table.h来实现的。把mysqrt。cxx添加当前的生成目录到搜索路径中。

当这个工程被构建时,首先它会构建MakeTable。运行MakeTable产生Table.h。最后,它会编译mysqrt.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.in looks like:

// 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)

Building an Installer (Step 6)

咱们想要发布咱们的程序让其余人可以使用它。咱们想要提供给他们不一样平台的二进制文件和代码。这里的安装是和咱们步骤3提供的安装方式有点不肯意的。在步骤3咱们是从源代码中构建安装二进制文件。咱们要构建安装包来支持二进制安装和包管理,例如cygwin, debian, RPMs等等。咱们会使用CPack来建立平台相关的安装包。咱们须要在顶层的CMakeLists.txt file添加以下几行。

# 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

Adding Support for a Dashboard (Step 7)

支持上传测试结果到dashboard是很是简单的。咱们已经定义了一些测试案例。咱们只须要运行这些测试而后提交他们到dashboard。咱们只须要在顶层的CMakeLists包含CTest模块,咱们就能够支持上传测试结果到Dashboard。

# enable dashboard scripting
include (CTest)

咱们建立一个CTestConfig.cmake文件,那样咱们能够指定dashboard上这个工程的名字。

set (CTEST_PROJECT_NAME "Tutorial")

CTest将会阅读这个和执行它。建立一个简单的dashboard,你能够运行CMake在你的工程里,改变生成文件的路径,而后运行ctest –D Experimental。而后你的测试结果会被上传到Kitware的公共dashboard中。

最后整个工程的源代码在cmake的源码路径Tests/Tutorial下面。

相关文章
相关标签/搜索