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