mirror of
https://github.com/apache/nuttx.git
synced 2025-01-12 22:08:35 +08:00
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