1#!/usr/bin/env python3 2 3import os, re 4from . import settings 5from xml.etree import ElementTree 6 7class CppcheckHTMLReportError(Exception): 8 pass 9 10class CppcheckTXTReportError(Exception): 11 pass 12 13 14def __elements_equal(el1, el2): 15 if type(el1) != type(el2): return False 16 17 if el1.find('location') is None: return False 18 if el2.find('location') is None: return False 19 20 el1_location = str(el1.find('location').attrib) 21 el2_location = str(el2.find('location').attrib) 22 23 if el1_location != el2_location: return False 24 25 return True 26 27 28def __contain_element(new, lst): 29 for elem in lst: 30 if __elements_equal(new, elem): 31 return True 32 return False 33 34 35def __get_xml_root_file(filename): 36 try: 37 result_xml_root = ElementTree.parse(filename).getroot() 38 except ElementTree.ParseError as e: 39 raise CppcheckHTMLReportError( 40 "XML parsing error in {}: {}".format(filename, e) 41 ) 42 return result_xml_root 43 44 45def __sanitize_cppcheck_xml_path(xml_tree, src_path, obj_path): 46 # Some path are relative to the source tree but some others are generated 47 # in the obj tree, for cppcheck when using cppcheck-htmlreport we can pass 48 # only one source tree where the files will be fetched if relative path are 49 # found. So for every path that does not exists in src tree, we guess it 50 # comes from obj tree and we put explicit absolute path to it 51 error_item_root = xml_tree.findall("errors")[0] 52 for error_item in error_item_root: 53 for location_item in error_item.findall("location"): 54 path = location_item.attrib["file"] 55 new_obj_path = obj_path + "/" + path 56 new_src_path = src_path + "/" + path 57 if (path[0] != "/") and (not os.path.isfile(new_src_path)) \ 58 and os.path.isfile(new_obj_path): 59 location_item.attrib["file"] = new_obj_path 60 61 62def cppcheck_merge_xml_fragments(fragments_list, out_xml_file, src_path, 63 obj_path): 64 65 result_xml = __get_xml_root_file(fragments_list[0]) 66 insert_point = result_xml.findall("errors")[0] 67 for xml_file in fragments_list[1:]: 68 xml_root = __get_xml_root_file(xml_file) 69 curr_elem_list = list(insert_point) 70 new_elem_list = list(xml_root.findall("errors")[0]) 71 for xml_error_elem in new_elem_list: 72 if not __contain_element(xml_error_elem, curr_elem_list): 73 insert_point.insert(1, xml_error_elem) 74 75 if result_xml is None: 76 return False 77 78 __sanitize_cppcheck_xml_path(result_xml, src_path, obj_path) 79 80 ElementTree.ElementTree(result_xml).write(out_xml_file) 81 82 return True 83 84 85def cppcheck_merge_txt_fragments(fragments_list, out_txt_file, strip_paths): 86 try: 87 with open(out_txt_file, "wt") as outfile: 88 # Using a set will remove automatically the duplicate lines 89 text_report_content = set() 90 for file in fragments_list: 91 try: 92 with open(file, "rt") as infile: 93 frag_lines = infile.readlines() 94 except OSError as e: 95 raise CppcheckTXTReportError( 96 "Issue with reading file {}: {}" 97 .format(file, e) 98 ) 99 text_report_content.update(frag_lines) 100 101 # Back to modifiable list 102 text_report_content = list(text_report_content) 103 # Strip path from report lines 104 for i in list(range(0, len(text_report_content))): 105 # Split by : separator 106 text_report_content[i] = text_report_content[i].split(":") 107 108 for path in strip_paths: 109 text_report_content[i][0] = \ 110 text_report_content[i][0].replace(path + "/", "") 111 112 # When the compilation is in-tree, the makefile places 113 # the directory in /xen/xen, making cppcheck produce 114 # relative path from there, so check if "xen/" is a prefix 115 # of the path and if it's not, check if it can be added to 116 # have a relative path from the repository instead of from 117 # /xen/xen 118 if not text_report_content[i][0].startswith("xen/"): 119 # cppcheck first entry is in this format: 120 # path/to/file(line,cols), remove (line,cols) 121 cppcheck_file = re.sub(r'\(.*\)', '', 122 text_report_content[i][0]) 123 if os.path.isfile(settings.xen_dir + "/" + cppcheck_file): 124 text_report_content[i][0] = \ 125 "xen/" + text_report_content[i][0] 126 127 # sort alphabetically for second field (misra rule) and as second 128 # criteria for the first field (file name) 129 text_report_content.sort(key = lambda x: (x[1], x[0])) 130 # merge back with : separator 131 text_report_content = [":".join(x) for x in text_report_content] 132 # Write the final text report 133 outfile.writelines(text_report_content) 134 except OSError as e: 135 raise CppcheckTXTReportError("Issue with writing file {}: {}" 136 .format(out_txt_file, e)) 137 138 139def cppcheck_strip_path_html(html_files, strip_paths): 140 for file in html_files: 141 try: 142 with open(file, "rt") as infile: 143 html_lines = infile.readlines() 144 except OSError as e: 145 raise CppcheckHTMLReportError("Issue with reading file {}: {}" 146 .format(file, e)) 147 for i in list(range(0, len(html_lines))): 148 for path in strip_paths: 149 html_lines[i] = html_lines[i].replace(path + "/", "") 150 try: 151 with open(file, "wt") as outfile: 152 outfile.writelines(html_lines) 153 except OSError as e: 154 raise CppcheckHTMLReportError("Issue with writing file {}: {}" 155 .format(file, e)) 156