| Index: scripts/slave/compile.py
|
| diff --git a/scripts/slave/compile.py b/scripts/slave/compile.py
|
| index 3a4eba45dcf4842a2c600dcdc62764ca2bb48346..0db39165e746c0caea99c0fc1745ca4822fb36dd 100755
|
| --- a/scripts/slave/compile.py
|
| +++ b/scripts/slave/compile.py
|
| @@ -13,6 +13,7 @@
|
|
|
| import datetime
|
| import errno
|
| +import json
|
| import multiprocessing
|
| import optparse
|
| import os
|
| @@ -75,6 +76,54 @@ class EchoDict(dict):
|
| fh.write('\n')
|
|
|
|
|
| +class NinjaFailedTargetCollector(object):
|
| + """This is to scrape the ninja output to collect failed "target"s.
|
| +
|
| + A target here is actually an output node in the ninja build graph. Thus it
|
| + could be an object file, a lib, an executable, output of an action, etc.
|
| +
|
| + When a build edge fails, its output nodes will be printed in a single line
|
| + in the following format these without quotes:
|
| + "FAILED: obj/path/to/file1.o obj/path/to/file2.o "
|
| + "FAILED: gen/path/to/name.cc gen/path/to/name.h "
|
| +
|
| + If a target is specified in the test spec but it is not defined in the gn or
|
| + gyp build config, an "unknown target" error occurs in these two possible
|
| + formats without quotes:
|
| + "ninja: error: unknown target 'executable_target'"
|
| + "ninja: error: unknown target 'exe1', did you mean 'exe2'?"
|
| + """
|
| + NINJA_FAILURE_PREFIX = 'FAILED: '
|
| + NINJA_UNKNOWN_TARGET_PREFIX = 'ninja: error: unknown target '
|
| +
|
| + def __init__(self, file_path):
|
| + self.file_path = file_path
|
| + self.failed_targets = []
|
| + self.unknown_targets = []
|
| + self.unrecognized_format = False
|
| +
|
| + def extract(self, line):
|
| + if line.startswith(self.NINJA_FAILURE_PREFIX):
|
| + for node in line[len(self.NINJA_FAILURE_PREFIX):].split(' '):
|
| + node = node.strip()
|
| + if node:
|
| + self.failed_targets.append(node)
|
| + elif line.startswith(self.NINJA_UNKNOWN_TARGET_PREFIX):
|
| + parts = line[len(self.NINJA_UNKNOWN_TARGET_PREFIX):].split('\'')
|
| + if len(parts) >= 3 and parts[1].strip():
|
| + self.unknown_targets.append(parts[1].strip())
|
| + else:
|
| + self.unrecognized_format = True
|
| +
|
| + def dump_as_json(self):
|
| + with open(self.file_path, 'wb') as f:
|
| + json.dump({
|
| + 'failed_targets': self.failed_targets,
|
| + 'unknown_targets': self.unknown_targets,
|
| + 'unrecognized_format': self.unrecognized_format,
|
| + }, f)
|
| +
|
| +
|
| def ReadHKLMValue(path, value):
|
| """Retrieve the install path from the registry for Visual Studio 8.0 and
|
| Incredibuild."""
|
| @@ -953,7 +1002,15 @@ def main_ninja(options, args):
|
|
|
| # Run the build.
|
| env.print_overrides()
|
| - exit_status = chromium_utils.RunCommand(command, env=env)
|
| + kwargs = {
|
| + 'env': env,
|
| + }
|
| + failed_target_collector = None
|
| + if options.ninja_compile_failure:
|
| + failed_target_collector = NinjaFailedTargetCollector(
|
| + options.ninja_compile_failure)
|
| + kwargs['parser_func'] = failed_target_collector.extract
|
| + exit_status = chromium_utils.RunCommand(command, **kwargs)
|
| if exit_status == 0 and options.ninja_ensure_up_to_date:
|
| # Run the build again if we want to check that the no-op build is clean.
|
| filter_obj = EnsureUpToDateFilter()
|
| @@ -967,6 +1024,8 @@ def main_ninja(options, args):
|
| print 'wasn\'t a no-op). Consult the first "ninja explain:" line for a'
|
| print 'likely culprit.'
|
| return 1
|
| + if exit_status != 0 and failed_target_collector:
|
| + failed_target_collector.dump_as_json()
|
| return exit_status
|
| finally:
|
| goma_teardown(options, env, exit_status)
|
| @@ -1280,6 +1339,10 @@ def real_main():
|
| help='Checks the output of the ninja builder to '
|
| 'confirm that a second compile immediately '
|
| 'the first is a no-op.')
|
| + option_parser.add_option('--ninja-compile-failure',
|
| + help='Specify a file to dump detailed info of '
|
| + 'compile failure from ninja. Effective only '
|
| + 'when building with ninja.')
|
|
|
| options, args = option_parser.parse_args()
|
|
|
|
|