cmake教程

目录

 

简介

教程

最简示例

增加版本号和配置头文件

增加一个库

安装与测试

增加系统自检

增加生成文件和生成器

创建一个安装器

Dashboard的支持


简介

       CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。只是 CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。

教程

最简示例

       最基本的项目是一个由源码文件编译成的可执行文件。对于一个简单的项目而言,一个三行的CMakeLists,txt文件就是所有需要的内容了。此文档将逐点介绍。下面是一个简单的CMakeLists.txt文件:

cmake_minimum_required (VERSION 2.8)

project (Tutorial)

add_executable(Tutorial tutorial.cxx)

 

其中cmake_minimum_required为cmake最低的版本号要求

project为项目的名称

add_executable为增加可执行文件,第一个参数为项目名称,接下来的参数为源代码文件,不包含头文件

 

注意,上述的CMakeLists.txt只是使用了小写字母命令。事实上大写、小写、大小写混合都是支持的。下述的示例tutorial.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;
}

 

 

当前的文件目录结构为

cmake教程_第1张图片

要编译当前的文件,需要先创建一个叫build的文件,用于保存中间文件,然后再使用cmake ..生成Makefile文件,最后使用make来生成可执行文件

 

mkdir build
cd build
cmake ..
make

 

输入命令后的示例:

cmake教程_第2张图片

生成的目录结构

cmake教程_第3张图片

可以看见生成了一堆中间文件,其中包括了Makefile和可执行文件Tutorial,这个文件名即CMakeLists.txt中编写的project名称。

 

执行示例:

 

增加版本号和配置头文件

       第一个要加入我们的最简项目中的特征是版本号。当你在源码中特意加入这个版本号特征时,CMakeLists.txt将十分灵活的实现它。为了新增版本号,我们修改了CMakeLists.txt,最新内容如下:

 

cmake_minimum_required (VERSION 2.8)
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)

 

       因为上述的配置文件将被重写如二叉树中,我们需要额外将这个目录加入到路径列表中以便搜索include文件。因此需要在源码书下创建一个文件TutorialConfig.h.in,内容如下:

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

 

当CMake配置这个头文件时,会从CMakeLists.txt中将@Tutorial_VERSION_MAJOR@ 和@Tutorial_VERSION_MINOR@的值替换成具体的数值。接下来需要修改tutorial.cxx文件,即需要将头文件包含进来使用版本号信息。最终的tutorial代码如下:

 

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

 

当前跟目录文件

 

重新编译后执行结果

 

增加一个库

       现在我们将增加一个库文件。这个库文件将包含对一个数开根号的实现。生成的可执行文件可以使用这个库文件来代替编译器提供的开根号函数。因此我们将这个库文件放入一个名为MathFunctions的次级目录。在CMakeLists.txt中将新增以下一行内容

 

add_library(MathFunctions mysqrt.cxx)

 

       源代码mysqrt.cxx有一个名为mysqrt的函数,提供跟编译器中sqrt函数相同的功能。为了确保我们新增的在次级目录下的库文件能被根目录下的CMakeLists.txt用到,我们将增加另一个include目录以便于MathFunctions/MathFunctions.h头文件能够找到这个函数原型。最后要变更的地方就是将这个新库文件加入到可执行文件中。在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的图形界面中显示出来,即一个默认值为ON的参数,用户可以按需更改。这个参数将被缓存起来,用户无需每次在用CMake编译项目时都需要配置一遍。下一个变更是让创建(build)和链接(linking)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的使用是用来搜集任何可选的库文件,以便于后续被链接到可执行文件中。这是大型项目中一种可使用的方法来维护多个可选组件。加入相应的修改后tutorial.cxx变成如下:

 

// 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
fprintf(stdout, "enter mysqrt\n");
  double outputValue = mysqrt(inputValue);
#else
  fprintf(stdout, "enter sqrt\n");
  double outputValue = sqrt(inputValue);
#endif
 
  fprintf(stdout,"The square root of %g is %g\n",
          inputValue, outputValue);
  return 0;
}

在上述代码中我们也用到USE_MYMATH。这个参数需要配置在TutorialConfig.h.in中来告诉CMake,配置内容如下

 

#cmakedefine USE_MYMATH

当前目录结构:

cmake教程_第4张图片

当前的根目录下的CMakeLists.txt文件内容:

cmake_minimum_required (VERSION 2.8)
project (Tutorial)
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)

# 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})

 

特别要注意的是option操作以及set操作需要写在configure_file之前,否则configure_file中配置的头文件可能会存在部分变量未被成功替换!

 

TutorialConfig.h.in内容:


 

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

MathFunctions/mysqrt.cxx

double mysqrt(float x)
{
    float xhalf = 0.5f * x;
    int i = *(int*)&x;
    i = 0x5f375a86 - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f-xhalf*x*x);
    x = x*(1.5f-xhalf*x*x);
    x = x*(1.5f-xhalf*x*x);

    return (double)1/x;
}

MathFunctions/MathFunctions.h

 

double mysqrt(float);

MathFunctions/CMakeLists.txt

 

add_library(MathFunctions mysqrt.cxx)

安装与测试

       下一步我们将增加安装规则和测试加入到项目中。安装规则十分直接。例如MathFunctions library我们将配置一个库和头文件,它根据加入到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)

       上述就是全部内容了。此时你应该可以创建(build)tutorial这个项目,然后输入make install(或者从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命令行工具来执行这些测试。第一个测试是简单的校验应用的运行,没有包含段错误或者其他crash或者返回值校验。这是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的调用,一个根据传入的参数的带名称,输入,结果的额外的测试将被加入项目中。

 

增加系统自检

       接下来让我们考虑向我们的工程中引入一些依赖于目标平台可能不具备的特征代码。在本例子中,我们将增加依赖是否目标平台有log和exp函数的代码。当然大部分平台都包含这些函数,假设本项目是特殊情况。如果平台有log函数,则就在mysqrt函数中调用。调用之前需要先用CheckFunctionExists检测可用性,然后写入根目录下的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.h.in文件,即如果CMake在平台上找到他们则定义这些变量:

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

在TutorialConfig.h调用configure_file命令之前,测试log和exp函数是十分必要的。configure命令可以立即使用当前CMake中的配置来配置文件。最后在mysqrt函数中我们可以,如果在系统上可用,我们可以提供一个可选的基于log和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
  . . .

 

增加生成文件和生成器

       在这一部分,我们将展示如何往创建应用的过程中生成源文件。本示例中我们将创建一个预先计算好的开根号表作为创建过程的一部分,然后将这个表编译进我们的程序中。为了完成这个操作,我们需要一个可以生成表的程序。在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的可执行文件被加入到任一其他的可执行文件中。然后我们增加一个特殊命令说明运行MakeTable如何生成Table.h。下一步我们需要让CMake知道mysqrt.cxx依赖与生成的Table.h文件。这个靠在MathFunctions的库文件的源码列表中添加生成的Table.h来实现。当然我们还需要将这个二进制目录加入到include目录中,以便于Table.h可以找到并被mysqrt.cxx引用。当这个项目创建时,它就会先创建MakeTable的可执行文件。然后它就会通过运行MakeTable来生成Table.h.最后,它就会编译包含Table.h的mysqrt.cxx文件来生成MathFunctions库。现在根目录下的CMakeLists.txt文件将包含上述所有的特征,内容如下:

 

cmake_minimum_required (VERSION 2.8)
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如下:

 

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

 

创建一个安装器

       下一步是我们要发型这个项目以便于其他人的使用。我们想在多平台上提供二进制文件或者源码发行版。这个跟上述的安装与测试有一点区别,我们这里是在源码上已经创建好的二进制文件进行安装。在这个示例中我们将创建一个安装包用来支持二进制安装和诸如cygwin,debian,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。创建一个二进制发型包你需要运行以下命令:

cpack --config CPackConfig.cmake

创建一个源码包你需要输入以下命令:

cpack --config CPackSourceConfig.cmake

 

cmake教程_第5张图片

 

cmake教程_第6张图片

 

cmake教程_第7张图片

 

Dashboard的支持

       增加提交测试结果到以表示是十分容易的。在早起的步骤中我们已经定义过一些了的测试,因此我们只需要运行这些测试用例并将他们提交到dashboard中。为了支持dashboard,需要在根目录下的CMakeLists.txt中增加CTest模块。

# enable dashboard scripting
include (CTest)

 

       当然也需要创建一个名为CTestConfig.cmake的文件用来在dashboard中指明项目的名称。

 

set (CTEST_PROJECT_NAME "Tutorial")

    Ctest在它运行的时候就好读取这个文件。为了创建一个简单的dashboard你可以在你的项目总执行CMake.更改目录到二进制树,然后执行ctest –D Experimental,你的dashboard将会被更新到Kitware的公共dashboard。

    这一部分好像没有什么用。。

翻译路径:https://cmake.org/cmake-tutorial/

其他翻译:https://www.zybuluo.com/khan-lau/note/254724(里面还有其他命令介绍很不错)

代码github:https://github.com/yxjay/cmake_learning

代码链接:https://download.csdn.net/download/u010317005/10729735

你可能感兴趣的:(C++)