# GNAThub (GNATdashboard)
# Copyright (C) 2013-2017, AdaCore
#
# This is free software; you can redistribute it and/or modify it under
# terms of the GNU General Public License as published by the Free Soft-
# ware Foundation; either version 3, or (at your option) any later ver-
# sion. This software is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN-
# TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
# License for more details. You should have received a copy of the GNU
# General Public License distributed with this software; see file
# COPYING3. If not, go to http://www.gnu.org/licenses for a complete copy
# of the license.
"""GNAThub plug-in for the Gcov command-line tool.
It exports the Gcov class which implements the :class:`GNAThub.Plugin`
interface. This allows GNAThub's plug-in scanner to automatically find this
module and load it as part of the GNAThub default execution.
"""
import os
import re
import GNAThub
from GNAThub import Console, Plugin, Reporter
[docs]class Gcov(Plugin, Reporter):
"""Gcov plugin for GNAThub.
Retrieves .gcov generated files from the project root object directory,
parses them and feeds the database with the data collected from each files.
"""
GCOV_EXT = '.gcov'
[docs] def __init__(self):
super(Gcov, self).__init__()
self.tool = None
self.rule = None
# A dictionary containing all messages, to allow bulk insertion in the
# database: key is an string representing the number of hits,
# value is the corresponding Message instance.
self.hits = {}
# List of resource messages suitable for tool level bulk insertion
self.resources_messages = []
self.matcher = re.compile("^\s*(.*):\s*(\d+):.*")
def __process_file(self, resource, filename, resources_messages):
"""Processe one file, adding in bulk all coverage info found.
:param GNAThub.Resource resource: the resource being processed
:param str filename: the name of the resource
"""
message_data = []
with open(filename, 'r') as gcov_file:
# Retrieve information for every source line.
# Skip the first 2 lines.
for line in gcov_file.readlines()[2:]:
matches = self.matcher.match(line)
if not matches:
continue
(hits, line) = matches.groups()
# Skip lines that do not contain coverage info
if hits == '-':
continue
# Line is not covered
if hits == '#####' or hits == '=====':
hits = '0'
line = int(line)
# Find the message corresponding to this hits number
if hits in self.hits:
msg = self.hits[hits]
else:
msg = GNAThub.Message(self.rule, hits)
self.hits[hits] = msg
message_data.append([msg, line, 1, 1])
# Preparing list for tool level insertion of resources messages
resources_messages.append([resource, message_data])
[docs] def report(self):
"""Analyse the report files generated by :program:`Gcov`.
Finds all .gcov files in the object directory and parses them.
Sets the exec_status property according to the success of the analysis:
* ``GNAThub.EXEC_SUCCESS``: on successful execution and analysis
* ``GNAThub.EXEC_FAILURE``: on any error
"""
# Clear existing references only if not incremental run
if not GNAThub.incremental():
self.log.info('clear existing results if any')
GNAThub.Tool.clear_references(self.name)
self.info('parse coverage reports (%s)' % self.GCOV_EXT)
try:
# Handle multiple object directories
if GNAThub.Project.object_dirs():
# If there are object directories defined in the project tree,
# look for .gcov files there.
files = []
for obj_dir in GNAThub.Project.object_dirs():
# Fetch all files in project object directories and
# retrieve only .gcov files, absolute path
for filename in os.listdir(obj_dir):
if filename.endswith(self.GCOV_EXT):
files.append(os.path.join(obj_dir, filename))
else:
# If any object directory is defined in .gpr, fetch all files
# in default project object directory and retrieve only .gcov
# files, absolute path
prj_object_dir = GNAThub.Project.artifacts_dir()
files = [os.path.join(prj_object_dir, filename)
for filename in os.listdir(prj_object_dir)
if filename.endswith(self.GCOV_EXT)]
except Exception as why:
self.log.exception('error during object directories handling')
self.error(str(why))
return GNAThub.EXEC_FAILURE
# If no .gcov file found, plugin returns on failure
if not files:
self.error('no %s file in object directory' % self.GCOV_EXT)
return GNAThub.EXEC_FAILURE
self.tool = GNAThub.Tool(self.name)
self.rule = GNAThub.Rule('coverage', 'coverage',
GNAThub.METRIC_KIND, self.tool)
total = len(files)
# List of resource messages suitable for tool level bulk insertion
resources_messages = []
try:
for index, filename in enumerate(files, start=1):
# Retrieve source fullname (`filename` is the *.gcov report
# file).
base, _ = os.path.splitext(os.path.basename(filename))
src = GNAThub.Project.source_file(base)
resource = GNAThub.Resource.get(src)
if resource:
self.__process_file(resource, filename, resources_messages)
Console.progress(index, total, new_line=(index == total))
# Tool level insert for resources messages
self.tool.add_messages(resources_messages, [])
except IOError as why:
self.log.exception('failed to parse reports')
self.error(str(why))
return GNAThub.EXEC_FAILURE
else:
return GNAThub.EXEC_SUCCESS