| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """A tool to build chrome, executed by buildbot. | 6 """A tool to build chrome, executed by buildbot. |
| 7 | 7 |
| 8 When this is run, the current directory (cwd) should be the outer build | 8 When this is run, the current directory (cwd) should be the outer build |
| 9 directory (e.g., chrome-release/build/). | 9 directory (e.g., chrome-release/build/). |
| 10 | 10 |
| 11 For a list of command-line options, call this script with '--help'. | 11 For a list of command-line options, call this script with '--help'. |
| 12 """ | 12 """ |
| 13 | 13 |
| 14 import datetime | 14 import datetime |
| 15 import errno | 15 import errno |
| 16 import json |
| 16 import multiprocessing | 17 import multiprocessing |
| 17 import optparse | 18 import optparse |
| 18 import os | 19 import os |
| 19 import re | 20 import re |
| 20 import shlex | 21 import shlex |
| 21 import sys | 22 import sys |
| 22 import time | 23 import time |
| 23 | 24 |
| 24 from common import chromium_utils | 25 from common import chromium_utils |
| 25 from slave import build_directory | 26 from slave import build_directory |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 68 fh = sys.stdout | 69 fh = sys.stdout |
| 69 fh.write('Environment variables modified in compile.py:\n') | 70 fh.write('Environment variables modified in compile.py:\n') |
| 70 for k in sorted(list(self.overrides)): | 71 for k in sorted(list(self.overrides)): |
| 71 if k in self: | 72 if k in self: |
| 72 fh.write(' %s=%s\n' % (k, self[k])) | 73 fh.write(' %s=%s\n' % (k, self[k])) |
| 73 else: | 74 else: |
| 74 fh.write(' %s (removed)\n' % k) | 75 fh.write(' %s (removed)\n' % k) |
| 75 fh.write('\n') | 76 fh.write('\n') |
| 76 | 77 |
| 77 | 78 |
| 79 class NinjaFailedTargetCollector(object): |
| 80 """This is to scrape the ninja output to collect failed "target"s. |
| 81 |
| 82 A target here is actually an output node in the ninja build graph. Thus it |
| 83 could be an object file, a lib, an executable, output of an action, etc. |
| 84 |
| 85 When a build edge fails, its output nodes will be printed in a single line |
| 86 in the following format these without quotes: |
| 87 "FAILED: obj/path/to/file1.o obj/path/to/file2.o " |
| 88 "FAILED: gen/path/to/name.cc gen/path/to/name.h " |
| 89 |
| 90 If a target is specified in the test spec but it is not defined in the gn or |
| 91 gyp build config, an "unknown target" error occurs in these two possible |
| 92 formats without quotes: |
| 93 "ninja: error: unknown target 'executable_target'" |
| 94 "ninja: error: unknown target 'exe1', did you mean 'exe2'?" |
| 95 """ |
| 96 NINJA_FAILURE_PREFIX = 'FAILED: ' |
| 97 NINJA_UNKNOWN_TARGET_PREFIX = 'ninja: error: unknown target ' |
| 98 |
| 99 def __init__(self, file_path): |
| 100 self.file_path = file_path |
| 101 self.failed_targets = [] |
| 102 self.unknown_targets = [] |
| 103 self.unrecognized_format = False |
| 104 |
| 105 def extract(self, line): |
| 106 if line.startswith(self.NINJA_FAILURE_PREFIX): |
| 107 for node in line[len(self.NINJA_FAILURE_PREFIX):].split(' '): |
| 108 node = node.strip() |
| 109 if node: |
| 110 self.failed_targets.append(node) |
| 111 elif line.startswith(self.NINJA_UNKNOWN_TARGET_PREFIX): |
| 112 parts = line[len(self.NINJA_UNKNOWN_TARGET_PREFIX):].split('\'') |
| 113 if len(parts) >= 3 and parts[1].strip(): |
| 114 self.unknown_targets.append(parts[1].strip()) |
| 115 else: |
| 116 self.unrecognized_format = True |
| 117 |
| 118 def dump_as_json(self): |
| 119 with open(self.file_path, 'wb') as f: |
| 120 json.dump({ |
| 121 'failed_targets': self.failed_targets, |
| 122 'unknown_targets': self.unknown_targets, |
| 123 'unrecognized_format': self.unrecognized_format, |
| 124 }, f) |
| 125 |
| 126 |
| 78 def ReadHKLMValue(path, value): | 127 def ReadHKLMValue(path, value): |
| 79 """Retrieve the install path from the registry for Visual Studio 8.0 and | 128 """Retrieve the install path from the registry for Visual Studio 8.0 and |
| 80 Incredibuild.""" | 129 Incredibuild.""" |
| 81 # Only available on Windows. | 130 # Only available on Windows. |
| 82 # pylint: disable=F0401 | 131 # pylint: disable=F0401 |
| 83 import win32api, win32con | 132 import win32api, win32con |
| 84 try: | 133 try: |
| 85 regkey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, path, 0, | 134 regkey = win32api.RegOpenKeyEx(win32con.HKEY_LOCAL_MACHINE, path, 0, |
| 86 win32con.KEY_READ) | 135 win32con.KEY_READ) |
| 87 value = win32api.RegQueryValueEx(regkey, value)[0] | 136 value = win32api.RegQueryValueEx(regkey, value)[0] |
| (...skipping 858 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 946 ['build%d-m4' % x for x in xrange(45, 48)]): | 995 ['build%d-m4' % x for x in xrange(45, 48)]): |
| 947 return min(10 * number_of_processors, 200) | 996 return min(10 * number_of_processors, 200) |
| 948 | 997 |
| 949 return 50 | 998 return 50 |
| 950 | 999 |
| 951 goma_jobs = determine_goma_jobs() | 1000 goma_jobs = determine_goma_jobs() |
| 952 command.append('-j%d' % goma_jobs) | 1001 command.append('-j%d' % goma_jobs) |
| 953 | 1002 |
| 954 # Run the build. | 1003 # Run the build. |
| 955 env.print_overrides() | 1004 env.print_overrides() |
| 956 exit_status = chromium_utils.RunCommand(command, env=env) | 1005 kwargs = { |
| 1006 'env': env, |
| 1007 } |
| 1008 failed_target_collector = None |
| 1009 if options.ninja_compile_failure: |
| 1010 failed_target_collector = NinjaFailedTargetCollector( |
| 1011 options.ninja_compile_failure) |
| 1012 kwargs['parser_func'] = failed_target_collector.extract |
| 1013 exit_status = chromium_utils.RunCommand(command, **kwargs) |
| 957 if exit_status == 0 and options.ninja_ensure_up_to_date: | 1014 if exit_status == 0 and options.ninja_ensure_up_to_date: |
| 958 # Run the build again if we want to check that the no-op build is clean. | 1015 # Run the build again if we want to check that the no-op build is clean. |
| 959 filter_obj = EnsureUpToDateFilter() | 1016 filter_obj = EnsureUpToDateFilter() |
| 960 # Append `-d explain` to help diagnose in the failure case. | 1017 # Append `-d explain` to help diagnose in the failure case. |
| 961 command += ['-d', 'explain'] | 1018 command += ['-d', 'explain'] |
| 962 chromium_utils.RunCommand(command, env=env, filter_obj=filter_obj) | 1019 chromium_utils.RunCommand(command, env=env, filter_obj=filter_obj) |
| 963 if not filter_obj.was_up_to_date: | 1020 if not filter_obj.was_up_to_date: |
| 964 print 'Failing build because ninja reported work to do.' | 1021 print 'Failing build because ninja reported work to do.' |
| 965 print 'This means that after completing a compile, another was run and' | 1022 print 'This means that after completing a compile, another was run and' |
| 966 print 'it resulted in still having work to do (that is, a no-op build' | 1023 print 'it resulted in still having work to do (that is, a no-op build' |
| 967 print 'wasn\'t a no-op). Consult the first "ninja explain:" line for a' | 1024 print 'wasn\'t a no-op). Consult the first "ninja explain:" line for a' |
| 968 print 'likely culprit.' | 1025 print 'likely culprit.' |
| 969 return 1 | 1026 return 1 |
| 1027 if exit_status != 0 and failed_target_collector: |
| 1028 failed_target_collector.dump_as_json() |
| 970 return exit_status | 1029 return exit_status |
| 971 finally: | 1030 finally: |
| 972 goma_teardown(options, env, exit_status) | 1031 goma_teardown(options, env, exit_status) |
| 973 | 1032 |
| 974 override_gsutil = None | 1033 override_gsutil = None |
| 975 if options.gsutil_py_path: | 1034 if options.gsutil_py_path: |
| 976 override_gsutil = [sys.executable, options.gsutil_py_path] | 1035 override_gsutil = [sys.executable, options.gsutil_py_path] |
| 977 | 1036 |
| 978 goma_utils.UploadNinjaLog( | 1037 goma_utils.UploadNinjaLog( |
| 979 options.target_output_dir, options.compiler, command, exit_status, | 1038 options.target_output_dir, options.compiler, command, exit_status, |
| (...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1273 ' credentials') | 1332 ' credentials') |
| 1274 option_parser.add_option('--verbose', action='store_true') | 1333 option_parser.add_option('--verbose', action='store_true') |
| 1275 option_parser.add_option('--gsutil-py-path', | 1334 option_parser.add_option('--gsutil-py-path', |
| 1276 help='Specify path to gsutil.py script.') | 1335 help='Specify path to gsutil.py script.') |
| 1277 option_parser.add_option('--ninja-path', default='ninja', | 1336 option_parser.add_option('--ninja-path', default='ninja', |
| 1278 help='Specify path to the ninja tool.') | 1337 help='Specify path to the ninja tool.') |
| 1279 option_parser.add_option('--ninja-ensure-up-to-date', action='store_true', | 1338 option_parser.add_option('--ninja-ensure-up-to-date', action='store_true', |
| 1280 help='Checks the output of the ninja builder to ' | 1339 help='Checks the output of the ninja builder to ' |
| 1281 'confirm that a second compile immediately ' | 1340 'confirm that a second compile immediately ' |
| 1282 'the first is a no-op.') | 1341 'the first is a no-op.') |
| 1342 option_parser.add_option('--ninja-compile-failure', |
| 1343 help='Specify a file to dump detailed info of ' |
| 1344 'compile failure from ninja. Effective only ' |
| 1345 'when building with ninja.') |
| 1283 | 1346 |
| 1284 options, args = option_parser.parse_args() | 1347 options, args = option_parser.parse_args() |
| 1285 | 1348 |
| 1286 if not options.src_dir: | 1349 if not options.src_dir: |
| 1287 options.src_dir = 'src' | 1350 options.src_dir = 'src' |
| 1288 options.src_dir = os.path.abspath(options.src_dir) | 1351 options.src_dir = os.path.abspath(options.src_dir) |
| 1289 | 1352 |
| 1290 options.build_dir = os.path.abspath(build_directory.GetBuildOutputDirectory( | 1353 options.build_dir = os.path.abspath(build_directory.GetBuildOutputDirectory( |
| 1291 os.path.basename(options.src_dir))) | 1354 os.path.basename(options.src_dir))) |
| 1292 | 1355 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1351 sys.stderr.write('Unknown build tool %s.\n' % repr(options.build_tool)) | 1414 sys.stderr.write('Unknown build tool %s.\n' % repr(options.build_tool)) |
| 1352 return 2 | 1415 return 2 |
| 1353 | 1416 |
| 1354 options.target_output_dir = get_target_build_dir(args, options) | 1417 options.target_output_dir = get_target_build_dir(args, options) |
| 1355 | 1418 |
| 1356 return main(options, args) | 1419 return main(options, args) |
| 1357 | 1420 |
| 1358 | 1421 |
| 1359 if '__main__' == __name__: | 1422 if '__main__' == __name__: |
| 1360 sys.exit(real_main()) | 1423 sys.exit(real_main()) |
| OLD | NEW |