CDash

随着项目测试需求的增加,跟踪测试结果会变得很大。对于在多个不同平台上进行夜间测试的项目来说尤其如此。在这些情况下,我们建议使用测试信息中心来汇总测试结果。

测试信息中心汇总了多个平台上许多测试的结果,它的超链接让人们可以快速深入研究更多细节。CTest 可执行文件包括支持制作测试信息中心的功能。当使用正确的选项运行时,CTest 将生成记录构建和测试结果的基于 XML 的输出,并将它们发布到信息中心服务器。信息中心服务器运行名为 CDash 的开源软件包。CDash 收集 XML 结果,并从中生成 HTML 网页。

在讨论如何使用 CTest 来生成信息中心之前,让我们考虑一下测试信息中心的主要组成部分。每天在指定的时间,信息中心服务器将打开一个新的信息中心,因此每天都会有一个新网页显示该 24 小时时段的测试结果。主页面上有链接,可让你快速浏览不同的日期。查看项目的首页(如 CMake 的信息中心,位于 www.cmake.org),你会看到它分为几个主要部分。靠近顶部,你将找到一组链接,允许你进入之前的测试信息中心,还有指向项目页面的链接,如错误跟踪器、文档等。

在其下方,你将找到结果组。通常,你会找到的组包括每晚、实验性、持续、覆盖和动态分析。信息中心条目被放置到的类别取决于它是如何生成的。最简单的是实验性条目,它表示项目源代码当前副本的测试信息中心结果。对于实验性信息中心,不能保证源代码是最新版本。相比之下,每晚的信息中心条目是其中 CTest 尝试将源代码更新到特定日期和时间的条目。预期特定日期的所有每晚信息中心条目都应基于相同的源代码。

持续信息中心条目是每次检入新文件时都会运行的条目。根据检入新文件的频率,一天的信息中心可能会有许多持续条目。持续信息中心对跨平台项目特别有帮助,在这些项目中,问题可能只出现在某些平台上。在那些情况下,开发人员可以提交一个对他们自己的平台有效的修改,然后另一个运行持续构建的平台可能会捕获到该错误,这样开发人员就可以迅速纠正问题。

动态分析和覆盖率仪表板旨在测试项目的内存安全性和代码覆盖率。动态分析仪表板条目是其中所有测试在启用内存访问/泄漏检查程序的情况下运行的条目。解析任何产生的错误或警告并进行总结和展示。这一点对于验证您的软件没有泄漏内存或从未初始化的内存中读取非常重要。覆盖率仪表板条目类似,其中会运行所有测试,但它们会跟踪正在执行的代码行。运行所有测试后,将会生成一份列出每行代码的执行次数的列表并显示在仪表板上。

将 CDash 仪表板支持添加到项目

开始使用 CDash 最简单的方法是注册一个帐户并在 https://my.cdash.org 创建一个新项目。

如果您希望安装自己的 CDash 服务器,请按照以下指南之一进行操作

客户端设置

要支持项目中的仪表板,您需要使用以下方式包含 CTest 模块。

# Include CDash dashboard testing module
include(CTest)

然后,CTest 模块将从您创建的或从 CDash 下载的 CTestConfig.cmake 文件读取设置。如果您已将 add_test 命令调用添加到您的项目,那么创建仪表板条目就像运行以下命令一样简单

ctest -D Experimental

-D 选项指示 CTest 创建仪表板条目。下一个参数表明要创建哪种类型的仪表板条目。创建仪表板条目涉及许多步骤,可以独立运行,也可以作为一个命令运行。在此示例中,Experimental 参数将导致 CTest 以一个命令执行许多不同的步骤。创建仪表板条目的不同步骤总结如下。

开始

准备新的仪表板条目。这会在构建目录中创建一个 Testing 子目录。 Testing 子目录将包含仪表板结果的子目录,其名称与仪表板时间相对应。 Testing 子目录还将包含一个临时测试结果的子目录,名为 Temporary

更新

执行源代码的源代码控制更新(通常用于每晚或连续运行)。目前,CTest 支持并发版本系统 (CVS)、Subversion、Git、Mercurial 和 Bazaar。

配置

在项目上运行 CMake 以确保 Make 文件或项目文件是最新的。

构建

使用指定的生成器构建软件。

测试

运行所有测试并记录测试结果。

MemoryCheck

使用 Purify 或 valgrind 执行内存检查。

Coverage

使用 gcov 或 Bullseye 收集源代码覆盖率信息。

Submit

将测试结果作为仪表盘条目提交给服务器。

以下语法可分别针对夜间或实验性条目运行步骤中的每个步骤

ctest -D NightlyStart
ctest -D NightlyBuild
ctest -D NightlyCoverage -D NightlySubmit

ctest -D ExperimentalStart
ctest -D ExperimentalConfigure
ctest -D ExperimentalCoverage -D ExperimentalSubmit

另外,您可以使用快捷方式一次执行最常见的组合。CTest 定义的快捷方式包括

ctest -D Experimental

执行开始、配置、构建、测试、覆盖率和提交命令。

ctest -D Nightly

执行开始、更新、配置、构建、测试、覆盖率和提交命令。

ctest -D Continuous

执行开始、更新、配置、构建、测试、覆盖率和提交命令。

ctest -D MemoryCheck

执行开始、配置、构建、MemoryCheck、覆盖率和提交命令。

首次设置仪表盘时,将 -D 选项与 -V 选项结合使用通常很有用。这将允许您看到仪表盘过程所有不同阶段的输出。同样,CTest 在它在您的二叉树中创建的 Testing/Temporary 目录中维护日志文件。在那里,您会找到最近仪表盘运行的日志文件。仪表盘结果(XML 文件)也存储在 Testing 目录中。

为项目自定义仪表盘

CTest 有几个选项可用来控制它如何处理项目。如果 CTest 在运行仪表盘时在二叉树中找到 CTestCustom.ctest 文件,它将加载这些文件并使用其中的设置来控制其行为。CTestCustom 文件的语法与常规 CMake 语法相同。也就是说,此文件中通常只使用 set 命令。这些命令指定 CTest 在执行测试时将考虑的属性。

仪表盘提交设置

一些基本的仪表盘设置在从 CDash 下载的文件中提供。如果您希望,您可以编辑这些初始值并提供其他值。设置的第一个值是夜间开始时间。这是世界各地的仪表盘将用于签出其夜间源代码副本的时间。此时间还控制仪表盘提交的组合方式。所有从夜间开始时间到下一个夜间开始时间之间的提交都将包含在同一天中。

# Dashboard is opened for submissions for a 24 hour period
# starting at the specified NIGHTLY_START_TIME. Time is
# specified in 24 hour format.
set (CTEST_NIGHTLY_START_TIME "01:00:00 UTC")

设置的下一组控制将测试结果提交到何处。这是 CDash 服务器的位置。

# CDash server to submit results (used by client)
set (CTEST_DROP_METHOD http)
set (CTEST_DROP_SITE "my.cdash.org")
set (CTEST_DROP_LOCATION "/submit.php?project=KensTest")
set (CTEST_DROP_SITE_CDASH TRUE)

CTEST_DROP_SITE 指定 CDash 服务器的位置。由 CDash 客户端生成的构建和测试结果将发送到此位置。 CTEST_DROP_LOCATION 是服务器上 CDash 客户端放置其构建和测试报告的目录或 HTTP URL。 CTEST_DROP_SITE_CDASH 指定当前服务器是 CDash,从而阻止 CTest 尝试“触发”提交(如果不设置此变量,仍然会执行此操作,以便与 Dart 和 Dart 2 向后兼容)。

当 CDash 目前仅支持 HTTP 放弃提交方法;然而,CTest 支持其他提交类型。 CTEST_DROP_METHOD 指定用于提交测试结果的方法。此最常见的设置将是通过超文本传输协议(HTTP)将测试数据传输到服务器的 HTTP。针对 FTP 和 SCP 等特殊情况支持其他发布方法。在以下示例中,使用 HTTP 协议提交结果的客户端会使用 Web 地址作为其发布站点。如果提交通过 FTP 进行,此位置是相对于 CTEST_DROP_SITE_USER 默认登录的位置。 CTEST_DROP_SITE_USER 指定了客户端在服务器上使用的 FTP 用户名。对于 FTP 提交,此用户通常是“匿名”。然而,可以使用任何可以与服务器通信的用户名。对于需要密码的 FTP 服务器,可以将其存储在 CTEST_DROP_SITE_PASSWORD 变量中。 CTEST_DROP_SITE_MODE(此示例中未使用)是可选变量,可以用它来指定 FTP 模式。大多数 FTP 服务器都将处理默认被动模式,但如果服务器没有,可以将模式明确设置为主动。

还可以在防火墙后运行 CTest。如果防火墙允许 FTP 或 HTTP 流量,则无需其他设置。如果防火墙需要 FTP/HTTP 代理或使用 SOCKS4 或 SOCKS5 类型的代理,则需要设置一些环境变量。HTTP_PROXY 和 FTP_PROXY 指定服务于 HTTP 和 FTP 代理请求的服务器。HTTP_PROXY_PORT 和 FTP_PROXY_PORT 指定 HTTP 和 FTP 代理所在的端口。HTTP_PROXY_TYPE 指定所用 HTTP 代理的类型。支持的三种不同类型的代理包括:默认(其中包括通用 HTTP/FTP 代理)、“SOCKS4”,以及用于指定 SOCKS4 和 SOCKS5 兼容代理的“SOCKS5”。

过滤错误和警告

默认情况下,CTest 有一系列正则表达式,它用来匹配生成过程的输出中的错误和警告。您可以在 CTestCustom.ctest 文件中使用如下所示的多个变量来覆盖这些设置。

set (CTEST_CUSTOM_WARNING_MATCH
  ${CTEST_CUSTOM_WARNING_MATCH}
  "{standard input}:[0-9][0-9]*: Warning: "
  )

set (CTEST_CUSTOM_WARNING_EXCEPTION
  ${CTEST_CUSTOM_WARNING_EXCEPTION}
  "tk8.4.5/[^/]+/[^/]+.c[:\"]"
  "xtree.[0-9]+. : warning C4702: unreachable code"
  "warning LNK4221"
  "variable .var_args[2]*. is used before its value is set"
  "jobserver unavailable"
  )

CTestCustom 文件的另一个有用特性是您可以使用它来限制针对内存检查仪表板运行的测试。使用 purify 或 valgrind 进行的内存检查是 CPU 密集型过程,对于通常需要一小时的仪表板来说可能需要二十个小时。为帮助减轻此问题,CTest 允许您按如下方式从小内存检查过程中排除部分测试

set (CTEST_CUSTOM_MEMCHECK_IGNORE
     ${CTEST_CUSTOM_MEMCHECK_IGNORE}
  TestSetGet
  otherPrint-ParaView
  Example-vtkLocal
  Example-vtkMy
  )

排除测试的格式只是一种测试名称的列表,正如在使用 add_test 时在 CMakeLists 文件中添加测试时所指定的那样。

除了 CTEST_CUSTOM_WARNING_MATCHCTEST_CUSTOM_WARNING_EXCEPTIONCTEST_CUSTOM_MEMCHECK_IGNORE 等已展示的设置外,CTest 还会检查其他一些变量。

CTEST_CUSTOM_ERROR_MATCH

其他正则表达式,用于将生成行视为错误行

CTEST_CUSTOM_ERROR_EXCEPTION

其他正则表达式,用于将生成行不视为错误行

CTEST_CUSTOM_WARNING_MATCH

其他正则表达式,用于将生成行视为警告行

CTEST_CUSTOM_WARNING_EXCEPTION

其他正则表达式,用于将生成行不视为警告行

CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS

在 CTest 停止报告错误之前出现的最大错误数(默认 50)

CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS

在 CTest 停止报告警告之前出现的最大警告数(默认 50)

CTEST_CUSTOM_COVERAGE_EXCLUDE

要从覆盖率分析中排除的文件的正则表达式

CTEST_CUSTOM_PRE_MEMCHECK

在执行内存检查之前要执行的命令列表

CTEST_CUSTOM_POST_MEMCHECK

在执行内存检查之后要执行的命令列表

CTEST_CUSTOM_MEMCHECK_IGNORE

在内存检查步骤中要排除的测试列表

CTEST_CUSTOM_PRE_TEST

在执行测试之前要执行的命令列表

CTEST_CUSTOM_POST_TEST

在执行测试之后要执行的命令列表

CTEST_CUSTOM_TESTS_IGNORE

在测试步骤中要排除的测试列表

CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE

通过测试的最大测试输出大小(默认 1k)

CTEST_CUSTOM_MAXIMUM_FAILED_TEST_OUTPUT_SIZE

对于失败的测试最大的测试输出大小(默认为 300k)

CTEST_CUSTOM_PRE_TESTCTEST_CUSTOM_POST_TEST 中指定的命令以及等效的内存检查命令将在每次 CTest 运行时执行一次。例如,如果所有的测试都需要一些初始设置和一些最终清理工作,则可以使用这些命令。

向仪表板添加注释

CTest 和 CDash 支持向仪表板提交添加注释文件。这些文件将在仪表板中显示为一个可点击图标,链接到所有文件的文本。要添加注释,请使用 -A 选项调用 CTest,后跟分号分隔的文件名列表。这些文件的内容将作为仪表板的注释提交。例如

ctest -D Continuous -A C:/MyNotes.txt;C:/OtherNotes.txt

使用仪表板提交注释的另一种方法是将注释复制或写入到二进制树的 Testing 目录下的 Notes 目录中的文件。CTest 在提交仪表板时在此处找到的任何文件也将上传为注释。

CTest 脚本

本节介绍如何编写基于命令的 CTest 脚本,该脚本允许维护者对仪表板的各个步骤进行细粒度控制。

仪表板维护者可以访问各个 CTest 命令函数,例如 ctest_configurectest_build。通过单独运行这些函数,用户可以灵活地开发自定义测试方案。下面是一个 CTest 脚本示例

cmake_minimum_required(VERSION 3.20)

set(CTEST_SITE          "andoria.kitware")
set(CTEST_BUILD_NAME    "Linux-g++")
set(CTEST_NOTES_FILES
    "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")

set(CTEST_DASHBOARD_ROOT   "$ENV{HOME}/Dashboards/My Tests")
set(CTEST_SOURCE_DIRECTORY "${CTEST_DASHBOARD_ROOT}/CMake")
set(CTEST_BINARY_DIRECTORY "${CTEST_DASHBOARD_ROOT}/CMake-gcc ")

set(CTEST_UPDATE_COMMAND    "/usr/bin/cvs")
set(CTEST_CONFIGURE_COMMAND
    "\"${CTEST_SOURCE_DIRECTORY}/bootstrap\"")
set(CTEST_BUILD_COMMAND     "/usr/bin/make -j 2")


ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY})

ctest_start(Nightly)
ctest_update(SOURCE "${CTEST_SOURCE_DIRECTORY}")
ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_build(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_test(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_submit()

第一个块包含有关提交的变量。

set(CTEST_SITE              "andoria.kitware")
set(CTEST_BUILD_NAME        "Linux-g++")
set(CTEST_NOTES_FILES
    "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")

这些变量用于在将结果提交到仪表板后识别系统。 CTEST_NOTES_FILES 是一个文件列表,这些文件应作为仪表板提交的注释提交。此变量对应于 ctest 的 -A 标志。

第二个块描述了 CTest 函数将用于执行任务的信息

set(CTEST_DASHBOARD_ROOT   "$ENV{HOME}/Dashboards/My Tests")
set(CTEST_SOURCE_DIRECTORY "${CTEST_DASHBOARD_ROOT}/CMake")
set(CTEST_BINARY_DIRECTORY "${CTEST_DASHBOARD_ROOT}/CMake-gcc ")
set(CTEST_UPDATE_COMMAND    "/usr/bin/cvs")
set(CTEST_CONFIGURE_COMMAND
   "\"${CTEST_SOURCE_DIRECTORY}/bootstrap\"")
set(CTEST_BUILD_COMMAND     "/usr/bin/make -j 2")

CTEST_UPDATE_COMMAND 是用于从存储库更新源目录的命令的路径。目前,CTest 支持并发版本系统 (CVS)、Subversion、Git、Mercurial 和 Bazaar。

配置和构建处理程序都支持两种模式。一种模式是提供该阶段将调用的完整命令。这是为了支持不使用 CMake 作为其配置或构建工具的项目而设计的。在这种情况下,您可以通过设置 CTEST_CONFIGURE_COMMANDCTEST_BUILD_COMMAND 变量来分别指定配置和构建项目的完整命令行。

对于构建步骤,您还应该设置变量CTEST_PROJECT_NAMECTEST_BUILD_CONFIGURATION,以指定如何构建项目。在这种情况下,CTEST_PROJECT_NAME将与顶级 CMakeLists 文件的project命令匹配。 CTEST_BUILD_CONFIGURATION应为 Release、Debug、MinSizeRel 或 RelWithDebInfo 之一。此外,可以将CTEST_BUILD_FLAGS作为提示提供给构建命令。基于 CMake 的项目的测试示例如下

set(CTEST_PROJECT_NAME "Grommit")
set(CTEST_BUILD_CONFIGURATION "Debug")

最后一个代码块执行实际测试和提交

ctest_empty_binary_directory(${CTEST_BINARY_DIRECTORY})

ctest_start(Nightly)

ctest_update(SOURCE
             "${CTEST_SOURCE_DIRECTORY}" RETURN_VALUE res)
ctest_configure(BUILD
                "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
ctest_build(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
ctest_test(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)
ctest_submit(RETURN_VALUE res)

ctest_empty_binary_directory命令清空目录和所有子目录。请注意,此命令内置了一个安全措施,即仅当顶级目录中存在 CMakeCache.txt 文件时才会删除该目录。这样做是为了防止 CTest 错误删除非构建目录。

代码块的其余部分包含对 CTest 实际函数的调用。每个函数都对应于一个 CTest -D 选项。例如,代替

ctest -D ExperimentalBuild

脚本将包含

ctest_start(Experimental)
ctest_build(BUILD "${CTEST_BINARY_DIRECTORY}" RETURN_VALUE res)

每个步骤都会产生一个返回值,该返回值指示该步骤是否成功。例如,连续信息中心的更新阶段的返回值可用于确定是否应运行信息中心的其余部分。

让我们检查一个更高级的 CTest 脚本。此脚本驱动对名为 Slicer 的应用程序的测试。Slicer 在内部使用 CMake,但通过一系列 Tcl 脚本驱动构建过程。此方法的一个问题是它不支持非源构建。此外,在 Windows 中,某些模块预构建,因此它们必须复制到构建目录。要测试这样的项目,我们将使用像这样的脚本

cmake_minimum_required(VERSION 3.20)

# set the dashboard specific variables -- name and notes
set(CTEST_SITE              "dash11.kitware")
set(CTEST_BUILD_NAME        "Win32-VS71")
set(CTEST_NOTES_FILES
     "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}")

# do not let any single test run for more than 1500 seconds
set(CTEST_TIMEOUT "1500")

# set the source and binary directories
set(CTEST_SOURCE_DIRECTORY  "C:/Dashboards/MyTests/slicer2")
set(CTEST_BINARY_DIRECTORY  "${CTEST_SOURCE_DIRECTORY}-build")

set (SLICER_SUPPORT
     "//Dash11/Shared/Support/SlicerSupport/Lib")
set (TCLSH   "${SLICER_SUPPORT}/win32/bin/tclsh84.exe")
# set the complete update, configure and build commands
set (CTEST_UPDATE_COMMAND
     "C:/Program Files/TortoiseCVS/cvs.exe")
set (CTEST_CONFIGURE_COMMAND
    "\"${TCLSH}\"
     \"${CTEST_BINARY_DIRECTORY}/Scripts/genlib.tcl\"")
set (CTEST_BUILD_COMMAND
    "\"${TCLSH}\"
     \"${CTEST_BINARY_DIRECTORY}/Scripts/cmaker.tcl\"")

# clear out the binary tree
file (WRITE "${CTEST_BINARY_DIRECTORY}/CMakeCache.txt"
      "// Dummy cache just so that ctest will wipe binary dir")
ctest_empty_binary_directory (${CTEST_BINARY_DIRECTORY})

# special variables for the Slicer build process
set (ENV{MSVC6}              "0")
set (ENV{GENERATOR}          "Visual Studio 7 .NET 2003")
set (ENV{MAKE}               "devenv.exe ")
set (ENV{COMPILER_PATH}
     "C:/Program Files/Microsoft Visual Studio .NET
2003/Common7/Vc7/bin")
set (ENV{CVS}                "${CTEST_UPDATE_COMMAND}")

# start and update the dashboard
ctest_start (Nightly)
ctest_update (SOURCE "${CTEST_SOURCE_DIRECTORY}")

# define a macro to copy a directory
macro (COPY_DIR srcdir destdir)
  exec_program ("${CMAKE_EXECUTABLE_NAME}" ARGS
               "-E copy_directory \"${srcdir}\" \"${destdir}\"")
endmacro ()

# Slicer does not support out of source builds so we
# first copy the source directory to the binary directory
# and then build it
copy_dir ("${CTEST_SOURCE_DIRECTORY}"
          "${CTEST_BINARY_DIRECTORY}")


# copy support libraries that slicer needs into the binary tree
copy_dir ("${SLICER_SUPPORT}"
          "${CTEST_BINARY_DIRECTORY}/Lib")

# finally do the configure, build, test and submit steps
ctest_configure (BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_build (BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_test (BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_submit ()

使用扩展的 CTest 脚本,我们对流程拥有完全控制权,因此我们可以在任何一点执行任意命令。例如,在执行项目更新后,脚本会将源树复制到构建目录。这允许它执行“非源构建”。

项目角色:CDash 为用户提供三种角色级别

  • 普通用户是具有项目的代码存储库的读和/或写访问权限的常规用户。

  • 网站维护人员负责定期提交到 CDash。

  • 项目管理员享有保留特权,可以在 CDash 中管理项目。

前两个级别可由用户自行定义。项目管理员访问权限必须由项目的另一位管理员或 CDash 服务器管理员授予。

提交备份

默认情况下,CDash 备份所有传入的 XML 提交并将它们放在backup目录中。默认时间范围为 48 小时。时间范围可以在config.local.php中更改,如下所示

$CDASH_BACKUP_TIMEFRAME=72;

如果项目是私有的,则建议将备份目录设置在 apache 根目录之外,以确保没有人可以访问 XML 文件,或将以下行添加到备份目录中的 .htaccess 中

<Files *>
order allow,deny
deny from all
</Files>

请注意,只有在新提交到达时才清空备份目录。如果需要,CDash 还可以从备份目录导入构建。

构建组

构建可以通过组进行整理。在 CDash 中,自动定义了三个组且无法移除:NightlyContinuousExperimental。这些组与 CTest 强制的组相同。每个组都有一个相关描述,在主控制面板中单击该组的名称时显示此描述。

在默认情况下,一个构建属于与 CTest 定义的构建类型相关的组,即一个夜间构建将转到 Nightly 部分。CDash 根据其名称、网站和构建类型匹配一个构建。例如,来自“midworld.kitware”网站且名为“Linux-gcc-4.3”的夜间构建将移到 Nightly 部分,除非定义“Linux-gcc-4.3”-“midworld.kitware”-“Nightly”的规则。有两种方式通过定义规则将构建移到给定的组:整体移动和单次移动。

单次移动允许仅修改特定构建。

如果作为项目的管理员登录,则在主控制面板页面中每个构建旁边会显示一个小文件夹图标。单击该图标会显示每个构建的一些选项。尤其是,项目管理员可以将一个构建标记为预期、将一个构建移动到特定组或者删除一个无效构建。

预计构建:项目管理员可以将特定构建标记为预期构建。这意味着预计这些构建每天提交。这使你能够在当天的控制面板中快速检查一个构建是否还未提交,或者通过单击主控制面板中的信息图标快速评估已经缺失的构建丢失了多长时间。

如果预计构建在前一天未提交,且为该项目选中“构建缺失电子邮件”选项,那么将发送电子邮件到网站维护员和项目管理员来提醒他们(有关更多信息,请参阅网站部分)。

电子邮件

当给定构建出现故障时,CDash 会向开发者和项目管理员发送电子邮件。电子邮件功能的配置位于三个地方:config.local.php 文件、项目管理部分和项目的组部分。

config.local.php 中,定义了两个变量来指定发送电子邮件的电子邮件地址和答复的地址。请注意,在当前版本的 CDash 中无法定义 SMTP 服务器,它假定本地电子邮件服务器正在计算机上运行。

$CDASH_EMAIL_FROM = '[email protected]';
$CDASH_EMAIL_REPLY = '[email protected]';

在项目的电子邮件配置部分中,几个参数可以进行调整来控制电子邮件功能。

在项目的“构建组”管理部分中,管理员可以决定是向特定组发送电子邮件还是仅发送摘要电子邮件。当当前天的至少一个构建失败时,为给定组发送摘要电子邮件。

网站

CDash 将站点称为在给定项目中提交至少一次构建的独立计算机。一个站点可以向存储在 CDash 中的多个项目提交多个构建(例如,夜间构建和连续构建)。

要查看站点说明,请从项目的仪表盘主页中单击该站点的名称。站点说明包括有关处理器类型和速度以及该计算机上可用内存量的信息。站点说明由 CTest 自动发送,但在某些情况下可能需要手动编辑。此外,如果对计算机进行升级,即升级内存;CDash 会跟踪说明的历史记录,允许用户比较升级前后的性能。

站点通常属于一名维护人员,该人员负责向 CDash 提交。当站点未进行提交时,提醒站点维护人员是十分重要的,因为它可能与配置问题相关。

站点被认领后,如果客户端计算机出于未知原因未提交,其维护人员将收到电子邮件,假设该站点在夜间提交。此外,该站点将出现在维护人员个人资料的“我的站点”部分中,便于快速查看该站点的状态。

站点页的另一个功能是显示计算机负载的饼图。假设一个站点向多个项目提交,那么了解计算机是否有空间向 CDash 提交其他内容通常很有用。饼图概述了每个项目的计算机提交时间。

图表

CDash 当前绘制三种类型的图表。这些图表根据数据库记录动态生成,且具有交互性。

构建时间图表显示构建项目所需的时间随时间而变化。

测试时间图表显示运行特定测试所需的时间以及随时间变化的状态(通过/失败)。

向构建添加说明

在某些情况下,告知其他开发人员目前有人正在查看构建的错误很有用。

日志记录

CDash 使用 error_log() PHP 函数支持内部日志记录机制。将记录任何关键的 SQL 错误。默认情况下,CDash 日志文件位于备份目录中,文件名为 cdash.log。日志文件的位置可以通过更改 config.local.php 配置文件中的变量进行修改。

$CDASH_BACKUP_DIRECTORY='/var/temp/cdashbackup/log';

如果日志文件位于标准位置,则可以直接从 CDash 访问该日志文件。

提供日志轮换机制,允许用户将当前日志文件限制为特定大小。

测试定时

CDash 支持对测试持续时间进行检查。CDash 保留数据库中每次测试运行所需时间的当前加权平均值、平均值和标准差。为了尽可能提高计算效率,采用了以下仅涉及上一次构建的公式。

// alpha is the current "window" for the computation
// By default, alpha is 0.3
newMean = (1-alpha)*oldMean + alpha*currentTime

newSD = sqrt((1-alpha)*SD*SD +
  alpha*(currentTime-newMean)*(currentTime-newMean)

基于以下逻辑定义测试的定时失败

if previousSD < thresholdSD then previousSD = thresholdSD
if currentTime > previousMean+multiplier*previousSD then fail

移动端支持

由于 CDash 是通过 XSLT 模板层编写的,因此开发新布局与添加新的渲染模板一样简单。作为演示,当前版本的 CDash 附带了一个 iPhone 网页模板。

http://mycdashserver/CDash/iphone

主页面显示服务器上托管的公开项目列表。单击项目名称可加载其当前仪表板。同样,单击给定版本可显示有关该版本的更详细信息。截至撰写本文时,此布局不支持登录和访问 CDash 私有部分的功能。

备份 CDash

CDash 使用的所有数据(日志除外)都存储在其数据库中。定期备份数据库非常重要,尤其是在执行 CDash 升级之前。有几种方法可以备份 MySQL 数据库。最简单的方法是使用 mysqldump 命令。

mysqldump -r cdashbackup.sql cdash

如果你专门使用 MyISAM 表,则可以在 MySQL 数据目录中复制 CDash 目录。请注意,在进行复制之前需要关闭 MySQL,以便在复制过程中不更改任何文件。与 MySQL 类似,PostGreSQL 有一个 pg_dump 实用程序。

pg_dump -U posgreSQL_user cdash > cdashbackup.sql

升级 CDash

当发布 CDash 新版本或者如果你决定从存储库进行更新时,如果当前数据库需要升级,CDash 会在首页上发出警告。升级到新发行版本时,应采取以下步骤

  1. 备份你的 SQL 数据库(参见上一节)。

  2. 备份你的 config.local.php(或 config.php)配置文件。

  3. 用最新版本替换当前的 cdash 目录,并在 cdash 目录中复制 config.local.php

  4. 用你的浏览器导航至你的 CDash 页面。(例如:https://127.0.0.1/CDash)。

  5. 注意主页面上的版本号,它应该与你正在升级到的版本匹配。

  6. 可能会出现以下消息:“当前数据库架构与你正在运行的 CDash 版本不匹配,在 CDash 的管理面板中升级你的数据库结构”。这是一个有用的提醒,用于执行以下步骤。

  7. 以管理员身份登录到 CDash。

  8. 在“管理”部分,单击“[CDash 维护]”。

  9. 单击“升级 CDash”:此过程可能需要一些时间,具体取决于你的数据库大小(不要关闭浏览器)。

    • 在 CDash 执行升级时,可能会出现进度消息。

    • 如果升级过程花费太长时间,则可以在 backup/cdash.log 文件中查看进程花费过多时间和/或失败的位置。

    • 据报告,在某些系统上,旋转图标永远不会变成对勾。如果你觉得升级花费的时间太长,请在 cdash.log 中查找“升级完成”字符串。

    • 在 50GB 数据库上,升级可能需要长达 2 个小时。

  10. 一些网络浏览器在升级时可能会出现问题(一些 JavaScript 变量未正确传递),在这种情况下,你可以执行单个更新。例如,从 CDash 1-2 升级到 1-4

    http://mywebsite.com/CDash/backwardCompatibilityTools.php?upgrade-1-4=1
    

CDash 维护

数据库维护:我们建议你定期执行数据库优化(重新索引、清除等)以维护一个稳定的数据库。MySQL 有一个名为 mysqlcheck 的实用程序,PostgreSQL 有多个实用程序,例如 vacuumdb

删除日期错误的构建:一些构建可能会因 XML 文件中的日期错误或 CDash 未识别时区(主要是由 PHP 导致)而以错误的日期提交至 CDash。这些构建不会显示在任何仪表盘中,因为开始时间无效。要移除这些构建

  1. 以管理员身份登录到 CDash。

  2. 在管理部分单击 [CDash 维护]。

  3. 单击“删除开始日期错误的构建”。

重新计算测试时间:如果您刚刚升级了 CDash,您可能会注意到当前提交显示大量因时间缺陷而失败的测试。这是因为 CDash 没有足够的示例点来计算每个测试的均值和标准差,特别是标准差可能很小(对于前几个示例可能为零)。您应当将“启用测试时间”关闭大约一周,或等到获得足够的构建提交,并且 CDash 已为每个测试时间计算出近似均值和标准差。

另一个选择是强制 CDash 为过去几天的每个测试计算均值和标准差。请注意,此过程可能需要很长时间,具体取决于所涉及的测试和项目数量。要重新计算测试时间

  1. 以管理员身份登录到 CDash。

  2. 在管理部分单击 [CDash 维护]。

  3. 指定要为其重新计算测试时间的日期数量(默认值为 4)。

  4. 单击“计算测试时间”。当此过程完成时,在此期间提交的测试的新均值、标准差和状态应当已更新。

自动构建删除

为了使数据库保持合理大小,CDash 可以自动清除旧构建。当前有两种设置自动构建删除的方法:如果没有 cron 作业,则编辑 config.local.php 并添加/编辑以下行

$CDASH_AUTOREMOVE_BUILDS='1';

CDash 会在当天的第一次提交时自动删除构建。请注意,删除构建可能会给数据库增加额外负载,或如果您的数据库较大并且提交数量高,则会减慢当前提交过程。如果您可以使用 cron 作业,则可以使用 PHP 命令行工具在方便的时间触发构建删除。例如,在每个星期日早上 6 点删除所有项目的构建

0 6 * * 0 php5 /var/www/CDash/autoRemoveBuilds.php all

请注意,“all”参数可以更改为特定的项目名称,以便从单个项目中清除构建。

CDash XML 架构

可以轻松扩展 CDash 中的 XML 解析器,以支持新特性。由 CTest 生成的当前 XML 架构及其特性(如书中所述)位于

http://public.kitware.com/Wiki/CDash:XML

子项目

CDash 支持将项目拆分为子项目。一些子项目可能反过来依赖于其他子项目。典型的真实项目包括库、可执行文件、测试套件、文档、网页和安装程序。将您的项目组织到定义明确的子项目中并在 CDash 仪表盘上呈现 nightly build 的结果可以帮助确定在不同粒度级别的问题所在。

具有子项目的项目,其顶部 CDash 页面的视图与没有任何子项目的项目不同。它包含一个关于整个项目的汇总行,然后是每个子项目的汇总行。

组织和定义子项目

要为您的项目添加子项目组织,您必须:(1) 为 CDash 定义子项目,以便它知道如何正确显示它们,以及 (2) 使用带 CTest 的构建脚本提交您项目的子项目构建。可能还需要对项目的 CMakeLists.txt 文件进行一些(重新)组织,以允许按子项目构建您的项目。

定义子项目及其依赖关系有两种方法:以项目管理员的身份在 CDash GUI 中交互进行定义,或者通过提交描述子项目和依赖关系的 Project.xml 文件来进行定义。

以交互方式添加子项目

作为项目管理员,“管理子项目”按钮将出现在 My CDash 页面中所有项目中。单击“管理子项目”按钮会打开管理子项目页面,您可以在其中为所管理的任何项目添加新的子项目或在现有子项目之间建立依赖关系。

自动添加子项目

定义 CDash 子项目及其依赖关系的另一种方法是,在 CTest 提交构建到 CDash 时提交“Project.xml”文件以及与 CTest 发送的常规提交文件。要定义与上述交互示例中相同的两个子项目(Exes 和 Libs),以及相同的依赖关系(Exes 依赖 Libs),Project.xml 文件将如下图例所示

<Project name="Tutorial">
  <SubProject name="Libs"></SubProject>
  <SubProject name="Exes">
    <Dependency name="Libs">
  </SubProject>
</Project>

编写或生成 Project.xml 文件后,可使用 FILES 参数向 ctest_submit 命令提交至 ctest -S 脚本中的 CDash,或通过为仪表盘提交配置的构建树中的 ctest 命令行直接提交。

从 ctest -S 脚本内部

ctest_submit(FILES "${CTEST_BINARY_DIRECTORY}/Project.xml")

从命令行

cd ../Project-build
ctest --extra-submit Project.xml

CDash 会根据 Project.xml 文件自动添加子项目和依赖关系。CDash 还会删除 Project.xml 文件中未定义的任何子项目或依赖关系。此外,如果多次提交相同的 Project.xml,第二次及后续提交将不会产生明显的影响:第一次提交添加/修改数据,第二次及后续提交发送相同的数据,因此无需更改。CDash 会跟踪子项目定义随着时间的变化,以允许项目进行演变。如果您查看过去某天的仪表盘,CDash 将根据该日期适用的子项目定义展示项目/子项目视图。

将 ctest_submit 与 PARTS 和 FILES 一起使用

ctest_submit 命令支持 PARTSFILES 参数。使用 PARTS,您可以通过每次 ctest_submit 调用发送任何 xml 文件子集。默认情况下,所有可用部分都会随每次对 ctest_submit 的调用发送。当所有仪表盘阶段完成后,脚本会等待,然后在运行结束时调用 ctest_submit 一次发送所有阶段的结果。或者,脚本可能会使用 PARTS 调用 ctest_submit 执行结果子集的部分提交。例如,您可以在 ctest_configure 后提交配置结果,在 ctest_build 后提交生成结果,在 ctest_test 后提交测试结果。这允许在构建进行期间发布信息。

使用 FILES,您可以将任意 XML 文件发送到 CDash。除了 CTest 发送的标准生成结果 XML 文件以外,CDash 还处理描述子项目和依赖项的 Project.xml 文件。以下是一个包含 ctest_submit 调用在其最后一行中的仪表盘脚本示例

ctest_start(Experimental)
ctest_update(SOURCE "${CTEST_SOURCE_DIRECTORY}")
ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_build(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_test(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_submit()

提交可以增量执行,每个提交部分随着其可用而分批发送

ctest_start(Experimental)
ctest_update(SOURCE "${CTEST_SOURCE_DIRECTORY}")
ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_submit(PARTS Update Configure Notes)

ctest_build(BUILD "${CTEST_BINARY_DIRECTORY}" APPEND)
ctest_submit(PARTS Build)

ctest_test(BUILD "${CTEST_BINARY_DIRECTORY}")
ctest_submit(PARTS Test)

增量分块提交意味着在构建仍在进行时,您可以在 CDash 仪表盘上实时检查配置阶段的结果。同样,您可以在测试仍在运行时实时检查构建阶段的结果。分块提交时,务必在 APPEND 关键字的 ctest_build 命令中使用。如果您不使用 APPEND,那么收到了 Build.xml 文件时,CDash 将会抹掉具有相同的构建名称、站点名称和构建戳记的任何现有构建。

将项目拆分成多个子项目

一个 ctest_build 调用构建所有内容,然后一个 ctest_test 调用测试所有内容,这对没有子项目的项目来说已经足够了,但如果您想按子项目在 CDash 上提交结果,您需要对项目和测试脚本做一些更改。对于您的项目,您需要确定哪些目标属于哪些子项目。如果您按照为每个子项目构建一个目标来组织您的 CMakeLists 文件,并且您能够根据子项目名称推导出(或查找)该目标的名称,那么将脚本修改为分成多个更小的配置/构建/测试块应该相对来说比较省力。要做到这一点,您可以根据需要以各种方式修改 CMakeLists 文件。最常见的更改如下所列。

CMakeLists.txt 修改

  • 目标名称与子项目名称相同,基于子项目名称给目标命名,或提供查找机制以从子项目名称映射到目标名称。

  • 可能添加自定义目标以使用 add_dependencies 来集合现有目标到子项目中,以说明自定义目标依赖于哪个现有目标。

  • LABELS 目标属性添加带有子项目名称的值的目标。

  • LABELS 测试属性添加带有子项目名称的值的测试。

接下来,您需要修改运行仪表板的 CTest 脚本。为了将您庞大的单一构建拆分成更小的子项目构建,您可以在 CTest 驱动程序脚本中使用 foreach 循环。为了帮助您遍历子项目,CDash 在 CTestConfig.cmake 中提供了一个名为 CTEST_PROJECT_SUBPROJECTS 的变量。鉴于上述示例,CDash 生成了如下的变量

set(CTEST_PROJECT_SUBPROJECTS Libs Exes)

CDash 对此列表中的元素进行排序,以便独立的子项目(不依赖于任何其他子项目)排在首位,接下来是仅依赖于独立子项目的子项目,然后是依赖于这些子项目的子项目。相同的逻辑一直持续,直到所有子项目在此列表中仅列出一次,并且以适合于逐个顺序构建它们的顺序。

要简化仅构建与子项目关联的目标,请使用名为 CTEST_BUILD_TARGET 的变量来告诉 ctest_build要构建的内容。要简化仅运行与子项目关联的测试,请将 LABELS 测试属性分配给测试,并使用 INCLUDE_LABEL 参数 ctest_test

ctest 驱动器脚本修改

  • 按照依赖顺序(从独立到最依赖...)迭代子项目。

  • 设置 SubProject 和 Label 全局属性 - CTest 使用这些属性将结果提交到 CDash 服务器上的正确子项目。

  • 为这个子项目构建目标:从子项目名称计算要构建的目标名称,设置 CTEST_BUILD_TARGET,调用 ctest_build

  • 使用 INCLUDEINCLUDE_LABEL 参数 ctest_test运行此子项目的测试。

  • 使用 PARTS 参数 ctest_submit在它们完成时提交部分结果。

为了说明这一点,以下示例显示了将构建拆分为较小部分所需的更改。假设子项目名称与构建子项目的组件所需的目标名称相同。例如,以下是我们在假设的 Tutorial 项目中从 CMakeLists.txt 中提取的代码段。唯一必要的添加(因为目标名称与子项目名称相同)是对每个目标和每个测试的 set_property 的调用。

# "Libs" is the library name (therefore a target name) and
# the subproject name
add_library (Libs ...)
set_property (TARGET Libs PROPERTY LABELS Libs)
add_test (LibsTest1 ...)
add_test (LibsTest2 ...)
set_property (TEST LibsTest1 LibsTest2 PROPERTY LABELS Libs)

# "Exes" is the executable name (therefore a target name)
# and the subproject name
add_executable (Exes ...)
target_link_libraries (Exes Libs)
set_property (TARGET Exes PROPERTY LABELS Exes)
add_test (ExesTest1 ...)
add_test (ExesTest2 ...)
set_property (TEST ExesTest1 ExesTest2 PROPERTY LABELS Exes)

以下示例说明在将此项目组织成子项目之前和之后,CTest 驱动器脚本可能是什么样子。在更改之前

ctest_start (Experimental)
ctest_update (SOURCE "${CTEST_SOURCE_DIRECTORY}")
ctest_configure (BUILD "${CTEST_BINARY_DIRECTORY}")
# builds *all* targets: Libs and Exes
ctest_build (BUILD "${CTEST_BINARY_DIRECTORY}")
# runs *all* tests
ctest_test (BUILD "${CTEST_BINARY_DIRECTORY}")
# submits everything all at once at the end
ctest_submit ()

在更改之后

ctest_start (Experimental)
ctest_update (SOURCE "${CTEST_SOURCE_DIRECTORY}")
ctest_submit (PARTS Update Notes)

# to get CTEST_PROJECT_SUBPROJECTS definition:
include ("${CTEST_SOURCE_DIRECTORY}/CTestConfig.cmake")

foreach (subproject ${CTEST_PROJECT_SUBPROJECTS})
  set_property (GLOBAL PROPERTY SubProject ${subproject})
  set_property (GLOBAL PROPERTY Label ${subproject})

  ctest_configure (BUILD "${CTEST_BINARY_DIRECTORY}")
  ctest_submit (PARTS Configure)

  set (CTEST_BUILD_TARGET "${subproject}")
  ctest_build (BUILD "${CTEST_BINARY_DIRECTORY}" APPEND)
    # builds target ${CTEST_BUILD_TARGET}
  ctest_submit (PARTS Build)

  ctest_test (BUILD "${CTEST_BINARY_DIRECTORY}"
    INCLUDE_LABEL "${subproject}"
  )
# runs only tests that have a LABELS property matching
# "${subproject}"
  ctest_submit (PARTS Test)
endforeach ()

在部分项目中,可能需要多个 ctest_build 步骤才能生成子项目的全部内容。例如,在 Trilinos 中,每个子项目都会生成 ${subproject}_libs target,然后再生成 all target 来生成测试套件中全部配置的可执行文件。他们还配置依赖项,以便只有在生成 all target 时,才生成需要为当前配置包构建的可执行文件。

通常,如果你使用完全相同的生成标记向 CDash 提交多个 Build.xml 文件,它会删除现有条目,然后在此处添加新条目。在需要多个 ctest_build 步骤的情况下,每个步骤都有自己的 ctest_submit(PARTS Build) 调用,请在所有 ctest_build 调用(属于同一组)中使用 APPEND 参数。 APPEND 标志指示 CDash 累积多次提交中的结果,并在仪表盘中一行中显示它们全部汇总的结果。从 CDash 的角度来看,多个 ctest_build 调用(具有相同的生成标记和子项目,且已启用 APPEND)会生成一个 CDash 生成。

在你最喜爱的基于 CMake 的项目中采用这些提示和技巧

  • LABELS 是对源文件、目标和测试适用的 CMake/CTest 属性。标签通过生成的 XML 文件发送到 CDash。

  • 使用 ctest_submit(PARTS ... 来执行增量提交。将在仪表盘上更快地显示结果。在提交部分内容时,不要忘记在 ctest_build 调用中使用 APPEND

  • 使用 INCLUDE_LABEL 以及 ctest_test 仅运行与正则表达式匹配的那些标签的测试。

  • 使用 CTEST_BUILD_TARGET 一次一个构建你的子项目,在此过程中提交子项目仪表板。