X Tutup
Skip to content

Commit c48fc9e

Browse files
committed
htmlreport: add author information
1 parent 46f7275 commit c48fc9e

File tree

1 file changed

+115
-23
lines changed

1 file changed

+115
-23
lines changed

htmlreport/cppcheck-htmlreport

Lines changed: 115 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
from __future__ import unicode_literals
44

5+
import datetime
56
import io
6-
import sys
7+
import locale
8+
import operator
79
import optparse
810
import os
9-
import operator
11+
import sys
12+
import subprocess
1013

1114
from collections import Counter
1215
from pygments import highlight
@@ -287,6 +290,74 @@ def html_escape(text):
287290
return escape(text, html_escape_table)
288291

289292

293+
def git_blame(line, path, file, blame_options):
294+
git_blame_dict = {}
295+
head, tail = os.path.split(file)
296+
if head != "":
297+
path = head
298+
299+
try:
300+
os.chdir(path)
301+
except:
302+
return {}
303+
304+
try:
305+
result = subprocess.check_output('git blame -L %d %s %s --porcelain -- %s' % (
306+
line, " -w" if "-w" in blame_options else "", " -M" if "-M" in blame_options else "", file))
307+
result = result.decode(locale.getpreferredencoding())
308+
except:
309+
return {}
310+
311+
if result.startswith('fatal'):
312+
return {}
313+
314+
disallowed_characters = '<>'
315+
for line in result.split('\n')[1:]:
316+
space_pos = line.find(' ')
317+
if space_pos > 30:
318+
break
319+
key = line[:space_pos]
320+
val = line[space_pos + 1:]
321+
322+
for character in disallowed_characters:
323+
val = val.replace(character, "")
324+
git_blame_dict[key] = val
325+
326+
datetime_object = datetime.date.fromtimestamp(float(git_blame_dict['author-time']))
327+
year = datetime_object.strftime("%Y")
328+
month = datetime_object.strftime("%m")
329+
day = datetime_object.strftime("%d")
330+
331+
git_blame_dict['author-time'] = '%s/%s/%s' % (day, month, year)
332+
333+
return git_blame_dict
334+
335+
336+
def tr_str(td_th, line, id, cwe, severity, message, author, author_mail, date, add_author, tr_class=None, htmlfile=None, message_class=None):
337+
ret = ''
338+
if htmlfile:
339+
ret += '<%s><a href="%s#line-%d">%d</a></%s>' % (td_th, htmlfile, line, line, td_th)
340+
for item in (id, cwe, severity):
341+
ret += '<%s>%s</%s>' % (td_th, item, td_th)
342+
else:
343+
for item in (line, id, cwe, severity):
344+
ret += '<%s>%s</%s>' % (td_th, item, td_th)
345+
if message_class:
346+
message_attribute = ' class="%s"' % message_class
347+
else:
348+
message_attribute = ''
349+
ret += '<%s%s>%s</%s>' % (td_th, message_attribute, html_escape(message), td_th)
350+
351+
if add_author:
352+
for item in (author, author_mail, date):
353+
ret += '<%s>%s</%s>' % (td_th, item, td_th)
354+
if tr_class:
355+
tr_attributes = ' class="%s"' % tr_class
356+
else:
357+
tr_attributes = ''
358+
return '<tr%s>%s</tr>' % (tr_attributes, ret)
359+
360+
290361
class AnnotateCodeFormatter(HtmlFormatter):
291362
errors = []
292363

@@ -405,8 +476,15 @@ if __name__ == '__main__':
405476
parser.add_option('--source-dir', dest='source_dir',
406477
help='Base directory where source code files can be '
407478
'found.')
479+
parser.add_option('--add-author-information', dest='add_author_information',
480+
help='Initially set to false'
481+
'Adds author, author-mail and time to htmlreport')
408482
parser.add_option('--source-encoding', dest='source_encoding',
409483
help='Encoding of source code.', default='utf-8')
484+
parser.add_option('--blame-options', dest='blame_options',
485+
help='[-w, -M] blame options which you can use to get author and author mail '
486+
'-w --> not including white spaces and returns original author of the line '
487+
'-M --> not including moving of lines and returns original author of the line')
410488

411489
# Parse options and make sure that we have an output directory set.
412490
options, args = parser.parse_args()
@@ -421,10 +499,19 @@ if __name__ == '__main__':
421499
parser.error('No report directory set.')
422500

423501
# Get the directory where source code files are located.
502+
cwd = os.getcwd()
424503
source_dir = os.getcwd()
425504
if options.source_dir:
426505
source_dir = options.source_dir
427506

507+
add_author_information = False
508+
if options.add_author_information:
509+
add_author_information = True
510+
511+
blame_options = ''
512+
if options.blame_options:
513+
blame_options = options.blame_options
514+
add_author_information = True
428515
# Parse the xml from all files defined in file argument
429516
# or from stdin. If no input is provided, stdin is used
430517
# Produce a simple list of errors.
@@ -589,7 +676,10 @@ if __name__ == '__main__':
589676
output_file.write(HTML_HEAD_END.replace("content", "content_index", 1))
590677

591678
output_file.write('\n <table>')
592-
output_file.write('\n <tr><th>Line</th><th>Id</th><th>CWE</th><th>Severity</th><th>Message</th></tr>')
679+
output_file.write(
680+
'\n %s' %
681+
tr_str('th', 'Line', 'Id', 'CWE', 'Severity', 'Message', 'Author', 'Author mail', 'Date (DD/MM/YYYY)', add_author=add_author_information))
682+
593683
for filename, data in sorted(files.items()):
594684
if filename in decode_errors: # don't print a link but a note
595685
output_file.write("\n <tr><td colspan=\"5\">%s</td></tr>" % filename)
@@ -605,10 +695,14 @@ if __name__ == '__main__':
605695
(data['htmlfile'], filename))
606696

607697
for error in sorted(data['errors'], key=lambda k: k['line']):
608-
error_class = ''
698+
if add_author_information:
699+
git_blame_dict = git_blame(error['line'], source_dir, error['file'], blame_options)
700+
else:
701+
git_blame_dict = {}
702+
message_class = None
609703
try:
610704
if error['inconclusive'] == 'true':
611-
error_class = 'class="inconclusive"'
705+
message_class = 'inconclusive'
612706
error['severity'] += ", inconcl."
613707
except KeyError:
614708
pass
@@ -620,23 +714,21 @@ if __name__ == '__main__':
620714
cwe_url = ""
621715

622716
if error['severity'] == 'error':
623-
error_class = 'class="error"'
624-
if error['id'] == 'missingInclude':
625-
output_file.write(
626-
'\n <tr class="%s"><td></td><td>%s</td><td></td><td>%s</td><td>%s</td></tr>' %
627-
(error['id'], error['id'], error['severity'], html_escape(error['msg'])))
628-
elif (error['id'] == 'unmatchedSuppression') and filename.endswith('*'):
629-
output_file.write(
630-
'\n <tr class="%s"><td></td><td>%s</td><td></td><td>%s</td><td %s>%s</td></tr>' %
631-
(error['id'], error['id'], error['severity'], error_class,
632-
html_escape(error['msg'])))
633-
else:
634-
output_file.write(
635-
'\n <tr class="%s"><td><a href="%s#line-%d">%d</a></td><td>%s</td><td>%s</td><td>%s</td><td %s>%s</td></tr>' %
636-
(error['id'], data['htmlfile'], error['line'], error['line'],
637-
error['id'], cwe_url, error['severity'], error_class,
638-
html_escape(error['msg'])))
717+
message_class = 'error'
639718

719+
is_file = filename != '' and not filename.endswith('*')
720+
line = error["line"] if is_file else ""
721+
htmlfile = data.get('htmlfile') if is_file else None
722+
723+
output_file.write(
724+
'\n %s' %
725+
tr_str('td', line, error["id"], cwe_url, error["severity"], error["msg"],
726+
git_blame_dict.get('author', 'Unknown'), git_blame_dict.get('author-mail', '---'),
727+
git_blame_dict.get('author-time', '---'),
728+
tr_class=error["id"],
729+
message_class=message_class,
730+
add_author=add_author_information,
731+
htmlfile=htmlfile))
640732
output_file.write('\n </table>')
641733
output_file.write(HTML_FOOTER % contentHandler.versionCppcheck)
642734

@@ -645,8 +737,8 @@ if __name__ == '__main__':
645737
sys.stderr.write("\nConsider changing source-encoding (for example: \"htmlreport ... --source-encoding=\"iso8859-1\"\"\n")
646738

647739
print('Creating style.css file')
648-
with io.open(os.path.join(options.report_dir, 'style.css'),
649-
'w') as css_file:
740+
os.chdir(cwd) # going back to the cwd to find style.css
741+
with io.open(os.path.join(options.report_dir, 'style.css'), 'w') as css_file:
650742
css_file.write(STYLE_FILE)
651743

652744
print("Creating stats.html (statistics)\n")

0 commit comments

Comments
 (0)
X Tutup