CMake Tutorial Notes

本文是记录学习CMake官方教程时的笔记与注意事项

注意:与本文对应的repository

GitHub/Gitee Link
GitHub CMakeOfficialTutorial
Gitee CMakeOfficialTutorial

前期准备

为了能够在windows平台下使用CMake,以避免可能出现的问题,需要准备以下两项

对于CMake,下载最新版的(当前最新为3.23.2)即可,记得添加cmake的路径到PATH中去。

对于MinGW,需要进入下载页面,不要下载MinGW-W64 Online Installer(太慢),而是选择最新的下载选项(如下)中的x86_64-win32-seh,它是对应windows平台的压缩包。

下载完毕之后,将其中的目录mingw64解压出来,然后把对应的路径加入到环境变量PATH中去,以便在terminal中使用时可以被搜索到。

对于MinGW需要特别注意的是,在其bin目录下面有mingw32-make.exe,但没有make.exe,需要自己单独把mingw32-make.exe复制出来一份并重新命名为make.exe,否则会出现如下的错误

$ cmake ../Step1 -G "Unix Makefiles"
CMake Error: CMake was unable to find a build program corresponding to "Unix Makefiles".  CMAKE_MAKE_PROGRAM is not set.  You probably need to select a different build tool.
CMake Error: CMAKE_C_COMPILER not set, after EnableLanguage
CMake Error: CMAKE_CXX_COMPILER not set, after EnableLanguage
-- Configuring incomplete, errors occurred!
See also "F:/Pyrad/CMakeLearning/CMakeTutorialLearning/Step1_build/CMakeFiles/CMakeOutput.log".

(可能的原因是CMake查找的是make而不是mingw32-make.exe,哪怕mingw32-make.exe已经在PATH中了)

此外,需要在CMakeLists.txt中指明需要使用mingw64对应的toolchain来编译,

SET(MY_MINGW64_HOME "D:/procs/mingw64")
SET(CMAKE_MAKE_PROGRAM "${MY_MINGW64_HOME}/bin/mingw32-make.exe")
SET(CMAKE_C_COMPILER "${MY_MINGW64_HOME}/bin/gcc.exe")
SET(CMAKE_CXX_COMPILER "${MY_MINGW64_HOME}/bin/g++.exe")

并且,在cmake编译时使用如下option

cmake <SRC_DIR> -G "Unix Makefiles"

否则在windows平台下,系统可能会按照VS的toolchain生成project文件,而不是像Linux下一样生成Makefile文件。

Step 1 基本

教程第一节

简述

本节介绍了一个简单的例子,只有一个cpp文件(以及稍后引入的通过cmake创建的一个头文件),通过引入一个简单的CMakeLists.txt来说明了如何编译这个简单的cpp源文件。

新的语法和命令

functions functions
CMAKE_MINIMUM_REQUIRED() PROJECT()
ADD_EXECUTABLE() CONFIGURE_FILE()
SET() MESSAGE()
TARGET_INCLUDE_DIRECTORIES()
variables variables
CMAKE_CXX_STANDARD CMAKE_CXX_STANDARD_REQUIRED
CMAKE_MAKE_PROGRAM CMAKE_C_COMPILER
CMAKE_CXX_COMPILER PROJECT_BINARY_DIR
PROJECT_SOURCE_DIR

关于CMakeLists.txt中基本语法(synopsis)的几点说明

  • 关键字大小写不敏感(但最好统一用大写或小写)

  • 变量是大小写敏感

这里的CMakeLists.txt文件的内容:

CMAKE_MINIMUM_REQUIRED(VERSION 3.10)

# Set the project name
PROJECT(Tutorial VERSION 1.0)

# specify the C++ standard
SET(CMAKE_CXX_STANDARD 11)
SET(CMAKE_CXX_STANDARD_REQUIRED True)

MESSAGE(STATUS "[PYRAD] This is the BINARY directory: " ${Tutorial_BINARY_DIR})
MESSAGE(STATUS "[PYRAD] This is the SOURCE directory: " ${Tutorial_SOURCE_DIR})

# Add the executable
ADD_EXECUTABLE(Tutorial tutorial.cxx)

# If you'd like to build in a Unix way in windows platform,
# add the following
SET(MY_MINGW64_HOME "D:/procs/mingw64")
SET(CMAKE_MAKE_PROGRAM "${MY_MINGW64_HOME}/bin/mingw32-make.exe")
SET(CMAKE_C_COMPILER "${MY_MINGW64_HOME}/bin/gcc.exe")
SET(CMAKE_CXX_COMPILER "${MY_MINGW64_HOME}/bin/g++.exe")


CONFIGURE_FILE(TutorialConfig.h.in TutorialConfig.h)


TARGET_INCLUDE_DIRECTORIES(Tutorial PUBLIC "${PROJECT_BINARY_DIR}" )

使用cmake进行编译

命令为

$ mkdir Step1_build
$ cd ./Step1_build
$ cmake ../Step1 -G "Unix Makefiles"
$ cmake --build .

在windows平台下显示了信息如下:

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step1_build (master)
$ cmake ../Step1 -G "Unix Makefiles"
-- The C compiler identification is GNU 8.1.0
-- The CXX compiler identification is GNU 8.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/procs/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/procs/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- [PYRAD] This is the BINARY directory: D:/Gitee/CMakeOfficialTutorial/Step1_build
-- [PYRAD] This is the SOURCE directory: D:/Gitee/CMakeOfficialTutorial/Step1
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step1_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step1_build (master)
$ cmake --build .
[ 50%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step1_build (master)
$ ls
cmake_install.cmake  CMakeCache.txt  CMakeFiles/  Makefile  Tutorial.exe*  TutorialConfig.h

编译完毕之后,可以在编译目录(当前是Step1_build)里面执行如下命令,得到如下结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step1_build (master)
$ ./Tutorial.exe 4294967296
The square root of 4.29497e+09 is 65536

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step1_build (master)
$ ./Tutorial.exe 10
The square root of 10 is 3.16228

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step1_build (master)
$ ./Tutorial.exe
D:\Gitee\CMakeOfficialTutorial\Step1_build\Tutorial.exe Version 1.0
Usage: D:\Gitee\CMakeOfficialTutorial\Step1_build\Tutorial.exe number

PROJECT 关键字

PROJECT文档帮助页面

其中PROJECT关键字指明工程名字,后面可以跟上所支持的语言(默认支持所有语言)。

PROJECT(Tutorial) # 指定工程名为Tutorial
PROJECT(Tutorial CXX) # 指定工程名为Tutorial,支持语言为C++
PROJECT(Tutorial C CXX) # 指定工程名为Tutorial,支持语言为C和C++
PROJECT(Tutorial VERSION 1.0) # 指定工程名为Tutorial,并且设定版本号为1.0

PROJECT文档帮助页面

其中,这个声明同时也隐式定义了两个CMake变量,分别是

  • ${PROJECT_NAME}_BINARY_DIR

    这个变量指明了存放一些binary等可执行文件的目录。比如在该项目里这个变量就会在cmake执行时被替换为Tutorial_BINARY_DIR

  • ${PROJECT_NAME}_SOURCE_DIR

    这个变量指明了存放源文件的目录。比如在该项目里这个变量就会在cmake执行时被替换为Tutorial_SOURCE_DIR

这两个隐式生成的CMake变量,会由于工程名的变化而产生变化,为了防止其发生变化,CMake还提供了两个预定义的变量,可以单独对其进行命名,防止因为工程名称发生变化而变化。

SET 关键字

SET帮助文档页面

SET关键字用来指定变量,如果后面跟一个列表,需要用空格隔开(如果文件名中有空格,那么该文件名就需要用双引号括起来)

SET(SRC_LIST main.cpp) # 声明了变量SRC_LIST
SET(SRC_LIST main.cpp t.cpp t2.cpp) # 声明了变量SRC_LIST

其中文件名可以带上后缀,也可以不带后缀,但是最好带上后缀,防止有歧义的情况发生(比如同时存在两个文件main.cppmain)。

MESSAGE 关键字

MESSAGE帮助文档页面

这个关键字向终端输出用户的自定义的信息,包括三种

  • SEND_ERROR

    产生错误,跳过生成过程

  • STATUS

    输出信息,前缀是--

  • FATAL_ERROR

    立即终止所有的cmake过程

在本例中,输出了两条信息,

# 变量${Tutorial_BINARY_DIR}是由cmake通过PROJECT所设定的名字自动赋值得到的
MESSAGE(STATUS "[PYRAD] This is the BINARY directory: " ${Tutorial_BINARY_DIR})

# 变量${Tutorial_SOURCE_DIR}也是由cmake通过PROJECT所设定的名字自动赋值得到的
MESSAGE(STATUS "[PYRAD] This is the SOURCE directory: " ${Tutorial_SOURCE_DIR})

Step2 添加库

教程第二节

Adding A Library 添加一个库

新的命令和语法

functions functions functions
add_library() add_subdirectory() target_link_libraries()
option() configure_file() list()
add_executable() target_include_directories()

指令

instructions instructions
if/endif

初始目录结构

Step2/
│───CMakeLists.txt
│───tutorial.cxx
│───TutorialConfig.h.in
│
└───MathFunctions/
    │───MathFunctions.h
    └───mysqrt.cxx

修改的部分

首先需要在Step2/CMakeLists.txt这个顶层的文件里面,添加如下的修改(这里不包含原有的部分)

# PART 1
# If you'd like to build in a Unix way in windows platform,
# add the following
SET(MY_MINGW64_HOME "D:/procs/mingw64")
SET(CMAKE_MAKE_PROGRAM "${MY_MINGW64_HOME}/bin/mingw32-make.exe")
SET(CMAKE_C_COMPILER "${MY_MINGW64_HOME}/bin/gcc.exe")
SET(CMAKE_CXX_COMPILER "${MY_MINGW64_HOME}/bin/g++.exe")

# PART 2
option(USE_MYMATH "Use tutorial provided math implementation" ON)

if(USE_MYMATH)
	# Add the MathFunctions library
	add_subdirectory(MathFunctions)
	list(APPEND EXTRA_LIBS MathFunctions)
	list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

# PART 3
# add the executable
add_executable(Tutorial tutorial.cxx MathFunctions/mysqrt.cxx)

# PART 4
target_link_directories(Tutorial PUBLIC ${EXTRA_LIBS})

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           "${EXTRA_INCLUDES}"
                           )

上面修改中:

  • 第一部分:仍然是指定在windows平台下的gcc/g++编译

  • 第二部分:设定了一个CMake的宏USE_MYMATH,以便可以在cmake编译期间打开或关闭。

    然后根据这个宏,通过命令add_subdirectory添加一个lib的目录(MathFunctions),以便cmake可以得知我们的lib源文件的目录

    同时,还定义了两个cmake中的list变量:EXTRA_LIBSEXTRA_INCLUDES

  • 第三部分:通过add_executable告诉cmake最终编译得到的二进制文件Tutorial需要从源文件tutorial.cxx和lib库的源文件MathFunctions/mysqrt.cxx(注意,这里在网页上忘记写了后者,会导致最后链接错误)

  • 第四部分:首先通过target_link_directories告诉了linker需要去搜索lib文件(比如这里的MathFunctions.a)的路径,它所指定的二进制名Tutorial,必须是在之前已经通过add_executable活着add_library已经定义。

    其次通过target_include_directories定义了在编译期间compiler需要去搜索的目录,以便找到所需的头文件。

TutorialConfig.h.in文件中,添加如下指令

#cmakedefine USE_MYMATH

这个指令的目的,主要是定义一个可以在cmake期间灵活修改(打开/关闭)的宏,以方便使用。

实际上,USE_MYMATH这个宏在两个地方被定了,

  • 第一个地方:top-level的CMakeLists.txt中,使用CMake的option()这个函数定义了USE_MYMATH这个宏,并赋予其初始值,这个地方是USE_MYMATH这个宏真正被定义的地方,而且是通过CMake来定义的

  • 第二个地方:文件TutorialConfig.h.in这个文件中。这个地方里面使用了CMake的指令#cmakedefine来定义USE_MYMATH这个宏,但它在cmake编译期间会被cmake做自动替换,替换为真正的C/C++的预编译指令:#define USE_MYMATH 0#define USE_MYMATH 1

    而由于USE_MYMATH这个宏是cmake定义的,所以在cmake期间,可以通过command line option来改变这个宏的值(ON/OFF),而随着改变的,是TutorialConfig.h.in这个文件根据cmake的编译指令而生成的文件中真正的C/C++的预编译指令所定义的USE_MYMATH这个宏值。

    比如,打开这个编译选项,使用cmake ../Step2 -G Unix Makefiles" -DUSE_MYMATH=ON(或者直接用cmake ../Step2 -G Unix Makefiles",因为USE_MYMATH的默认值是ON),那么通过TutorialConfig.h.in这个文件生成的头文件TutorialConfig.h中,预编译指令就是:#define USE_MYMATH 1

    如果关闭这个编译选项,使用cmake ../Step2 -G Unix Makefiles" -DUSE_MYMATH=OFF,那么通过TutorialConfig.h.in这个文件生成的头文件TutorialConfig.h中,预编译指令就是:#define USE_MYMATH 0 ,总之,就是cmake会根据option中定义的宏,在根据编译时的command liine option是否打开,自动替换#cmakedefine USE_MYMATH成正确的真正的C/C++的预编译指令。

tutorial.cxx源文件的中,修改的部分如下,

// PART 1
#ifdef USE_MYMATH
#include "MathFunctions.h"
#endif // USE_MYMATH

int main(int argc, char* argv[])
{
  // ... ... 

  // PART 2
  // calculate square root
  #ifdef USE_MYMATH
  const double outputValue = mysqrt(inputValue);
  #else
  const double outputValue = sqrt(inputValue);
  #endif // USE_MYMATH
 
  // ... ...
  
  return 0;
}

上面修改的,

第一部分:是根据宏USE_MYMATH来决定是否添加头文件MathFunctions.h

第二部分:是根据宏USE_MYMATH来决定是否使用标准库中的函数sqrt还是我们这里自定义的库中的函数mysqrt

修改之后

添加并修改文件完毕之后,目录结构如下

STEP2/
│───CMakeLists.txt*
│───tutorial.cxx*
│───TutorialConfig.h.in*
│
└───MathFunctions/
    │───CMakeLists.txt* (Newly added)
    │───MathFunctions.h
    └───mysqrt.cxx

打开USE_MYMATH编译

编译(打开USE_MYMATH宏)

cmake ../Step2 -G "Unix Makefiles"
cmake --build .

输出结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ cmake ../Step2 -G "Unix Makefiles"
-- The C compiler identification is GNU 8.1.0
-- The CXX compiler identification is GNU 8.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/procs/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/procs/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step2_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ cmake --build .
[ 20%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[ 40%] Building CXX object CMakeFiles/Tutorial.dir/MathFunctions/mysqrt.cxx.obj
[ 60%] Linking CXX executable Tutorial.exe
[ 60%] Built target Tutorial
[ 80%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.obj
[100%] Linking CXX static library libMathFunctions.a
[100%] Built target MathFunctions

执行编译后的二进制文件,得到如下输出,可以看到,使用的是我们自定义库中的mysqrt函数。

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ ./Tutorial.exe 4294967296
Computing sqrt of 4.29497e+09 to be 2.14748e+09
Computing sqrt of 4.29497e+09 to be 1.07374e+09
Computing sqrt of 4.29497e+09 to be 5.36871e+08
Computing sqrt of 4.29497e+09 to be 2.68435e+08
Computing sqrt of 4.29497e+09 to be 1.34218e+08
Computing sqrt of 4.29497e+09 to be 6.71089e+07
Computing sqrt of 4.29497e+09 to be 3.35545e+07
Computing sqrt of 4.29497e+09 to be 1.67773e+07
Computing sqrt of 4.29497e+09 to be 8.38878e+06
Computing sqrt of 4.29497e+09 to be 4.19465e+06
The square root of 4.29497e+09 is 4.19465e+06

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ ./Tutorial.exe 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ ./Tutorial.exe
D:\Gitee\CMakeOfficialTutorial\Step2_build\Tutorial.exe Version 1.0
Usage: D:\Gitee\CMakeOfficialTutorial\Step2_build\Tutorial.exe number

关闭USE_MYMATH编译

使用如下命令,在关闭自定义的宏USE_MYMATH之后编译

cmake ../Step2 -G "Unix Makefiles" -DUSE_MYMATH=OFF
cmake --build .

编译输出(这里偷懒了,没有rm -rf ./*,就让cmake增量编译吧)

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ cmake ../Step2 -G "Unix Makefiles" -DUSE_MYMATH=OFF
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step2_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ cmake --build .
Consolidate compiler generated dependencies of target Tutorial
[ 33%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[ 66%] Building CXX object CMakeFiles/Tutorial.dir/MathFunctions/mysqrt.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial

执行编译后的二进制文件,得到如下输出,可以看到,使用的是标准库中的sqrt函数。

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ ./Tutorial.exe 4294967296
The square root of 4.29497e+09 is 65536

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ ./Tutorial.exe 10
The square root of 10 is 3.16228

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step2_build (master)
$ ./Tutorial.exe
D:\Gitee\CMakeOfficialTutorial\Step2_build\Tutorial.exe Version 1.0
Usage: D:\Gitee\CMakeOfficialTutorial\Step2_build\Tutorial.exe number

Step3 添加库优化

教程第三节

新的语法和命令

commands commands
target_compile_definitions() target_compile_options()
target_include_directories() target_link_libraries()
variables variables
INTERFACE CMAKE_CURRENT_SOURCE_DIR

本节主要讲述了,如何在一个库(lib)目录里面的CMakeLists.txt里面通过添加如下语句,使得当该库(lib)目录在被使用时(即被link时),由cmake自动include当前库的源文件目录。这样,在主目录下面的CMakeLists.txt里面就不用再显式地include这个库(lib)的目录以便查找其头文件。(但还是要在top-level的CMakeLists.txt里面显示地通过target_link_libraries来指明需要链接的目录)

target_include_directories(MathFunctions
                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                           )

改动一

MathFunctions/CMakeLists.txt中,添加如下指令

# Remember INTERFACE means things that consumers 
# require but the producer doesn't. 
# State that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                           )

改动二

在主目录的CMakeLists.txt中,把如下指令

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
  list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")
endif()

修改为

# add the MathFunctions library
if(USE_MYMATH)
  add_subdirectory(MathFunctions)
  list(APPEND EXTRA_LIBS MathFunctions)
endif()

即,删除list(APPEND EXTRA_INCLUDES "${PROJECT_SOURCE_DIR}/MathFunctions")

对应的,还是在主目录的CMakeLists.txt中,把如下指令

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           ${EXTRA_INCLUDES}
                           )

修改为

# add the binary tree to the search path for include files
# so that we will find TutorialConfig.h
target_include_directories(Tutorial PUBLIC
                           "${PROJECT_BINARY_DIR}"
                           )

也就是说,cmake会自动查找需要的库的源文件目录,而不用再在主目录的CMakeLists.txt中显式指明。

编译结果

同样地,使用如下两条命令进行编译

cmake ../Step3 -G "Unix Makefiles"
cmake --build .

结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step3_build (master)
$ cmake ../Step3 -G "Unix Makefiles"
-- The C compiler identification is GNU 8.1.0
-- The CXX compiler identification is GNU 8.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/procs/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/procs/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step3_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step3_build (master)
$ cmake --build .
[ 25%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.obj
[ 50%] Linking CXX static library libMathFunctions.a
[ 50%] Built target MathFunctions
[ 75%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial

Step4 安装和测试

教程第四节

本节主要讲述了如何设置install目录,以便编译完成之后,把对应的二进制文件、头文件以及库文件拷贝到设定好的目录中去。(The install rules are fairly simple: for MathFunctions we want to install the library and header file and for the application we want to install the executable and configured header.)

新的语法和命令

commands variables
install() CMAKE_INSTALL_PREFIX

改动一

在主目录的CMakeLists.txt中,添加如下的指令

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

这样是说明

  • 把二进制文件Tutorial拷贝到install目录下的bin目录中去

  • 把头文件TutorialConfig.h拷贝到install目录下的include目录中去

需要说明的是,如果在CMakeLists.txt中没有指明变量CMAKE_INSTALL_PREFIX,那么这个变量的默认值在windows平台是C:/Program Files(x86)/${PROJECT_NAME},在linux平台是/usr/local

所以这两条指令实际上是说

  • 把二进制文件Tutorial拷贝到C:/Program Files(x86)/Tutorial/bin目录里去

  • 把头文件TutorialConfig.h拷贝到C:/Program Files(x86)/Tutorial/include目录里去去

那么在这里就会有权限的问题,如果后面使用make --install .就会出现没有权限拷贝的问题,所以为了避免该问题,需要在CMakeLists.txt中设定合适的CMAKE_INSTALL_PREFIX

# Set install path
set(MY_INSTALL_DIR "MyInstallPath")
set(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}/${MY_INSTALL_DIR}")

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

或者,另一种办法是在cmake --install .时,添加--prefix <prefix_to_path>来覆盖CMAKE_INSTALL_PREFIX变量的值。

改动二

MathFunctions/CMakeLists.txt中,添加如下指令

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

同样地,根据前面改动一中的说明,这两条指令实际上是说

  • 把库文件libMathFunctions.a拷贝到install目录下的lib目录中去

  • 把头文件MathFunctions.h拷贝到install目录下的include目录中去

编译和安装

使用如下的三条命令来编译和安装

同样地,使用如下两条命令进行编译

cmake ../Step4 -G "Unix Makefiles"
cmake --build .
cmake --install .

结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step4_build (master)
$ cmake ../Step4 -G "Unix Makefiles"
-- The C compiler identification is GNU 8.1.0
-- The CXX compiler identification is GNU 8.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/procs/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/procs/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step4_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step4_build (master)
$ cmake --build .
[ 25%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.obj
[ 50%] Linking CXX static library libMathFunctions.a
[ 50%] Built target MathFunctions
[ 75%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step4_build (master)
$ cmake --install .
-- Install configuration: ""
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step4_build/MyInstallPath/bin/Tutorial.exe
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step4_build/MyInstallPath/include/TutorialConfig.h
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step4_build/MyInstallPath/lib/libMathFunctions.a
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step4_build/MyInstallPath/include/MathFunctions.h

注意,如果需要在cmake的时候手动指明install path,就用如下指令

cmake ../Step4 -G "Unix Makefiles"
cmake --build .
cmake --install . --prefix /my/install/prefix

编译安装结果

在编译和安装结束之后,这里CMakeLists.txt中指明的目录MyInstallPath目录的tree结构如下

MyInstallPath/
├───bin/
│       Tutorial.exe
│
├───include/
│       MathFunctions.h
│       TutorialConfig.h
│
└───lib/
        libMathFunctions.a

添加测试支持

在top-level的CMakeLists.txt中添加如下的testing support

enable_testing()

# PART 1
# does the application run
add_test(NAME Runs COMMAND Tutorial 25)

# does the usage message work?
add_test(NAME Usage COMMAND Tutorial)
set_tests_properties(Usage
  PROPERTIES PASS_REGULAR_EXPRESSION "Usage:.*number"
  )

# PART 2
# define a function to simplify adding tests
function(do_test target arg result)
  add_test(NAME Comp${arg} COMMAND ${target} ${arg})
  set_tests_properties(Comp${arg}
    PROPERTIES PASS_REGULAR_EXPRESSION ${result}
    )
endfunction()

# do a bunch of result based tests
do_test(Tutorial 4 "4 is 2")
do_test(Tutorial 9 "9 is 3")
do_test(Tutorial 5 "5 is 2.236")
do_test(Tutorial 7 "7 is 2.645")
do_test(Tutorial 25 "25 is 5")
do_test(Tutorial -25 "-25 is (-nan|nan|0)")
do_test(Tutorial 0.0001 "0.0001 is 0.01")
  • 第一部分使用了cmakeadd_testset_tests_properties来做简单测试

  • 第二部分定义了一个定制化的函数,并用来测试

如何运行测试

不要切换到install目录下面去,而是要到编译目录下面去(因为有文件CTestTestfile.cmake才行,否则ctest找不到test)。

使用如下命令测试

ctest -N
ctest -VV

结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step4_build (master)
$ ls
cmake_install.cmake  CMakeFiles/
install_manifest.txt  MathFunctions/  Tutorial.exe*
CMakeCache.txt       CTestTestfile.cmake  Makefile  

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step4_build (master)
$ ctest.exe -N
Test project D:/Gitee/CMakeOfficialTutorial/Step4_build
  Test #1: Runs
  Test #2: Usage
  Test #3: Comp4
  Test #4: Comp9
  Test #5: Comp5
  Test #6: Comp7
  Test #7: Comp25
  Test #8: Comp-25
  Test #9: Comp0.0001

Total Tests: 9

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step4_build (master)
$ ctest.exe -VV
UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step4_build/DartConfiguration.tcl
UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step4_build/DartConfiguration.tcl
Test project D:/Gitee/CMakeOfficialTutorial/Step4_build
Constructing a list of tests
Done constructing a list of tests
Updating test list for fixtures
Added 0 tests to meet fixture requirements
Checking test dependency graph...
Checking test dependency graph end
test 1
    Start 1: Runs

1: Test command: D:\Gitee\CMakeOfficialTutorial\Step4_build\Tutorial.exe "25"
1: Test timeout computed to be: 10000000
1: Computing sqrt of 25 to be 13
... ...
... ...
9: Computing sqrt of 0.0001 to be 0.01
9: The square root of 0.0001 is 0.01
9/9 Test #9: Comp0.0001 .......................   Passed    0.07 sec

100% tests passed, 0 tests failed out of 9

Total Test time (real) =   1.15 sec
  

Step5 系统自省(System Introspection)

教程第五节

本节主要讲述了如何通过cmake指令来查找系统平台是否提供了所预期的函数,并根据检查结果进行(条件选择)编译。

新的语法和命令

commands commands
include() check_symbol_exists()
unset() target_compile_definitions()

改动一

MathFunctions/CMakeLists.txt中,添加如下指令

# PART 1
# does this system provide the log and exp functions?
include(CheckSymbolExists)
check_symbol_exists(log "math.h" HAVE_LOG)
check_symbol_exists(exp "math.h" HAVE_EXP)
if(NOT (HAVE_LOG AND HAVE_EXP))
  unset(HAVE_LOG CACHE)
  unset(HAVE_EXP CACHE)
  set(CMAKE_REQUIRED_LIBRARIES "m")
  check_symbol_exists(log "math.h" HAVE_LOG)
  check_symbol_exists(exp "math.h" HAVE_EXP)
  if(HAVE_LOG AND HAVE_EXP)
    target_link_libraries(MathFunctions PRIVATE m)
  endif()
endif()

# PART 2
if(HAVE_LOG AND HAVE_EXP)
  target_compile_definitions(MathFunctions
                             PRIVATE "HAVE_LOG" "HAVE_EXP")
endif()

有两部分

  • 第一部分(PART 1)

    • 通过include(CheckSymbolExists)引入宏CheckSymbolExists,用来检查一个Symbol是否存在

    • 检查系统中(platform头文件)是否存在logexp这两个函数,把检查结果分别存入两个变量HAVE_LOG以及HAVE_EXP

    • 如果没有,就说明有可能现在是其他的一些platform,再次做检查

  • 第二部分(PART 2)

    • 如果前面设置的两个变量HAVE_LOG以及HAVE_EXP都是True,那么就设定两个在源代码中可以使用的宏,名字叫HAVE_LOG以及HAVE_EXP

改动二

MathFunctions/mysqrt.cxxt中,修改代码如下

把如下这行代码

  double result = x;

修改为

#if defined(HAVE_LOG) && defined(HAVE_EXP)
  double result = exp(log(x) * 0.5);
  std::cout << "Computing sqrt of " << x << " to be " << result
            << " using log and exp" << std::endl;
#else
  double result = x;
#endif

可以看到,这里在源代码中检查了宏HAVE_LOG以及HAVE_EXP,这两个宏是在CMake期间定义的。

同时也要加上#include <cmath>这一行。

编译和安装

同样地,使用如下的三条命令来编译和安装

cmake ../Step5 -G "Unix Makefiles"
cmake --build .
cmake --install . --prefix /my/install/prefix

结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step5_build (master)
$ cmake ../Step5 -G "Unix Makefiles"
-- The C compiler identification is GNU 8.1.0
-- The CXX compiler identification is GNU 8.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/procs/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/procs/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Looking for log
-- Looking for log - found
-- Looking for exp
-- Looking for exp - found
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step5_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step5_build (master)
$ cmake --build .
[ 25%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.obj
[ 50%] Linking CXX static library libMathFunctions.a
[ 50%] Built target MathFunctions
[ 75%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step5_build (master)
$ cmake --install .
-- Install configuration: ""
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step5_build/MyInstallPath/bin/Tutorial.exe
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step5_build/MyInstallPath/include/TutorialConfig.h
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step5_build/MyInstallPath/lib/libMathFunctions.a
-- Installing: D:/Gitee/CMakeOfficialTutorial/Step5_build/MyInstallPath/include/MathFunctions.h

需要注意的是,cmake ../Step4 -G "Unix Makefiles"时有打印如下信息,

-- Looking for log
-- Looking for log - found
-- Looking for exp
-- Looking for exp - found

这表示,按照我们在CMakeLists.txt中的定义,在查找当前平台对应的logexp函数(并且找到了)。

Step6 添加自定义命令和生成文件

教程第六节

简述

本节主要讲述了

  • 通过cmake的语法,如何生成一个头文件,使得其他源文件可以引用该头文件

  • 该头文件不是手动写的,而是在cmake编译期间生成的,不在源代码的目录内(当然源代码的目录里有如何生成这个头文件的源代码)

具体地,本节是通过一个源代码文件(C++),生成一个头文件,该头文件里面是一个数组,表示的是10以内的数的平方根的结果,然后由MathFunctions/mysqrt.cxx引用它,以便在计算0以内的数的平方根的时候,直接查询该数组得出结果。

新的语法和命令

commands variables
add_custom_command() CMAKE_CURRENT_BINARY_DIR

CMAKE_CURRENT_BINARY_DIR是当前cmake编译的目录(full path)

add_custom_command()这个command相当于在Makefile中的一条生成-依赖指令,目的是通过定义的一条command来生成一个output,比如教程中的

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
)

它相当于是Makefile中的

OUTPUT: DEPENDS
	COMMAND

具体地是

${CMAKE_CURRENT_BINARY_DIR}/Table.h: MakeTable
	MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h

也就是说,根据MakeTable这个依赖,会生成一个对应的Table.h头文件(target)。

改动一

首先删除了上一节中关于HAVE_LOGHAVE_EXP的宏的创建、检查和引用的代码部分。

改动二

其次,在MathFunctions/CMakeLists.txt中,添加如下两条指令

add_executable(MakeTable MakeTable.cxx)

add_custom_command(
  OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  COMMAND MakeTable ${CMAKE_CURRENT_BINARY_DIR}/Table.h
  DEPENDS MakeTable
  )

第一条是说,要通过MakeTable.cxx生成一个二进制文件(用来运行产生Table.h这个文件)

第二条相当于定义了一条规则,要生成一个Table.h这个target,它的依赖是MakeTable这个二进制文件。

改动三

再次,在MathFunctions/CMakeLists.txt中,修改如下两条指令

add_library(MathFunctions
            mysqrt.cxx
            ${CMAKE_CURRENT_BINARY_DIR}/Table.h
            )
target_include_directories(MathFunctions
          INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
          PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
          )

第一条指令是指mysqrt.cxx文件在生成MathFunctions这个lib是时候,实际上依赖于我们的生成文件Table.h的。

第二条指令是把cmake当前编译的目录加入到头文件的包含的目录列表中去,以便mysqrt.cxx中include这个生成的头文件时,可以找到它。

改动四

最后,需要修改MathFunctions/mysqrt.cxx中源代码,见下,需要注意的是,官网上的代码似乎有错,这里做了修复。

#include <iostream>
#include "MathFunctions.h"
// A generated header file by cmake
#include "Table.h"
// a hack square root calculation using simple operations
double mysqrt(double x) {
  if (x <= 0) {    return 0;  }
  // Use the table to help find an initial value
  // Note that varialbe sqrtTable is defined in a generated
  // header file "Table.h"
  double result = x;
  if (x >= 1 && x < 10) {
    std::cout << "Use the table to help find an initial value " << std::endl;
    result = sqrtTable[static_cast<int>(x)];
	std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
	return result;
  }

  // do ten iterations
  for (int i = 0; i < 10; ++i) {
    if (result <= 0) { result = 0.1; }
    double delta = x - (result * result);
    result = result + 0.5 * delta / result;
    std::cout << "Computing sqrt of " << x << " to be " << result << std::endl;
  }

  return result;
}

实际上,生成文件Table.h的内容如下,

double sqrtTable[] = {
0,
1,
1.41421,
1.73205,
2,
2.23607,
2.44949,
2.64575,
2.82843,
3,
0};

它就是是一个数组,表示的是10以内的数的平方根的结果,然后由MathFunctions/mysqrt.cxx引用它,

编译和安装

同样地,使用如下的三条命令来编译和安装

cmake ../Step6 -G "Unix Makefiles"
cmake --build .
cmake --install . --prefix /my/install/prefix

结果

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step6_build (master)
$ cmake ../Step6 -G "Unix Makefiles"
-- The C compiler identification is GNU 8.1.0
-- The CXX compiler identification is GNU 8.1.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: D:/procs/mingw64/bin/gcc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: D:/procs/mingw64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step6_build

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step6_build (master)
$ cmake --build .
[ 14%] Building CXX object MathFunctions/CMakeFiles/MakeTable.dir/MakeTable.cxx.obj
[ 28%] Linking CXX executable MakeTable.exe
[ 28%] Built target MakeTable
[ 42%] Generating Table.h
[ 57%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.obj
[ 71%] Linking CXX static library libMathFunctions.a
[ 71%] Built target MathFunctions
[ 85%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial

注意,在cmake --build .时,有明显的信息说明生成了生成文件Table.h,如下

[ 28%] Built target MakeTable
[ 42%] Generating Table.h

最后,测试生成的二进制文件,发现在计算10以内的平方根时,确实使用了查表的方法,故结果正确。

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step6_build (master)
$ ./Tutorial.exe 2
Use the table to help find an initial value
Computing sqrt of 2 to be 1.41421
The square root of 2 is 1.41421

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step6_build (master)
$ ./Tutorial.exe 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228

Step7 生成安装包

教程第七节

简述

本节主要讲述了,通过cpack,如何生成一个安装包文件

关于generator的帮助页面:cpack-generators(7)

或者可以直接查询:cpack --help

新的语法和命令

工具:cpack(它是和cmake在一个bin目录下)

module:InstallRequiredSystemLibraries

variables variables
CPACK_RESOURCE_FILE_LICENSE CPACK_PACKAGE_VERSION_MAJOR
CPACK_PACKAGE_VERSION_MINOR CPACK_SOURCE_GENERATOR

其中,在top-level的CMakeLists.txtinclude(InstallRequiredSystemLibraries)这个module,使得它能够找到对应的runtime lib以便生成安装包。

后面的几个变量,是cpack在打包过程当中用到的,其中CPACK_SOURCE_GENERATOR指定了安装包的文件格式(.tar.gz等)

改动

在top-level的CMakeLists.txt中,添加如下指令

# For packing use (by cpack)
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}")
set(CPACK_SOURCE_GENERATOR "TGZ")
include(CPack)

打包

首先仍然需要编译生成二进制文件,然后调用cpack打包

cmake ../Step7 -G "Unix Makefiles"
cmake --build .
cpack -G ZIP

这里需要注意的是,虽然在top-level的CMakeLists.txt中指明了generator是TGZ,但它需要用到NSIS,否则就无法完成打包(这里本地没有安装NSIS)

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step7_build (master)
$ cpack
CPack Error: Cannot find NSIS compiler makensis: likely it is not installed, or not in your PATH
CPack Error: Could not read NSIS registry value. This is usually caused by NSIS not being installed. Please install NSIS from http://nsis.sourceforge.net
CPack Error: Cannot initialize the generator NSIS

所以,这里在cpack的command line中重新指明generator为ZIP,以便生成.zip

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step7_build (master)
$ cpack -G ZIP
CPack: Create package using ZIP
CPack: Install projects
CPack: - Run preinstall target for: Tutorial
CPack: - Install project: Tutorial []
CPack: Create package
CPack: - package: D:/Gitee/CMakeOfficialTutorial/Step7_build/Tutorial-1.0-win64.zip generated.

打包完成后,在目录下就生成了安装包Tutorial-1.0-win64.zip,正如上述log中所说。

Step8 添加Testing Dashboard支持

教程第八节

简述

本节主要讲述了如何添加Testing Dashboard的支持。

实际上,ctest这里就是QA系统的一部分,它帮助完成编译,运行testcases,并且将结果上传到定义好的网站上去(这里利用了Kitware提供的一个公共网站)。

工具:ctest

module:CTest

variables variables
CTEST_PROJECT_NAME CTEST_NIGHTLY_START_TIME
CTEST_DROP_METHOD CTEST_DROP_SITE
CTEST_DROP_LOCATION CTEST_DROP_SITE_CDASH

改动一

因为CTest这个module在被include进来的时候,会自动调用enable_testing(),所以在top-level的CMakeLists.txt里面,把

# enable testing
enable_testing()

替换为

# enable dashboard scripting
# CTest module will automatically call enable_testing()
include(CTest)

改动二

在top-level目录下面,需要创建一个CTestConfig.cmake文件,用来给CTest这个module指明以下信息

  • The project name

  • The project “Nightly” start time

    • The time when a 24 hour “day” starts for this project.

  • The URL of the CDash instance where the submission’s generated documents will be sent

set(CTEST_PROJECT_NAME "CMakeTutorial")
set(CTEST_NIGHTLY_START_TIME "00:00:00 EST")

set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=CMakeTutorial")
set(CTEST_DROP_SITE_CDASH TRUE)

运行结果

首先像以往一样,利用cmake生成Makefile

cmake ../Step8 -G "Unix Makefiles"

但之后不用编译,而是直接运行ctest

ctest -VV -D Experimental

运行之后的log如下:

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step8_build (master)
$ ctest -VV -D Experimental
UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Parse Config file:D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
   Site: SSEA
   Build name: Win32-make
Create new tag: 20220610-1344 - Experimental
Configure project
Configure with command: "D:/procs/CMake/bin/cmake.exe" "D:/Gitee/CMakeOfficialTutorial/Step8"
Run command: "D:/procs/CMake/bin/cmake.exe" "D:/Gitee/CMakeOfficialTutorial/Step8"
-- Configuring done
-- Generating done
-- Build files have been written to: D:/Gitee/CMakeOfficialTutorial/Step8_build
Command exited with the value: 0
UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Parse Config file:D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Build project
MakeCommand:D:/procs/CMake/bin/cmake.exe --build . --config "${CTEST_CONFIGURATION_TYPE}"
Run command: "D:/procs/CMake/bin/cmake.exe" "--build" "." "--config" "Release"
[ 14%] Building CXX object MathFunctions/CMakeFiles/MakeTable.dir/MakeTable.cxx.obj
[ 28%] Linking CXX executable MakeTable.exe
[ 28%] Built target MakeTable
[ 42%] Generating Table.h
[ 57%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.obj
[ 71%] Linking CXX static library libMathFunctions.a
[ 71%] Built target MathFunctions
[ 85%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.obj
[100%] Linking CXX executable Tutorial.exe
[100%] Built target Tutorial
Command exited with the value: 0
MakeCommand:D:/procs/CMake/bin/cmake.exe --build . --config "${CTEST_CONFIGURATION_TYPE}"
   0 Compiler errors
   0 Compiler warnings
UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Parse Config file:D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Test project D:/Gitee/CMakeOfficialTutorial/Step8_build
Constructing a list of tests
Done constructing a list of tests
Updating test list for fixtures
Added 0 tests to meet fixture requirements
Checking test dependency graph...
Checking test dependency graph end
test 1
    Start 1: Runs

1: Test command: D:\Gitee\CMakeOfficialTutorial\Step8_build\Tutorial.exe "25"
1: Test timeout computed to be: 1500
1: Computing sqrt of 25 to be 13
1: Computing sqrt of 25 to be 7.46154
1: Computing sqrt of 25 to be 5.40603
1: Computing sqrt of 25 to be 5.01525
1: Computing sqrt of 25 to be 5.00002
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: Computing sqrt of 25 to be 5
1: The square root of 25 is 5
1/9 Test #1: Runs .............................   Passed    0.20 sec

#### 省略了一些log

UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Parse Config file:D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
 target directory list [D:/Gitee/CMakeOfficialTutorial/Step8_build/CMakeFiles/TargetDirectories.txt]
Performing coverage
 COVFILE environment variable not found, not running  bullseye
   globbing for coverage in: D:/Gitee/CMakeOfficialTutorial/Step8_build/CMakeFiles/Continuous.dir
   globbing for coverage in: D:/Gitee/CMakeOfficialTutorial/Step8_build/CMakeFiles/ContinuousBuild.dir
   globbing for coverage in: 

### 省略了一些log
   
D:/Gitee/CMakeOfficialTutorial/Step8_build/MathFunctions/CMakeFiles/test.dir
 Cannot find any GCov coverage files.
 Not a valid Intel Coverage command.
 Cannot find any Python Trace.py coverage files.
 Cannot find Cobertura XML file: D:/Gitee/CMakeOfficialTutorial/Step8_build/coverage.xml
 Cannot find GTM coverage file: D:/Gitee/CMakeOfficialTutorial/Step8_build/gtm_coverage.mcov
 Cannot find Cache coverage file: D:/Gitee/CMakeOfficialTutorial/Step8_build/cache_coverage.cmcov
 Cannot find Jacoco coverage files: D:/Gitee/CMakeOfficialTutorial/Step8/*jacoco.xml
 Cannot find BlanketJS coverage files: D:/Gitee/CMakeOfficialTutorial/Step8/*.json
 Cannot find Delphi coverage files: D:/Gitee/CMakeOfficialTutorial/Step8_build/*(*.pas).html
 Cannot find any coverage files. Ignoring Coverage request.
UpdateCTestConfiguration  from :D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Parse Config file:D:/Gitee/CMakeOfficialTutorial/Step8_build/DartConfiguration.tcl
Submit files
   SubmitURL: http://my.cdash.org/submit.php?project=CMakeTutorial
   Upload file: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Configure.xml to http://my.cdash.org/submit.php?project=CMakeTutorial&FileName=SSEA___Win32-make___20220610-1344-Experimental___XML___Configure.xml&build=Win32-make&site=SSEA&stamp=20220610-1344-Experimental&MD5=68697147e46c795bd021649a4a8fbb06 Size: 1731
   Uploaded: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Configure.xml
   Upload file: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Build.xml to http://my.cdash.org/submit.php?project=CMakeTutorial&FileName=SSEA___Win32-make___20220610-1344-Experimental___XML___Build.xml&build=Win32-make&site=SSEA&stamp=20220610-1344-Experimental&MD5=5eebae65efe28a7be70508f63cea7428 Size: 1561
   Uploaded: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Build.xml
   Upload file: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Test.xml to http://my.cdash.org/submit.php?project=CMakeTutorial&FileName=SSEA___Win32-make___20220610-1344-Experimental___XML___Test.xml&build=Win32-make&site=SSEA&stamp=20220610-1344-Experimental&MD5=a2c6321fc5ad2113c587ca5e404c65ba Size: 13335
   Uploaded: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Test.xml
   Upload file: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Done.xml to http://my.cdash.org/submit.php?project=CMakeTutorial&FileName=SSEA___Win32-make___20220610-1344-Experimental___XML___Done.xml&build=Win32-make&site=SSEA&stamp=20220610-1344-Experimental&MD5=c37457026038fb68e75bd8fb1e50d2ef Size: 112
   Uploaded: D:/Gitee/CMakeOfficialTutorial/Step8_build/Testing/20220610-1344/Done.xml
   Submission successful

可以看到,ctest自动进行了编译,并且之后运行了CMakeLists.txt中的unittest,然后,把结果上传到了Kitware的公共网页:https://my.cdash.org/index.php?project=CMakeTutorial.

Step9

教程第八节

简述

本节主要讲述了如何通过option函数来设置变量BUILD_SHARED_LIBS,然后编译出动态链接库。

新的语法和命令

command variables
target_compile_definitions

其中,在top-level的CMakeLists.txtinclude(InstallRequiredSystemLibraries)这个module,使得它能够找到对应

BUILD_SHARED_LIBS

Global flag to cause add_library() to create shared libraries if on.

If present and true, this will cause all libraries to be built shared unless the library was explicitly added as a static library. This variable is often added to projects as an option() so that each user of a project can decide if they want to build the project using shared or static libraries.

改动一

在top-level的CMakeLists.txt中,添加如下指令

# control where the static and shared libraries are built so that on windows
# we don't need to tinker with the path to run the executable
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}")

option(BUILD_SHARED_LIBS "Build using shared libraries" ON)

其中option(BUILD_SHARED_LIBS "Build using shared libraries" ON)就是用来控制编译出来动态链接库(除法显示指定生成静态库)。

改动二

其次,在MathFunctions/CMakeLists.txt中,改动完成之后如下

# add the library that runs
add_library(MathFunctions MathFunctions.cxx)

# state that anybody linking to us needs to include the current source dir
# to find MathFunctions.h, while we don't.
target_include_directories(MathFunctions
                           INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}
                           )

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

  target_compile_definitions(MathFunctions PRIVATE "USE_MYMATH")

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

  # library that just does sqrt
  add_library(SqrtLibrary STATIC
              mysqrt.cxx
              ${CMAKE_CURRENT_BINARY_DIR}/Table.h
              )

  # state that we depend on our binary dir to find Table.h
  target_include_directories(SqrtLibrary PRIVATE
                             ${CMAKE_CURRENT_BINARY_DIR}
                             )

  target_link_libraries(MathFunctions PRIVATE SqrtLibrary)
endif()

# define the symbol stating we are using the declspec(dllexport) when
# building on windows
target_compile_definitions(MathFunctions PRIVATE "EXPORTING_MYMATH")

# install rules
set(installable_libs MathFunctions)
if(TARGET SqrtLibrary)
  list(APPEND installable_libs SqrtLibrary)
endif()
install(TARGETS ${installable_libs} DESTINATION lib)
install(FILES MathFunctions.h DESTINATION include)

注意,

  • 这里定义要编译出来的库名虽然是MathFunctions,但和上节不同的是,它依赖于文件MathFunctions.cxx,而不再是mysqrt.cxx

  • 文件mysqrt.cxx是用来生成另外一个库(SqrtLibrary),但这个库是根据宏USE_MYMATH来决定是否要生成。

改动三

MathFunctions/mysqrt.cxx中,将函数double mysqrt(double x)置于mathfunctions::detail这个namespace下面。

改动四

We also need to make some changes in tutorial.cxx, so that it no longer uses USE_MYMATH:

  1. Always include MathFunctions.h

  2. Always use mathfunctions::sqrt

  3. Don’t include cmath

改动五

在文件MathFunctions/MathFunctions.h中,export一个在namespace mathfunction下面的函数

#if defined(_WIN32)
#  if defined(EXPORTING_MYMATH)
#    define DECLSPEC __declspec(dllexport)
#  else
#    define DECLSPEC __declspec(dllimport)
#  endif
#else // non windows
#  define DECLSPEC
#endif

namespace mathfunctions {
double DECLSPEC sqrt(double x);
}

改动六

MathFunctions/CMakeLists.txt中,添加如下指令

# state that SqrtLibrary need PIC when the default is shared libraries
set_target_properties(SqrtLibrary PROPERTIES
                      POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
                      )

target_link_libraries(MathFunctions PRIVATE SqrtLibrary)

根据教程提示,如果不加这一部分会导致链接失败,不过,local测试,不加这一部也能编译通过???不值为何??

At this point, if you build everything, you may notice that linking fails as we are combining a static library without position independent code with a library that has position independent code. The solution to this is to explicitly set the POSITION_INDEPENDENT_CODE target property of SqrtLibrary to be True no matter the build type.

打开USE_MYMATH编译

编译(打开USE_MYMATH宏)

cmake ../Step9 -G "Unix Makefiles" -DUSE_MYMATH=ON
cmake --build .

然后测试

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step9_build (master)
$ ./Tutorial.exe 1
Use the table to help find an initial value
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
Computing sqrt of 1 to be 1
The square root of 1 is 1

显然,生成了SqrtLibrary.a这个库,并且利用了其中的mysqrt来计算平方根。

关闭USE_MYMATH编译

使用如下命令,在关闭自定义的宏USE_MYMATH之后编译

cmake ../Step9 -G "Unix Makefiles" -DUSE_MYMATH=OFF
cmake --build .

然后测试

Pyrad@SSEA MINGW64 /d/Gitee/CMakeOfficialTutorial/Step9_build (master)
$ ./Tutorial.exe 2
The square root of 2 is 1.41421

显然,没有生成SqrtLibrary.a这个库,用的是标准库中的sqrt函数来计算平方根。