Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions itk_common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,114 @@ if(NOT DEFINED dashboard_loop)
endif()
endif()

# CI log section helpers — emit collapsible group markers for
# GitHub Actions, Azure DevOps, and GitLab CI.
# GitLab CI requires unique section IDs within a job log, so callers
# in the loop below include _dashboard_iteration in the ID.
string(ASCII 27 _CI_ESC)

function(ci_section_start section_id title)
if(DEFINED ENV{GITLAB_CI})
string(TIMESTAMP _epoch "%s" UTC)
message("${_CI_ESC}[0Ksection_start:${_epoch}:${section_id}[collapsed=true]\r${_CI_ESC}[0K${title}")
elseif(DEFINED ENV{GITHUB_ACTIONS})
message("::group::${title}")
elseif(DEFINED ENV{TF_BUILD})
message("##[group]${title}")
else()
message("--- ${title} ---")
endif()
endfunction()

function(ci_section_end section_id)
if(DEFINED ENV{GITLAB_CI})
string(TIMESTAMP _epoch "%s" UTC)
message("${_CI_ESC}[0Ksection_end:${_epoch}:${section_id}\r${_CI_ESC}[0K")
elseif(DEFINED ENV{GITHUB_ACTIONS})
message("::endgroup::")
elseif(DEFINED ENV{TF_BUILD})
message("##[endgroup]")
else()
message("--- end ${section_id} ---")
endif()
endfunction()

# Extract and print build warnings/errors from Build.xml so they
# appear directly in the CI log. ctest_build() only exposes counts
# (NUMBER_WARNINGS / NUMBER_ERRORS) — the actual compiler messages
# live inside the XML that CTest writes for CDash submission.
function(ci_report_build_diagnostics binary_dir num_warnings num_errors)
if(num_warnings EQUAL 0 AND num_errors EQUAL 0)
return()
endif()

# Locate the Build.xml via the TAG file that CTest maintains.
set(_tag_file "${binary_dir}/Testing/TAG")
if(NOT EXISTS "${_tag_file}")
return()
endif()
file(STRINGS "${_tag_file}" _tag_lines)
list(GET _tag_lines 0 _tag_dir)
set(_build_xml "${binary_dir}/Testing/${_tag_dir}/Build.xml")
if(NOT EXISTS "${_build_xml}")
return()
endif()

# Read Build.xml — escape semicolons so CMake list operations
# don't mangle lines that happen to contain them.
file(READ "${_build_xml}" _xml)
string(REPLACE ";" "\\;" _xml "${_xml}")
string(REPLACE "\n" ";" _xml_lines "${_xml}")

# Walk the XML line-by-line, tracking whether we are inside a
# <Warning> or <Error> block, and collect the <Text> content.
set(_in_warning FALSE)
set(_in_error FALSE)
set(_warning_texts "")
set(_error_texts "")
foreach(_line IN LISTS _xml_lines)
if("${_line}" MATCHES "<Warning>")
set(_in_warning TRUE)
elseif("${_line}" MATCHES "</Warning>")
set(_in_warning FALSE)
elseif("${_line}" MATCHES "<Error>")
set(_in_error TRUE)
elseif("${_line}" MATCHES "</Error>")
set(_in_error FALSE)
endif()
if("${_line}" MATCHES "<Text>(.*)</Text>")
if(_in_warning)
list(APPEND _warning_texts "${CMAKE_MATCH_1}")
elseif(_in_error)
list(APPEND _error_texts "${CMAKE_MATCH_1}")
endif()
endif()
endforeach()

# Print collected diagnostics so they are visible in CI output.
if(num_errors GREATER 0)
message("========== BUILD ERRORS (${num_errors}) ==========")
foreach(_t IN LISTS _error_texts)
message(" ${_t}")
endforeach()
endif()
if(num_warnings GREATER 0)
message("========== BUILD WARNINGS (${num_warnings}) ==========")
foreach(_t IN LISTS _warning_texts)
message(" ${_t}")
endforeach()
endif()
message("====================================================")
endfunction()

if(COMMAND dashboard_hook_init)
dashboard_hook_init()
endif()

set(dashboard_done 0)
set(_dashboard_iteration 0)
while(NOT dashboard_done)
math(EXPR _dashboard_iteration "${_dashboard_iteration} + 1")
if(dashboard_loop)
set(START_TIME ${CTEST_ELAPSED_TIME})
endif()
Expand Down Expand Up @@ -423,39 +525,57 @@ while(NOT dashboard_done)
message("Found ${count} changed files")

if(dashboard_fresh OR NOT dashboard_continuous OR count GREATER 0)
ci_section_start("configure_${_dashboard_iteration}" "Configure")
ctest_configure(RETURN_VALUE configure_return)
ctest_read_custom_files(${CTEST_BINARY_DIRECTORY})
ci_section_end("configure_${_dashboard_iteration}")

ci_section_start("build_${_dashboard_iteration}" "Build")
if(COMMAND dashboard_hook_build)
dashboard_hook_build()
endif()
ctest_build(RETURN_VALUE build_return
NUMBER_ERRORS build_errors
NUMBER_WARNINGS build_warnings)
ci_section_end("build_${_dashboard_iteration}")

# Intentionally placed OUTSIDE the collapsible build section so
# that warnings and errors are always visible in the CI log
# without having to expand the build section.
ci_report_build_diagnostics(
"${CTEST_BINARY_DIRECTORY}" "${build_warnings}" "${build_errors}")

ci_section_start("test_${_dashboard_iteration}" "Test")
if(COMMAND dashboard_hook_test)
dashboard_hook_test()
endif()
ctest_test(${CTEST_TEST_ARGS} RETURN_VALUE test_return)
ci_section_end("test_${_dashboard_iteration}")

if(dashboard_do_coverage)
ci_section_start("coverage_${_dashboard_iteration}" "Coverage")
if(COMMAND dashboard_hook_coverage)
dashboard_hook_coverage()
endif()
ctest_coverage(${CTEST_COVERAGE_ARGS})
ci_section_end("coverage_${_dashboard_iteration}")
endif()
if(dashboard_do_memcheck)
ci_section_start("memcheck_${_dashboard_iteration}" "MemCheck")
if(COMMAND dashboard_hook_memcheck)
dashboard_hook_memcheck()
endif()
ctest_memcheck(${CTEST_MEMCHECK_ARGS})
ci_section_end("memcheck_${_dashboard_iteration}")
endif()
ci_section_start("submit_${_dashboard_iteration}" "Submit to CDash")
if(COMMAND dashboard_hook_submit)
dashboard_hook_submit()
endif()
if(NOT dashboard_no_submit)
ctest_submit()
endif()
ci_section_end("submit_${_dashboard_iteration}")
if(COMMAND dashboard_hook_end)
dashboard_hook_end()
endif()
Expand Down