[rocprof-compute] Automate ctest coverage and test cases on runners with CDash (#1481)

* Add nightly coverage workflow

* ruff formatting

* temp workflow testing

* restore workflow file

* add workflow condition

* update workflow file

* update workflow file

* fix typo in run-ci.py

* edit run-ci.py

* add python deps install

* add python deps install

* add python deps install

* add python deps install

* check if enable coverage is on when using workflow

* remove github CI breakdown and fix enable coverage

* set cache variables must be set before dashboard starts

* Update run-ci.py

* Update run-ci.py to fix ctest cache

* Update rocprofiler-compute-code-coverage.yml to install tests

* Update rocprofiler-compute-code-coverage.yml

* Restore workflow file

* Update run-ci.py

* Simplify workflow build command

* Update run-ci.py to build tests

* edited run-ci script

* edit ctest configure commands

* edit ctest configure commands to be on one line

* edit ctest configure command to include path to amdclang++

* update clang check in tests/cmakelists.txt

* update rocm

* update rocm

* update rocm version 7.0.2

* update tests/CMakeLists.txt

* use tarball instead for rocm install

* apt install rocm-dev instead for 7.0.0 release

* workflow tweaks

* update to use new 'tools' dir

* install rocm-dev

* add CMAKE_CXX_COMPILER as clang

* update tests/cmakelists.txt

* update cdasg site and build names

* remove run automatically on pull requests

* ruff format

* increased timeouts for tests

* add back reruns for workflow testing

* fix typo

* rename workflow "nightly" -> "code"

* added tracks to keep track of gpu (325 vs 355)

* remove test_db_connector.py

* revert build names and tracking

* update workflow pushes

* CMake format

* changed parallel level back to 1
This commit is contained in:
jamessiddeley-amd
2025-11-17 09:24:24 -05:00
committed by GitHub
parent 75ad45d5f1
commit d49e2e35fd
8 changed files with 392 additions and 5996 deletions
@@ -1,89 +0,0 @@
name: rocprofiler-compute CDash Coverage Upload
on:
push:
branches: [ develop ]
paths:
- 'projects/rocprofiler-compute/coverage/Coverage.xml'
workflow_dispatch:
inputs:
coverage_file:
description: 'Path to coverage XML file'
required: false
default: 'projects/rocprofiler-compute/coverage/Coverage.xml'
concurrency:
group: rocprofiler-compute-cdash-${{ github.ref }}
cancel-in-progress: true
jobs:
upload-coverage:
runs-on: ubuntu-latest
name: Upload Coverage to CDash
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: projects/rocprofiler-compute
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.9'
- name: Check Coverage File Exists
working-directory: projects/rocprofiler-compute
run: |
COVERAGE_FILE="${{ github.event.inputs.coverage_file || 'coverage/Coverage.xml' }}"
if [[ "$COVERAGE_FILE" == projects/rocprofiler-compute/* ]]; then
COVERAGE_FILE="${COVERAGE_FILE#projects/rocprofiler-compute/}"
fi
if [ ! -f "$COVERAGE_FILE" ]; then
echo "ERROR: Coverage file not found at $COVERAGE_FILE!"
echo "Please run manual coverage generation"
echo "Use: cd projects/rocprofiler-compute && ./utils/update_coverage.sh"
exit 1
fi
COVERAGE_INFO=$(python3 -c "
import xml.etree.ElementTree as ET
try:
tree = ET.parse('$COVERAGE_FILE')
root = tree.getroot()
line_rate = float(root.get('line-rate', 0)) * 100
print(f'{line_rate:.1f}%')
except Exception as e:
print(f'Error parsing coverage: {e}')
exit(1)
")
echo "Line Coverage: $COVERAGE_INFO"
echo "COVERAGE_INFO=$COVERAGE_INFO" >> $GITHUB_ENV
echo "COVERAGE_FILE=$COVERAGE_FILE" >> $GITHUB_ENV
- name: Upload to CDash
working-directory: projects/rocprofiler-compute
run: |
GIT_SHA_SHORT=$(git rev-parse --short HEAD)
BRANCH_NAME=${GITHUB_REF#refs/heads/}
python3 utils/run-ci.py \
--coverage-file "${{ env.COVERAGE_FILE }}" \
--build-name "${BRANCH_NAME}-${GIT_SHA_SHORT}-$(date +%Y%m%d)" \
--mode "Continuous" \
--site "Monorepo-Upload" \
--project-name "rocprofiler-compute" \
--source-dir "." \
--binary-dir "build"
- name: Summary
run: |
echo "## CDash Upload Complete!" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Branch**: ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY
echo "- **Line Coverage**: ${{ env.COVERAGE_INFO }}" >> $GITHUB_STEP_SUMMARY
echo "- **Commit**: $(git rev-parse --short HEAD)" >> $GITHUB_STEP_SUMMARY
echo "- **CDash Project**: rocprofiler-compute" >> $GITHUB_STEP_SUMMARY
echo "- **Coverage File**: ${{ env.COVERAGE_FILE }}" >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,157 @@
name: rocprofiler-compute Code Coverage
run-name: rocprofiler-compute-code-coverage
on:
schedule:
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
runner_matrix:
description: 'Runner configuration'
required: false
type: string
env:
ROCM_PATH: "/opt/rocm"
ROCM_VERSION: "7.0.2"
jobs:
prepare_matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.generate_matrix.outputs.matrix }}
steps:
- name: Generate matrix
id: generate_matrix
run: |
MATRIX='[{"os-release":"22.04","gpu":"mi355","arch":"gfx950","runner":"linux-mi355-1gpu-ossci-rocm","code-name":"jammy"},{"os-release":"24.04","gpu":"mi355","arch":"gfx950","runner":"linux-mi355-1gpu-ossci-rocm","code-name":"noble"},{"os-release":"22.04","gpu":"mi325","arch":"gfx94X","runner":"linux-mi325-1gpu-ossci-rocm","code-name":"jammy"},{"os-release":"24.04","gpu":"mi325","arch":"gfx94X","runner":"linux-mi325-1gpu-ossci-rocm","code-name":"noble"}]'
if [ -n "${{ github.event.inputs.runner_matrix }}" ]; then
MATRIX='${{ github.event.inputs.runner_matrix }}'
fi
echo "matrix=${MATRIX}" >> $GITHUB_OUTPUT
coverage:
name: Coverage • ${{ matrix.system.gpu }} • Ubuntu ${{ matrix.system.os-release }}
needs: prepare_matrix
strategy:
fail-fast: false
matrix:
system: ${{ fromJSON(needs.prepare_matrix.outputs.matrix) }}
runs-on: ${{ matrix.system.runner }}
env:
HIP_PLATFORM: "amd"
OMPI_ALLOW_RUN_AS_ROOT: 1
OMPI_ALLOW_RUN_AS_ROOT_CONFIRM: 1
permissions:
packages: read
container:
image: ghcr.io/rocm/rocprofiler-ubuntu:${{ matrix.system.os-release }}-systems-ci-${{ matrix.system.arch }}
options:
--privileged
--ipc host
--group-add video
--device /dev/kfd
--device /dev/dri
--cap-add CAP_SYS_ADMIN
steps:
- uses: actions/checkout@v5
with:
sparse-checkout: projects/rocprofiler-compute/
- name: Update system packages
run: |
apt-get update
apt-get dist-upgrade -y
echo "✅ System packages updated!"
- name: Install amdgpu and dependencies
shell: bash
run: |
ROCM_MAJOR=$(echo ${{ env.ROCM_VERSION }} | sed 's/\./ /g' | awk '{print $1}')
ROCM_MINOR=$(echo ${{ env.ROCM_VERSION }} | sed 's/\./ /g' | awk '{print $2}')
ROCM_VERSN=$(( (${ROCM_MAJOR}*10000)+(${ROCM_MINOR}*100) ))
wget -N -P /tmp/ https://repo.radeon.com/amdgpu-install/${ROCM_MAJOR}.${ROCM_MINOR}/ubuntu/${{ matrix.system.code-name }}/amdgpu-install_${ROCM_MAJOR}.${ROCM_MINOR}.${ROCM_VERSN}-1_all.deb
apt-get install -y /tmp/amdgpu-install_${ROCM_MAJOR}.${ROCM_MINOR}.${ROCM_VERSN}-1_all.deb
apt-get update
apt install -y amd-smi-lib libdw-dev
echo "✅ amdgpu and dependencies Installed!"
- name: Install MPI
run: |
apt install -y libopenmpi-dev
echo "✅ MPI Installed!"
- name: Install ROCm
run: |
apt install -y rocm-dev
echo "✅ ROCm Installation Complete!"
- name: Install Python Dependencies
working-directory: projects/rocprofiler-compute
run: |
pip install -r requirements.txt --break-system-packages --ignore-installed
pip install -r requirements-test.txt --break-system-packages --ignore-installed
echo "✅ Python dependencies installed!"
- name: Configure, Build, Test, and Upload Coverage
id: run_coverage
timeout-minutes: 120
working-directory: projects/rocprofiler-compute
continue-on-error: true
run: |
set -e
git config --global --add safe.directory ${GITHUB_WORKSPACE}
git config --global --add safe.directory ${PWD}
BUILD_NAME="ROCm/rocprofiler-compute-${{ github.ref_name }}-ubuntu-${{ matrix.system.os-release }}-${{ matrix.system.gpu }}"
SITE_NAME="${{ matrix.system.runner }}"
PATH=${{ env.ROCM_PATH }}/bin:${{ env.ROCM_PATH }}/llvm/bin:$PATH \
LD_LIBRARY_PATH=${{ env.ROCM_PATH }}/lib:${{ env.ROCM_PATH }}/llvm/lib:$LD_LIBRARY_PATH \
python3 ./tools/run-ci.py \
--build-name "${BUILD_NAME}" \
--site "${SITE_NAME}" \
--mode "Nightly" \
--build-jobs 16 \
--pytest-numprocs 8 \
--install
- name: Check Coverage Configuration
if: always()
working-directory: projects/rocprofiler-compute
run: |
echo "=== Checking Build Configuration ==="
if [ -f "build/CMakeCache.txt" ]; then
echo "CMakeCache.txt contents (relevant variables):"
grep -E "ENABLE_TESTS|ENABLE_COVERAGE|INSTALL_TESTS|PYTEST_NUMPROCS" build/CMakeCache.txt || echo "⚠️ Coverage variables not found!"
echo ""
echo "=== Checking for coverage.xml ==="
if [ -f "build/coverage.xml" ]; then
echo "✅ coverage.xml found!"
ls -lh build/coverage.xml
else
echo "❌ coverage.xml NOT found!"
fi
echo ""
echo "=== Checking CTest results ==="
if [ -f "build/Testing/Temporary/LastTest.log" ]; then
echo "Last 50 lines of test log:"
tail -n 50 build/Testing/Temporary/LastTest.log
fi
else
echo "❌ build/CMakeCache.txt not found - build may have failed!"
fi
- name: Upload Logs on Failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: test-logs-${{ matrix.system.gpu }}-${{ matrix.system.os-release }}
path: |
projects/rocprofiler-compute/build/Testing/Temporary/LastTest*.log
projects/rocprofiler-compute/build/coverage.xml
retention-days: 7
+8 -4
View File
@@ -164,7 +164,7 @@ if(LOCALHOST MATCHES "TheraS01|.*\.thera\.amd\.com|thera-hn")
file(READ ${PROJECT_SOURCE_DIR}/cmake/modfile.thera.mod mod_additions)
file(
APPEND
${PROJECT_BINARY_DIR}/${MOD_INSTALL_PATH}/${ROCPROFCOMPUTE_FULL_VERSION}.lua
${PROJECT_BINARY_DIR}/${MOD_INSTALL_PATH}/${ROCPROFCOMPUTE_FULL_VERSION}.lua
${mod_additions}
)
list(POP_BACK CMAKE_MESSAGE_INDENT)
@@ -336,7 +336,11 @@ set_tests_properties(
test_profile_misc
test_profile_path
test_profile_roofline
PROPERTIES LABELS "profile" RESOURCE_GROUPS gpus:1
test_profile_section
test_profile_pc_sampling
test_profile_sets_func
test_profile_live_attach_detach
PROPERTIES LABELS "profile" RESOURCE_GROUPS gpus:1 TIMEOUT 1800
)
# ---------------------------
@@ -463,6 +467,7 @@ if(${ENABLE_COVERAGE})
DEPENDS "${ALL_TESTS_STRING}"
LABELS "coverage"
ENVIRONMENT "COVERAGE_FILE=${PROJECT_SOURCE_DIR}/.coverage"
TIMEOUT 600
)
endif()
@@ -489,8 +494,7 @@ install(
src/config.py
src/rocprof_compute_base.py
src/roofline.py
VERSION
VERSION.sha
VERSION VERSION.sha
DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/${PROJECT_NAME}
COMPONENT main
)
File diff suppressed because it is too large Load Diff
@@ -1,77 +0,0 @@
# Coverage Workflow for rocprofiler-compute in Monorepo
## Overview
This document describes the coverage generation and CDash upload workflow for `rocprofiler-compute` within the super-repo structure.
## Directory Structure
```
monorepo/
├── projects/
│ └── rocprofiler-compute/
│ ├── CMakeLists.txt
│ ├── coverage/
│ │ └── coverage-latest.xml # committed coverage file
│ ├── tools/
│ │ ├── update_coverage.sh # coverage generation/update script
│ │ └── run-ci.py # CDash upload script
│ └── ...
└── .github/
└── workflows/
└── rocprofiler-compute-cdash.yml # gitHub Action for auto-upload
```
## Workflows
### 1. Manual Coverage Generation (Periodic Update)
Run this periodically to update the coverage baseline:
```bash
# From monorepo root
cd projects/rocprofiler-compute
./tools/update_coverage.sh
# This will:
# - Build with coverage enabled
# - Run all tests
# - Generate coverage.xml
# - Copy/overwrite to coverage/coverage-latest.xml
# - Show instructions for committing
```
### 2. Commit Coverage to Repository
```bash
# From monorepo root
git add projects/rocprofiler-compute/coverage/coverage-latest.xml
git commit -m "rocprofiler-compute: Update coverage to XX.X% line coverage"
git push origin develop
```
### 3. Automatic CDash Upload
When `coverage/coverage-latest.xml` is pushed to `develop` branch:
- GitHub Action triggers automatically
- Uploads coverage to CDash
- Posts summary to GitHub Actions
## CDash Project
View results at: https://my.cdash.org/index.php?project=rocprofiler-compute
## Maintenance
## Troubleshooting
### Coverage Generation Fails
```bash
#check Python dependencies
pip install coverage pytest pytest-cov
#verify tests can run
cd projects/rocprofiler-compute/build
ctest --verbose
```
@@ -1,10 +1,10 @@
set(CMAKE_HIP_COMPILER "amdclang++" CACHE STRING "desired c++ compiler" FORCE)
if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang")
if(CMAKE_HIP_COMPILER_ID STREQUAL "Clang" OR CMAKE_HIP_COMPILER_ID STREQUAL "ROCMClang")
message(STATUS "Using ${CMAKE_HIP_COMPILER} to build for amdgpu backend")
else()
message(
FATAL_ERROR
"'amdclang++' compiler required to compile test binaries for ROCm platform."
"'amdclang++' compiler required to compile test binaries for ROCm platform. Found: ${CMAKE_HIP_COMPILER_ID}"
)
endif()
+225 -155
View File
@@ -23,13 +23,8 @@
# THE SOFTWARE.
##############################################################################
"""
CI script to upload coverage XML files to CDash
"""
import argparse
import os
import shutil
import subprocess
import sys
import tempfile
@@ -60,16 +55,44 @@ def detect_repo_structure():
return False, None, cwd
def create_ctest_script(args):
"""Generate a CTest script for uploading coverage data"""
def generate_ctest_dashboard_script(args, source_dir, binary_dir):
"""
Generate a complete CTest dashboard script that handles:
- Configuration
- Build
- Test
- Coverage
- Submit to CDash
"""
cache_entries = [
"CMAKE_BUILD_TYPE:STRING=Release",
f"CMAKE_PREFIX_PATH:PATH={os.environ.get('ROCM_PATH', '/opt/rocm')}",
"ENABLE_TESTS:BOOL=ON",
"INSTALL_TESTS:BOOL=ON",
"ENABLE_COVERAGE:BOOL=ON",
f"PYTEST_NUMPROCS:STRING={args.pytest_numprocs}",
]
test_args = ""
if args.ctest_args:
test_args = " ".join(args.ctest_args)
# ruff: noqa
script_content = f"""
cmake_minimum_required(VERSION 3.19)
##############################################################################
# CTest Dashboard Script for rocprofiler-compute
# Auto-generated by run-ci.py
##############################################################################
# Dashboard configuration
set(CTEST_PROJECT_NAME "{args.project_name}")
set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
set(CTEST_DROP_SITE_CDASH TRUE)
# CDash submission settings
if(CMAKE_VERSION VERSION_GREATER 3.14)
set(CTEST_SUBMIT_URL "{args.submit_url}")
else()
@@ -78,93 +101,143 @@ else()
set(CTEST_DROP_LOCATION "{args.submit_path}")
endif()
# Build identification
set(CTEST_SITE "{args.site}")
set(CTEST_BUILD_NAME "{args.build_name}")
set(CTEST_SOURCE_DIRECTORY "{args.source_dir}")
set(CTEST_BINARY_DIRECTORY "{args.binary_dir}")
set(CTEST_SOURCE_DIRECTORY "{source_dir}")
set(CTEST_BINARY_DIRECTORY "{binary_dir}")
file(MAKE_DIRECTORY "${{CTEST_BINARY_DIRECTORY}}")
# Build config
set(CTEST_CMAKE_GENERATOR "Unix Makefiles")
set(CTEST_BUILD_CONFIGURATION "Release")
if(NOT EXISTS "${{CTEST_BINARY_DIRECTORY}}/CMakeCache.txt")
file(WRITE "${{CTEST_BINARY_DIRECTORY}}/CMakeCache.txt"
"CMAKE_PROJECT_NAME:STATIC={args.project_name}\\n")
endif()
# Config CMake command with all required options
set(CTEST_CONFIGURE_COMMAND "cmake -B ${{CTEST_BINARY_DIRECTORY}} ${{CTEST_SOURCE_DIRECTORY}} -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH={os.environ.get("ROCM_PATH", "/opt/rocm")} -DENABLE_TESTS=ON -DINSTALL_TESTS=ON -DENABLE_COVERAGE=ON -DPYTEST_NUMPROCS={args.pytest_numprocs}")
file(WRITE "${{CTEST_BINARY_DIRECTORY}}/CTestConfig.cmake" "
set(CTEST_PROJECT_NAME \\"{args.project_name}\\")
set(CTEST_NIGHTLY_START_TIME \\"01:00:00 UTC\\")
if(CMAKE_VERSION VERSION_GREATER 3.14)
set(CTEST_SUBMIT_URL \\"{args.submit_url}\\")
else()
set(CTEST_DROP_METHOD \\"https\\")
set(CTEST_DROP_SITE \\"{args.cdash_host}\\")
set(CTEST_DROP_LOCATION \\"{args.submit_path}\\")
endif()
set(CTEST_DROP_SITE_CDASH TRUE)
")
message(STATUS "CMake configure command: ${{CTEST_CONFIGURE_COMMAND}}")
message(STATUS "Starting {args.mode} dashboard...")
ctest_start({args.mode})
set(COVERAGE_SRC_FILE "{args.coverage_file}")
set(COVERAGE_DEST_FILE "${{CTEST_BINARY_DIRECTORY}}/coverage.xml")
# Config step
message(STATUS "Configuring project...")
ctest_configure(
RETURN_VALUE configure_result
CAPTURE_CMAKE_ERROR configure_error
)
if(NOT EXISTS "${{COVERAGE_SRC_FILE}}")
message(FATAL_ERROR "Coverage file not found: ${{COVERAGE_SRC_FILE}}")
if(configure_result OR configure_error)
message(WARNING "Configuration had issues but continuing...")
endif()
file(COPY "${{COVERAGE_SRC_FILE}}" DESTINATION "${{CTEST_BINARY_DIRECTORY}}")
get_filename_component(SRC_FILENAME "${{COVERAGE_SRC_FILE}}" NAME)
if(NOT SRC_FILENAME STREQUAL "coverage.xml")
file(RENAME "${{CTEST_BINARY_DIRECTORY}}/${{SRC_FILENAME}}"
"${{COVERAGE_DEST_FILE}}")
# Build step
message(STATUS "Building project...")
set(CTEST_BUILD_FLAGS "-j{args.build_jobs}")
ctest_build(
TARGET {"install" if args.install else "all"}
RETURN_VALUE build_result
CAPTURE_CMAKE_ERROR build_error
)
if(build_result OR build_error)
message(WARNING "Build had issues but continuing...")
endif()
message(STATUS "Processing coverage file: ${{COVERAGE_DEST_FILE}}")
# Test step
message(STATUS "Running tests...")
ctest_test(
{test_args}
PARALLEL_LEVEL 1
RETURN_VALUE test_result
CAPTURE_CMAKE_ERROR test_error
)
file(MAKE_DIRECTORY "${{CTEST_BINARY_DIRECTORY}}/Testing")
file(MAKE_DIRECTORY "${{CTEST_BINARY_DIRECTORY}}/Testing/CoverageInfo")
file(COPY "${{COVERAGE_DEST_FILE}}"
DESTINATION "${{CTEST_BINARY_DIRECTORY}}/Testing/CoverageInfo")
ctest_coverage()
ctest_submit(PARTS Coverage
RETRY_COUNT 3
RETRY_DELAY 5
CAPTURE_CMAKE_ERROR submit_error)
if(submit_error)
message(FATAL_ERROR "Failed to submit coverage to CDash. "
"Error code: ${{submit_error}}")
if(test_result OR test_error)
message(WARNING "Some tests failed but continuing with coverage and upload...")
endif()
message(STATUS "Successfully submitted coverage to CDash")
# Coverage step (if coverage file exists)
set(COVERAGE_FILE "${{CTEST_BINARY_DIRECTORY}}/coverage.xml")
if(EXISTS "${{COVERAGE_FILE}}")
message(STATUS "Processing coverage data...")
file(MAKE_DIRECTORY "${{CTEST_BINARY_DIRECTORY}}/Testing/CoverageInfo")
file(COPY "${{COVERAGE_FILE}}"
DESTINATION "${{CTEST_BINARY_DIRECTORY}}/Testing/CoverageInfo/")
ctest_coverage(
RETURN_VALUE coverage_result
CAPTURE_CMAKE_ERROR coverage_error
)
if(coverage_result OR coverage_error)
message(WARNING "Coverage processing had issues but continuing...")
endif()
else()
message(STATUS "No coverage file found at ${{COVERAGE_FILE}}")
message(STATUS "Skipping coverage step")
endif()
message(STATUS "Submitting to CDash...")
ctest_submit(
RETRY_COUNT 3
RETRY_DELAY 5
RETURN_VALUE submit_result
CAPTURE_CMAKE_ERROR submit_error
)
if(submit_result OR submit_error)
message(WARNING "CDash submission encountered issues")
message(STATUS "Results available locally in: ${{CTEST_BINARY_DIRECTORY}}/Testing/")
else()
message(STATUS "Successfully submitted to CDash!")
message(STATUS "View at: {args.submit_url.replace("/submit.php?project=", "/index.php?project=")}")
message(STATUS "Build name: {args.build_name}")
endif()
message(STATUS "")
message(STATUS "========================================")
message(STATUS "Dashboard Summary")
message(STATUS "========================================")
if(test_result)
message(STATUS "Tests: FAILED (some tests failed or timed out)")
else()
message(STATUS "Tests: PASSED")
endif()
if(submit_result OR submit_error)
message(STATUS "CDash Upload: FAILED")
else()
message(STATUS "CDash Upload: SUCCESS")
endif()
message(STATUS "========================================")
# For nightly/continuous, don't fail on test failures
# Only warn in Experimental mode
if("{args.mode}" STREQUAL "Experimental" AND test_result)
message(WARNING "Tests failed in Experimental mode")
endif()
"""
return script_content
def main():
if not os.getenv("CI"):
print("WARNING: CDash upload should normally only be done from CI/CD")
response = input("Continue anyway? (y/N): ")
if response.lower() != "y":
print("Aborted.")
return 1
parser = argparse.ArgumentParser(
description="Upload coverage XML to CDash for rocprofiler-compute"
description="CI script for rocprofiler-compute: build, test, and upload to CDash"
)
# required
parser.add_argument(
"--coverage-file", required=True, help="Path to coverage XML file"
"--build-name",
"--name",
dest="build_name",
required=True,
help="Build name for CDash",
)
parser.add_argument("--build-name", required=True, help="Build name for CDash")
# optional
parser.add_argument(
"--project-name", default="rocprofiler-compute", help="CDash project name"
"--project-name",
default="rocprofiler-compute",
help="CDash project name",
)
parser.add_argument(
"--submit-url",
@@ -172,7 +245,9 @@ def main():
help="CDash submission URL",
)
parser.add_argument(
"--cdash-host", default="my.cdash.org", help="CDash host (for older CMake)"
"--cdash-host",
default="my.cdash.org",
help="CDash host (for older CMake)",
)
parser.add_argument(
"--submit-path",
@@ -186,11 +261,13 @@ def main():
)
parser.add_argument(
"--source-dir",
"-S",
default=None,
help="Source directory path (auto-detected if not provided)",
)
parser.add_argument(
"--binary-dir",
"-B",
default=None,
help="Binary directory path (defaults to source-dir/build)",
)
@@ -201,10 +278,33 @@ def main():
help="CTest dashboard mode",
)
parser.add_argument(
"--dry-run", action="store_true", help="Generate script but don't execute"
"--build-jobs",
"-j",
type=int,
default=8,
help="Number of parallel build jobs",
)
parser.add_argument(
"--install",
action="store_true",
help="Build install target instead of all target",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Generate dashboard script but don't execute",
)
parser.add_argument(
"--pytest-numprocs",
type=int,
default=4,
help="Number of parallel processes for pytest",
)
args = parser.parse_args()
args, unknown = parser.parse_known_args()
args.cmake_args = []
args.ctest_args = []
is_monorepo, monorepo_root, project_root = detect_repo_structure()
@@ -220,113 +320,83 @@ def main():
else:
args.site = os.uname().nodename
coverage_path = Path(args.coverage_file)
if not coverage_path.is_absolute():
if not coverage_path.exists():
coverage_path = project_root / args.coverage_file
if not coverage_path.exists():
print(f"ERROR: Coverage file not found: {coverage_path}", file=sys.stderr)
print(f" Searched in: {Path(args.coverage_file).absolute()}")
print(f" And in: {project_root / args.coverage_file}")
return 1
try:
import xml.etree.ElementTree as ET
tree = ET.parse(coverage_path)
root = tree.getroot()
if root.tag != "coverage":
print(
f"ERROR: File does not appear to be a coverage XML file "
f"(root tag: {root.tag})",
file=sys.stderr,
)
return 1
line_rate = float(root.get("line-rate", 0)) * 100
print(f"Line Coverage: {line_rate:.1f}%")
except Exception as e:
print(f"ERROR: Could not parse coverage XML file: {e}", file=sys.stderr)
return 1
args.coverage_file = str(coverage_path.absolute())
args.source_dir = str(Path(args.source_dir).absolute())
args.binary_dir = str(Path(args.binary_dir).absolute())
source_dir = Path(args.source_dir).absolute()
binary_dir = Path(args.binary_dir).absolute()
print("=" * 80)
print("rocprofiler-compute CI Dashboard")
print("=" * 80)
print(f"Repository type: {'Monorepo' if is_monorepo else 'Standalone'}")
print(f"Project root: {project_root}")
print(f"Uploading coverage file: {args.coverage_file}")
print(f"Source directory: {source_dir}")
print(f"Binary directory: {binary_dir}")
print(f"Build name: {args.build_name}")
print(f"Project: {args.project_name}")
print(f"Dashboard mode: {args.mode}")
print(f"CDash project: {args.project_name}")
print(f"Site: {args.site}")
print(f"Submit URL: {args.submit_url}")
print("=" * 80)
# Ensure build directory exists
os.makedirs(args.binary_dir, exist_ok=True)
# Copy CTestConfig.cmake if it exists in source
source_ctest_config = Path(args.source_dir) / "CTestConfig.cmake"
if source_ctest_config.exists():
shutil.copy2(source_ctest_config, Path(args.binary_dir) / "CTestConfig.cmake")
print("Copied CTestConfig.cmake to build directory")
script_content = create_ctest_script(args)
script_content = generate_ctest_dashboard_script(args, source_dir, binary_dir)
if args.dry_run:
print("\nGenerated CTest script:")
print("=" * 50)
print("\nGenerated CTest Dashboard Script:")
print("=" * 80)
print(script_content)
print("=" * 80)
return 0
binary_dir.mkdir(parents=True, exist_ok=True)
with tempfile.NamedTemporaryFile(
mode="w", suffix=".cmake", delete=False, dir=binary_dir
) as f:
f.write(script_content)
script_path = f.name
print(f"\nDashboard script: {script_path}")
print("=" * 80)
try:
with tempfile.NamedTemporaryFile(mode="w", suffix=".cmake", delete=False) as f:
f.write(script_content)
script_path = f.name
print(f"\nScript written to: {script_path}")
original_dir = os.getcwd()
os.chdir(args.source_dir)
cmd = ["ctest", "-S", script_path, "-V"]
print(f"Executing: {' '.join(cmd)}")
print(f"Working directory: {os.getcwd()}")
print(f"Running: {' '.join(cmd)}")
print("=" * 80)
print()
result = subprocess.run(cmd, capture_output=True, text=True)
if result.stdout:
print("\nSTDOUT:")
print(result.stdout)
if result.stderr:
print("\nSTDERR:")
print(result.stderr)
os.chdir(original_dir)
if result.returncode != 0:
print(f"\nCTest failed with return code: {result.returncode}")
return result.returncode
print("\n✅ Coverage successfully uploaded to CDash!")
cdash_url = args.submit_url.replace(
"/submit.php?project=", "/index.php?project="
process = subprocess.Popen(
cmd,
cwd=source_dir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True,
)
print(f"View results at: {cdash_url}")
return 0
for line in process.stdout:
print(line, end="")
returncode = process.wait()
print()
print("=" * 80)
if returncode == 0:
print("✅ Dashboard completed successfully!")
else:
print(f"⚠️ Dashboard completed with return code: {returncode}")
print("=" * 80)
return returncode
except Exception as e:
print(f"Error executing CTest script: {e}", file=sys.stderr)
print(f"\nError executing dashboard script: {e}", file=sys.stderr)
import traceback
traceback.print_exc()
return 1
finally:
if "script_path" in locals():
try:
os.unlink(script_path)
except Exception:
pass
try:
os.unlink(script_path)
except Exception:
pass
if __name__ == "__main__":
@@ -1,81 +0,0 @@
#!/bin/bash
##############################################################################
# MIT License
#
# Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
##############################################################################
# generate_coverage.sh
set -e
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BUILD_DIR="$PROJECT_ROOT/build"
COVERAGE_FILE="$PROJECT_ROOT/coverage/Coverage.xml"
echo "=== Generating Coverage for Mainline Promotion ==="
echo "Setting up clean build..."
rm -rf "$BUILD_DIR"
mkdir -p "$BUILD_DIR"
cd "$BUILD_DIR"
echo "Configuring with coverage enabled..."
cmake -DENABLE_COVERAGE=ON -DENABLE_TESTS=ON -DPYTEST_NUMPROCS=8 ..
echo "Building..."
make -j$(nproc)
echo "Running ALL tests to generate coverage (this may take a while)..."
ctest --verbose --output-on-failure --parallel 1 || {
echo "WARNING: Some tests failed, but continuing with coverage generation"
}
echo "Generating coverage report..."
ctest -R generate_coverage_report --output-on-failure --parallel 2
if [ ! -f "coverage.xml" ]; then
echo "ERROR: coverage.xml not generated"
exit 1
fi
COVERAGE_INFO=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('coverage.xml')
root = tree.getroot()
line_rate = float(root.get('line-rate', 0)) * 100
print(f'{line_rate:.1f}%')
")
mkdir -p "$PROJECT_ROOT/coverage"
cp coverage.xml "$COVERAGE_FILE"
echo ""
echo "=== Coverage Generated Successfully ==="
echo "Line Coverage: $COVERAGE_INFO"
echo "File: $COVERAGE_FILE"
echo ""
echo "Next steps:"
echo "1. git add $COVERAGE_FILE"
echo "2. git commit -m 'Update coverage: $COVERAGE_INFO'"
echo "3. Proceed with push to develop"
echo "4. CDash upload will happen automatically on push"
echo ""