Port CTest's log scraping logic to Spack (#5561)
- This steals the magic regular expressions that CTest uses to parse log files and addds them to Spack. See here: https://github.com/Kitware/CMake/blob/master/Source/CTest/cmCTestBuildHandler.cxx These are BSD licensed, so the port is in `externa/ctest_log_parser.py` - We currently use these to do better filtering of errors from build output. Plan is to use them to generate good CDash output.
This commit is contained in:
parent
8648e2cda5
commit
29ca18e348
313
lib/spack/external/ctest_log_parser.py
vendored
Normal file
313
lib/spack/external/ctest_log_parser.py
vendored
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# CMake - Cross Platform Makefile Generator
|
||||||
|
# Copyright 2000-2017 Kitware, Inc. and Contributors
|
||||||
|
# All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions
|
||||||
|
# are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer in the
|
||||||
|
# documentation and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# * Neither the name of Kitware, Inc. nor the names of Contributors
|
||||||
|
# may be used to endorse or promote products derived from this
|
||||||
|
# software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# The above copyright and license notice applies to distributions of
|
||||||
|
# CMake in source and binary form. Third-party software packages supplied
|
||||||
|
# with CMake under compatible licenses provide their own copyright notices
|
||||||
|
# documented in corresponding subdirectories or source files.
|
||||||
|
#
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# CMake was initially developed by Kitware with the following sponsorship:
|
||||||
|
#
|
||||||
|
# * National Library of Medicine at the National Institutes of Health
|
||||||
|
# as part of the Insight Segmentation and Registration Toolkit (ITK).
|
||||||
|
#
|
||||||
|
# * US National Labs (Los Alamos, Livermore, Sandia) ASC Parallel
|
||||||
|
# Visualization Initiative.
|
||||||
|
#
|
||||||
|
# * National Alliance for Medical Image Computing (NAMIC) is funded by the
|
||||||
|
# National Institutes of Health through the NIH Roadmap for Medical
|
||||||
|
# Research, Grant U54 EB005149.
|
||||||
|
#
|
||||||
|
# * Kitware, Inc.
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
"""Functions to parse build logs and extract error messages.
|
||||||
|
|
||||||
|
This is a python port of the regular expressions CTest uses to parse log
|
||||||
|
files here:
|
||||||
|
|
||||||
|
https://github.com/Kitware/CMake/blob/master/Source/CTest/cmCTestBuildHandler.cxx
|
||||||
|
|
||||||
|
This file takes the regexes verbatim from there and adds some parsing
|
||||||
|
algorithms that duplicate the way CTest scrapes log files. To keep this
|
||||||
|
up to date with CTest, just make sure the ``*_matches`` and
|
||||||
|
``*_exceptions`` lists are kept up to date with CTest's build handler.
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
from six import StringIO
|
||||||
|
from six import string_types
|
||||||
|
|
||||||
|
|
||||||
|
error_matches = [
|
||||||
|
"^[Bb]us [Ee]rror",
|
||||||
|
"^[Ss]egmentation [Vv]iolation",
|
||||||
|
"^[Ss]egmentation [Ff]ault",
|
||||||
|
":.*[Pp]ermission [Dd]enied",
|
||||||
|
"([^ :]+):([0-9]+): ([^ \\t])",
|
||||||
|
"([^:]+): error[ \\t]*[0-9]+[ \\t]*:",
|
||||||
|
"^Error ([0-9]+):",
|
||||||
|
"^Fatal",
|
||||||
|
"^Error: ",
|
||||||
|
"^Error ",
|
||||||
|
"[0-9] ERROR: ",
|
||||||
|
"^\"[^\"]+\", line [0-9]+: [^Ww]",
|
||||||
|
"^cc[^C]*CC: ERROR File = ([^,]+), Line = ([0-9]+)",
|
||||||
|
"^ld([^:])*:([ \\t])*ERROR([^:])*:",
|
||||||
|
"^ild:([ \\t])*\\(undefined symbol\\)",
|
||||||
|
"([^ :]+) : (error|fatal error|catastrophic error)",
|
||||||
|
"([^:]+): (Error:|error|undefined reference|multiply defined)",
|
||||||
|
"([^:]+)\\(([^\\)]+)\\) ?: (error|fatal error|catastrophic error)",
|
||||||
|
"^fatal error C[0-9]+:",
|
||||||
|
": syntax error ",
|
||||||
|
"^collect2: ld returned 1 exit status",
|
||||||
|
"ld terminated with signal",
|
||||||
|
"Unsatisfied symbol",
|
||||||
|
"^Unresolved:",
|
||||||
|
"Undefined symbol",
|
||||||
|
"^Undefined[ \\t]+first referenced",
|
||||||
|
"^CMake Error.*:",
|
||||||
|
":[ \\t]cannot find",
|
||||||
|
":[ \\t]can't find",
|
||||||
|
": \\*\\*\\* No rule to make target [`'].*\\'. Stop",
|
||||||
|
": \\*\\*\\* No targets specified and no makefile found",
|
||||||
|
": Invalid loader fixup for symbol",
|
||||||
|
": Invalid fixups exist",
|
||||||
|
": Can't find library for",
|
||||||
|
": internal link edit command failed",
|
||||||
|
": Unrecognized option [`'].*\\'",
|
||||||
|
"\", line [0-9]+\\.[0-9]+: [0-9]+-[0-9]+ \\([^WI]\\)",
|
||||||
|
"ld: 0706-006 Cannot find or open library file: -l ",
|
||||||
|
"ild: \\(argument error\\) can't find library argument ::",
|
||||||
|
"^could not be found and will not be loaded.",
|
||||||
|
"s:616 string too big",
|
||||||
|
"make: Fatal error: ",
|
||||||
|
"ld: 0711-993 Error occurred while writing to the output file:",
|
||||||
|
"ld: fatal: ",
|
||||||
|
"final link failed:",
|
||||||
|
"make: \\*\\*\\*.*Error",
|
||||||
|
"make\\[.*\\]: \\*\\*\\*.*Error",
|
||||||
|
"\\*\\*\\* Error code",
|
||||||
|
"nternal error:",
|
||||||
|
"Makefile:[0-9]+: \\*\\*\\* .* Stop\\.",
|
||||||
|
": No such file or directory",
|
||||||
|
": Invalid argument",
|
||||||
|
"^The project cannot be built\\.",
|
||||||
|
"^\\[ERROR\\]",
|
||||||
|
"^Command .* failed with exit code",
|
||||||
|
]
|
||||||
|
|
||||||
|
error_exceptions = [
|
||||||
|
"instantiated from ",
|
||||||
|
"candidates are:",
|
||||||
|
": warning",
|
||||||
|
": \\(Warning\\)",
|
||||||
|
": note",
|
||||||
|
"Note:",
|
||||||
|
"makefile:",
|
||||||
|
"Makefile:",
|
||||||
|
":[ \\t]+Where:",
|
||||||
|
"([^ :]+):([0-9]+): Warning",
|
||||||
|
"------ Build started: .* ------",
|
||||||
|
]
|
||||||
|
|
||||||
|
#: Regexes to match file/line numbers in error/warning messages
|
||||||
|
warning_matches = [
|
||||||
|
"([^ :]+):([0-9]+): warning:",
|
||||||
|
"([^ :]+):([0-9]+): note:",
|
||||||
|
"^cc[^C]*CC: WARNING File = ([^,]+), Line = ([0-9]+)",
|
||||||
|
"^ld([^:])*:([ \\t])*WARNING([^:])*:",
|
||||||
|
"([^:]+): warning ([0-9]+):",
|
||||||
|
"^\"[^\"]+\", line [0-9]+: [Ww](arning|arnung)",
|
||||||
|
"([^:]+): warning[ \\t]*[0-9]+[ \\t]*:",
|
||||||
|
"^(Warning|Warnung) ([0-9]+):",
|
||||||
|
"^(Warning|Warnung)[ :]",
|
||||||
|
"WARNING: ",
|
||||||
|
"([^ :]+) : warning",
|
||||||
|
"([^:]+): warning",
|
||||||
|
"\", line [0-9]+\\.[0-9]+: [0-9]+-[0-9]+ \\([WI]\\)",
|
||||||
|
"^cxx: Warning:",
|
||||||
|
".*file: .* has no symbols",
|
||||||
|
"([^ :]+):([0-9]+): (Warning|Warnung)",
|
||||||
|
"\\([0-9]*\\): remark #[0-9]*",
|
||||||
|
"\".*\", line [0-9]+: remark\\([0-9]*\\):",
|
||||||
|
"cc-[0-9]* CC: REMARK File = .*, Line = [0-9]*",
|
||||||
|
"^CMake Warning.*:",
|
||||||
|
"^\\[WARNING\\]",
|
||||||
|
]
|
||||||
|
|
||||||
|
#: Regexes to match file/line numbers in error/warning messages
|
||||||
|
warning_exceptions = [
|
||||||
|
"/usr/.*/X11/Xlib\\.h:[0-9]+: war.*: ANSI C\\+\\+ forbids declaration",
|
||||||
|
"/usr/.*/X11/Xutil\\.h:[0-9]+: war.*: ANSI C\\+\\+ forbids declaration",
|
||||||
|
"/usr/.*/X11/XResource\\.h:[0-9]+: war.*: ANSI C\\+\\+ forbids declaration",
|
||||||
|
"WARNING 84 :",
|
||||||
|
"WARNING 47 :",
|
||||||
|
"makefile:",
|
||||||
|
"Makefile:",
|
||||||
|
"warning: Clock skew detected. Your build may be incomplete.",
|
||||||
|
"/usr/openwin/include/GL/[^:]+:",
|
||||||
|
"bind_at_load",
|
||||||
|
"XrmQGetResource",
|
||||||
|
"IceFlush",
|
||||||
|
"warning LNK4089: all references to [^ \\t]+ discarded by .OPT:REF",
|
||||||
|
"ld32: WARNING 85: definition of dataKey in",
|
||||||
|
"cc: warning 422: Unknown option \"\\+b",
|
||||||
|
"_with_warning_C",
|
||||||
|
]
|
||||||
|
|
||||||
|
#: Regexes to match file/line numbers in error/warning messages
|
||||||
|
file_line_matches = [
|
||||||
|
"^Warning W[0-9]+ ([a-zA-Z.\\:/0-9_+ ~-]+) ([0-9]+):",
|
||||||
|
"^([a-zA-Z./0-9_+ ~-]+):([0-9]+):",
|
||||||
|
"^([a-zA-Z.\\:/0-9_+ ~-]+)\\(([0-9]+)\\)",
|
||||||
|
"^[0-9]+>([a-zA-Z.\\:/0-9_+ ~-]+)\\(([0-9]+)\\)",
|
||||||
|
"^([a-zA-Z./0-9_+ ~-]+)\\(([0-9]+)\\)",
|
||||||
|
"\"([a-zA-Z./0-9_+ ~-]+)\", line ([0-9]+)",
|
||||||
|
"File = ([a-zA-Z./0-9_+ ~-]+), Line = ([0-9]+)",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class LogEvent(object):
|
||||||
|
"""Class representing interesting events (e.g., errors) in a build log."""
|
||||||
|
def __init__(self, text, line_no,
|
||||||
|
source_file=None, source_line_no=None,
|
||||||
|
pre_context=None, post_context=None):
|
||||||
|
self.text = text
|
||||||
|
self.line_no = line_no
|
||||||
|
self.source_file = source_file,
|
||||||
|
self.source_line_no = source_line_no,
|
||||||
|
self.pre_context = pre_context if pre_context is not None else []
|
||||||
|
self.post_context = post_context if post_context is not None else []
|
||||||
|
self.repeat_count = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start(self):
|
||||||
|
"""First line in the log with text for the event or its context."""
|
||||||
|
return self.line_no - len(self.pre_context)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end(self):
|
||||||
|
"""Last line in the log with text for event or its context."""
|
||||||
|
return self.line_no + len(self.post_context) + 1
|
||||||
|
|
||||||
|
def __getitem__(self, line_no):
|
||||||
|
"""Index event text and context by actual line number in file."""
|
||||||
|
if line_no == self.line_no:
|
||||||
|
return self.text
|
||||||
|
elif line_no < self.line_no:
|
||||||
|
return self.pre_context[line_no - self.line_no]
|
||||||
|
elif line_no > self.line_no:
|
||||||
|
return self.post_context[line_no - self.line_no - 1]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Returns event lines and context."""
|
||||||
|
out = StringIO()
|
||||||
|
for i in range(self.start, self.end):
|
||||||
|
if i == self.line_no:
|
||||||
|
out.write(' >> %-6d%s' % (i, self[i]))
|
||||||
|
else:
|
||||||
|
out.write(' %-6d%s' % (i, self[i]))
|
||||||
|
return out.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
class BuildError(LogEvent):
|
||||||
|
"""LogEvent subclass for build errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class BuildWarning(LogEvent):
|
||||||
|
"""LogEvent subclass for build warnings."""
|
||||||
|
|
||||||
|
|
||||||
|
def _match(matches, exceptions, line):
|
||||||
|
"""True if line matches a regex in matches and none in exceptions."""
|
||||||
|
return (any(m.search(line) for m in matches) and
|
||||||
|
not any(e.search(line) for e in exceptions))
|
||||||
|
|
||||||
|
|
||||||
|
class CTestLogParser(object):
|
||||||
|
"""Log file parser that extracts errors and warnings."""
|
||||||
|
def __init__(self):
|
||||||
|
def compile(regex_array):
|
||||||
|
return [re.compile(regex) for regex in regex_array]
|
||||||
|
|
||||||
|
self.error_matches = compile(error_matches)
|
||||||
|
self.error_exceptions = compile(error_exceptions)
|
||||||
|
self.warning_matches = compile(warning_matches)
|
||||||
|
self.warning_exceptions = compile(warning_exceptions)
|
||||||
|
self.file_line_matches = compile(file_line_matches)
|
||||||
|
|
||||||
|
def parse(self, stream, context=6):
|
||||||
|
"""Parse a log file by searching each line for errors and warnings.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
stream (str or file-like): filename or stream to read from
|
||||||
|
context (int): lines of context to extract around each log event
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(tuple): two lists containig ``BuildError`` and
|
||||||
|
``BuildWarning`` objects.
|
||||||
|
"""
|
||||||
|
if isinstance(stream, string_types):
|
||||||
|
with open(stream) as f:
|
||||||
|
return self.parse(f)
|
||||||
|
|
||||||
|
lines = [line for line in stream]
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
warnings = []
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
# use CTest's regular expressions to scrape the log for events
|
||||||
|
if _match(self.error_matches, self.error_exceptions, line):
|
||||||
|
event = BuildError(line.strip(), i + 1)
|
||||||
|
errors.append(event)
|
||||||
|
elif _match(self.warning_matches, self.warning_exceptions, line):
|
||||||
|
event = BuildWarning(line.strip(), i + 1)
|
||||||
|
warnings.append(event)
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# get file/line number for each event, if possible
|
||||||
|
for flm in self.file_line_matches:
|
||||||
|
match = flm.search(line)
|
||||||
|
if match:
|
||||||
|
event.source_file, source_line_no = match.groups()
|
||||||
|
|
||||||
|
# add log context, as well
|
||||||
|
event.pre_context = [
|
||||||
|
l.rstrip() for l in lines[i - context:i]]
|
||||||
|
event.post_context = [
|
||||||
|
l.rstrip() for l in lines[i + 1:i + context + 1]]
|
||||||
|
|
||||||
|
return errors, warnings
|
@ -747,14 +747,14 @@ def long_message(self):
|
|||||||
# The error happened in some external executed process. Show
|
# The error happened in some external executed process. Show
|
||||||
# the build log with errors highlighted.
|
# the build log with errors highlighted.
|
||||||
if self.build_log:
|
if self.build_log:
|
||||||
events = parse_log_events(self.build_log)
|
errors, warnings = parse_log_events(self.build_log)
|
||||||
nerr = len(events)
|
nerr = len(errors)
|
||||||
if nerr > 0:
|
if nerr > 0:
|
||||||
if nerr == 1:
|
if nerr == 1:
|
||||||
out.write("\n1 error found in build log:\n")
|
out.write("\n1 error found in build log:\n")
|
||||||
else:
|
else:
|
||||||
out.write("\n%d errors found in build log:\n" % nerr)
|
out.write("\n%d errors found in build log:\n" % nerr)
|
||||||
out.write(make_log_context(events))
|
out.write(make_log_context(errors))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# The error happened in in the Python code, so try to show
|
# The error happened in in the Python code, so try to show
|
||||||
|
53
lib/spack/spack/test/util/log_parser.py
Normal file
53
lib/spack/spack/test/util/log_parser.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2017, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/llnl/spack
|
||||||
|
# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License (as
|
||||||
|
# published by the Free Software Foundation) version 2.1, February 1999.
|
||||||
|
#
|
||||||
|
# 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 terms and
|
||||||
|
# conditions of the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
from ctest_log_parser import CTestLogParser
|
||||||
|
|
||||||
|
|
||||||
|
def test_log_parser(tmpdir):
|
||||||
|
log_file = tmpdir.join('log.txt')
|
||||||
|
|
||||||
|
with log_file.open('w') as f:
|
||||||
|
f.write("""#!/bin/sh\n
|
||||||
|
checking build system type... x86_64-apple-darwin16.6.0
|
||||||
|
checking host system type... x86_64-apple-darwin16.6.0
|
||||||
|
error: weird_error.c:145: something weird happened E
|
||||||
|
checking for gcc... /Users/gamblin2/src/spack/lib/spack/env/clang/clang
|
||||||
|
checking whether the C compiler works... yes
|
||||||
|
/var/tmp/build/foo.py:60: warning: some weird warning W
|
||||||
|
checking for C compiler default output file name... a.out
|
||||||
|
ld: fatal: linker thing happened E
|
||||||
|
checking for suffix of executables...
|
||||||
|
configure: error: in /path/to/some/file: E
|
||||||
|
configure: error: cannot run C compiled programs. E
|
||||||
|
""")
|
||||||
|
|
||||||
|
parser = CTestLogParser()
|
||||||
|
errors, warnings = parser.parse(str(log_file))
|
||||||
|
|
||||||
|
assert len(errors) == 4
|
||||||
|
assert all(e.text.endswith('E') for e in errors)
|
||||||
|
|
||||||
|
assert len(warnings) == 1
|
||||||
|
assert all(w.text.endswith('W') for w in warnings)
|
@ -24,84 +24,43 @@
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
import re
|
|
||||||
from six import StringIO
|
from six import StringIO
|
||||||
|
|
||||||
|
from ctest_log_parser import CTestLogParser
|
||||||
from llnl.util.tty.color import colorize
|
from llnl.util.tty.color import colorize
|
||||||
|
|
||||||
|
|
||||||
class LogEvent(object):
|
def parse_log_events(stream, context=6):
|
||||||
"""Class representing interesting events (e.g., errors) in a build log."""
|
|
||||||
def __init__(self, text, line_no,
|
|
||||||
pre_context='', post_context='', repeat_count=0):
|
|
||||||
self.text = text
|
|
||||||
self.line_no = line_no
|
|
||||||
self.pre_context = pre_context
|
|
||||||
self.post_context = post_context
|
|
||||||
self.repeat_count = repeat_count
|
|
||||||
|
|
||||||
@property
|
|
||||||
def start(self):
|
|
||||||
"""First line in the log with text for the event or its context."""
|
|
||||||
return self.line_no - len(self.pre_context)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def end(self):
|
|
||||||
"""Last line in the log with text for event or its context."""
|
|
||||||
return self.line_no + len(self.post_context) + 1
|
|
||||||
|
|
||||||
def __getitem__(self, line_no):
|
|
||||||
"""Index event text and context by actual line number in file."""
|
|
||||||
if line_no == self.line_no:
|
|
||||||
return self.text
|
|
||||||
elif line_no < self.line_no:
|
|
||||||
return self.pre_context[line_no - self.line_no]
|
|
||||||
elif line_no > self.line_no:
|
|
||||||
return self.post_context[line_no - self.line_no - 1]
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""Returns event lines and context."""
|
|
||||||
out = StringIO()
|
|
||||||
for i in range(self.start, self.end):
|
|
||||||
if i == self.line_no:
|
|
||||||
out.write(' >> %-6d%s' % (i, self[i]))
|
|
||||||
else:
|
|
||||||
out.write(' %-6d%s' % (i, self[i]))
|
|
||||||
return out.getvalue()
|
|
||||||
|
|
||||||
|
|
||||||
def parse_log_events(logfile, context=6):
|
|
||||||
"""Extract interesting events from a log file as a list of LogEvent.
|
"""Extract interesting events from a log file as a list of LogEvent.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
logfile (str): name of the build log to parse
|
stream (str or fileobject): build log name or file object
|
||||||
context (int): lines of context to extract around each log event
|
context (int): lines of context to extract around each log event
|
||||||
|
|
||||||
Currently looks for lines that contain the string 'error:', ignoring case.
|
Returns:
|
||||||
|
(tuple): two lists containig ``BuildError`` and
|
||||||
|
``BuildWarning`` objects.
|
||||||
|
|
||||||
TODO: Extract warnings and other events from the build log.
|
This is a wrapper around ``ctest_log_parser.CTestLogParser`` that
|
||||||
|
lazily constructs a single ``CTestLogParser`` object. This ensures
|
||||||
|
that all the regex compilation is only done once.
|
||||||
"""
|
"""
|
||||||
with open(logfile, 'r') as f:
|
if parse_log_events.ctest_parser is None:
|
||||||
lines = [line for line in f]
|
parse_log_events.ctest_parser = CTestLogParser()
|
||||||
|
|
||||||
log_events = []
|
return parse_log_events.ctest_parser.parse(stream, context)
|
||||||
for i, line in enumerate(lines):
|
|
||||||
if re.search(r'\berror:', line, re.IGNORECASE):
|
|
||||||
event = LogEvent(
|
#: lazily constructed CTest log parser
|
||||||
line.strip(),
|
parse_log_events.ctest_parser = None
|
||||||
i + 1,
|
|
||||||
[l.rstrip() for l in lines[i - context:i]],
|
|
||||||
[l.rstrip() for l in lines[i + 1:i + context + 1]])
|
|
||||||
log_events.append(event)
|
|
||||||
return log_events
|
|
||||||
|
|
||||||
|
|
||||||
def make_log_context(log_events):
|
def make_log_context(log_events):
|
||||||
"""Get error context from a log file.
|
"""Get error context from a log file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
log_events (list of LogEvent): list of events created by, e.g.,
|
log_events (list of LogEvent): list of events created by
|
||||||
``parse_log_events``
|
``ctest_log_parser.parse()``
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: context from the build log with errors highlighted
|
str: context from the build log with errors highlighted
|
||||||
|
Loading…
Reference in New Issue
Block a user