cmake 教程

  1. 一个基本的开始

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

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

上面的cmake是采用小写写成的。cmake支持采用大写,也可以采用小写或者混合大小写的命令来。这个源码将会计算一个数的平方根。第一版是比较简单的。as follow:

// 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里面会更灵活一些。

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的文件如下所示。

// 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.添加运行时库

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

add_library(MathFunctions mysqrt.cxx)

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

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,使用者可以按需要改变 。设置后会被保存在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下面。

转载于:https://my.oschina.net/u/2461850/blog/1606886

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值