blob: c0d6b36031d6e9a578c88015b8c2c9a994b59fef [file] [log] [blame]
#!/bin/bash -x
# Copyright 2016 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script collects code coverage data for C++ sources, after the tests
# were executed.
#
# Bazel C++ code coverage collection support is poor and limited. There is
# an ongoing effort to improve this (tracking issue #1118).
#
# Bazel uses the lcov tool for gathering coverage data. There is also
# an experimental support for clang llvm coverage, which uses the .profraw
# data files to compute the coverage report.
#
# This script assumes the following environment variables are set:
# - COVERAGE_DIR Directory containing metadata files needed for
# coverage collection (e.g. gcda files, profraw).
# - COVERAGE_MANIFEST Location of the instrumented file manifest.
# - COVERAGE_OUTPUT_FILE Location of the final coverage report.
# - COVERAGE_GCOV_PATH Location of gcov. This is set by the TestRunner.
# - ROOT Location from where the code coverage collection
# was invoked.
#
# The script looks in $COVERAGE_DIR for the C++ metadata coverage files (either
# gcda or profraw) and uses either lcov or gcov to get the coverage data.
# The coverage data is placed in $COVERAGE_OUTPUT_FILE.
# Checks if clang llvm coverage should be used instead of lcov.
function uses_llvm() {
if stat "${COVERAGE_DIR}"/*.profraw >/dev/null 2>&1; then
return 0
fi
return 1
}
function init_gcov() {
# Symlink the gcov tool such with a link called gcov. Clang comes with a tool
# called llvm-cov, which behaves like gcov if symlinked in this way (otherwise
# we would need to invoke it with "llvm-cov gcov").
# For more details see https://llvm.org/docs/CommandGuide/llvm-cov.html.
GCOV="${COVERAGE_DIR}/gcov"
ln -s "${COVERAGE_GCOV_PATH}" "${GCOV}"
}
# Computes code coverage data using the clang generated metadata found under $COVERAGE_DIR.
# Writes the collected coverage into ${COVERAGE_OUTPUT_FILE}.
function llvm_coverage() {
export LLVM_PROFILE_FILE="${COVERAGE_DIR}/%h-%p-%m.profraw"
"${COVERAGE_GCOV_PATH}" merge -output "${COVERAGE_OUTPUT_FILE}" "${COVERAGE_DIR}"/*.profraw
}
# Computes code coverage data using gcda files found under $COVERAGE_DIR.
# Writes the collected coverage into ${COVERAGE_OUTPUT_FILE} in lcov format.
function lcov_coverage() {
cat "${COVERAGE_MANIFEST}" | grep ".gcno$" | while read gcno; do
mkdir -p "${COVERAGE_DIR}/$(dirname ${gcno})"
cp "${ROOT}/${gcno}" "${COVERAGE_DIR}/${gcno}"
done
# Run lcov over the .gcno and .gcda files to generate the lcov tracefile.
# -c - Collect coverage data
# --no-external - Do not collect coverage data for system files
# --ignore-errors graph - Ignore missing .gcno files; Bazel only instruments some files
# -q - Quiet mode
# --gcov-tool "${GCOV}" - Pass the local symlink to be uses as gcov by lcov
# -b /proc/self/cwd - Use this as a prefix for all source files instead of
# the current directory
# -d "${COVERAGE_DIR}" - Directory to search for .gcda files
# -o "${COVERAGE_OUTPUT_FILE}" - Output file
LCOV=$(which lcov)
if [[ ! -x $LCOV ]]; then
LCOV=/usr/bin/lcov
fi
$LCOV -c --no-external --ignore-errors graph -q \
--gcov-tool "${GCOV}" -b /proc/self/cwd \
-d "${COVERAGE_DIR}" -o "${COVERAGE_OUTPUT_FILE}"
# Fix up the paths to be relative by removing the prefix we specified above.
sed -i -e "s*/proc/self/cwd/**g" "${COVERAGE_OUTPUT_FILE}"
}
function main() {
init_gcov
if uses_llvm; then
llvm_coverage
else
lcov_coverage
fi
}
main