| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import os | 6 import os |
| 7 import re | 7 import re |
| 8 | 8 |
| 9 from waterfall import extractor_util | 9 from waterfall import extractor_util |
| 10 from waterfall import waterfall_config | 10 from waterfall import waterfall_config |
| (...skipping 195 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 206 'target': object_file, | 206 'target': object_file, |
| 207 'source': source_file | 207 'source': source_file |
| 208 }) | 208 }) |
| 209 else: | 209 else: |
| 210 match = self.WINDOWS_LINK_FAILURE_PATTERN.search(line) | 210 match = self.WINDOWS_LINK_FAILURE_PATTERN.search(line) |
| 211 if match: | 211 if match: |
| 212 # Failure is a linker error. | 212 # Failure is a linker error. |
| 213 target = match.group(1) | 213 target = match.group(1) |
| 214 signal.AddTarget({'target': target}) | 214 signal.AddTarget({'target': target}) |
| 215 | 215 |
| 216 def _GetFailedOutputNodes(self, line): |
| 217 """Returns the list of failed output nodes.""" |
| 218 # Possible format: |
| 219 # FAILED: obj/path/to/file.o |
| 220 # FAILED: target.exe |
| 221 # FAILED: "target with space in name" |
| 222 failed_output_nodes = [] |
| 223 |
| 224 while line: |
| 225 quote_index = line.find('"') |
| 226 if quote_index < 0: |
| 227 sub_part = line |
| 228 remaining_part = None |
| 229 else: |
| 230 sub_part = line[:quote_index] |
| 231 match_quote_index = line.find('"', quote_index + 1) |
| 232 if match_quote_index < 0: |
| 233 return [] # Return an empty list for unexpected format. |
| 234 failed_output_nodes.append( |
| 235 line[quote_index + 1: match_quote_index]) |
| 236 remaining_part = line[match_quote_index + 1:] |
| 237 line = remaining_part |
| 238 |
| 239 for node in sub_part.split(' '): |
| 240 node = node.strip() |
| 241 if node: |
| 242 failed_output_nodes.append(node) |
| 243 |
| 244 return failed_output_nodes |
| 245 |
| 216 def Extract(self, failure_log, test_name, step_name, bot_name, master_name): | 246 def Extract(self, failure_log, test_name, step_name, bot_name, master_name): |
| 217 signal = FailureSignal() | 247 signal = FailureSignal() |
| 218 failure_started = False | 248 failure_started = False |
| 219 is_build_command_line = False | 249 is_build_command_line = False |
| 250 failed_output_nodes = [] |
| 220 | 251 |
| 221 if (master_name == self.MAC_MASTER_NAME_FOR_COMPILE and | 252 if (master_name == self.MAC_MASTER_NAME_FOR_COMPILE and |
| 222 bot_name in self.IOS_BUILDER_NAMES_FOR_COMPILE): | 253 bot_name in self.IOS_BUILDER_NAMES_FOR_COMPILE): |
| 223 error_lines = [] | 254 error_lines = [] |
| 224 for line in reversed(failure_log.splitlines()): | 255 for line in reversed(failure_log.splitlines()): |
| 225 if (not failure_started and | 256 if (not failure_started and |
| 226 self.ERROR_LINE_END_PATTERN1.match(line)): | 257 self.ERROR_LINE_END_PATTERN1.match(line)): |
| 227 failure_started = True | 258 failure_started = True |
| 228 continue | 259 continue |
| 229 | 260 |
| 230 if failure_started: | 261 if failure_started: |
| 231 if line.startswith(self.IOS_ERROR_LINE_START_PREFIX): | 262 if line.startswith(self.IOS_ERROR_LINE_START_PREFIX): |
| 232 failure_started = False | 263 failure_started = False |
| 233 for l in error_lines[:-4]: | 264 for l in error_lines[:-4]: |
| 234 self.ExtractFiles(l, signal) | 265 self.ExtractFiles(l, signal) |
| 235 error_lines = [] | 266 error_lines = [] |
| 236 else: | 267 else: |
| 237 error_lines.append(line) | 268 error_lines.append(line) |
| 238 else: | 269 else: |
| 239 strict_regex = waterfall_config.EnableStrictRegexForCompileLinkFailures( | 270 strict_regex = waterfall_config.EnableStrictRegexForCompileLinkFailures( |
| 240 master_name, bot_name) | 271 master_name, bot_name) |
| 241 for line in failure_log.splitlines(): | 272 for line in failure_log.splitlines(): |
| 242 if line.startswith(self.FAILURE_START_LINE_PREFIX): | 273 if line.startswith(self.FAILURE_START_LINE_PREFIX): |
| 243 if not failure_started: | 274 if not failure_started: |
| 244 failure_started = True | 275 failure_started = True |
| 276 line = line[len(self.FAILURE_START_LINE_PREFIX):] |
| 277 failed_output_nodes.extend(self._GetFailedOutputNodes(line)) |
| 245 is_build_command_line = True | 278 is_build_command_line = True |
| 246 continue | 279 continue |
| 247 elif is_build_command_line: | 280 elif is_build_command_line: |
| 248 if strict_regex: | 281 if strict_regex: |
| 249 self.ExtractFailedOutputNodes(line, signal) | 282 self.ExtractFailedOutputNodes(line, signal) |
| 250 else: | 283 else: |
| 251 self.GetFailedTarget(line, signal) | 284 self.GetFailedTarget(line, signal) |
| 252 is_build_command_line = False | 285 is_build_command_line = False |
| 253 continue | 286 continue |
| 254 elif self.FAILURE_WITH_ERROR_PATTERN.match(line): | 287 elif self.FAILURE_WITH_ERROR_PATTERN.match(line): |
| 255 # It is possible the target and source file associated with a compile | 288 # It is possible the target and source file associated with a compile |
| 256 # failure is logged outside a 'FAILED: ... 1 error generated' block, | 289 # failure is logged outside a 'FAILED: ... 1 error generated' block, |
| 257 # so extract targets regardless of failure_started. | 290 # so extract targets regardless of failure_started. |
| 258 if not strict_regex: | 291 if not strict_regex: |
| 259 self.GetFailedTarget(line, signal) | 292 self.GetFailedTarget(line, signal) |
| 260 elif (failure_started and | 293 elif (failure_started and |
| 261 (self.ERROR_LINE_END_PATTERN1.match(line) or | 294 (self.ERROR_LINE_END_PATTERN1.match(line) or |
| 262 self.ERROR_LINE_END_PATTERN2.search(line) or | 295 self.ERROR_LINE_END_PATTERN2.search(line) or |
| 263 self.OUTSIDE_FAILURE_LINE_PATTERN.match(line))): | 296 self.OUTSIDE_FAILURE_LINE_PATTERN.match(line))): |
| 264 failure_started = False | 297 failure_started = False |
| 265 elif failure_started and line.startswith( | 298 elif failure_started and line.startswith( |
| 266 self.NINJA_FAILURE_END_LINE_PREFIX): # pragma: no cover | 299 self.NINJA_FAILURE_END_LINE_PREFIX): # pragma: no cover |
| 267 break | 300 break |
| 268 | 301 |
| 269 if failure_started or line.startswith(self.NINJA_ERROR_LINE_PREFIX): | 302 if failure_started or line.startswith(self.NINJA_ERROR_LINE_PREFIX): |
| 270 # either within the compile errors or is a ninja error. | 303 # either within the compile errors or is a ninja error. |
| 271 self.ExtractFiles(line, signal) | 304 self.ExtractFiles(line, signal) |
| 272 | 305 |
| 306 signal.failed_output_nodes = sorted(set(failed_output_nodes)) |
| 273 return signal | 307 return signal |
| 274 | 308 |
| 275 | 309 |
| 276 class CheckPermExtractor(Extractor): | 310 class CheckPermExtractor(Extractor): |
| 277 """For CheckPerm, only extracts files.""" | 311 """For CheckPerm, only extracts files.""" |
| 278 | 312 |
| 279 def Extract(self, failure_log, *_): | 313 def Extract(self, failure_log, *_): |
| 280 signal = FailureSignal() | 314 signal = FailureSignal() |
| 281 | 315 |
| 282 for line in reversed(failure_log.splitlines()): | 316 for line in reversed(failure_log.splitlines()): |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 525 def ExtractSignal(master_name, bot_name, step_name, test_name, failure_log): | 559 def ExtractSignal(master_name, bot_name, step_name, test_name, failure_log): |
| 526 """Uses an appropriate extractor to extract failure signals. | 560 """Uses an appropriate extractor to extract failure signals. |
| 527 | 561 |
| 528 Returns: | 562 Returns: |
| 529 A FailureSignal. | 563 A FailureSignal. |
| 530 """ | 564 """ |
| 531 # Fall back to a general-but-maybe-not-accurate extractor. | 565 # Fall back to a general-but-maybe-not-accurate extractor. |
| 532 extractor_class = EXTRACTORS.get(step_name, GeneralExtractor) | 566 extractor_class = EXTRACTORS.get(step_name, GeneralExtractor) |
| 533 return extractor_class().Extract( | 567 return extractor_class().Extract( |
| 534 failure_log, test_name, step_name, bot_name, master_name) | 568 failure_log, test_name, step_name, bot_name, master_name) |
| OLD | NEW |