|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +# MFC Coverage Generation Script |
| 4 | +# This script generates coverage reports locally using gcovr with the same |
| 5 | +# configuration as the CI workflow. |
| 6 | + |
| 7 | +set -e |
| 8 | + |
| 9 | +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| 10 | +MFC_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" |
| 11 | + |
| 12 | +echo "MFC Coverage Generation" |
| 13 | +echo "======================" |
| 14 | +echo "Root directory: $MFC_ROOT" |
| 15 | +echo |
| 16 | + |
| 17 | +# Check if gcovr is available |
| 18 | +if ! command -v gcovr &> /dev/null; then |
| 19 | + echo "Error: gcovr is not installed. Install it with:" |
| 20 | + echo " pip install gcovr" |
| 21 | + exit 1 |
| 22 | +fi |
| 23 | + |
| 24 | +cd "$MFC_ROOT" |
| 25 | + |
| 26 | +# Check if MFC was built with coverage |
| 27 | +if [ ! -d "build" ]; then |
| 28 | + echo "Error: No build directory found. Build MFC with coverage first:" |
| 29 | + echo " ./mfc.sh build --gcov -j \$(nproc)" |
| 30 | + exit 1 |
| 31 | +fi |
| 32 | + |
| 33 | +# Look for gcov files |
| 34 | +GCOV_FILES=$(find build -name "*.gcda" 2>/dev/null | wc -l) |
| 35 | +if [ "$GCOV_FILES" -eq 0 ]; then |
| 36 | + echo "Warning: No coverage data found. Run tests first:" |
| 37 | + echo " ./mfc.sh test -j \$(nproc) -f EA8FA07E -t 9E2CA336" |
| 38 | + echo |
| 39 | +fi |
| 40 | + |
| 41 | +echo "Generating coverage reports..." |
| 42 | + |
| 43 | +# Generate XML coverage report (same as CI) |
| 44 | +gcovr \ |
| 45 | + --root "$MFC_ROOT" \ |
| 46 | + --object-directory "$MFC_ROOT/build" \ |
| 47 | + --filter '.*/fypp/.+\.f90$' \ |
| 48 | + --exclude '.*(tests|examples|toolchain|docs|benchmarks)/.*' \ |
| 49 | + --gcov-ignore-parse-errors \ |
| 50 | + --xml-pretty -o coverage.xml |
| 51 | + |
| 52 | +echo "Generated coverage.xml" |
| 53 | + |
| 54 | +# Also generate a broader report for debugging |
| 55 | +gcovr \ |
| 56 | + --root "$MFC_ROOT" \ |
| 57 | + --object-directory "$MFC_ROOT/build" \ |
| 58 | + --exclude '.*(tests|examples|toolchain|docs|benchmarks)/.*' \ |
| 59 | + --gcov-ignore-parse-errors \ |
| 60 | + --xml-pretty -o coverage_all.xml |
| 61 | + |
| 62 | +echo "Generated coverage_all.xml (broader scope for debugging)" |
| 63 | + |
| 64 | +# Generate HTML coverage report for local viewing |
| 65 | +gcovr \ |
| 66 | + --root "$MFC_ROOT" \ |
| 67 | + --object-directory "$MFC_ROOT/build" \ |
| 68 | + --filter '.*/fypp/.+\.f90$' \ |
| 69 | + --exclude '.*(tests|examples|toolchain|docs|benchmarks)/.*' \ |
| 70 | + --gcov-ignore-parse-errors \ |
| 71 | + --html-details -o coverage.html |
| 72 | + |
| 73 | +echo "Generated coverage.html" |
| 74 | + |
| 75 | +# Rewrite paths from generated .fpp.f90 -> repo .fpp (same as CI) |
| 76 | +python3 - <<'PY' |
| 77 | +import os, xml.etree.ElementTree as ET |
| 78 | +
|
| 79 | +print("Rewriting XML paths from generated .fpp.f90 to source .fpp files...") |
| 80 | +
|
| 81 | +fn = "coverage.xml" |
| 82 | +if not os.path.exists(fn): |
| 83 | + print(f"Error: {fn} not found") |
| 84 | + exit(1) |
| 85 | +
|
| 86 | +t = ET.parse(fn) |
| 87 | +r = t.getroot() |
| 88 | +remapped = 0 |
| 89 | +
|
| 90 | +for c in r.iter('class'): |
| 91 | + p = c.get('filename') or '' |
| 92 | + if p.endswith('.fpp.f90'): |
| 93 | + base = os.path.basename(p).rsplit('.fpp.f90', 1)[0] + '.fpp' |
| 94 | + for sub in ('pre_process','simulation','post_process','common'): |
| 95 | + cand = os.path.join('src', sub, base) |
| 96 | + if os.path.exists(cand): |
| 97 | + c.set('filename', cand) |
| 98 | + remapped += 1 |
| 99 | + break |
| 100 | +
|
| 101 | +t.write(fn, encoding='utf-8', xml_declaration=True) |
| 102 | +print(f"Remapped {remapped} file paths in coverage.xml") |
| 103 | +PY |
| 104 | + |
| 105 | +echo |
| 106 | +echo "Coverage reports generated:" |
| 107 | +echo " - coverage.xml (for Codecov upload)" |
| 108 | +echo " - coverage.html (for local viewing)" |
| 109 | +echo |
| 110 | +echo "To view the HTML report, open coverage.html in your browser." |
0 commit comments