gcov/script: gcov.sh is implemented using Python
Signed-off-by: wangmingrong1 <wangmingrong1@xiaomi.com>
This commit is contained in:
parent
7548db1980
commit
7d6b2e4804
2 changed files with 152 additions and 147 deletions
152
tools/gcov.py
Executable file
152
tools/gcov.py
Executable file
|
@ -0,0 +1,152 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# tools/gcov.py
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership. The
|
||||||
|
# ASF licenses this file to you 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.
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
def copy_file_endswith(endswith, source_dir, target_dir):
|
||||||
|
print(f"Collect {endswith} files {source_dir} -> {target_dir}")
|
||||||
|
|
||||||
|
if not os.path.exists(target_dir):
|
||||||
|
os.makedirs(target_dir)
|
||||||
|
|
||||||
|
for root, _, files in os.walk(source_dir):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(endswith):
|
||||||
|
source_file = os.path.join(root, file)
|
||||||
|
target_file = os.path.join(target_dir, file)
|
||||||
|
shutil.copy2(source_file, target_file)
|
||||||
|
|
||||||
|
|
||||||
|
def arg_parser():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Code coverage generation tool.", add_help=False
|
||||||
|
)
|
||||||
|
parser.add_argument("-t", dest="gcov_tool", help="Path to gcov tool")
|
||||||
|
parser.add_argument("-s", dest="gcno_dir", help="Directory containing gcno files")
|
||||||
|
parser.add_argument("-a", dest="gcda_dir", help="Directory containing gcda files")
|
||||||
|
parser.add_argument("--debug", action="store_true", help="Enable debug mode")
|
||||||
|
parser.add_argument(
|
||||||
|
"-x",
|
||||||
|
dest="only_copy",
|
||||||
|
action="store_true",
|
||||||
|
help="Only copy *.gcno and *.gcda files",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"gcov_dir",
|
||||||
|
nargs="?",
|
||||||
|
default=os.getcwd(),
|
||||||
|
help="Directory to store gcov data and report",
|
||||||
|
)
|
||||||
|
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = arg_parser()
|
||||||
|
|
||||||
|
root_dir = os.getcwd()
|
||||||
|
gcov_dir = os.path.abspath(args.gcov_dir)
|
||||||
|
gcno_dir = os.path.abspath(args.gcno_dir) if args.gcno_dir else root_dir
|
||||||
|
gcda_dir = os.path.abspath(args.gcda_dir) if args.gcda_dir else root_dir
|
||||||
|
|
||||||
|
coverage_file = os.path.join(gcov_dir, "coverage.info")
|
||||||
|
result_dir = os.path.join(gcov_dir, "result")
|
||||||
|
gcov_data_dir = os.path.join(gcov_dir, "data")
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
debug_file = os.path.join(gcov_dir, "debug.log")
|
||||||
|
sys.stdout = open(debug_file, "w+")
|
||||||
|
|
||||||
|
os.makedirs(os.path.join(gcov_dir, "data"), exist_ok=True)
|
||||||
|
|
||||||
|
# Collect gcno, gcda files
|
||||||
|
copy_file_endswith(".gcno", gcno_dir, gcov_data_dir)
|
||||||
|
copy_file_endswith(".gcda", gcda_dir, gcov_data_dir)
|
||||||
|
|
||||||
|
# Only copy files
|
||||||
|
if args.only_copy:
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# lcov tool is required
|
||||||
|
if shutil.which("lcov") is None:
|
||||||
|
print(
|
||||||
|
"Error: Code coverage generation tool is not detected, please install lcov."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# lcov collect coverage data to coverage_file
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"lcov",
|
||||||
|
"-c",
|
||||||
|
"-d",
|
||||||
|
gcov_data_dir,
|
||||||
|
"-o",
|
||||||
|
coverage_file,
|
||||||
|
"--rc",
|
||||||
|
"lcov_branch_coverage=1",
|
||||||
|
"--gcov-tool",
|
||||||
|
args.gcov_tool,
|
||||||
|
"--ignore-errors",
|
||||||
|
"gcov",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
stdout=sys.stdout,
|
||||||
|
stderr=sys.stdout,
|
||||||
|
)
|
||||||
|
|
||||||
|
# genhtml generate coverage report
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"genhtml",
|
||||||
|
"--branch-coverage",
|
||||||
|
"-o",
|
||||||
|
result_dir,
|
||||||
|
coverage_file,
|
||||||
|
"--ignore-errors",
|
||||||
|
"source",
|
||||||
|
],
|
||||||
|
check=True,
|
||||||
|
stdout=sys.stdout,
|
||||||
|
stderr=sys.stdout,
|
||||||
|
)
|
||||||
|
|
||||||
|
print(
|
||||||
|
"Copy the following link and open it in the browser to view the coverage report:"
|
||||||
|
)
|
||||||
|
print(f"file://{os.path.join(result_dir, 'index.html')}")
|
||||||
|
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("Failed to generate coverage file.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
shutil.rmtree(gcov_data_dir)
|
||||||
|
os.remove(coverage_file)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
147
tools/gcov.sh
147
tools/gcov.sh
|
@ -1,147 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
# tools/gcov.sh
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
# contributor license agreements. See the NOTICE file distributed with
|
|
||||||
# this work for additional information regarding copyright ownership.
|
|
||||||
# The ASF licenses this file to you 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.
|
|
||||||
#
|
|
||||||
|
|
||||||
ROOT_DIR=$(pwd)
|
|
||||||
GCNO_DIR=$ROOT_DIR
|
|
||||||
GCDA_DIR=$ROOT_DIR
|
|
||||||
|
|
||||||
show_help() {
|
|
||||||
echo "Usage: $0 [-t gcov_tool] [-s gcno_dir] [-a gcda_dir] [-x] [gcov_dir]"
|
|
||||||
echo " -t gcov_tool: path to gcov tool, e.g. ./nuttx/tools/gcov.sh -t arm-none-eabi-gcov"
|
|
||||||
echo " -s gcno_dir: directory containing gcno files (relative or absolute path allowed)"
|
|
||||||
echo " -a gcda_dir: directory containing gcda files (relative or absolute path allowed)"
|
|
||||||
echo " -x: only copy *.gcno and *.gcda files"
|
|
||||||
echo " gcov_dir: directory to store gcov data and report (optional)"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
convert_to_absolute_path() {
|
|
||||||
local dir_path=$1
|
|
||||||
if [ -z "$dir_path" ]; then
|
|
||||||
echo "Error: Directory path is empty."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Convert to absolute path if not already
|
|
||||||
if [ ! "${dir_path:0:1}" = "/" ]; then
|
|
||||||
dir_path=$(realpath "$dir_path" 2>/dev/null)
|
|
||||||
fi
|
|
||||||
echo "$dir_path"
|
|
||||||
}
|
|
||||||
|
|
||||||
while getopts "a:s:t:xh" opt
|
|
||||||
|
|
||||||
do
|
|
||||||
case $opt in
|
|
||||||
a)
|
|
||||||
GCDA_DIR=$(convert_to_absolute_path "$OPTARG")
|
|
||||||
if [ ! -d "$GCDA_DIR" ]; then
|
|
||||||
echo "Error: Invalid gcda directory: $OPTARG"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
s)
|
|
||||||
GCNO_DIR=$(convert_to_absolute_path "$OPTARG")
|
|
||||||
if [ ! -d "$GCNO_DIR" ]; then
|
|
||||||
echo "Error: Invalid gcno directory: $OPTARG"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
t)
|
|
||||||
GCOV_TOOL="--gcov-tool $OPTARG"
|
|
||||||
;;
|
|
||||||
x)
|
|
||||||
ONLY_COPY=1
|
|
||||||
;;
|
|
||||||
h)
|
|
||||||
show_help
|
|
||||||
;;
|
|
||||||
?)
|
|
||||||
show_help
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
done
|
|
||||||
|
|
||||||
# Handle gcov_dir as the last positional argument
|
|
||||||
|
|
||||||
shift $((OPTIND - 1))
|
|
||||||
if [ $# -gt 1 ]; then
|
|
||||||
echo "Error: Too many arguments."
|
|
||||||
show_help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ $# -eq 1 ]; then
|
|
||||||
GCOV_DIR=$(convert_to_absolute_path "$1")
|
|
||||||
else
|
|
||||||
GCOV_DIR=${ROOT_DIR}/gcov
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p ${GCOV_DIR} ${GCOV_DIR}/data
|
|
||||||
cd ${GCOV_DIR}
|
|
||||||
|
|
||||||
# Collect gcno files
|
|
||||||
|
|
||||||
find ${GCNO_DIR}/ -name "*.gcno" -exec cp {} ${GCOV_DIR}/data > /dev/null 2>&1 \;
|
|
||||||
|
|
||||||
# Collect gcda files
|
|
||||||
|
|
||||||
find ${GCDA_DIR}/ -name "*.gcda" -exec cp {} ${GCOV_DIR}/data > /dev/null 2>&1 \;
|
|
||||||
|
|
||||||
# Ensure ONLY_COPY is initialized as an integer
|
|
||||||
ONLY_COPY=${ONLY_COPY:-0}
|
|
||||||
|
|
||||||
if [ "$ONLY_COPY" -eq 1 ]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$GCOV_TOOL" ]; then
|
|
||||||
echo "Error: -t is a required option."
|
|
||||||
show_help
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
type lcov > /dev/null 2>&1
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Code coverage generation tool is not detected, please install lcov"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
files=$(find ${GCOV_DIR}/data -name "*.gcda" 2> /dev/null | wc -l)
|
|
||||||
if [ "$files" == "0" ] ;then
|
|
||||||
echo "gcda file not found in directory ${ROOT_DIR}"
|
|
||||||
echo "Please run ./nuttx before using gcov.sh to generate the coverage report"
|
|
||||||
echo "Or copy the gcda file in the device to ${ROOT_DIR}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate coverage text report
|
|
||||||
lcov -c -d ${GCOV_DIR}/data -o coverage.info --rc lcov_branch_coverage=1 ${GCOV_TOOL} --ignore-errors gcov
|
|
||||||
|
|
||||||
# Generate coverage page report
|
|
||||||
genhtml --branch-coverage -o result coverage.info --ignore-errors source
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "Failed to generate coverage file"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "Copy the following link and open it in the browser to view the coverage report"
|
|
||||||
echo "file://${GCOV_DIR}/result/index.html"
|
|
Loading…
Reference in a new issue