| 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 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 fh = sys.stdout | 65 fh = sys.stdout |
| 66 fh.write('Environment variables modified in compile.py:\n') | 66 fh.write('Environment variables modified in compile.py:\n') |
| 67 for k in sorted(list(self.overrides)): | 67 for k in sorted(list(self.overrides)): |
| 68 if k in self: | 68 if k in self: |
| 69 fh.write(' %s=%s\n' % (k, self[k])) | 69 fh.write(' %s=%s\n' % (k, self[k])) |
| 70 else: | 70 else: |
| 71 fh.write(' %s (removed)\n' % k) | 71 fh.write(' %s (removed)\n' % k) |
| 72 fh.write('\n') | 72 fh.write('\n') |
| 73 | 73 |
| 74 | 74 |
| 75 # TODO(tikuta): move to goma_utils.py |
| 75 def goma_setup(options, env): | 76 def goma_setup(options, env): |
| 76 """Sets up goma if necessary. | 77 """Sets up goma if necessary. |
| 77 | 78 |
| 78 If using the Goma compiler, first call goma_ctl to ensure the proxy is | 79 If using the Goma compiler, first call goma_ctl to ensure the proxy is |
| 79 available, and returns (True, instance of cloudtail subprocess). | 80 available, and returns (True, instance of cloudtail subprocess). |
| 80 If it failed to start up compiler_proxy, modify options.compiler and | 81 If it failed to start up compiler_proxy, modify options.compiler and |
| 81 options.goma_dir and returns (False, None) | 82 options.goma_dir and returns (False, None) |
| 82 | 83 |
| 83 """ | 84 """ |
| 84 if options.compiler not in ('goma', 'goma-clang'): | 85 if options.compiler not in ('goma', 'goma-clang'): |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 216 # Drop goma from options.compiler | 217 # Drop goma from options.compiler |
| 217 options.compiler = options.compiler.replace('goma-', '') | 218 options.compiler = options.compiler.replace('goma-', '') |
| 218 if options.compiler == 'goma': | 219 if options.compiler == 'goma': |
| 219 options.compiler = None | 220 options.compiler = None |
| 220 # Reset options.goma_dir. | 221 # Reset options.goma_dir. |
| 221 options.goma_dir = None | 222 options.goma_dir = None |
| 222 env['GOMA_DISABLED'] = '1' | 223 env['GOMA_DISABLED'] = '1' |
| 223 return False, None | 224 return False, None |
| 224 | 225 |
| 225 | 226 |
| 227 # TODO(tikuta): move to goma_utils.py |
| 226 def goma_teardown(options, env, exit_status, cloudtail_proc): | 228 def goma_teardown(options, env, exit_status, cloudtail_proc): |
| 227 """Tears down goma if necessary. """ | 229 """Tears down goma if necessary. """ |
| 228 if (options.compiler in ('goma', 'goma-clang') and | 230 if (options.compiler in ('goma', 'goma-clang') and |
| 229 options.goma_dir): | 231 options.goma_dir): |
| 230 override_gsutil = None | 232 override_gsutil = None |
| 231 if options.gsutil_py_path: | 233 if options.gsutil_py_path: |
| 232 override_gsutil = [sys.executable, options.gsutil_py_path] | 234 override_gsutil = [sys.executable, options.gsutil_py_path] |
| 233 | 235 |
| 234 # If goma compiler_proxy crashes during the build, there could be crash | 236 # If goma compiler_proxy crashes during the build, there could be crash |
| 235 # dump. | 237 # dump. |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 338 if not entry: | 340 if not entry: |
| 339 continue | 341 continue |
| 340 key, value = entry.split('=', 1) | 342 key, value = entry.split('=', 1) |
| 341 env_in_file[key] = value | 343 env_in_file[key] = value |
| 342 env_in_file.update(env_to_store) | 344 env_in_file.update(env_to_store) |
| 343 with open(path, 'wb') as f: | 345 with open(path, 'wb') as f: |
| 344 f.write(nul.join(['%s=%s' % (k, v) for k, v in env_in_file.iteritems()])) | 346 f.write(nul.join(['%s=%s' % (k, v) for k, v in env_in_file.iteritems()])) |
| 345 f.write(nul * 2) | 347 f.write(nul * 2) |
| 346 | 348 |
| 347 | 349 |
| 348 def main_ninja(options, args): | 350 # TODO(tikuta): move to goma_utils |
| 349 """Interprets options, clobbers object files, and calls ninja.""" | 351 def determine_goma_jobs(): |
| 352 # We would like to speed up build on Windows a bit, since it is slowest. |
| 353 number_of_processors = 0 |
| 354 try: |
| 355 number_of_processors = multiprocessing.cpu_count() |
| 356 except NotImplementedError: |
| 357 print 'cpu_count() is not implemented, using default value 50.' |
| 358 return 50 |
| 350 | 359 |
| 351 # Prepare environment. | 360 assert number_of_processors > 0 |
| 352 env = EchoDict(os.environ) | 361 |
| 353 goma_ready, goma_cloudtail = goma_setup(options, env) | 362 # When goma is used, 10 * number_of_processors is basically good in |
| 354 exit_status = -1 | 363 # various situations according to our measurement. Build speed won't |
| 364 # be improved if -j is larger than that. |
| 365 # |
| 366 # Since Mac had process number limitation before, we had to set |
| 367 # the upper limit to 50. Now that the process number limitation is 2000, |
| 368 # so we would be able to use 10 * number_of_processors. |
| 369 # For the safety, we'd like to set the upper limit to 200. |
| 370 # |
| 371 # Note that currently most try-bot build slaves have 8 processors. |
| 372 if chromium_utils.IsMac() or chromium_utils.IsWindows(): |
| 373 return min(10 * number_of_processors, 200) |
| 374 |
| 375 # For Linux, we also would like to use 10 * cpu. However, not sure |
| 376 # backend resource is enough, so let me set Linux and Linux x64 builder |
| 377 # only for now. |
| 378 hostname = goma_utils.GetShortHostname() |
| 379 if hostname in ( |
| 380 ['build14-m1', 'build48-m1'] + |
| 381 # Also increasing cpus for v8/blink trybots. |
| 382 ['build%d-m4' % x for x in xrange(45, 48)] + |
| 383 # Also increasing cpus for LTO buildbots. |
| 384 ['slave%d-c1' % x for x in [20, 33] + range(78, 108)]): |
| 385 return min(10 * number_of_processors, 200) |
| 386 |
| 387 return 50 |
| 388 |
| 389 |
| 390 def main_ninja(options, args, env): |
| 391 """This function calls ninja. |
| 392 |
| 393 Args: |
| 394 options (Option): options for ninja command. |
| 395 args (str): extra args for ninja command. |
| 396 env (dict): Used when ninja command executes. |
| 397 |
| 398 Returns: |
| 399 int: ninja command exit status. |
| 400 |
| 401 """ |
| 402 |
| 355 try: | 403 try: |
| 356 if not goma_ready: | |
| 357 assert options.compiler not in ('goma', 'goma-clang') | |
| 358 assert options.goma_dir is None | |
| 359 | |
| 360 # ninja is different from all the other build systems in that it requires | |
| 361 # most configuration to be done at gyp time. This is why this function does | |
| 362 # less than the other comparable functions in this file. | |
| 363 print 'chdir to %s' % options.src_dir | 404 print 'chdir to %s' % options.src_dir |
| 364 os.chdir(options.src_dir) | 405 os.chdir(options.src_dir) |
| 365 | 406 |
| 366 command = [options.ninja_path, '-w', 'dupbuild=err', | 407 command = [options.ninja_path, '-w', 'dupbuild=err', |
| 367 '-C', options.target_output_dir] | 408 '-C', options.target_output_dir] |
| 368 | 409 |
| 369 # HACK(yyanagisawa): update environment files on |env| update. | 410 # HACK(yyanagisawa): update environment files on |env| update. |
| 370 # For compiling on Windows, environment in environment files are used. | 411 # For compiling on Windows, environment in environment files are used. |
| 371 # It means even if enviroment such as GOMA_DISABLED is updated in | 412 # It means even if enviroment such as GOMA_DISABLED is updated in |
| 372 # compile.py, the update will be ignored. | 413 # compile.py, the update will be ignored. |
| (...skipping 15 matching lines...) Expand all Loading... |
| 388 command.extend(options.build_args) | 429 command.extend(options.build_args) |
| 389 command.extend(args) | 430 command.extend(args) |
| 390 | 431 |
| 391 maybe_set_official_build_envvars(options, env) | 432 maybe_set_official_build_envvars(options, env) |
| 392 | 433 |
| 393 if options.compiler: | 434 if options.compiler: |
| 394 print 'using', options.compiler | 435 print 'using', options.compiler |
| 395 | 436 |
| 396 if options.compiler in ('goma', 'goma-clang'): | 437 if options.compiler in ('goma', 'goma-clang'): |
| 397 assert options.goma_dir | 438 assert options.goma_dir |
| 398 | 439 assert options.goma_jobs |
| 399 def determine_goma_jobs(): | 440 command.append('-j%d' % options.goma_jobs) |
| 400 # We would like to speed up build on Windows a bit, since it is slowest. | |
| 401 number_of_processors = 0 | |
| 402 try: | |
| 403 number_of_processors = multiprocessing.cpu_count() | |
| 404 except NotImplementedError: | |
| 405 print 'cpu_count() is not implemented, using default value 50.' | |
| 406 return 50 | |
| 407 | |
| 408 assert number_of_processors > 0 | |
| 409 | |
| 410 # When goma is used, 10 * number_of_processors is basically good in | |
| 411 # various situations according to our measurement. Build speed won't | |
| 412 # be improved if -j is larger than that. | |
| 413 # | |
| 414 # Since Mac had process number limitation before, we had to set | |
| 415 # the upper limit to 50. Now that the process number limitation is 2000, | |
| 416 # so we would be able to use 10 * number_of_processors. | |
| 417 # For the safety, we'd like to set the upper limit to 200. | |
| 418 # | |
| 419 # Note that currently most try-bot build slaves have 8 processors. | |
| 420 if chromium_utils.IsMac() or chromium_utils.IsWindows(): | |
| 421 return min(10 * number_of_processors, 200) | |
| 422 | |
| 423 # For Linux, we also would like to use 10 * cpu. However, not sure | |
| 424 # backend resource is enough, so let me set Linux and Linux x64 builder | |
| 425 # only for now. | |
| 426 hostname = goma_utils.GetShortHostname() | |
| 427 if hostname in ( | |
| 428 ['build14-m1', 'build48-m1'] + | |
| 429 # Also increasing cpus for v8/blink trybots. | |
| 430 ['build%d-m4' % x for x in xrange(45, 48)] + | |
| 431 # Also increasing cpus for LTO buildbots. | |
| 432 ['slave%d-c1' % x for x in [20, 33] + range(78, 108)]): | |
| 433 return min(10 * number_of_processors, 200) | |
| 434 | |
| 435 return 50 | |
| 436 | |
| 437 goma_jobs = determine_goma_jobs() | |
| 438 command.append('-j%d' % goma_jobs) | |
| 439 | 441 |
| 440 # Run the build. | 442 # Run the build. |
| 441 env.print_overrides() | 443 env.print_overrides() |
| 442 exit_status = chromium_utils.RunCommand(command, env=env) | 444 exit_status = chromium_utils.RunCommand(command, env=env) |
| 443 if exit_status == 0 and options.ninja_ensure_up_to_date: | 445 if exit_status == 0 and options.ninja_ensure_up_to_date: |
| 444 # Run the build again if we want to check that the no-op build is clean. | 446 # Run the build again if we want to check that the no-op build is clean. |
| 445 filter_obj = EnsureUpToDateFilter() | 447 filter_obj = EnsureUpToDateFilter() |
| 446 # Append `-d explain` to help diagnose in the failure case. | 448 # Append `-d explain` to help diagnose in the failure case. |
| 447 command += ['-d', 'explain'] | 449 command += ['-d', 'explain'] |
| 448 chromium_utils.RunCommand(command, env=env, filter_obj=filter_obj) | 450 chromium_utils.RunCommand(command, env=env, filter_obj=filter_obj) |
| 449 if not filter_obj.was_up_to_date: | 451 if not filter_obj.was_up_to_date: |
| 450 print 'Failing build because ninja reported work to do.' | 452 print 'Failing build because ninja reported work to do.' |
| 451 print 'This means that after completing a compile, another was run and' | 453 print 'This means that after completing a compile, another was run and' |
| 452 print 'it resulted in still having work to do (that is, a no-op build' | 454 print 'it resulted in still having work to do (that is, a no-op build' |
| 453 print 'wasn\'t a no-op). Consult the first "ninja explain:" line for a' | 455 print 'wasn\'t a no-op). Consult the first "ninja explain:" line for a' |
| 454 print 'likely culprit.' | 456 print 'likely culprit.' |
| 455 return 1 | 457 return 1 |
| 456 return exit_status | 458 return exit_status |
| 457 finally: | 459 finally: |
| 458 goma_teardown(options, env, exit_status, goma_cloudtail) | |
| 459 | |
| 460 override_gsutil = None | 460 override_gsutil = None |
| 461 if options.gsutil_py_path: | 461 if options.gsutil_py_path: |
| 462 override_gsutil = [sys.executable, options.gsutil_py_path] | 462 override_gsutil = [sys.executable, options.gsutil_py_path] |
| 463 | 463 |
| 464 goma_utils.UploadNinjaLog( | 464 goma_utils.UploadNinjaLog( |
| 465 options.target_output_dir, options.compiler, command, exit_status, | 465 options.target_output_dir, options.compiler, command, exit_status, |
| 466 override_gsutil=override_gsutil) | 466 override_gsutil=override_gsutil) |
| 467 | 467 |
| 468 | 468 |
| 469 def get_target_build_dir(args, options): | 469 def get_target_build_dir(args, options): |
| 470 """Keep this function in sync with src/build/landmines.py""" | 470 """Keep this function in sync with src/build/landmines.py""" |
| 471 if chromium_utils.IsLinux() and options.cros_board: | 471 if chromium_utils.IsLinux() and options.cros_board: |
| 472 # When building ChromeOS's Simple Chrome workflow, the output directory | 472 # When building ChromeOS's Simple Chrome workflow, the output directory |
| 473 # has a CROS board name suffix. | 473 # has a CROS board name suffix. |
| 474 outdir = 'out_%s' % (options.cros_board,) | 474 outdir = 'out_%s' % (options.cros_board,) |
| 475 elif options.out_dir: | 475 elif options.out_dir: |
| 476 outdir = options.out_dir | 476 outdir = options.out_dir |
| 477 else: | 477 else: |
| 478 outdir = 'out' | 478 outdir = 'out' |
| 479 return os.path.abspath(os.path.join(options.src_dir, outdir, options.target)) | 479 return os.path.abspath(os.path.join(options.src_dir, outdir, options.target)) |
| 480 | 480 |
| 481 | 481 |
| 482 def real_main(): | 482 def get_parsed_options(): |
| 483 option_parser = optparse.OptionParser() | 483 option_parser = optparse.OptionParser() |
| 484 option_parser.add_option('--clobber', action='store_true', default=False, | 484 option_parser.add_option('--clobber', action='store_true', default=False, |
| 485 help='delete the output directory before compiling') | 485 help='delete the output directory before compiling') |
| 486 option_parser.add_option('--target', default='Release', | 486 option_parser.add_option('--target', default='Release', |
| 487 help='build target (Debug or Release)') | 487 help='build target (Debug or Release)') |
| 488 option_parser.add_option('--src-dir', default=None, | 488 option_parser.add_option('--src-dir', default=None, |
| 489 help='path to the root of the source tree') | 489 help='path to the root of the source tree') |
| 490 option_parser.add_option('--mode', default='dev', | 490 option_parser.add_option('--mode', default='dev', |
| 491 help='build mode (dev or official) controlling ' | 491 help='build mode (dev or official) controlling ' |
| 492 'environment variables set during build') | 492 'environment variables set during build') |
| (...skipping 28 matching lines...) Expand all Loading... |
| 521 help='Enable goma CompilerInfo cache') | 521 help='Enable goma CompilerInfo cache') |
| 522 option_parser.add_option('--goma-store-local-run-output', default=None, | 522 option_parser.add_option('--goma-store-local-run-output', default=None, |
| 523 help='Store local run output to goma servers.') | 523 help='Store local run output to goma servers.') |
| 524 option_parser.add_option('--goma-fail-fast', action='store_true') | 524 option_parser.add_option('--goma-fail-fast', action='store_true') |
| 525 option_parser.add_option('--goma-disable-local-fallback', action='store_true') | 525 option_parser.add_option('--goma-disable-local-fallback', action='store_true') |
| 526 option_parser.add_option('--goma-jsonstatus', | 526 option_parser.add_option('--goma-jsonstatus', |
| 527 help='Specify a file to dump goma_ctl jsonstatus.') | 527 help='Specify a file to dump goma_ctl jsonstatus.') |
| 528 option_parser.add_option('--goma-service-account-json-file', | 528 option_parser.add_option('--goma-service-account-json-file', |
| 529 help='Specify a file containing goma service account' | 529 help='Specify a file containing goma service account' |
| 530 ' credentials') | 530 ' credentials') |
| 531 option_parser.add_option('--goma-jobs', default=None, |
| 532 help='The number of jobs for ninja -j.') |
| 531 option_parser.add_option('--gsutil-py-path', | 533 option_parser.add_option('--gsutil-py-path', |
| 532 help='Specify path to gsutil.py script.') | 534 help='Specify path to gsutil.py script.') |
| 533 option_parser.add_option('--ninja-path', default='ninja', | 535 option_parser.add_option('--ninja-path', default='ninja', |
| 534 help='Specify path to the ninja tool.') | 536 help='Specify path to the ninja tool.') |
| 535 option_parser.add_option('--ninja-ensure-up-to-date', action='store_true', | 537 option_parser.add_option('--ninja-ensure-up-to-date', action='store_true', |
| 536 help='Checks the output of the ninja builder to ' | 538 help='Checks the output of the ninja builder to ' |
| 537 'confirm that a second compile immediately ' | 539 'confirm that a second compile immediately ' |
| 538 'the first is a no-op.') | 540 'the first is a no-op.') |
| 539 | 541 |
| 540 options, args = option_parser.parse_args() | 542 options, args = option_parser.parse_args() |
| 541 | 543 |
| 542 if not options.src_dir: | 544 if not options.src_dir: |
| 543 options.src_dir = 'src' | 545 options.src_dir = 'src' |
| 544 options.src_dir = os.path.abspath(options.src_dir) | 546 options.src_dir = os.path.abspath(options.src_dir) |
| 545 | 547 |
| 546 options.target_output_dir = get_target_build_dir(args, options) | 548 options.target_output_dir = get_target_build_dir(args, options) |
| 547 | 549 |
| 548 assert options.build_tool in (None, 'ninja') | 550 assert options.build_tool in (None, 'ninja') |
| 549 return main_ninja(options, args) | 551 return options, args |
| 552 |
| 553 |
| 554 def real_main(): |
| 555 options, args = get_parsed_options() |
| 556 |
| 557 # Prepare environment. |
| 558 env = EchoDict(os.environ) |
| 559 |
| 560 # start goma |
| 561 goma_ready, goma_cloudtail = goma_setup(options, env) |
| 562 |
| 563 if not goma_ready: |
| 564 assert options.compiler not in ('goma', 'goma-clang') |
| 565 assert options.goma_dir is None |
| 566 elif options.goma_jobs is None: |
| 567 options.goma_jobs = determine_goma_jobs() |
| 568 |
| 569 # build |
| 570 exit_status = main_ninja(options, args, env) |
| 571 |
| 572 # stop goma |
| 573 goma_teardown(options, env, exit_status, goma_cloudtail) |
| 574 |
| 575 return exit_status |
| 550 | 576 |
| 551 | 577 |
| 552 if '__main__' == __name__: | 578 if '__main__' == __name__: |
| 553 sys.exit(real_main()) | 579 sys.exit(real_main()) |
| OLD | NEW |