def __cmd_line(self): """Create GNATcheck command line arguments list. :return: the GNATcheck command line :rtype: collections.Iterable[str] """ cmd_line = [ 'gnatcheck', '--show-rule', '-o', self.output, '-P', GNAThub.Project.path()] if GNAThub.u_process_all(): cmd_line.extend(['-U']) # Keeping this for later implemntation of -U main switch # if GNAThub.u_main(): # cmd_line.extend(['-U']) # cmd_line.extend([GNAThub.u_main()]) cmd_line.extend(['-j%d' % GNAThub.jobs()]) cmd_line = cmd_line + GNAThub.Project.scenario_switches() if GNAThub.Project.target(): cmd = '{}-{}'.format(GNAThub.Project.target(), cmd_line[0]) if self.__cmd_exists(cmd): cmd_line[0] = cmd else: cmd_line.extend(['--target', GNAThub.Project.target()]) if GNAThub.Project.runtime(): cmd_line.extend(('--RTS', GNAThub.Project.runtime())) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) return cmd_line
def __cmd_line(self): """Create GNATmetric command line arguments list. :return: the GNATmetric command line :rtype: collections.Iterable[str] """ cmd_line = [ self.name, '-ox', self.output, '-P', GNAThub.Project.path() ] if GNAThub.u_process_all(): cmd_line.extend(['-U']) # Keeping this for later implementation of -U main switch # if GNAThub.u_main(): # cmd_line.extend(['-U']) # cmd_line.extend([GNAThub.u_main()]) cmd_line = cmd_line + GNAThub.Project.scenario_switches() if GNAThub.Project.target(): cmd_line[0] = '{}-{}'.format(GNAThub.Project.target(), cmd_line[0]) if GNAThub.Project.runtime(): cmd_line.extend(('--RTS', GNAThub.Project.runtime())) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) return cmd_line
def __add_message(self, src, line, column, rule_id, msg, tag=None): """Add GNATcheck message to current session database. :param str src: Message source file. :param str line: Message line number. :param str column: Message column number. :param str rule_id: Message's rule identifier. :param str msg: Description of the message. """ # Cache the rules if rule_id in self.rules: rule = self.rules[rule_id] else: rule = GNAThub.Rule(rule_id, rule_id, GNAThub.RULE_KIND, self.tool) self.rules[rule_id] = rule # Set predefined unspecified ranking for all GNATcheck messages ranking = GNAThub.RANKING_UNSPECIFIED # Cache the messages if (rule, msg, ranking) in self.messages: message = self.messages[(rule, msg, ranking)] else: if tag: message = GNAThub.Message(rule, msg, ranking, 0, tag) else: message = GNAThub.Message(rule, msg, ranking) self.messages[(rule, msg, ranking)] = message # Add the message to the given resource self.bulk_data[src].append( [message, int(line), int(column), int(column)])
def __cmd_line(self): """Create GNATmetric command line arguments list. :return: the GNATmetric command line :rtype: collections.Iterable[str] """ cmd_line = [ self.name, '-ox', self.output, '-P', GNAThub.Project.path()] if GNAThub.u_process_all(): cmd_line.extend(['-U']) # Keeping this for later implementation of -U main switch # if GNAThub.u_main(): # cmd_line.extend(['-U']) # cmd_line.extend([GNAThub.u_main()]) cmd_line = cmd_line + GNAThub.Project.scenario_switches() if GNAThub.Project.target(): cmd_line[0] = '{}-{}'.format(GNAThub.Project.target(), cmd_line[0]) if GNAThub.Project.runtime(): cmd_line.extend(('--RTS', GNAThub.Project.runtime())) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) return cmd_line
def __add_message(self, src, line, column, rule_id, msg, category): """Add GNATprove message to current session database. :param str src: message source file :param str line: message line number :param str column: message column number :param str rule_id: message rule identifier :param str msg: description of the message :param str category: the category of the message """ # Get message ranking value ranking = self.__get_ranking(category) # Cache the rules if rule_id in self.rules: rule = self.rules[rule_id] else: rule = GNAThub.Rule(rule_id, rule_id, GNAThub.RULE_KIND, self.tool) self.rules[rule_id] = rule # Cache messages if (rule, msg, ranking) in self.messages: message = self.messages[(rule, msg, ranking)] else: message = GNAThub.Message(rule, msg, ranking) self.messages[(rule, msg, ranking)] = message # Add the message to the given resource self.bulk_data[src].append( [message, int(line), int(column), int(column)])
def report(self): """Parse GNATmetric XML report and save data to the database. Returns according to the success of the analysis: * ``GNAThub.EXEC_SUCCESS``: transactions committed to database * ``GNAThub.EXEC_FAILURE``: error while parsing the xml report """ self.info('clear existing results if any') GNAThub.Tool.clear_references(self.name) self.info('analyse report') self.tool = GNAThub.Tool(self.name) self.log.debug('parse XML report: %s', self.output) try: tree = ElementTree.parse(self.output) # Fetch all files files = tree.findall('./file') total = len(files) # List of resource messages suitable for tool level bulk insertion resources_messages = [] for index, node in enumerate(files, start=1): resource = GNAThub.Resource.get(node.attrib.get('name')) # Save file level metrics if not resource: self.warn('skip "%s" message (file not found)' % node.attrib.get('name')) continue self.firstunit = True resources_messages.append([resource, self.parse_metrics(node)]) self.tool.add_messages([], self.parse_units(node, resource)) Console.progress(index, total, new_line=(index == total)) # Retrieve the project metrics resource = GNAThub.Resource(GNAThub.Project.name(), GNAThub.PROJECT_KIND) resources_messages.append([resource, self.parse_metrics(tree)]) self.tool.add_messages(resources_messages, []) except ParseError as why: self.log.exception('failed to parse XML report') self.error('%s (%s:%s)' % (why, why.filename, why.lineno)) return GNAThub.EXEC_FAILURE else: return GNAThub.EXEC_SUCCESS
def execute_reporters(): """Whether to execute plugins implementing :class:`GNAThub.Reporter`. --runners-only and --reporters-only are mutually exclusive. Runners should be executed if --reporters-only is specified or if none is specified. :rtype: boolean """ return GNAThub.reporters_only() or not GNAThub.runners_only()
def execute_reporters(): """Whether to execute plugins implementing :class:`GNAThub.Reporter`. --runners-only and --reporters-only are mutually exclusive. Runners should be executed if --reporters-only is specified or if none is specified. :rtype: boolean """ return GNAThub.reporters_only() or not GNAThub.runners_only()
def mainloop(self): """Plugin main loop.""" LOG.info('registered %d plugins', len(self.plugins)) backlog = [] # Early exit if no plug-in are scheduled to be run if not self.plugins: self.info('nothing to do') return # Execute each plug-in in order try: for cls in self.plugins: try: # Create a new instance plugin, elapsed = cls(), None # Execute the plug-in elapsed = self.execute(plugin) except KeyboardInterrupt: raise except Exception as why: LOG.exception('plug-in execution failed') self.error('%s: unexpected error: %s', plugin.name, why) finally: if plugin.exec_status != GNAThub.NOT_EXECUTED: # A plugin could not have been executed depending on # the command line (--runners-only/--reporters-only). backlog.append((plugin.name, { 'time': elapsed or 0, 'success': (plugin.exec_status == GNAThub.EXEC_SUCCESS) })) except KeyboardInterrupt: self.info(os.linesep + 'Interrupt caught...') # Write results to file fname = os.path.join(GNAThub.root(), 'gnathub.backlog') try: with open(fname, 'w') as fd: fd.write(json.dumps(backlog)) except IOError as why: LOG.exception('could not write result file %s', fname) self.error('%s: unexpected error: %s', fname, why) if not GNAThub.dry_run() and not GNAThub.quiet(): # Display a summary for plugin, results in backlog: if results['success']: Console.ok(plugin) else: Console.ko(plugin)
def mainloop(self): """Plugin main loop.""" LOG.info('registered %d plugins', len(self.plugins)) backlog = [] # Early exit if no plug-in are scheduled to be run if not self.plugins: self.info('nothing to do') return # Execute each plug-in in order try: for cls in self.plugins: try: # Create a new instance plugin, elapsed = cls(), None # Execute the plug-in elapsed = self.execute(plugin) except KeyboardInterrupt: raise except Exception as why: LOG.exception('plug-in execution failed') self.error('%s: unexpected error: %s', plugin.name, why) finally: if plugin.exec_status != GNAThub.NOT_EXECUTED: # A plugin could not have been executed depending on # the command line (--runners-only/--reporters-only). backlog.append((plugin.name, { 'time': elapsed or 0, 'success': ( plugin.exec_status == GNAThub.EXEC_SUCCESS) })) except KeyboardInterrupt: self.info(os.linesep + 'Interrupt caught...') # Write results to file fname = os.path.join(GNAThub.root(), 'gnathub.backlog') try: with open(fname, 'w') as fd: fd.write(json.dumps(backlog)) except IOError as why: LOG.exception('could not write result file %s', fname) self.error('%s: unexpected error: %s', fname, why) if not GNAThub.dry_run() and not GNAThub.quiet(): # Display a summary for plugin, results in backlog: if results['success']: Console.ok(plugin) else: Console.ko(plugin)
def __msg_reader_cmd_line(): """Create GNATprove Message Reader command line arguments list. :return: the GNATprove message reader command line :rtype: collections.Iterable[str] """ cmd_line = ['gnatprove', '-P', GNAThub.Project.path()] if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_line.extend(['--report=all', '-j', str(GNAThub.jobs()), '--output-msg-only', '--ide-progress-bar']) return cmd_line + GNAThub.Project.scenario_switches()
def add_message(rule, message, ranking, line_no, col_begin, col_end): bulk_messages.append([ GNAThub.Message(rule, message, ranking=ranking), line_no, col_begin, col_end, ])
def parse_units(self, node, resource): """Recursively parse the unit node until all of them are found""" # Map of entities for a ressource entities_messages = [] if not node.findall('./unit'): return [] for unit in node.findall('./unit'): ename = unit.attrib.get('name') ekind = unit.attrib.get('kind') if self.firstunit: ekind = "compilation unit" self.firstunit = False else: if ekind.startswith('procedure'): ekind = ekind.replace("procedure", "action") elif ekind.startswith('function'): ekind = ekind.replace("function", "action") eline = unit.attrib.get('line') ecol = unit.attrib.get('col') # A resource can have multiple entities with the same name entity = GNAThub.Entity(ename, ekind, int(eline), int(ecol), int(ecol), resource) entities_messages.append([entity, self.parse_metrics(unit, True)]) entities_messages += self.parse_units(unit, resource) return entities_messages
def __cmd_line(self): """Create GNATstack command line arguments list. :return: the GNATstack command line :rtype: collections.Iterable[str] """ cmd_line = [ 'gnatstack', '-Q', '-x', '-Wa', '-P', GNAThub.Project.path() ] + GNAThub.Project.scenario_switches() if GNAThub.Project.runtime(): cmd_line.extend(('--RTS', GNAThub.Project.runtime())) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) return cmd_line
def full_json(self, path): """Create and fill an object, then create the JSON file to the given path This one is for debugging purpose :param path string: the path to create the file """ tmp = { 'project': GNAThub.Project.name(), '_total_message_count': sum(self.message_count.itervalues()), '_database': GNAThub.database(), 'creation_time': int(time.time()), 'properties': self.props or None, 'tools': self.tools or None, 'rules': self.rules or None, 'ranking': self.ranking or None, 'review_status': self.review_status or None, 'modules': [module.to_json() for name, module in self.modules.iteritems()], 'sources': self.sources } _write_json(path, tmp, indent=2)
def add_message(rule, message, ranking, line_no, column_no): bulk_messages.append([ GNAThub.Message(rule, message, ranking=ranking), line_no, column_no, column_no, ])
def __msg_reader_cmd_line(): """Create GNATprove Message Reader command line arguments list. :return: the GNATprove message reader command line :rtype: collections.Iterable[str] """ cmd_line = ['gnatprove', '-P', GNAThub.Project.path()] if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_line.extend([ '--report=all', '-j', str(GNAThub.jobs()), '--output-msg-only', '--ide-progress-bar' ]) return cmd_line + GNAThub.Project.scenario_switches()
def output_dir(self): """Return the path to the directory where to generate the HTML report. :return: the full path to the output directory :rtype: str """ return os.path.join(GNAThub.root(), self.name)
def __msg_reader_cmd_line(): """Create CodePeer Message Reader command line arguments list. :return: the CodePeer message reader command line :rtype: collections.Iterable[str] """ cmd_start = ['codepeer', '-P', GNAThub.Project.path()] if GNAThub.subdirs(): cmd_start.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_start.extend([ToolArgsPlaceholder('codepeer')]) cmd_end = [ '-output-msg-only', '-csv', ToolArgsPlaceholder('codepeer_msg_reader') ] return cmd_start + GNAThub.Project.scenario_switches() + cmd_end
def _import_codepeer_bridge(filename): app.logger.info("Import info into codepeer_bridge") name = 'codepeer_bridge' cmd = [ 'codepeer_bridge', '--output-dir=' + OUTPUT_DIR, '--db-dir=' + DB_DIR, '--import-reviews=' + filename ] GNAThub.Run(name, cmd, out=SERVER_LOG, append_out=True)
def __cmd_line(self): """Create GNATstack command line arguments list. :return: the GNATstack command line :rtype: collections.Iterable[str] """ cmd_line = [ 'gnatstack', '-Q', '-x', '-Wa', '-P', GNAThub.Project.path()] + GNAThub.Project.scenario_switches() if GNAThub.Project.target(): cmd_line[0] = '{}-{}'.format(GNAThub.Project.target(), cmd_line[0]) if GNAThub.Project.runtime(): cmd_line.extend(('--RTS', GNAThub.Project.runtime())) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) return cmd_line
def __msg_reader_cmd_line(report): """Create CodePeer Message Reader command line arguments list. :return: the CodePeer message reader command line :rtype: collections.Iterable[str] """ cmd_start = ['codepeer', '-P', GNAThub.Project.path()] if GNAThub.subdirs(): cmd_start.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_start.extend([ToolArgsPlaceholder('codepeer')]) cmd_end = [ '-output-msg-only', '-csv', '-out', report, ToolArgsPlaceholder('codepeer_msg_reader') ] return cmd_start + GNAThub.Project.scenario_switches() + cmd_end
def report(self): """Parse GNATcheck output file report. Returns according to the success of the analysis: * ``GNAThub.EXEC_SUCCESS``: on successful execution and analysis * ``GNAThub.EXEC_FAILURE``: on any error Identify two type of messages with different format: * basic message * message for package instantiation """ self.info('clear existing results if any') GNAThub.Tool.clear_references(self.name) self.info('analyse report') self.tool = GNAThub.Tool(self.name) self.log.debug('parse report: %s', self.output) if not os.path.exists(self.output): self.error('no report found') return GNAThub.EXEC_FAILURE try: with open(self.output, 'r') as output: lines = output.readlines() total = len(lines) for index, line in enumerate(lines, start=1): self.log.debug('parse line: %s', line) match = self._MESSAGE.match(line) if match: self.log.debug('matched: %s', str(match.groups())) self.__parse_line(match) else: match2 = self._MESSAGE_INST.match(line) if match2: self.log.debug('matched 2: %s', str(match2.groups())) self.__parse_line_inst(match2) Console.progress(index, total, new_line=(index == total)) except IOError as why: self.log.exception('failed to parse report') self.error('%s (%s:%d)' % (why, os.path.basename(self.output), total)) return GNAThub.EXEC_FAILURE else: self.__do_bulk_insert() return GNAThub.EXEC_SUCCESS
def _export_codeper_bridge(filename): app.logger.info("Export info from codepeer_bridge") name = 'codepeer_bridge' cmd = [ 'codepeer_bridge', '--output-dir=' + OUTPUT_DIR, '--db-dir=' + DB_DIR, '--export-reviews=' + os.path.join(GNAThub.Project.object_dir(), 'gnathub', 'html-report', 'data', filename) ] GNAThub.Run(name, cmd, out=SERVER_LOG, append_out=True)
def run(self): """Execute GNATstack. Returns according to the success of the execution of the tool: * ``GNAThub.EXEC_SUCCESS``: on successful execution * ``GNAThub.EXEC_FAILURE``: on any error """ return GNAThub.EXEC_SUCCESS if GNAThub.Run(self.name, self.__cmd_line( )).status in GNATstack.VALID_EXIT_CODES else GNAThub.EXEC_FAILURE
def run(self): """Execute GNATmetric. Returns according to the success of the execution of the tool: * ``GNAThub.EXEC_SUCCESS``: on successful execution * ``GNAThub.EXEC_FAILURE``: on any error """ status = GNAThub.Run(self.name, self.__cmd_line()).status return GNAThub.EXEC_SUCCESS if status == 0 else GNAThub.EXEC_FAILURE
def __init__(self): super(GNATmetric, self).__init__() if GNAThub.dry_run_without_project(): return self.tool = None self.output = os.path.join(GNAThub.Project.object_dir(), 'metrix.xml') self.rules = {} self.messages = {} self.firstunit = False
def workdir(): """Return the path to sonar execution directory. Located within GNAThub's root directory: :file:`<project_object_dir>/gnathub/sonar` :return: the path to the working directory :rtype: str """ return os.path.join(GNAThub.root(), SonarQube.EXEC_DIRECTORY)
def report(self): """Execute the SonarQube Scanner. Returns according to the successful of the analysis: * ``GNAThub.EXEC_SUCCESS``: on successful execution and analysis * ``GNAThub.EXEC_FAILURE``: on any error """ return GNAThub.EXEC_SUCCESS if GNAThub.Run( self.name, self.__cmd_line(), workdir=SonarQube.workdir()).status == 0 else GNAThub.EXEC_FAILURE
def __cmd_line(): """Create GNATprove command line arguments list. :return: the GNATprove command line :rtype: collections.Iterable[str] """ cmd_line = ['gnatprove', '-P', GNAThub.Project.path()] if GNAThub.u_process_all(): cmd_line.extend(['-U']) # Keeping this for later implementation of -U main switch # if GNAThub.u_main(): # cmd_line.extend([GNAThub.u_main()]) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_line.extend(['--report=all', '-j', str(GNAThub.jobs())]) return cmd_line + GNAThub.Project.scenario_switches()
def run(self): """Execute GNATprove. Sets the exec_status property according to the success of the execution of the tool: * ``GNAThub.EXEC_SUCCESS``: on successful execution * ``GNAThub.EXEC_FAILURE``: on any error """ return GNAThub.EXEC_SUCCESS if GNAThub.Run( self.name, self.__cmd_line()).status == 0 else GNAThub.EXEC_FAILURE
def __cmd_line(): """Create GNATprove command line arguments list. :return: the GNATprove command line :rtype: collections.Iterable[str] """ cmd_line = ['gnatprove', '-P', GNAThub.Project.path()] if GNAThub.u_process_all(): cmd_line.extend(['-U']) # Keeping this for later implementation of -U main switch # if GNAThub.u_main(): # cmd_line.extend([GNAThub.u_main()]) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_line.extend(['--report=all', '-j', str(GNAThub.jobs())]) return cmd_line + GNAThub.Project.scenario_switches()
def parse_metrics(self, node, entity=False): """Parse the xml *node* returns a list of metrics""" message_data = [] for metric in node.findall('./metric'): name = metric.attrib.get('name') if name in self.rules: rule = self.rules[name] else: rule = GNAThub.Rule(name, name, GNAThub.METRIC_KIND, self.tool) self.rules[name] = rule if (rule, metric.text, GNATmetric.RANKING) in self.messages: msg = self.messages[(rule, metric.text, GNATmetric.RANKING)] else: msg = GNAThub.Message(rule, metric.text, GNATmetric.RANKING) self.messages[(rule, metric.text, GNATmetric.RANKING)] = msg message_data.append([msg, 0, 1, 1]) return message_data
def __add_message(self, src, line, column, rule_id, msg, category, tool_msg_id, properties): """Add CodePeer message to current session database. :param str src: message source file :param str line: message line number :param str column: message column number :param str rule_id: message rule identifier :param str msg: description of the message :param str category: the category of the message :param str tool_msg_id: the original id of the message :param properties: the message properties :type properties: collections.Iterable[GNAThub.Property] or None """ # Get message ranking value ranking = self.__get_ranking(category) # Cache the rules if rule_id in self.rules: rule = self.rules[rule_id] else: rule = GNAThub.Rule(rule_id, rule_id, GNAThub.RULE_KIND, self.tool) self.rules[rule_id] = rule # Get message id from string msg_id = 0 if tool_msg_id and tool_msg_id.strip().isdigit(): msg_id = int(tool_msg_id) # Cache the messages if (rule, msg, ranking, msg_id) in self.messages: message = self.messages[(rule, msg, ranking, msg_id)] else: message = GNAThub.Message(rule, msg, ranking, msg_id, properties) self.messages[(rule, msg, ranking, msg_id)] = message # Add the message to the given resource self.bulk_data[src].append( [message, int(line), int(column), int(column)])
def __cmd_line(): """Create CodePeer command line arguments list. :return: the CodePeer command line :rtype: collections.Iterable[str] """ cmd_line = ['codepeer', '-P', GNAThub.Project.path(), '-j%d' % GNAThub.jobs()] if GNAThub.u_process_all(): cmd_line.extend(['-U']) # Keeping this for -U main switch implemntation # if GNAThub.u_main(): # cmd_line.extend(['-U']) # cmd_line.extend([GNAThub.u_main()]) if GNAThub.subdirs(): cmd_line.extend(['--subdirs=' + GNAThub.subdirs()]) return cmd_line + GNAThub.Project.scenario_switches()
def __msg_reader_cmd_line(report): """Create CodePeer Message Reader command line arguments list. :return: the CodePeer message reader command line :rtype: collections.Iterable[str] """ cmd_start = ['codepeer', '-P', GNAThub.Project.path()] if GNAThub.subdirs(): cmd_start.extend(['--subdirs=' + GNAThub.subdirs()]) cmd_start.extend([ToolArgsPlaceholder('codepeer')]) dest = os.path.join(GNAThub.Project.artifacts_dir(), 'codepeer', 'codepeer_run') cmd_end = [ '-output-msg-only', '-csv', '-csv-out', report, ToolArgsPlaceholder('codepeer_msg_reader'), '-db-info', dest ] return cmd_start + GNAThub.Project.scenario_switches() + cmd_end
def emit(self, record): """Inherited.""" if record.name in self.loggers: logger = self.loggers[record.name] else: logger = GNAThub.Logger(record.name) self.loggers[record.name] = logger try: message = self.format(record) logger.log(message) except KeyboardInterrupt, SystemExit: raise
def __init__(self): super(GNATcoverage, self).__init__() if GNAThub.dry_run_without_project(): return self.GNATCOVERAGE_OUTPUT = os.path.join( GNAThub.Project.artifacts_dir()) self.XML_EXT = '.xml' self.tool = None # Mapping: coverage level -> issue rule for this coverage. self.issue_rules = {}
def execute(cls, plugin): """Execute the plugin. Call methods setup, execute and teardown for a plugin instance. :param GNAThub.Plugin plugin: instance of the plugin to execute :return: the execution time in seconds :rtype: int """ elapsed = 0 if cls.should_execute(plugin): cls.info('execute plug-in %s', plugin.name) if GNAThub.dry_run(): # Early exit if dry-run mode is enabled plugin.exec_status = GNAThub.EXEC_SUCCESS return 0 LOG.info('%s: set up environment', plugin.name) start = time.time() plugin.setup() if cls.execute_runners() and cls.is_runner(plugin): LOG.info('%s: produce results', plugin.name) plugin.exec_status = plugin.run() if (cls.execute_reporters() and cls.is_reporter(plugin) and plugin.exec_status in ( GNAThub.EXEC_SUCCESS, GNAThub.NOT_EXECUTED)): LOG.info('%s: collect results', plugin.name) plugin.exec_status = plugin.report() LOG.info('%s: post execution', plugin.name) plugin.teardown() elapsed = time.time() - start if plugin.exec_status == GNAThub.EXEC_SUCCESS: plugin.info('completed (in %d seconds)' % elapsed) elif plugin.exec_status == GNAThub.EXEC_FAILURE: plugin.error('execution failed') else: assert plugin.exec_status == GNAThub.NOT_EXECUTED plugin.info('not executed') return elapsed
def filter_to_json(self, path): """Create and fill an object, then create the JSON file to the given path This one is for filter panel :param path string: the path to create the file """ tmp = { 'project': GNAThub.Project.name(), '_total_message_count': sum(self.message_count.itervalues()), '_database': GNAThub.database(), 'creation_time': int(time.time()), 'properties': self.props or None, 'tools': self.tools or None, 'rules': self.rules or None, 'ranking': self.ranking or None, 'review_status': self.review_status or None } _write_json(path, tmp, indent=2)
def full_json(self, path): """Create and fill an object, then create the JSON file to the given path This one is for debugging purpose :param path string: the path to create the file """ tmp = { 'project': GNAThub.Project.name(), '_total_message_count': sum(self.message_count.itervalues()), '_database': GNAThub.database(), 'creation_time': int(time.time()), 'properties': self.props or None, 'tools': self.tools or None, 'rules': self.rules or None, 'ranking': self.ranking or None, 'review_status': self.review_status or None, 'modules': [module.to_json() for name, module in self.modules.iteritems()], 'sources': self.sources } _write_json(path, tmp, indent=2)
def report(self): """Analyse the report files generated by :program:`GNATcoverage`. Finds all .xcov 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.GNATCOV_EXT) # Fetch all files in project object directory and retrieve only # .xcov files, absolute path files = [os.path.join(GNAThub.Project.object_dir(), filename) for filename in os.listdir(GNAThub.Project.object_dir()) if filename.endswith(self.GNATCOV_EXT)] # If no .xcov file found, plugin returns on failure if not files: self.error('no %s file in object directory' % self.GNATCOV_EXT) return GNAThub.EXEC_FAILURE self.tool = GNAThub.Tool(self.name) for cov_level in ('stmt', 'decision', 'mcdc'): self.issue_rules[cov_level] = GNAThub.Rule( cov_level, cov_level, GNAThub.RULE_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 *.xcov 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, ValueError) as why: self.log.exception('failed to parse reports') self.error(str(why)) return GNAThub.EXEC_FAILURE else: return GNAThub.EXEC_SUCCESS
def report(self): """Execute CodePeer message reader and parses the output. 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.info('clear existing results if any') GNAThub.Tool.clear_references(self.name) self.info('extract results with msg_reader to %s' % self.csv_report) proc = GNAThub.Run( self.output_dir, self.__msg_reader_cmd_line(self.csv_report)) if proc.status != 0: return GNAThub.EXEC_FAILURE self.info('analyse CSV report form %s' % self.csv_report) self.tool = GNAThub.Tool(self.name) self.log.debug('parse report: %s', self.csv_report) if not os.path.isfile(self.csv_report): self.error('no report found') return GNAThub.EXEC_FAILURE with open(self.csv_report, 'rb') as report: # Compute the total number of lines for progress report (-1 because # the first line in irrelevant to the analysis). index, total = 0, len(report.readlines()) - 1 # Reset the read cursor to the first byte report.seek(0) # Create the tag "New" for new CodePeer messages added_tag = GNAThub.Property('codepeer:added', 'Added') removed_tag = GNAThub.Property('codepeer:removed', 'Removed') unchanged_tag = GNAThub.Property('codepeer:unchanged', 'Unchanged') try: # Parse the file and drop the first line (containing the # columns name). reader = csv.reader(report, quotechar='\"') # Drop the first line (containing the columns name) header = reader.next() self.log.debug('drop header line: %s', header) # Iterate over each relevant record for index, record in enumerate(reader, start=1): self.log.debug('parse record: %r', record) # Each row is a list of strings: # # File, Line, Column, Category, History, Has_Review, # Ranking, Kind, Message, Classification, CWE, Checks, # Primary_Checks, Subp, Timestamp, Approved By, Comment, # Message_Id ( source, line, column, rule, history, has_review, severity, category, message, classification, cwe, checks, pchecks, subp, timestamp, app_by, comment, message_id ) = record[:18] if not severity or severity == 'suppressed': # Some versions of codepeer report an empty severity # for suppressed messages: map this to 'info'. severity = 'info' rule_id = rule.lower() self.__add_message( source, line, column, rule_id, message, severity, message_id, [added_tag if history == 'added' else (removed_tag if history == 'removed' else unchanged_tag)] ) if index % 100 == 1 or index == total: Console.progress( index, total, new_line=(index == total)) except csv.Error as why: self.log.exception('failed to parse CSV report') self.error('%s (%s:%d)' % ( why, os.path.basename(self.csv_report), index)) return GNAThub.EXEC_FAILURE else: self.__do_bulk_insert() return GNAThub.EXEC_SUCCESS
def auto_discover_plugins(cls): """Retrieve all plugins for GNAThub. This routine first lists all available scripts for this run of GNAThub. It then tries to load each one of them and collect any Plugin declared in those scripts. This list of plugins is then filtered given the parameters of the run, ie.: * If the switch --plugins is supplied on the command line, execute only plugins whose name is in this list; * Otherwise, if the project file contains the GNATdashboard.Plugins attribute, execute only plugins whose name is in this list; * Otherwise, execute all available plugins. :return: the list of plugins available in the current environment :rtype: collections.Iterable[GNAThub.Plugin] """ # Locate all Python scripts that might hold the definition of one or # more plugins. scripts = set() for name, path in GNAThub.repositories().items(): if not os.path.isdir(path): LOG.info('skip repository [%s] (not found)', name) continue LOG.info('load scripts from [%s] repository', name) repo_scripts = list(cls.walk_repository(path)) LOG.info(' + %d new script(s) found', len(repo_scripts)) scripts.update(repo_scripts) if len(repo_scripts): # Add the repository to sys.path so that modules can import # themselves. Do this only if scripts were found in this # repository. sys.path.append(path) # Compute the plugins list given the program input and their priority. # Priority order: # 1. Command line # 2. Project file # 3. All discoverable plugins if GNAThub.plugins(): LOG.info('use user-defined list of plugins (--plugins switch)') explicit = [p.strip() for p in GNAThub.plugins().split(',')] elif GNAThub.Project.property_as_list('Plugins'): LOG.info('use project-defined list of plugins (attr. Plugins)') explicit = GNAThub.Project.property_as_list('Plugins') else: LOG.info('use all discoverable plugins') explicit = None # Generate the final list of plugin classes. Inspect Python scripts to # extract class definition and filter out those that will not be used. LOG.info('located %d scripts', len(scripts)) plugins = sum([list(cls.inspect(s)) for s in scripts], []) def is_plugin(clazz, name): """Check whether this plugin name is ``name``. :param type clazz: the plugin type object :param str name: the expected name :return: ``True`` if this plugin name is ``name`` :rtype: boolean """ return ( clazz.__name__.lower() == name.lower() or clazz().name.lower() == name.lower() ) def contains_plugin_name(clazz, names): """Check whether the plugin name is in ``names``. :param type clazz: the plugin type object :param collections.Iterable[str] names: the list of name :return: ``True`` if the plugin name is in ``names`` :rtype: boolean """ for name in names: if is_plugin(clazz, name): return True return False if explicit: # Cleanup user input if needed. The following statement remove # None and empty string values as well as duplicates. It also set # the plugin name to lower case for future comparison. explicit = set([p.lower() for p in explicit if p]) # Filter out any plugin whose name is not in the "explicit" set plugins = [c for c in plugins if contains_plugin_name(c, explicit)] # Remove explicitly disabled plugins for name in GNAThub.Project.property_as_list('Plugins_Off'): for clazz in plugins: if is_plugin(clazz, name): LOG.info('disable %s [Plugin_Off]', name) plugins.remove(clazz) break LOG.warn('%s explicitly disabled but not loaded', name) return cls.schedule(plugins)
def report(self): """Parse GNATstack output file report. Returns 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('analyse report') self.tool = GNAThub.Tool(self.name) if not os.path.exists(self.output): self.error('no report found') return GNAThub.EXEC_FAILURE self.log.debug('parse XML report: %s', self.output) # List of resource messages suitable for tool level bulk insertion resources_messages = [] # Map of list of messages by entities entities_messages_map = {} # List of entity messages suitable for tool level bulk insertion entities_messages = [] # List of indirect call location indirect_loc_list = [] try: tree = ElementTree.parse(self.output) global_node = tree.find('./global') # Retrieve the metrics and the map of subprogram by id subprograms = tree.find('./subprogramset').findall('./subprogram') if subprograms: unknown_global = GNAThub.Rule("Unknown Global Stack Usage", "Unknown Global Stack Usage", GNAThub.METRIC_KIND, self.tool) static_global = GNAThub.Rule("Static Global Stack Usage", "Static Global Stack Usage", GNAThub.METRIC_KIND, self.tool) unknown_local = GNAThub.Rule("Unknown Local Stack Usage", "Unknown Local Stack Usage", GNAThub.METRIC_KIND, self.tool) static_local = GNAThub.Rule("Static Local Stack Usage", "Static Local Stack Usage", GNAThub.METRIC_KIND, self.tool) for node in subprograms: subprogram_id = node.attrib.get('id') locations = node.find('./locationset').findall('./location') global_usage = node.find('./globalstackusage') local_usage = node.find('./localstackusage') name = node.attrib.get('prefixname') if name == "indirect call": # The columns are only defined here so save them for later line = locations[0].attrib.get('line') column = locations[0].attrib.get('column') indirect_loc_list.append([line, column]) continue else: name = self.pretty_print_name(name) for loc in locations: file = loc.attrib.get('file') line = loc.attrib.get('line') column = loc.attrib.get('column') if file in self.resources: resource = self.resources[file] else: resource = GNAThub.Resource(file, GNAThub.FILE_KIND) self.resources[file] = resource # entities default value for kind is set to "porcedure" entity = GNAThub.Entity(name, "action", int(line), int(column), int(column), resource) # Only link the id to the first location of the entity if subprogram_id not in self.subprograms: self.subprograms[subprogram_id] = entity else: continue size = global_usage.attrib.get('size') if global_usage.attrib.get('qualifier') == "UNKNOWN": metric = unknown_global else: metric = static_global global_metric = GNAThub.Message(metric, size, ranking=GNATstack.RANKING) size = local_usage.attrib.get('size') if local_usage.attrib.get('qualifier') == "UNKNOWN": metric = unknown_local else: metric = static_local local_metric = GNAThub.Message(metric, size, ranking=GNATstack.RANKING) entities_messages_map[subprogram_id] = ( [[global_metric, 0, 1, 1], [local_metric, 0, 1, 1]]) # Analyse the indirect calls indirects = global_node.find('./indirectset').findall('./indirect') if indirects: indirect_rule = GNAThub.Rule("Indirect Call", "Indirect Call", GNAThub.RULE_KIND, self.tool) for node in indirects: indirect_id = node.attrib.get('id') if indirect_id not in self.subprograms: continue set = node.find('./indirectcallset').findall('./indirectcall') for call in set: line = call.find('./line').find('./value').text # Go through the list of saved locations and use the # line to retrieve a corresponding column column = 1 pos = -1 for ix in range(len(indirect_loc_list)): if indirect_loc_list[ix][0] == line: pos = ix column = indirect_loc_list[pos][1] continue if pos != -1: indirect_loc_list.pop(pos) message = GNAThub.Message(indirect_rule, 'indirect call', ranking=GNATstack.RANKING) entities_messages_map[indirect_id].append([message, int(line), int(column), int(column)]) # Analyse the external calls externals = global_node.find('./externalset').findall('./external') if externals: external_rule = GNAThub.Rule("External Call", "External Call", GNAThub.RULE_KIND, self.tool) for node in externals: subprogram_id = node.attrib.get('id') if subprogram_id not in self.subprograms: continue message = GNAThub.Message(external_rule, "external call", ranking=GNATstack.RANKING) entities_messages_map[subprogram_id].append([message, 0, 1, 1]) # Analyse the potential cycle cycles = global_node.find('./cycleset').findall('./cycle') if cycles: cycle_rule = GNAThub.Rule("Potential Cycle", "Potential Cycle", GNAThub.RULE_KIND, self.tool) for node in cycles: cycle_subprograms = node.findall('./subprogram') cycle_list = [] for sub in cycle_subprograms: subprogram_id = sub.attrib.get('id') cycle_list.append(self.subprograms[subprogram_id].name) subprogram_id = cycle_subprograms[0].attrib.get('id') cycle_list.append(self.subprograms[subprogram_id].name) message = GNAThub.Message(cycle_rule, "potential cycle detected:\n\t\t" + "\n\t\t".join(cycle_list), ranking=GNATstack.RANKING) entities_messages_map[subprogram_id].append([message, 0, 1, 1]) # Analyse the unbounded frames unboundeds = ( global_node.find('./unboundedset').findall('./unbounded')) if unboundeds: unbounded_rule = GNAThub.Rule("Unbounded Frame", "Unbounded Frame", GNAThub.RULE_KIND, self.tool) for node in unboundeds: subprogram_id = node.attrib.get('id') if subprogram_id in self.subprograms: message = GNAThub.Message(unbounded_rule, "This frame is unbounded", ranking=GNATstack.RANKING) entities_messages_map[subprogram_id].append([message, 0, 1, 1]) # Analyse the entry points entries = tree.find('./entryset').findall('./entry') # There is always an entry, so create the rule anyway entry_rule = GNAThub.Rule("Entry point", "Entry point", GNAThub.RULE_KIND, self.tool) for node in entries: subprogram_id = node.attrib.get('id') if subprogram_id not in self.subprograms: continue entity = self.subprograms[subprogram_id] local_stack = node.find('./localstackusage') size = local_stack.attrib.get('size') qualifier = local_stack.attrib.get('qualifier') if qualifier == "UNKNOWN": text = "The estimated" else: text = "The" text += (' call stack size for the entry point "%s" is %s' % (entity.name, str(size))) callchain_list = [] for sub in node.find('./callchain').findall('./subprogram'): chain_id = sub.attrib.get('id') callchain_list.append(self.subprograms[chain_id].name) text += (" and the callchain is:\n\t\t%s" % "\n\t\t".join(callchain_list)) message = GNAThub.Message(entry_rule, text, ranking=GNATstack.RANKING) entities_messages_map[subprogram_id].append([message, 0, 1, 1]) # Project message explaining the accuracy of the metrics accurate = global_node.find('./accurate') if accurate.find('./value').text == "FALSE": project = GNAThub.Resource(GNAThub.Project.name(), GNAThub.PROJECT_KIND) rule = GNAThub.Rule("Accuracy", "Accuracy", GNAThub.RULE_KIND, self.tool) text = ("worst case may not be accurate because of: " + ("indirect calls/" if indirects else "") + ("cycles/" if cycles else "") + ("unbounded frames/" if unboundeds else "") + ("external calls" if externals else "")) text = text[:-1] if text[-1] == '/' else text message = GNAThub.Message(rule, text, ranking=GNATstack.RANKING) resources_messages.append([project, [[message, 0, 1, 1]]]) # Insert the messages and the metrics for key, value in entities_messages_map.iteritems(): entities_messages.append([self.subprograms[key], value]) self.tool.add_messages(resources_messages, entities_messages) except ParseError as why: self.log.exception('failed to parse XML report') self.error('%s (%s:%s)' % (why, why.filename, why.lineno)) return GNAThub.EXEC_FAILURE else: return GNAThub.EXEC_SUCCESS
def report(self): """Parse GNATmetric XML report and save data to the database. Returns according to the success of the analysis: * ``GNAThub.EXEC_SUCCESS``: transactions committed to database * ``GNAThub.EXEC_FAILURE``: error while parsing the xml report """ # Clear existing references only if not incremental run if not GNAThub.incremental(): self.info('clear existing results if any') GNAThub.Tool.clear_references(self.name) self.info('analyse report') self.tool = GNAThub.Tool(self.name) self.log.debug('parse XML report: %s', self.output) try: tree = ElementTree.parse(self.output) # Parse the config first to create the GNAThub rules self.parse_config(tree) # Fetch all files files = tree.findall('./file') total = len(files) # List of resource messages suitable for tool level bulk insertion resources_messages = [] for index, node in enumerate(files, start=1): resource = GNAThub.Resource.get(node.attrib.get('name')) # Save file level metrics if not resource: self.warn('skip "%s" message (file not found)' % node.attrib.get('name')) continue self.firstunit = True resources_messages.append([resource, self.parse_metrics(node)]) self.tool.add_messages([], self.parse_units(node, resource)) Console.progress(index, total, new_line=(index == total)) # Retrieve the project metrics resource = GNAThub.Resource(GNAThub.Project.name(), GNAThub.PROJECT_KIND) resources_messages.append([resource, self.parse_metrics(tree)]) self.tool.add_messages(resources_messages, []) except ParseError as why: self.log.exception('failed to parse XML report') self.error('%s (%s:%s)' % (why, why.filename, why.lineno)) return GNAThub.EXEC_FAILURE else: return GNAThub.EXEC_SUCCESS
# of the license. import os import inspect import logging import GNAThub from GNAThub import Console # Create this script logger __file__ = inspect.getfile(inspect.currentframe()) MODULE, _ = os.path.splitext(os.path.basename(__file__)) LOG = logging.getLogger(MODULE) # Define default path to server.py script DEFAULT_SCRIPT_PATH = GNAThub.engine_repository() SCRIPT_NAME = 'server.py' # Default port value DEFAULT_PORT = 8080 # Determine script path and check is different of default value script_path = DEFAULT_SCRIPT_PATH # TO DO : Add handling when path is given via --server-dir if not os.path.exists(script_path): repo_msg = script_path + ' repository does not exist' Console.error(repo_msg, prefix=MODULE) else: msg = 'load script from ' + script_path + ' repository'
if tool.name == tool_name: return True return False def relpath(path): """Returns the relative path to :param:`path` from BASEDIR. :param str path: The full path. :returns: str """ return os.path.relpath(path, BASEDIR) assertTrue(os.path.isdir(GNAThub.root())) assertEqual(relpath(GNAThub.root()), os.path.join('obj', 'gnathub')) assertTrue(os.path.isdir(GNAThub.logs())) assertEqual(relpath(GNAThub.logs()), os.path.join('obj', 'gnathub', 'logs')) assertTrue(os.path.isfile(GNAThub.database())) assertEqual( relpath(GNAThub.database()), os.path.join('obj', 'gnathub', 'gnathub.db') ) #DB content check # Check tool exists TOOL = 'gnatmetric'
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) # 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 files = [os.path.join(GNAThub.Project.object_dir(), filename) for filename in os.listdir(GNAThub.Project.object_dir()) if filename.endswith(self.GCOV_EXT)] # 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
def report(self): """Execute GNATprove message reader and parses the output. 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.info('clear existing results if any') GNAThub.Tool.clear_references(self.name) self.info('extract results with msg_reader') proc = GNAThub.Run( self.output_dir, self.__msg_reader_cmd_line(), out=self.output) if proc.status != 0: return GNAThub.EXEC_FAILURE self.info('analyse report') self.tool = GNAThub.Tool(self.name) self.log.debug('parse report: %s', self.output_dir) if not os.path.isdir(self.output_dir): self.error('no report found') return GNAThub.EXEC_FAILURE for entry in os.listdir(self.output_dir): filename, ext = os.path.splitext(entry) if not ext == '.spark': continue self.log.debug('parse file: %s', entry) try: with open(os.path.join(self.output_dir, entry), 'rb') as spark: results = json.load(spark) for record in chain(results['flow'], results['proof']): if 'msg_id' not in record or 'file' not in record: continue self.log.debug('found record %s', json.dumps(record)) msg_id = record['msg_id'] filename = record['file'] self.msg_ids[(filename, msg_id)] = record except IOError as why: self.log.exception('failed to parse GNATprove .spark file') self.error('%s (%s:%d)' % ( why, os.path.basename(self.output))) try: with open(self.output, 'rb') as fdin: # Compute the total number of lines for progress report lines = fdin.readlines() index, total = 0, len(lines) for index, line in enumerate(lines, start=1): self.log.debug('parse line: %r', line) match = self._MESSAGE.match(line) if match: self.log.debug('matched: %s', str(match.groups())) self.__parse_line(match) Console.progress(index, total, new_line=(index == total)) except IOError as why: self.log.exception('failed to parse GNATprove output') self.error('%s (%s:%d)' % ( why, os.path.basename(self.output), total)) return GNAThub.EXEC_FAILURE else: self.__do_bulk_insert() return GNAThub.EXEC_SUCCESS