1#!/usr/bin/env python3 2# Copyright 2020 the V8 project authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6# This is main driver for gcmole tool. See README for more details. 7# Usage: CLANG_BIN=clang-bin-dir python tools/gcmole/gcmole.py [arm|arm64|ia32|x64] 8 9from multiprocessing import cpu_count 10from pathlib import Path 11 12import collections 13import difflib 14import json 15import optparse 16import os 17import re 18import subprocess 19import sys 20import threading 21import queue 22 23 24ArchCfg = collections.namedtuple( 25 "ArchCfg", ["name", "cpu", "triple", "arch_define", "arch_options"]) 26 27# TODO(cbruni): use gn desc by default for platform-specific settings 28OPTIONS_64BIT = [ 29 "-DV8_COMPRESS_POINTERS", 30 "-DV8_COMPRESS_POINTERS_IN_SHARED_CAGE", 31 "-DV8_EXTERNAL_CODE_SPACE", 32 "-DV8_SHORT_BUILTIN_CALLS", 33 "-DV8_SHARED_RO_HEAP", 34] 35 36ARCHITECTURES = { 37 "ia32": 38 ArchCfg( 39 name="ia32", 40 cpu="x86", 41 triple="i586-unknown-linux", 42 arch_define="V8_TARGET_ARCH_IA32", 43 arch_options=["-m32"], 44 ), 45 "arm": 46 ArchCfg( 47 name="arm", 48 cpu="arm", 49 triple="i586-unknown-linux", 50 arch_define="V8_TARGET_ARCH_ARM", 51 arch_options=["-m32"], 52 ), 53 # TODO(cbruni): Use detailed settings: 54 # arch_options = OPTIONS_64BIT + [ "-DV8_WIN64_UNWINDING_INFO" ] 55 "x64": 56 ArchCfg( 57 name="x64", 58 cpu="x64", 59 triple="x86_64-unknown-linux", 60 arch_define="V8_TARGET_ARCH_X64", 61 arch_options=[]), 62 "arm64": 63 ArchCfg( 64 name="arm64", 65 cpu="arm64", 66 triple="x86_64-unknown-linux", 67 arch_define="V8_TARGET_ARCH_ARM64", 68 arch_options=[], 69 ), 70} 71ARCHITECTURES['x86'] = ARCHITECTURES['ia32'] 72 73 74def log(format, *args, **kwargs): 75 mark = ("#", "=", "-", ".")[kwargs.get("level", 0)] 76 print(mark * 2, str(format).format(*list(map(str, args)))) 77 78 79def fatal(format): 80 log(format) 81 sys.exit(1) 82 83 84# ----------------------------------------------------------------------------- 85# Clang invocation 86 87 88def make_clang_command_line(plugin, plugin_args, options): 89 arch_cfg = ARCHITECTURES[options.v8_target_cpu] 90 prefixed_plugin_args = [] 91 if plugin_args: 92 for arg in plugin_args: 93 prefixed_plugin_args += [ 94 "-Xclang", 95 "-plugin-arg-" + plugin, 96 "-Xclang", 97 arg, 98 ] 99 log("Using generated files in {}", options.v8_build_dir / 'gen') 100 icu_src_dir = options.v8_root_dir / 'third_party/icu/source' 101 return ([ 102 options.clang_bin_dir / "clang++", 103 "-std=c++17", 104 "-c", 105 "-Xclang", 106 "-load", 107 "-Xclang", 108 options.clang_plugins_dir / "libgcmole.so", 109 "-Xclang", 110 "-plugin", 111 "-Xclang", 112 plugin, 113 ] + prefixed_plugin_args + [ 114 "-Xclang", 115 "-triple", 116 "-Xclang", 117 arch_cfg.triple, 118 "-fno-exceptions", 119 "-Wno-everything", 120 "-D", 121 arch_cfg.arch_define, 122 "-DENABLE_DEBUGGER_SUPPORT", 123 "-DV8_ENABLE_WEBASSEMBLY", 124 "-DV8_GC_MOLE", 125 "-DV8_INTL_SUPPORT", 126 "-I{}".format(options.v8_root_dir), 127 "-I{}".format(options.v8_root_dir / 'include'), 128 "-I{}".format(options.v8_build_dir / 'gen'), 129 "-I{}".format(icu_src_dir / 'common'), 130 "-I{}".format(icu_src_dir / 'i18n'), 131 ] + arch_cfg.arch_options) 132 133 134def invoke_clang_plugin_for_file(filename, cmd_line, verbose): 135 args = cmd_line + [filename] 136 args = list(map(str, args)) 137 if verbose: 138 print("popen ", " ".join(args)) 139 p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 140 stdout, stderr = p.communicate() 141 return p.returncode, stdout.decode("utf-8"), stderr.decode("utf-8") 142 143 144def invoke_clang_plugin_for_files_in_queue(i, input_queue, output_queue, 145 cancel_event, cmd_line, verbose): 146 success = False 147 try: 148 while not cancel_event.is_set(): 149 filename = input_queue.get_nowait() 150 ret, stdout, stderr = invoke_clang_plugin_for_file( 151 filename, cmd_line, verbose) 152 output_queue.put_nowait((filename, ret, stdout, stderr)) 153 if ret != 0: 154 break 155 except KeyboardInterrupt: 156 log("[{}] Interrupting", i, level=1) 157 except queue.Empty: 158 success = True 159 finally: 160 # Emit a success bool so that the reader knows that there was either an 161 # error or all files were processed. 162 output_queue.put_nowait(success) 163 164 165def invoke_clang_plugin_for_each_file(filenames, plugin, plugin_args, options): 166 cmd_line = make_clang_command_line(plugin, plugin_args, options) 167 verbose = options.verbose 168 if options.sequential: 169 log("Sequential execution.") 170 for filename in filenames: 171 log(filename, level=1) 172 returncode, stdout, stderr = invoke_clang_plugin_for_file( 173 filename, cmd_line, verbose) 174 if returncode != 0: 175 sys.stderr.write(stderr) 176 sys.exit(returncode) 177 yield filename, stdout, stderr 178 else: 179 log("Parallel execution.") 180 cpus = cpu_count() 181 input_queue = queue.Queue() 182 output_queue = queue.Queue() 183 threads = [] 184 try: 185 for filename in filenames: 186 input_queue.put(filename) 187 188 cancel_event = threading.Event() 189 190 for i in range(min(len(filenames), cpus)): 191 threads.append( 192 threading.Thread( 193 target=invoke_clang_plugin_for_files_in_queue, 194 args=(i, input_queue, output_queue, cancel_event, cmd_line, 195 verbose))) 196 197 for t in threads: 198 t.start() 199 200 num_finished = 0 201 while num_finished < len(threads): 202 output = output_queue.get() 203 if type(output) == bool: 204 if output: 205 num_finished += 1 206 continue 207 else: 208 break 209 filename, returncode, stdout, stderr = output 210 log(filename, level=2) 211 if returncode != 0: 212 sys.stderr.write(stderr) 213 sys.exit(returncode) 214 yield filename, stdout, stderr 215 216 finally: 217 cancel_event.set() 218 for t in threads: 219 t.join() 220 221 222# ----------------------------------------------------------------------------- 223 224 225def parse_gn_file(options, for_test): 226 if for_test: 227 return {"all": [options.v8_root_dir / "tools/gcmole/gcmole-test.cc"]} 228 result = {} 229 gn_files = [ 230 ("BUILD.gn", re.compile('"([^"]*?\.cc)"'), ""), 231 ("test/cctest/BUILD.gn", re.compile('"(test-[^"]*?\.cc)"'), 232 Path("test/cctest/")), 233 ] 234 for filename, pattern, prefix in gn_files: 235 path = options.v8_root_dir / filename 236 with open(path) as gn_file: 237 gn = gn_file.read() 238 for condition, sources in re.findall("### gcmole\((.*?)\) ###(.*?)\]", gn, 239 re.MULTILINE | re.DOTALL): 240 if condition not in result: 241 result[condition] = [] 242 for file in pattern.findall(sources): 243 result[condition].append(options.v8_root_dir / prefix / file) 244 245 return result 246 247 248def evaluate_condition(cond, props): 249 if cond == "all": 250 return True 251 252 m = re.match("(\w+):(\w+)", cond) 253 if m is None: 254 fatal("failed to parse condition: {}", cond) 255 p, v = m.groups() 256 if p not in props: 257 fatal("undefined configuration property: {}", p) 258 259 return props[p] == v 260 261 262def build_file_list(options, for_test): 263 sources = parse_gn_file(options, for_test) 264 props = { 265 "os": "linux", 266 "arch": options.v8_target_cpu, 267 "mode": "debug", 268 "simulator": "" 269 } 270 ret = [] 271 for condition, files in list(sources.items()): 272 if evaluate_condition(condition, props): 273 ret += files 274 return ret 275 276 277# ----------------------------------------------------------------------------- 278# GCSuspects Generation 279 280# Note that the gcsuspects file lists functions in the form: 281# mangled_name,unmangled_function_name 282# 283# This means that we can match just the function name by matching only 284# after a comma. 285ALLOWLIST = [ 286 # The following functions call CEntryStub which is always present. 287 "MacroAssembler.*,CallRuntime", 288 "CompileCallLoadPropertyWithInterceptor", 289 "CallIC.*,GenerateMiss", 290 # DirectCEntryStub is a special stub used on ARM. 291 # It is pinned and always present. 292 "DirectCEntryStub.*,GenerateCall", 293 # TODO GCMole currently is sensitive enough to understand that certain 294 # functions only cause GC and return Failure simulataneously. 295 # Callsites of such functions are safe as long as they are properly 296 # check return value and propagate the Failure to the caller. 297 # It should be possible to extend GCMole to understand this. 298 "Heap.*,TryEvacuateObject", 299 # Ignore all StateTag methods. 300 "StateTag", 301 # Ignore printing of elements transition. 302 "PrintElementsTransition", 303 # CodeCreateEvent receives AbstractCode (a raw ptr) as an argument. 304 "CodeCreateEvent", 305 "WriteField", 306] 307 308GC_PATTERN = ",.*Collect.*Garbage" 309SAFEPOINT_PATTERN = ",SafepointSlowPath" 310ALLOWLIST_PATTERN = "|".join("(?:{})".format(p) for p in ALLOWLIST) 311 312 313def merge_regexp(pattern_dict): 314 return re.compile("|".join("(?P<{}>{})".format(key, value) 315 for (key, value) in list(pattern_dict.items()))) 316 317 318IS_SPECIAL_WITHOUT_ALLOW_LIST = merge_regexp({ 319 "gc": GC_PATTERN, 320 "safepoint": SAFEPOINT_PATTERN 321}) 322IS_SPECIAL_WITH_ALLOW_LIST = merge_regexp({ 323 "gc": GC_PATTERN, 324 "safepoint": SAFEPOINT_PATTERN, 325 "allow": ALLOWLIST_PATTERN 326}) 327 328 329class GCSuspectsCollector: 330 331 def __init__(self, options): 332 self.gc = {} 333 self.gc_caused = collections.defaultdict(lambda: set()) 334 self.funcs = {} 335 self.current_caller = None 336 self.allowlist = options.allowlist 337 self.is_special = IS_SPECIAL_WITH_ALLOW_LIST if self.allowlist else IS_SPECIAL_WITHOUT_ALLOW_LIST 338 339 def add_cause(self, name, cause): 340 self.gc_caused[name].add(cause) 341 342 def parse(self, lines): 343 for funcname in lines: 344 if not funcname: 345 continue 346 347 if funcname[0] != "\t": 348 self.resolve(funcname) 349 self.current_caller = funcname 350 else: 351 name = funcname[1:] 352 callers_for_name = self.resolve(name) 353 callers_for_name.add(self.current_caller) 354 355 def resolve(self, name): 356 if name not in self.funcs: 357 self.funcs[name] = set() 358 m = self.is_special.search(name) 359 if m: 360 if m.group("gc"): 361 self.gc[name] = True 362 self.add_cause(name, "<GC>") 363 elif m.group("safepoint"): 364 self.gc[name] = True 365 self.add_cause(name, "<Safepoint>") 366 elif m.group("allow"): 367 self.gc[name] = False 368 369 return self.funcs[name] 370 371 def propagate(self): 372 log("Propagating GC information") 373 374 def mark(funcname, callers): 375 for caller in callers: 376 if caller not in self.gc: 377 self.gc[caller] = True 378 mark(caller, self.funcs[caller]) 379 self.add_cause(caller, funcname) 380 381 for funcname, callers in list(self.funcs.items()): 382 if self.gc.get(funcname, False): 383 mark(funcname, callers) 384 385 386def generate_gc_suspects(files, options): 387 # Reset the global state. 388 collector = GCSuspectsCollector(options) 389 390 log("Building GC Suspects for {}", options.v8_target_cpu) 391 for _, stdout, _ in invoke_clang_plugin_for_each_file(files, "dump-callees", 392 [], options): 393 collector.parse(stdout.splitlines()) 394 collector.propagate() 395 # TODO(cbruni): remove once gcmole.cc is migrated 396 write_gcmole_results(collector, options, options.v8_root_dir) 397 write_gcmole_results(collector, options, options.out_dir) 398 399 400def write_gcmole_results(collector, options, dst): 401 # gcsuspects contains a list("mangled_full_name,name") of all functions that 402 # could cause a gc (directly or indirectly). 403 # 404 # EXAMPLE 405 # _ZN2v88internal4Heap16CreateApiObjectsEv,CreateApiObjects 406 # _ZN2v88internal4Heap17CreateInitialMapsEv,CreateInitialMaps 407 # ... 408 with open(dst / "gcsuspects", "w") as out: 409 for name, value in list(collector.gc.items()): 410 if value: 411 out.write(name + "\n") 412 # gccauses contains a map["mangled_full_name,name"] => list(inner gcsuspects) 413 # Where the inner gcsuspects are functions directly called in the outer 414 # function that can cause a gc. The format is encoded for simplified 415 # deserialization in gcmole.cc. 416 # 417 # EXAMPLE: 418 # _ZN2v88internal4Heap17CreateHeapObjectsEv,CreateHeapObjects 419 # start,nested 420 # _ZN2v88internal4Heap16CreateApiObjectsEv,CreateApiObjects 421 # _ZN2v88internal4Heap17CreateInitialMapsEv,CreateInitialMaps 422 # ... 423 # end,nested 424 # ... 425 with open(dst / "gccauses", "w") as out: 426 for name, causes in list(collector.gc_caused.items()): 427 out.write("{}\n".format(name)) 428 out.write("start,nested\n") 429 for cause in causes: 430 out.write("{}\n".format(cause)) 431 out.write("end,nested\n") 432 log("GCSuspects and gccauses generated for {} in '{}'", options.v8_target_cpu, 433 dst) 434 435 436# ------------------------------------------------------------------------------ 437# Analysis 438 439 440def check_correctness_for_arch(options, for_test): 441 files = build_file_list(options, for_test) 442 443 if not options.reuse_gcsuspects: 444 generate_gc_suspects(files, options) 445 else: 446 log("Reusing GCSuspects for {}", options.v8_target_cpu) 447 448 processed_files = 0 449 errors_found = False 450 output = "" 451 452 log("Searching for evaluation order problems " + 453 (' and dead variables' if options.dead_vars else '') + "for" + 454 options.v8_target_cpu) 455 plugin_args = [] 456 if options.dead_vars: 457 plugin_args.append("--dead-vars") 458 if options.verbose: 459 plugin_args.append("--verbose") 460 if options.verbose_trace: 461 plugin_args.append("--verbose-trace") 462 for _, _, stderr in invoke_clang_plugin_for_each_file(files, "find-problems", 463 plugin_args, options): 464 processed_files = processed_files + 1 465 if not errors_found: 466 errors_found = re.search("^[^:]+:\d+:\d+: (warning|error)", stderr, 467 re.MULTILINE) is not None 468 if for_test: 469 output = output + stderr 470 else: 471 sys.stdout.write(stderr) 472 473 log("Done processing {} files.", processed_files) 474 log("Errors found" if errors_found else "No errors found") 475 476 return errors_found, output 477 478 479def test_run(options): 480 if not options.test_run: 481 return True 482 log("Test Run") 483 errors_found, output = check_correctness_for_arch(options, True) 484 if not errors_found: 485 log("Test file should produce errors, but none were found. Output:") 486 print(output) 487 return False 488 489 new_file = options.out_dir / "test-expectations-gen.txt" 490 with open(new_file, "w") as f: 491 f.write(output) 492 log("Wrote test-results: {}", new_file) 493 494 expected_file = options.v8_root_dir / "tools/gcmole/test-expectations.txt" 495 with open(expected_file) as exp_file: 496 expectations = exp_file.read() 497 498 if output != expectations: 499 diff_file = options.out_dir / "test_output.diff" 500 print("#" * 79) 501 log("Output mismatch from running tests.") 502 log("Please run gcmole manually with --test-run --verbose.") 503 log("Expected: " + expected_file) 504 log("New: " + new_file) 505 log("*Diff:* " + diff_file) 506 print("#" * 79) 507 for line in difflib.unified_diff( 508 expectations.splitlines(), 509 output.splitlines(), 510 fromfile=str(new_file), 511 tofile=str(diff_file), 512 lineterm="", 513 ): 514 print(line) 515 516 print("#" * 79) 517 log("Full output") 518 log("Expected: " + expected_file) 519 log("Diff: " + diff_file) 520 log("*New:* " + new_file) 521 print("#" * 79) 522 print(output) 523 print("#" * 79) 524 525 return False 526 527 log("Tests ran successfully") 528 return True 529 530 531# ============================================================================= 532def relative_parents(path, level=0): 533 return Path(os.path.relpath(str(path.resolve().parents[level]))) 534 535 536def main(args): 537 # Print arguments for better debugging on the bots 538 # Get a clean parent path relative to PWD 539 gcmole_dir = relative_parents(Path(args[0])) 540 541 parser = optparse.OptionParser() 542 archs = list(ARCHITECTURES.keys()) 543 parser.add_option( 544 "--v8-target-cpu", 545 type="choice", 546 choices=archs, 547 help="Tested CPU architecture. Choices: {}".format(archs), 548 metavar="CPU") 549 default_clang_bin_dir = gcmole_dir / 'gcmole-tools/bin' 550 parser.add_option( 551 "--clang-bin-dir", 552 metavar="DIR", 553 help="Build dir of the custom clang version for gcmole." + \ 554 "Default: env['CLANG_DIR'] or '{}'".format(default_clang_bin_dir)) 555 parser.add_option( 556 "--clang-plugins-dir", 557 metavar="DIR", 558 help="Containing dir for libgcmole.so." 559 "Default: env['CLANG_PLUGINS'] or '{}'".format(gcmole_dir)) 560 default_root_dir = relative_parents(gcmole_dir, 1) 561 parser.add_option( 562 "--v8-root-dir", 563 metavar="DIR", 564 default=default_root_dir, 565 help="V8 checkout directory. Default: '{}'".format(default_root_dir)) 566 parser.add_option( 567 "--v8-build-dir", 568 metavar="BUILD_DIR", 569 help="GN build dir for v8. Default: 'out/CPU.Release'. " 570 "Config must match cpu specified by --v8-target-cpu") 571 parser.add_option( 572 "--out-dir", 573 metavar="DIR", 574 help="Output location for the gcsuspect and gcauses file." 575 "Default: BUILD_DIR/gen/tools/gcmole") 576 parser.add_option( 577 "--is-bot", 578 action="store_true", 579 default=False, 580 help="Flag for setting build bot specific settings.") 581 582 group = optparse.OptionGroup(parser, "GCMOLE options") 583 group.add_option( 584 "--reuse-gcsuspects", 585 action="store_true", 586 default=False, 587 help="Don't build gcsuspects file and reuse previously generated one.") 588 group.add_option( 589 "--sequential", 590 action="store_true", 591 default=False, 592 help="Don't use parallel python runner.") 593 group.add_option( 594 "--verbose", 595 action="store_true", 596 default=False, 597 help="Print commands to console before executing them.") 598 group.add_option( 599 "--no-dead-vars", 600 action="store_false", 601 dest="dead_vars", 602 default=True, 603 help="Don't perform dead variable analysis.") 604 group.add_option( 605 "--verbose-trace", 606 action="store_true", 607 default=False, 608 help="Enable verbose tracing from the plugin itself." 609 "This can be useful to debug finding dead variable.") 610 group.add_option( 611 "--no-allowlist", 612 action="store_true", 613 default=True, 614 dest="allowlist", 615 help="When building gcsuspects allowlist certain functions as if they can be " 616 "causing GC. Currently used to reduce number of false positives in dead " 617 "variables analysis. See TODO for ALLOWLIST in gcmole.py") 618 group.add_option( 619 "--test-run", 620 action="store_true", 621 default=False, 622 help="Test gcmole on tools/gcmole/gcmole-test.cc") 623 parser.add_option_group(group) 624 625 (options, args) = parser.parse_args() 626 627 if not options.v8_target_cpu: 628 # Backwards compatibility 629 if len(args[0]) > 0 and args[0] in archs: 630 options.v8_target_cpu = args[0] 631 log("Using --v8-target-cpu={}", options.v8_target_cpu) 632 else: 633 parser.error("Missing --v8-target-cpu option") 634 635 options.is_bot = False 636 verify_and_convert_dirs(parser, options, gcmole_dir, default_clang_bin_dir) 637 verify_clang_plugin(parser, options) 638 prepare_gcmole_files(options) 639 verify_build_config(parser, options) 640 641 any_errors_found = False 642 if not test_run(options): 643 any_errors_found = True 644 else: 645 errors_found, output = check_correctness_for_arch(options, False) 646 any_errors_found = any_errors_found or errors_found 647 648 sys.exit(1 if any_errors_found else 0) 649 650 651def verify_and_convert_dirs(parser, options, gcmole_dir, default_clang_bin_dir): 652 # Verify options for setting directors and convert the input strings to Path 653 # objects. 654 options.v8_root_dir = Path(options.v8_root_dir) 655 656 if not options.clang_bin_dir: 657 if os.getenv("CLANG_BIN"): 658 options.clang_bin_dir = Path(os.getenv("CLANG_BIN")) 659 options.is_bot = True 660 else: 661 options.clang_bin_dir = default_clang_bin_dir 662 if not (options.clang_bin_dir / 'clang++').exists(): 663 options.clang_bin_dir = Path(gcmole_dir, 664 "tools/gcmole/bootstrap/build/bin") 665 log("Using --clang-bin-dir={}", options.clang_bin_dir) 666 else: 667 options.clang_bin_dir = Path(options.clang_bin_dir) 668 669 if not options.clang_plugins_dir: 670 if os.getenv("CLANG_PLUGINS"): 671 options.clang_plugins_dir = Path(os.getenv("CLANG_PLUGINS")) 672 else: 673 options.clang_plugins_dir = gcmole_dir.resolve() 674 log("Using --clang-plugins-dir={}", options.clang_plugins_dir) 675 else: 676 options.clang_plugins_dir = Path(options.clang_plugins_dir) 677 678 if not options.v8_build_dir: 679 config = ARCHITECTURES[options.v8_target_cpu] 680 options.v8_build_dir = options.v8_root_dir / ('out/%s.release' % 681 config.name) 682 # Fallback for build bots. 683 if not options.v8_build_dir.exists() and os.getenv("CLANG_BIN"): 684 options.v8_build_dir = options.v8_root_dir / 'out/build' 685 log("Using --v8-build-dir={}", options.v8_build_dir) 686 else: 687 options.v8_build_dir = Path(options.v8_build_dir) 688 689 if not options.out_dir: 690 options.out_dir = options.v8_build_dir / 'gen/tools/gcmole' 691 if options.v8_build_dir.exists(): 692 options.out_dir.mkdir(parents=True, exist_ok=True) 693 else: 694 options.out_dir = Path(options.out_dir) 695 696 for flag in [ 697 "--v8-root-dir", "--v8-build-dir", "--clang-bin-dir", 698 "--clang-plugins-dir", "--out-dir" 699 ]: 700 dir = getattr(options, parser.get_option(flag).dest) 701 if not dir.is_dir(): 702 parser.error("{}='{}' does not exist!".format(flag, dir)) 703 704 705def verify_clang_plugin(parser, options): 706 libgcmole_path = options.clang_plugins_dir / "libgcmole.so" 707 if not libgcmole_path.is_file(): 708 parser.error("'{}' does not exist. Please build gcmole first.".format( 709 libgcmole_path)) 710 clang_path = options.clang_bin_dir / "clang++" 711 if not clang_path.is_file(): 712 parser.error( 713 "'{}' does not exist. Please build gcmole first.".format(clang_path)) 714 715 716def prepare_gcmole_files(options): 717 cmd = [ 718 "ninja", "-C", options.v8_build_dir, "v8_gcmole_files", 719 "v8_dump_build_config" 720 ] 721 cmd = list(map(str, cmd)) 722 log("Preparing files: {}", " ".join(cmd)) 723 try: 724 subprocess.check_call(cmd) 725 except: 726 # Ignore ninja task errors on the bots 727 if options.is_bot: 728 log("Ninja command failed, ignoring errors.") 729 else: 730 raise 731 732 733def verify_build_config(parser, options): 734 if options.is_bot: 735 #TODO(cbruni): Fix, currently not supported on the bots 736 return 737 config_file = options.v8_build_dir / 'v8_build_config.json' 738 with open(config_file) as f: 739 config = json.load(f) 740 found_cpu = None 741 for key in ('v8_target_cpu', 'target_cpu', 'current_cpu'): 742 found_cpu = config.get('v8_target_cpu') 743 if found_cpu == options.v8_target_cpu: 744 return 745 parser.error("Build dir '{}' config doesn't match request cpu. {}: {}".format( 746 options.v8_build_dir, options.v8_target_cpu, found_cpu)) 747 748 749if __name__ == "__main__": 750 main(sys.argv) 751