/
build_log_analyzer.py
190 lines (158 loc) · 6.17 KB
/
build_log_analyzer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# -*- coding: utf-8 -*-
#
# This tool helps you to rebase package to the latest version
# Copyright (C) 2013-2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# he Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY 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 along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Authors: Petr Hracek <phracek@redhat.com>
import re
import os
from rebasehelper.logger import logger
class BuildLogAnalyzerMissingError(RuntimeError):
"""Error indicating build.log is missing"""
pass
class BuildLogAnalyzerMakeError(RuntimeError):
"""Error indicating failure problem with building"""
pass
class BuildLogAnalyzerPatchError(RuntimeError):
"""Error indicating failure problem with building"""
pass
class BuildLogAnalyzer(object):
"""Class analyze the log provided by build programs"""
log_dirname = ""
@classmethod
def parse_log(cls, dir_name, log_name):
"""
Function analyze the logs for specific section
:param log_name: Logfile name which is analyzed
:param start_string: Start string
:return: list of files
"""
log_dictionary = {'build.log': cls._parse_build_log,
'mock.log': cls._parse_mock_log}
return log_dictionary[log_name](os.path.join(dir_name, log_name))
@classmethod
def _parse_build_log(cls, log_name):
"""
Function analyzes log files in our case build.log
:param log_name:
:return: list of files which are either missing or not exists
"""
files = {}
with open(log_name, 'r') as f:
lines = f.read()
if not lines:
logger.debug('Problem with openning log %s', log_name)
raise BuildLogAnalyzerMissingError
# Test for finding files which exists in sources
# but are not mentioned in spec file
missing_re = r'error:\s+Installed\s+'
missing_source_re = r'RPM build errors:'
e_reg = 'EXCEPTION:'
if cls._find_patch_error(lines):
raise BuildLogAnalyzerPatchError('Patching failed during building. '
'Look at the build log %s', log_name)
section = cls._find_section(lines, missing_re, e_reg)
if section:
section = section.replace('File not found by glob:', '').replace('File not found:', '')
logger.debug('Found missing files which are not in SPEC file: %s', section)
files['missing'] = cls._get_files_from_string(section)
else:
section = cls._find_section(lines, missing_source_re, e_reg)
if section:
if cls._find_make_error(lines):
raise BuildLogAnalyzerMakeError('Look at the build log %s', log_name)
else:
files_from_section = cls._get_files_from_string(section)
if files_from_section:
logger.debug('Found files which does not exist in source: %s', section)
files['deleted'] = files_from_section
else:
logger.info('Not known issue')
raise RuntimeError
else:
logger.info('We did not find a reason why build failed.')
logger.info('Look at the build log %s', log_name)
return files
@classmethod
def _find_make_error(cls, section):
for x in map(str.strip, section.split('\n')):
if 'make' in x and 'Error' in x:
logger.info('Package build failed')
return True
return False
@classmethod
def _find_patch_error(cls, section):
section_lst = section.split('\n')
found_hunk = False
for index, x in enumerate(map(str.strip, section_lst)):
if x.startswith('Hunk') and 'FAILED' in x:
found_hunk = True
if x.startswith('RPM build errors') and found_hunk:
return True
return False
@classmethod
def _get_files_from_string(cls, section):
"""
Function returns files from string
If row begins with / then it appends the rest of row to field
"""
files = []
for x in map(str.strip, section.split('\n')):
for dirs in ['usr', 'etc', 'opt', 'bin', 'var', 'sbin']:
pos = x.find(dirs)
if pos != -1:
x = x[pos-1:]
break
if x.startswith('/') and x not in files:
files.append(x)
return files
@classmethod
def _parse_mock_log(cls, log_name):
"""
:param log_name: mock logfile
:return: files which failed
"""
with open(log_name, 'r') as f:
lines = f.read()
if not lines:
logger.debug('Problem with openning log %s', log_name)
raise BuildLogAnalyzerMissingError
return None
@classmethod
def _find_section(cls, lines, s_reg, e_reg=None):
"""
get string from substring
:param log_name: file_name to analyze
:param s_reg: Start regular expression
:param e_reg: End regular expression
"""
sub_lines = None
s_search = re.search(s_reg, lines)
s_pos = e_pos = 0
if s_search:
s_pos = s_search.start()
if e_reg:
e_search = re.search(e_reg, lines)
if e_search:
e_pos = e_search.start()
if int(s_pos) == 0 or int(e_pos) == 0:
return None
if not e_reg:
sub_lines = lines[s_pos:]
else:
sub_lines = lines[s_pos:e_pos]
return sub_lines