CDash Overview
CDash is Kitware's open-source testing dashboard that aggregates build, test, coverage, and dynamic analysis results from CTest submissions. It provides a web-based view of project health across multiple platforms, compilers, and configurations. See the CTest dashboard client documentation for the official reference.
Dashboard Anatomy
A CDash dashboard displays submissions organized by date and group:
flowchart LR
A[Developer Machine] -->|ctest -D Experimental| D[CDash Server]
B[Nightly CI Job] -->|ctest -D Nightly| D
C[Commit Hook] -->|ctest -D Continuous| D
D --> E[Build Warnings/Errors]
D --> F[Test Pass/Fail]
D --> G[Coverage %]
D --> H[Memory Leaks]
E --> I[Web Dashboard]
F --> I
G --> I
H --> I
CTest Dashboard Modes
Experimental Mode
Experimental submissions are ad-hoc — developers run them locally to check their changes before committing. They appear in a separate group on the dashboard and don't affect the project's health indicators.
# Quick experimental submission from build directory
cd build
ctest -D Experimental
# This performs: start → configure → build → test → submit
# All in one command with default settings
Nightly Mode
Nightly builds run at a scheduled time (default 2:00 AM project time), check out the latest code, and submit comprehensive results. They form the baseline for project health.
# Nightly submission — typically from a scheduled CI job
ctest -D Nightly
# The nightly start time is configured in CTestConfig.cmake
# CTest updates source to the nightly timestamp before building
Continuous Mode
# Continuous mode — triggered by commits
ctest -D Continuous
# Only builds/tests if source has changed since last submission
# Efficient for post-commit hooks or frequent polling
Configuration
CTestConfig.cmake
# CTestConfig.cmake — placed in project root alongside CMakeLists.txt
set(CTEST_PROJECT_NAME "MyProject")
set(CTEST_NIGHTLY_START_TIME "02:00:00 UTC")
# CDash server connection
set(CTEST_DROP_METHOD "https")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=MyProject")
set(CTEST_DROP_SITE_CDASH TRUE)
# Optional: authentication token for private dashboards
set(CTEST_AUTH_TOKEN "$ENV{CDASH_AUTH_TOKEN}")
CTest Scripts
For fine-grained control, use CTest scripting mode with ctest -S script.cmake:
# dashboard.cmake — full CTest dashboard script
set(CTEST_SOURCE_DIRECTORY "/path/to/source")
set(CTEST_BINARY_DIRECTORY "/path/to/build")
set(CTEST_CMAKE_GENERATOR "Ninja")
set(CTEST_BUILD_CONFIGURATION "Release")
# Configure build name for dashboard identification
set(CTEST_SITE "github-actions-ubuntu")
set(CTEST_BUILD_NAME "Linux-GCC12-Release")
# Execute the dashboard steps
ctest_start("Nightly")
ctest_update()
ctest_configure()
ctest_build()
ctest_test(PARALLEL_LEVEL 8)
ctest_coverage()
ctest_memcheck()
ctest_submit()
Objective: Submit your first test results to a CDash dashboard.
Use the public CDash instance at https://open.cdash.org to create a test project. Add CTestConfig.cmake to your project, add a few tests with add_test(), then run ctest -D Experimental. Verify your submission appears on the dashboard within seconds.
Submitting Results
Each ctest_* command generates XML files in build/Testing/. The ctest_submit() command uploads them to CDash. You can submit partial results — for example, build + test without coverage:
# Selective submission — only build and test results
ctest_start("Experimental")
ctest_configure()
ctest_build(NUMBER_ERRORS num_errors NUMBER_WARNINGS num_warnings)
ctest_test(RETURN_VALUE test_result)
# Submit only specific parts
ctest_submit(PARTS Configure Build Test)
# Check results in script
if(num_errors GREATER 0)
message(WARNING "Build had ${num_errors} errors")
endif()
if(NOT test_result EQUAL 0)
message(WARNING "Some tests failed")
endif()
# Submit with retry logic for unreliable networks
ctest -D Experimental --submit-index 3
# View what would be submitted without actually sending
ctest -D Experimental --no-submit
# Submit previously generated results
ctest -D ExperimentalSubmit
Coverage Reporting
CDash displays line-by-line coverage data. Configure your project for coverage collection with gcov or llvm-cov, then ctest_coverage() gathers and submits the results.
# CMakeLists.txt — enable coverage flags
option(ENABLE_COVERAGE "Enable code coverage" OFF)
if(ENABLE_COVERAGE)
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
add_compile_options(--coverage -O0 -g)
add_link_options(--coverage)
endif()
endif()
add_executable(myapp main.cpp utils.cpp)
add_test(NAME unit_tests COMMAND myapp --test)
# coverage-dashboard.cmake — CTest script with coverage
set(CTEST_SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}")
set(CTEST_BINARY_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/build-cov")
set(CTEST_CMAKE_GENERATOR "Ninja")
set(CTEST_BUILD_CONFIGURATION "Debug")
set(CTEST_COVERAGE_COMMAND "gcov")
# Configure with coverage enabled
set(CTEST_CONFIGURE_OPTIONS "-DENABLE_COVERAGE=ON")
ctest_start("Experimental")
ctest_configure()
ctest_build()
ctest_test()
ctest_coverage()
ctest_submit()
CTEST_COVERAGE_COMMAND to llvm-cov and CTEST_COVERAGE_EXTRA_FLAGS to "gcov" for gcov-compatible output that CDash understands.
Memory Checking
CDash tracks memory errors (leaks, invalid reads, uninitialized values) from tools like Valgrind, AddressSanitizer, and Dr. Memory. The ctest_memcheck() command runs tests under a memory checker and uploads results.
# memcheck-dashboard.cmake — Valgrind memory checking
set(CTEST_SOURCE_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}")
set(CTEST_BINARY_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/build-memcheck")
set(CTEST_CMAKE_GENERATOR "Ninja")
set(CTEST_BUILD_CONFIGURATION "Debug")
# Valgrind configuration
set(CTEST_MEMORYCHECK_COMMAND "/usr/bin/valgrind")
set(CTEST_MEMORYCHECK_COMMAND_OPTIONS
"--leak-check=full --show-leak-kinds=all --track-origins=yes")
set(CTEST_MEMORYCHECK_SUPPRESSIONS_FILE
"${CTEST_SOURCE_DIRECTORY}/valgrind.supp")
ctest_start("Experimental")
ctest_configure()
ctest_build()
ctest_memcheck(PARALLEL_LEVEL 4)
ctest_submit()
# Run memory check locally without submitting
ctest -T MemCheck
# View memory check results
cat build/Testing/Temporary/MemoryChecker.*.log
# Use AddressSanitizer instead of Valgrind (faster)
cmake -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" ..
ctest -T Test
# ASan output appears in test logs, visible on CDash
Objective: Create a project with an intentional memory leak and detect it via CDash.
Write a test that allocates memory without freeing it. Configure ctest_memcheck() with Valgrind, submit to CDash, and observe the "Dynamic Analysis" tab showing the leak location, size, and stack trace. Then fix the leak and verify CDash shows zero defects on the next submission.
Setting Up CDash Server
For private projects, host your own CDash instance. CDash is a PHP/MySQL application deployed via Docker:
# Pull and run the official CDash Docker image
docker pull kitware/cdash:latest
docker run -d \
--name cdash \
-p 8080:80 \
-v cdash-data:/var/lib/mysql \
-e CDASH_DB_HOST=localhost \
-e CDASH_DB_NAME=cdash \
-e CDASH_DB_LOGIN=cdash \
-e CDASH_DB_PASS=secure_password \
kitware/cdash:latest
# Access dashboard at http://localhost:8080
# Create admin account on first visit
# Then create a project and note the submit URL
flowchart TD
A[CI Runner 1] -->|HTTPS POST| B[CDash Web Server]
C[CI Runner 2] -->|HTTPS POST| B
D[Developer] -->|HTTPS POST| B
B --> E[PHP Application]
E --> F[(MySQL Database)]
E --> G[File Storage - XML/Logs]
B --> H[Web Dashboard UI]
H --> I[Build Summary]
H --> J[Test Results]
H --> K[Coverage Trends]
H --> L[Memory Analysis]
GitHub Actions Integration
Automate CDash submissions from CI pipelines. This workflow submits Nightly results every day and Experimental results on every push:
# .github/workflows/cdash.yml
name: CDash Dashboard
on:
push:
branches: [main]
schedule:
- cron: '0 2 * * *' # Nightly at 2 AM UTC
jobs:
dashboard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y ninja-build valgrind lcov
- name: Configure
run: |
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug \
-DENABLE_COVERAGE=ON -S . -B build
- name: Submit to CDash
env:
CDASH_AUTH_TOKEN: ${{ secrets.CDASH_TOKEN }}
run: |
cd build
if [ "${{ github.event_name }}" = "schedule" ]; then
ctest -D Nightly
else
ctest -D Experimental
fi
# Alternative: Use a CTest script for more control
# .github/scripts/dashboard.cmake
set(CTEST_SOURCE_DIRECTORY "$ENV{GITHUB_WORKSPACE}")
set(CTEST_BINARY_DIRECTORY "$ENV{GITHUB_WORKSPACE}/build")
set(CTEST_CMAKE_GENERATOR "Ninja")
set(CTEST_SITE "github-actions")
set(CTEST_BUILD_NAME "$ENV{GITHUB_REF_NAME}-$ENV{GITHUB_SHA}")
set(CTEST_BUILD_CONFIGURATION "Debug")
set(CTEST_COVERAGE_COMMAND "gcov")
set(dashboard_model "$ENV{CDASH_MODEL}")
if(NOT dashboard_model)
set(dashboard_model "Experimental")
endif()
ctest_start(${dashboard_model})
ctest_configure()
ctest_build(NUMBER_ERRORS errors)
ctest_test(PARALLEL_LEVEL $ENV{NPROC})
ctest_coverage()
ctest_submit()
Objective: Set up a complete GitHub Actions workflow that submits to CDash on every push.
Create a repository with tests and coverage enabled, configure CTestConfig.cmake pointing to open.cdash.org, add the GitHub Actions workflow above, and push. Verify that each commit creates a new dashboard entry with build, test, and coverage data.
Conclusion & Next Steps
CDash transforms isolated CI results into a unified project health dashboard with historical trends, cross-platform comparisons, and actionable regression alerts. Combined with coverage and memory-checking, it provides comprehensive quality metrics that improve team accountability and code reliability.
Next in the Series
In Part 28: Porting Projects to CMake, we'll tackle the practical challenge of migrating existing projects from Makefiles, Autotools, and qmake to modern CMake — covering incremental strategies, common pitfalls, and validation techniques.