| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 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 # TODO(hinoka): Use logging. | 6 # TODO(hinoka): Use logging. |
| 7 | 7 |
| 8 import cStringIO | 8 import cStringIO |
| 9 import codecs | 9 import codecs |
| 10 import collections | 10 import collections |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 path.join(ROOT_DIR, # .recipe_deps | 79 path.join(ROOT_DIR, # .recipe_deps |
| 80 path.pardir, # slave | 80 path.pardir, # slave |
| 81 path.pardir, # scripts | 81 path.pardir, # scripts |
| 82 path.pardir), # build_internal | 82 path.pardir), # build_internal |
| 83 ]) | 83 ]) |
| 84 | 84 |
| 85 | 85 |
| 86 CHROMIUM_GIT_HOST = 'https://chromium.googlesource.com' | 86 CHROMIUM_GIT_HOST = 'https://chromium.googlesource.com' |
| 87 CHROMIUM_SRC_URL = CHROMIUM_GIT_HOST + '/chromium/src.git' | 87 CHROMIUM_SRC_URL = CHROMIUM_GIT_HOST + '/chromium/src.git' |
| 88 | 88 |
| 89 # Official builds use buildspecs, so this is a special case. | |
| 90 BUILDSPEC_TYPE = collections.namedtuple('buildspec', | |
| 91 ('container', 'version')) | |
| 92 BUILDSPEC_RE = (r'^/chrome-internal/trunk/tools/buildspec/' | |
| 93 '(build|branches|releases)/(.+)$') | |
| 94 GIT_BUILDSPEC_PATH = ('https://chrome-internal.googlesource.com/chrome/tools/' | |
| 95 'buildspec') | |
| 96 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*' | 89 BRANCH_HEADS_REFSPEC = '+refs/branch-heads/*' |
| 97 | 90 |
| 98 BUILDSPEC_COMMIT_RE = ( | |
| 99 re.compile(r'Buildspec for.*version (\d+\.\d+\.\d+\.\d+)'), | |
| 100 re.compile(r'Create (\d+\.\d+\.\d+\.\d+) buildspec'), | |
| 101 re.compile(r'Auto-converted (\d+\.\d+\.\d+\.\d+) buildspec to git'), | |
| 102 ) | |
| 103 | |
| 104 # Regular expression that matches a single commit footer line. | 91 # Regular expression that matches a single commit footer line. |
| 105 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)') | 92 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)') |
| 106 | 93 |
| 107 # Footer metadata keys for regular and gsubtreed mirrored commit positions. | 94 # Footer metadata keys for regular and gsubtreed mirrored commit positions. |
| 108 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position' | 95 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position' |
| 109 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position' | 96 COMMIT_ORIGINAL_POSITION_FOOTER_KEY = 'Cr-Original-Commit-Position' |
| 110 # Regular expression to parse a commit position | |
| 111 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}') | |
| 112 | 97 |
| 113 # Regular expression to parse gclient's revinfo entries. | 98 # Regular expression to parse gclient's revinfo entries. |
| 114 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$') | 99 REVINFO_RE = re.compile(r'^([^:]+):\s+([^@]+)@(.+)$') |
| 115 | 100 |
| 116 # Used by 'ResolveSvnRevisionFromGitiles' | |
| 117 GIT_SVN_PROJECT_MAP = { | |
| 118 'webkit': { | |
| 119 'svn_url': 'svn://svn.chromium.org/blink', | |
| 120 'branch_map': [ | |
| 121 (r'trunk', r'refs/heads/master'), | |
| 122 (r'branches/([^/]+)', r'refs/branch-heads/\1'), | |
| 123 ], | |
| 124 }, | |
| 125 'v8': { | |
| 126 'svn_url': 'https://v8.googlecode.com/svn', | |
| 127 'branch_map': [ | |
| 128 (r'trunk', r'refs/heads/candidates'), | |
| 129 (r'branches/bleeding_edge', r'refs/heads/master'), | |
| 130 (r'branches/([^/]+)', r'refs/branch-heads/\1'), | |
| 131 ], | |
| 132 }, | |
| 133 'nacl': { | |
| 134 'svn_url': 'svn://svn.chromium.org/native_client', | |
| 135 'branch_map': [ | |
| 136 (r'trunk/src/native_client', r'refs/heads/master'), | |
| 137 ], | |
| 138 }, | |
| 139 } | |
| 140 | |
| 141 # Key for the 'git-svn' ID metadata commit footer entry. | |
| 142 GIT_SVN_ID_FOOTER_KEY = 'git-svn-id' | |
| 143 # e.g., git-svn-id: https://v8.googlecode.com/svn/trunk@23117 | |
| 144 # ce2b1a6d-e550-0410-aec6-3dcde31c8c00 | |
| 145 GIT_SVN_ID_RE = re.compile(r'((?:\w+)://[^@]+)@(\d+)\s+(?:[a-zA-Z0-9\-]+)') | |
| 146 | |
| 147 | |
| 148 # This is the git mirror of the buildspecs repository. We could rely on the svn | |
| 149 # checkout, now that the git buildspecs are checked in alongside the svn | |
| 150 # buildspecs, but we're going to want to pull all the buildspecs from here | |
| 151 # eventually anyhow, and there's already some logic to pull from git (for the | |
| 152 # old git_buildspecs.git repo), so just stick with that. | |
| 153 GIT_BUILDSPEC_REPO = ( | |
| 154 'https://chrome-internal.googlesource.com/chrome/tools/buildspec') | |
| 155 | 101 |
| 156 # Copied from scripts/recipes/chromium.py. | 102 # Copied from scripts/recipes/chromium.py. |
| 157 GOT_REVISION_MAPPINGS = { | 103 GOT_REVISION_MAPPINGS = { |
| 158 '/chrome/trunk/src': { | 104 CHROMIUM_SRC_URL: { |
| 159 'src/': 'got_revision', | 105 'src/': 'got_revision', |
| 160 'src/native_client/': 'got_nacl_revision', | 106 'src/native_client/': 'got_nacl_revision', |
| 161 'src/tools/swarm_client/': 'got_swarm_client_revision', | 107 'src/tools/swarm_client/': 'got_swarm_client_revision', |
| 162 'src/tools/swarming_client/': 'got_swarming_client_revision', | 108 'src/tools/swarming_client/': 'got_swarming_client_revision', |
| 163 'src/third_party/WebKit/': 'got_webkit_revision', | 109 'src/third_party/WebKit/': 'got_webkit_revision', |
| 164 'src/third_party/webrtc/': 'got_webrtc_revision', | 110 'src/third_party/webrtc/': 'got_webrtc_revision', |
| 165 'src/v8/': 'got_v8_revision', | 111 'src/v8/': 'got_v8_revision', |
| 166 } | 112 } |
| 167 } | 113 } |
| 168 | 114 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 181 try: | 127 try: |
| 182 execfile(os.path.join( | 128 execfile(os.path.join( |
| 183 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'), | 129 BUILD_INTERNAL_DIR, 'scripts', 'slave', 'bot_update_cfg.py'), |
| 184 local_vars) | 130 local_vars) |
| 185 except Exception: | 131 except Exception: |
| 186 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place. | 132 # Same as if BUILD_INTERNAL_DIR didn't exist in the first place. |
| 187 print 'Warning: unable to read internal configuration file.' | 133 print 'Warning: unable to read internal configuration file.' |
| 188 print 'If this is an internal bot, this step may be erroneously inactive.' | 134 print 'If this is an internal bot, this step may be erroneously inactive.' |
| 189 internal_data = local_vars | 135 internal_data = local_vars |
| 190 | 136 |
| 191 RECOGNIZED_PATHS = { | |
| 192 # If SVN path matches key, the entire URL is rewritten to the Git url. | |
| 193 '/chrome/trunk/src': | |
| 194 CHROMIUM_SRC_URL, | |
| 195 '/chrome/trunk/src/tools/cros.DEPS': | |
| 196 CHROMIUM_GIT_HOST + '/chromium/src/tools/cros.DEPS.git', | |
| 197 '/chrome-internal/trunk/src-internal': | |
| 198 'https://chrome-internal.googlesource.com/chrome/src-internal.git', | |
| 199 } | |
| 200 RECOGNIZED_PATHS.update(internal_data.get('RECOGNIZED_PATHS', {})) | |
| 201 | 137 |
| 202 | 138 |
| 203 # How many times to try before giving up. | 139 # How many times to try before giving up. |
| 204 ATTEMPTS = 5 | 140 ATTEMPTS = 5 |
| 205 | 141 |
| 206 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py') | 142 GIT_CACHE_PATH = path.join(DEPOT_TOOLS_DIR, 'git_cache.py') |
| 207 | 143 |
| 208 # Find the patch tool. | 144 # Find the patch tool. |
| 209 if sys.platform.startswith('win'): | 145 if sys.platform.startswith('win'): |
| 210 if not BUILD_INTERNAL_DIR: | 146 if not BUILD_INTERNAL_DIR: |
| (...skipping 18 matching lines...) Expand all Loading... |
| 229 | 165 |
| 230 | 166 |
| 231 class PatchFailed(SubprocessFailed): | 167 class PatchFailed(SubprocessFailed): |
| 232 pass | 168 pass |
| 233 | 169 |
| 234 | 170 |
| 235 class GclientSyncFailed(SubprocessFailed): | 171 class GclientSyncFailed(SubprocessFailed): |
| 236 pass | 172 pass |
| 237 | 173 |
| 238 | 174 |
| 239 class SVNRevisionNotFound(Exception): | |
| 240 pass | |
| 241 | |
| 242 | |
| 243 class InvalidDiff(Exception): | 175 class InvalidDiff(Exception): |
| 244 pass | 176 pass |
| 245 | 177 |
| 246 | 178 |
| 247 class Inactive(Exception): | |
| 248 """Not really an exception, just used to exit early cleanly.""" | |
| 249 pass | |
| 250 | |
| 251 | |
| 252 RETRY = object() | 179 RETRY = object() |
| 253 OK = object() | 180 OK = object() |
| 254 FAIL = object() | 181 FAIL = object() |
| 255 | 182 |
| 256 | 183 |
| 257 class PsPrinter(object): | 184 class PsPrinter(object): |
| 258 def __init__(self, interval=300): | 185 def __init__(self, interval=300): |
| 259 self.interval = interval | 186 self.interval = interval |
| 260 self.active = sys.platform.startswith('linux2') | 187 self.active = sys.platform.startswith('linux2') |
| 261 self.thread = None | 188 self.thread = None |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 370 | 297 |
| 371 def get_gclient_spec(solutions, target_os, target_os_only, git_cache_dir): | 298 def get_gclient_spec(solutions, target_os, target_os_only, git_cache_dir): |
| 372 return GCLIENT_TEMPLATE % { | 299 return GCLIENT_TEMPLATE % { |
| 373 'solutions': pprint.pformat(solutions, indent=4), | 300 'solutions': pprint.pformat(solutions, indent=4), |
| 374 'cache_dir': '"%s"' % git_cache_dir, | 301 'cache_dir': '"%s"' % git_cache_dir, |
| 375 'target_os': ('\ntarget_os=%s' % target_os) if target_os else '', | 302 'target_os': ('\ntarget_os=%s' % target_os) if target_os else '', |
| 376 'target_os_only': '\ntarget_os_only=%s' % target_os_only | 303 'target_os_only': '\ntarget_os_only=%s' % target_os_only |
| 377 } | 304 } |
| 378 | 305 |
| 379 | 306 |
| 380 def maybe_ignore_revision(revision, buildspec): | |
| 381 """Handle builders that don't care what buildbot tells them to build. | |
| 382 | |
| 383 This is especially the case with branch builders that build from buildspecs | |
| 384 and/or trigger off multiple repositories, where the --revision passed in has | |
| 385 nothing to do with the solution being built. Clearing the revision in this | |
| 386 case causes bot_update to use HEAD rather that trying to checkout an | |
| 387 inappropriate version of the solution. | |
| 388 """ | |
| 389 if buildspec and buildspec.container == 'branches': | |
| 390 return [] | |
| 391 return revision | |
| 392 | |
| 393 | |
| 394 def solutions_printer(solutions): | 307 def solutions_printer(solutions): |
| 395 """Prints gclient solution to stdout.""" | 308 """Prints gclient solution to stdout.""" |
| 396 print 'Gclient Solutions' | 309 print 'Gclient Solutions' |
| 397 print '=================' | 310 print '=================' |
| 398 for solution in solutions: | 311 for solution in solutions: |
| 399 name = solution.get('name') | 312 name = solution.get('name') |
| 400 url = solution.get('url') | 313 url = solution.get('url') |
| 401 print '%s (%s)' % (name, url) | 314 print '%s (%s)' % (name, url) |
| 402 if solution.get('deps_file'): | 315 if solution.get('deps_file'): |
| 403 print ' Dependencies file is %s' % solution['deps_file'] | 316 print ' Dependencies file is %s' % solution['deps_file'] |
| (...skipping 14 matching lines...) Expand all Loading... |
| 418 print ' %s: Ignore' % deps_name | 331 print ' %s: Ignore' % deps_name |
| 419 for k, v in solution.iteritems(): | 332 for k, v in solution.iteritems(): |
| 420 # Print out all the keys we don't know about. | 333 # Print out all the keys we don't know about. |
| 421 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps', | 334 if k in ['name', 'url', 'deps_file', 'custom_vars', 'custom_deps', |
| 422 'managed']: | 335 'managed']: |
| 423 continue | 336 continue |
| 424 print ' %s is %s' % (k, v) | 337 print ' %s is %s' % (k, v) |
| 425 print | 338 print |
| 426 | 339 |
| 427 | 340 |
| 428 def solutions_to_git(input_solutions): | 341 def modify_solutions(input_solutions): |
| 429 """Modifies urls in solutions to point at Git repos. | 342 """Modifies urls in solutions to point at Git repos. |
| 430 | 343 |
| 431 returns: (git solution, svn root of first solution) tuple. | 344 returns: new solution dictionary |
| 432 """ | 345 """ |
| 433 assert input_solutions | 346 assert input_solutions |
| 434 solutions = copy.deepcopy(input_solutions) | 347 solutions = copy.deepcopy(input_solutions) |
| 435 first_solution = True | |
| 436 buildspec = None | |
| 437 for solution in solutions: | 348 for solution in solutions: |
| 438 original_url = solution['url'] | 349 original_url = solution['url'] |
| 439 parsed_url = urlparse.urlparse(original_url) | 350 parsed_url = urlparse.urlparse(original_url) |
| 440 parsed_path = parsed_url.path | 351 parsed_path = parsed_url.path |
| 441 | 352 |
| 442 # Rewrite SVN urls into Git urls. | |
| 443 buildspec_m = re.match(BUILDSPEC_RE, parsed_path) | |
| 444 if first_solution and buildspec_m: | |
| 445 solution['url'] = GIT_BUILDSPEC_PATH | |
| 446 buildspec = BUILDSPEC_TYPE( | |
| 447 container=buildspec_m.group(1), | |
| 448 version=buildspec_m.group(2), | |
| 449 ) | |
| 450 solution['deps_file'] = path.join(buildspec.container, buildspec.version, | |
| 451 'DEPS') | |
| 452 elif parsed_path in RECOGNIZED_PATHS: | |
| 453 solution['url'] = RECOGNIZED_PATHS[parsed_path] | |
| 454 solution['deps_file'] = '.DEPS.git' | |
| 455 elif parsed_url.scheme == 'https' and 'googlesource' in parsed_url.netloc: | |
| 456 pass | |
| 457 else: | |
| 458 print 'Warning: %s' % ('path %r not recognized' % parsed_path,) | |
| 459 | |
| 460 # Strip out deps containing $$V8_REV$$, etc. | |
| 461 if 'custom_deps' in solution: | |
| 462 new_custom_deps = {} | |
| 463 for deps_name, deps_value in solution['custom_deps'].iteritems(): | |
| 464 if deps_value and '$$' in deps_value: | |
| 465 print 'Dropping %s:%s from custom deps' % (deps_name, deps_value) | |
| 466 else: | |
| 467 new_custom_deps[deps_name] = deps_value | |
| 468 solution['custom_deps'] = new_custom_deps | |
| 469 | |
| 470 if first_solution: | |
| 471 root = parsed_path | |
| 472 first_solution = False | |
| 473 | |
| 474 solution['managed'] = False | 353 solution['managed'] = False |
| 475 # We don't want gclient to be using a safesync URL. Instead it should | 354 # We don't want gclient to be using a safesync URL. Instead it should |
| 476 # using the lkgr/lkcr branch/tags. | 355 # using the lkgr/lkcr branch/tags. |
| 477 if 'safesync_url' in solution: | 356 if 'safesync_url' in solution: |
| 478 print 'Removing safesync url %s from %s' % (solution['safesync_url'], | 357 print 'Removing safesync url %s from %s' % (solution['safesync_url'], |
| 479 parsed_path) | 358 parsed_path) |
| 480 del solution['safesync_url'] | 359 del solution['safesync_url'] |
| 481 return solutions, root, buildspec | 360 |
| 361 return solutions |
| 482 | 362 |
| 483 | 363 |
| 484 def remove(target): | 364 def remove(target): |
| 485 """Remove a target by moving it into build.dead.""" | 365 """Remove a target by moving it into build.dead.""" |
| 486 dead_folder = path.join(BUILDER_DIR, 'build.dead') | 366 dead_folder = path.join(BUILDER_DIR, 'build.dead') |
| 487 if not path.exists(dead_folder): | 367 if not path.exists(dead_folder): |
| 488 os.makedirs(dead_folder) | 368 os.makedirs(dead_folder) |
| 489 os.rename(target, path.join(dead_folder, uuid.uuid4().hex)) | 369 os.rename(target, path.join(dead_folder, uuid.uuid4().hex)) |
| 490 | 370 |
| 491 | 371 |
| 492 def ensure_no_checkout(dir_names, scm_dirname): | 372 def ensure_no_checkout(dir_names): |
| 493 """Ensure that there is no undesired checkout under build/. | 373 """Ensure that there is no undesired checkout under build/.""" |
| 494 | 374 build_dir = os.getcwd() |
| 495 If there is an incorrect checkout under build/, then | 375 has_checkout = any(path.exists(path.join(build_dir, dir_name, '.git')) |
| 496 move build/ to build.dead/ | |
| 497 This function will check each directory in dir_names. | |
| 498 | |
| 499 scm_dirname is expected to be either ['.svn', '.git'] | |
| 500 """ | |
| 501 assert scm_dirname in ['.svn', '.git', '*'] | |
| 502 has_checkout = any(path.exists(path.join(os.getcwd(), dir_name, scm_dirname)) | |
| 503 for dir_name in dir_names) | 376 for dir_name in dir_names) |
| 504 | 377 if has_checkout: |
| 505 if has_checkout or scm_dirname == '*': | |
| 506 build_dir = os.getcwd() | |
| 507 prefix = '' | |
| 508 if scm_dirname != '*': | |
| 509 prefix = '%s detected in checkout, ' % scm_dirname | |
| 510 | |
| 511 for filename in os.listdir(build_dir): | 378 for filename in os.listdir(build_dir): |
| 512 deletion_target = path.join(build_dir, filename) | 379 deletion_target = path.join(build_dir, filename) |
| 513 print '%sdeleting %s...' % (prefix, deletion_target), | 380 print '.git detected in checkout, deleting %s...' % deletion_target, |
| 514 remove(deletion_target) | 381 remove(deletion_target) |
| 515 print 'done' | 382 print 'done' |
| 516 | 383 |
| 517 | 384 |
| 518 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir): | 385 def gclient_configure(solutions, target_os, target_os_only, git_cache_dir): |
| 519 """Should do the same thing as gclient --spec='...'.""" | 386 """Should do the same thing as gclient --spec='...'.""" |
| 520 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: | 387 with codecs.open('.gclient', mode='w', encoding='utf-8') as f: |
| 521 f.write(get_gclient_spec( | 388 f.write(get_gclient_spec( |
| 522 solutions, target_os, target_os_only, git_cache_dir)) | 389 solutions, target_os, target_os_only, git_cache_dir)) |
| 523 | 390 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 597 footers[m.group(1)] = m.group(2).strip() | 464 footers[m.group(1)] = m.group(2).strip() |
| 598 return footers | 465 return footers |
| 599 | 466 |
| 600 | 467 |
| 601 def get_commit_message_footer(message, key): | 468 def get_commit_message_footer(message, key): |
| 602 """Returns: (str/None) The footer value for 'key', or None if none was found. | 469 """Returns: (str/None) The footer value for 'key', or None if none was found. |
| 603 """ | 470 """ |
| 604 return get_commit_message_footer_map(message).get(key) | 471 return get_commit_message_footer_map(message).get(key) |
| 605 | 472 |
| 606 | 473 |
| 607 def get_svn_rev(git_hash, dir_name): | |
| 608 log = git('log', '-1', git_hash, cwd=dir_name) | |
| 609 git_svn_id = get_commit_message_footer(log, GIT_SVN_ID_FOOTER_KEY) | |
| 610 if not git_svn_id: | |
| 611 return None | |
| 612 m = GIT_SVN_ID_RE.match(git_svn_id) | |
| 613 if not m: | |
| 614 return None | |
| 615 return int(m.group(2)) | |
| 616 | |
| 617 | |
| 618 def get_git_hash(revision, branch, sln_dir): | |
| 619 """We want to search for the SVN revision on the git-svn branch. | |
| 620 | |
| 621 Note that git will search backwards from origin/master. | |
| 622 """ | |
| 623 match = "^%s: [^ ]*@%s " % (GIT_SVN_ID_FOOTER_KEY, revision) | |
| 624 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch | |
| 625 cmd = ['log', '-E', '--grep', match, '--format=%H', '--max-count=1', ref] | |
| 626 result = git(*cmd, cwd=sln_dir).strip() | |
| 627 if result: | |
| 628 return result | |
| 629 raise SVNRevisionNotFound('We can\'t resolve svn r%s into a git hash in %s' % | |
| 630 (revision, sln_dir)) | |
| 631 | |
| 632 | |
| 633 def emit_log_lines(name, lines): | 474 def emit_log_lines(name, lines): |
| 634 for line in lines.splitlines(): | 475 for line in lines.splitlines(): |
| 635 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line) | 476 print '@@@STEP_LOG_LINE@%s@%s@@@' % (name, line) |
| 636 print '@@@STEP_LOG_END@%s@@@' % name | 477 print '@@@STEP_LOG_END@%s@@@' % name |
| 637 | 478 |
| 638 | 479 |
| 639 def emit_properties(properties): | 480 def emit_properties(properties): |
| 640 for property_name, property_value in sorted(properties.items()): | 481 for property_name, property_value in sorted(properties.items()): |
| 641 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value) | 482 print '@@@SET_BUILD_PROPERTY@%s@"%s"@@@' % (property_name, property_value) |
| 642 | 483 |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 | 518 |
| 678 | 519 |
| 679 def force_revision(folder_name, revision): | 520 def force_revision(folder_name, revision): |
| 680 split_revision = revision.split(':', 1) | 521 split_revision = revision.split(':', 1) |
| 681 branch = 'master' | 522 branch = 'master' |
| 682 if len(split_revision) == 2: | 523 if len(split_revision) == 2: |
| 683 # Support for "branch:revision" syntax. | 524 # Support for "branch:revision" syntax. |
| 684 branch, revision = split_revision | 525 branch, revision = split_revision |
| 685 | 526 |
| 686 if revision and revision.upper() != 'HEAD': | 527 if revision and revision.upper() != 'HEAD': |
| 687 if revision and revision.isdigit() and len(revision) < 40: | 528 git('checkout', '--force', revision, cwd=folder_name) |
| 688 # rev_num is really a svn revision number, convert it into a git hash. | |
| 689 git_ref = get_git_hash(int(revision), branch, folder_name) | |
| 690 else: | |
| 691 # rev_num is actually a git hash or ref, we can just use it. | |
| 692 git_ref = revision | |
| 693 git('checkout', '--force', git_ref, cwd=folder_name) | |
| 694 else: | 529 else: |
| 695 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch | 530 ref = branch if branch.startswith('refs/') else 'origin/%s' % branch |
| 696 git('checkout', '--force', ref, cwd=folder_name) | 531 git('checkout', '--force', ref, cwd=folder_name) |
| 697 | 532 |
| 533 |
| 698 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir): | 534 def git_checkout(solutions, revisions, shallow, refs, git_cache_dir): |
| 699 build_dir = os.getcwd() | 535 build_dir = os.getcwd() |
| 700 # Before we do anything, break all git_cache locks. | 536 # Before we do anything, break all git_cache locks. |
| 701 if path.isdir(git_cache_dir): | 537 if path.isdir(git_cache_dir): |
| 702 git('cache', 'unlock', '-vv', '--force', '--all', | 538 git('cache', 'unlock', '-vv', '--force', '--all', |
| 703 '--cache-dir', git_cache_dir) | 539 '--cache-dir', git_cache_dir) |
| 704 for item in os.listdir(git_cache_dir): | 540 for item in os.listdir(git_cache_dir): |
| 705 filename = os.path.join(git_cache_dir, item) | 541 filename = os.path.join(git_cache_dir, item) |
| 706 if item.endswith('.lock'): | 542 if item.endswith('.lock'): |
| 707 raise Exception('%s exists after cache unlock' % filename) | 543 raise Exception('%s exists after cache unlock' % filename) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 748 # Exited abnormally, theres probably something wrong. | 584 # Exited abnormally, theres probably something wrong. |
| 749 # Lets wipe the checkout and try again. | 585 # Lets wipe the checkout and try again. |
| 750 tries_left -= 1 | 586 tries_left -= 1 |
| 751 if tries_left > 0: | 587 if tries_left > 0: |
| 752 print 'Something failed: %s.' % str(e) | 588 print 'Something failed: %s.' % str(e) |
| 753 print 'waiting 5 seconds and trying again...' | 589 print 'waiting 5 seconds and trying again...' |
| 754 time.sleep(5) | 590 time.sleep(5) |
| 755 else: | 591 else: |
| 756 raise | 592 raise |
| 757 remove(sln_dir) | 593 remove(sln_dir) |
| 758 except SVNRevisionNotFound: | |
| 759 tries_left -= 1 | |
| 760 if tries_left > 0: | |
| 761 # If we don't have the correct revision, wait and try again. | |
| 762 print 'We can\'t find revision %s.' % revision | |
| 763 print 'The svn to git replicator is probably falling behind.' | |
| 764 print 'waiting 5 seconds and trying again...' | |
| 765 time.sleep(5) | |
| 766 else: | |
| 767 raise | |
| 768 | 594 |
| 769 git('clean', '-dff', cwd=sln_dir) | 595 git('clean', '-dff', cwd=sln_dir) |
| 770 | 596 |
| 771 if first_solution: | 597 if first_solution: |
| 772 git_ref = git('log', '--format=%H', '--max-count=1', | 598 git_ref = git('log', '--format=%H', '--max-count=1', |
| 773 cwd=sln_dir).strip() | 599 cwd=sln_dir).strip() |
| 774 first_solution = False | 600 first_solution = False |
| 775 return git_ref | 601 return git_ref |
| 776 | 602 |
| 777 | 603 |
| 778 def _download(url): | 604 def _download(url): |
| 779 """Fetch url and return content, with retries for flake.""" | 605 """Fetch url and return content, with retries for flake.""" |
| 780 for attempt in xrange(ATTEMPTS): | 606 for attempt in xrange(ATTEMPTS): |
| 781 try: | 607 try: |
| 782 return urllib2.urlopen(url).read() | 608 return urllib2.urlopen(url).read() |
| 783 except Exception: | 609 except Exception: |
| 784 if attempt == ATTEMPTS - 1: | 610 if attempt == ATTEMPTS - 1: |
| 785 raise | 611 raise |
| 786 | 612 |
| 787 | 613 |
| 788 def parse_diff(diff): | |
| 789 """Takes a unified diff and returns a list of diffed files and their diffs. | |
| 790 | |
| 791 The return format is a list of pairs of: | |
| 792 (<filename>, <diff contents>) | |
| 793 <diff contents> is inclusive of the diff line. | |
| 794 """ | |
| 795 result = [] | |
| 796 current_diff = '' | |
| 797 current_header = None | |
| 798 for line in diff.splitlines(): | |
| 799 # "diff" is for git style patches, and "Index: " is for SVN style patches. | |
| 800 if line.startswith('diff') or line.startswith('Index: '): | |
| 801 if current_header: | |
| 802 # If we are in a diff portion, then save the diff. | |
| 803 result.append((current_header, '%s\n' % current_diff)) | |
| 804 git_header_match = re.match(r'diff (?:--git )?(\S+) (\S+)', line) | |
| 805 svn_header_match = re.match(r'Index: (.*)', line) | |
| 806 | |
| 807 if git_header_match: | |
| 808 # First, see if its a git style header. | |
| 809 from_file = git_header_match.group(1) | |
| 810 to_file = git_header_match.group(2) | |
| 811 if from_file != to_file and from_file.startswith('a/'): | |
| 812 # Sometimes git prepends 'a/' and 'b/' in front of file paths. | |
| 813 from_file = from_file[2:] | |
| 814 current_header = from_file | |
| 815 | |
| 816 elif svn_header_match: | |
| 817 # Otherwise, check if its an SVN style header. | |
| 818 current_header = svn_header_match.group(1) | |
| 819 | |
| 820 else: | |
| 821 # Otherwise... I'm not really sure what to do with this. | |
| 822 raise InvalidDiff('Can\'t process header: %s\nFull diff:\n%s' % | |
| 823 (line, diff)) | |
| 824 | |
| 825 current_diff = '' | |
| 826 current_diff += '%s\n' % line | |
| 827 if current_header: | |
| 828 # We hit EOF, gotta save the last diff. | |
| 829 result.append((current_header, current_diff)) | |
| 830 return result | |
| 831 | |
| 832 | |
| 833 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision, | 614 def apply_rietveld_issue(issue, patchset, root, server, _rev_map, _revision, |
| 834 email_file, key_file, whitelist=None, blacklist=None): | 615 email_file, key_file, whitelist=None, blacklist=None): |
| 835 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') | 616 apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') |
| 836 else 'apply_issue') | 617 else 'apply_issue') |
| 837 cmd = [apply_issue_bin, | 618 cmd = [apply_issue_bin, |
| 838 # The patch will be applied on top of this directory. | 619 # The patch will be applied on top of this directory. |
| 839 '--root_dir', root, | 620 '--root_dir', root, |
| 840 # Tell apply_issue how to fetch the patch. | 621 # Tell apply_issue how to fetch the patch. |
| 841 '--issue', issue, | 622 '--issue', issue, |
| 842 '--server', server, | 623 '--server', server, |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 920 os.remove(flag_file) | 701 os.remove(flag_file) |
| 921 | 702 |
| 922 | 703 |
| 923 def emit_flag(flag_file): | 704 def emit_flag(flag_file): |
| 924 """Deposit a bot update flag on the system to tell gclient not to run.""" | 705 """Deposit a bot update flag on the system to tell gclient not to run.""" |
| 925 print 'Emitting flag file at %s' % flag_file | 706 print 'Emitting flag file at %s' % flag_file |
| 926 with open(flag_file, 'wb') as f: | 707 with open(flag_file, 'wb') as f: |
| 927 f.write('Success!') | 708 f.write('Success!') |
| 928 | 709 |
| 929 | 710 |
| 930 def get_commit_position_for_git_svn(url, revision): | |
| 931 """Generates a commit position string for a 'git-svn' URL/revision. | |
| 932 | |
| 933 If the 'git-svn' URL maps to a known project, we will construct a commit | |
| 934 position branch value by applying substitution on the SVN URL. | |
| 935 """ | |
| 936 # Identify the base URL so we can strip off trunk/branch name | |
| 937 project_config = branch = None | |
| 938 for _, project_config in GIT_SVN_PROJECT_MAP.iteritems(): | |
| 939 if url.startswith(project_config['svn_url']): | |
| 940 branch = url[len(project_config['svn_url']):] | |
| 941 break | |
| 942 | |
| 943 if branch: | |
| 944 # Strip any leading slashes | |
| 945 branch = branch.lstrip('/') | |
| 946 | |
| 947 # Try and map the branch | |
| 948 for pattern, repl in project_config.get('branch_map', ()): | |
| 949 nbranch, subn = re.subn(pattern, repl, branch, count=1) | |
| 950 if subn: | |
| 951 print 'INFO: Mapped SVN branch to Git branch [%s] => [%s]' % ( | |
| 952 branch, nbranch) | |
| 953 branch = nbranch | |
| 954 break | |
| 955 else: | |
| 956 # Use generic 'svn' branch | |
| 957 print 'INFO: Could not resolve project for SVN URL %r' % (url,) | |
| 958 branch = 'svn' | |
| 959 return '%s@{#%s}' % (branch, revision) | |
| 960 | |
| 961 | |
| 962 def get_commit_position(git_path, revision='HEAD'): | 711 def get_commit_position(git_path, revision='HEAD'): |
| 963 """Dumps the 'git' log for a specific revision and parses out the commit | 712 """Dumps the 'git' log for a specific revision and parses out the commit |
| 964 position. | 713 position. |
| 965 | 714 |
| 966 If a commit position metadata key is found, its value will be returned. | 715 If a commit position metadata key is found, its value will be returned. |
| 967 | |
| 968 Otherwise, we will search for a 'git-svn' metadata entry. If one is found, | |
| 969 we will compose a commit position from it, using its SVN revision value as | |
| 970 the revision. | |
| 971 | |
| 972 If the 'git-svn' URL maps to a known project, we will construct a commit | |
| 973 position branch value by truncating the URL, mapping 'trunk' to | |
| 974 "refs/heads/master". Otherwise, we will return the generic branch, 'svn'. | |
| 975 """ | 716 """ |
| 717 # TODO(iannucci): Use git-footers for this. |
| 976 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) | 718 git_log = git('log', '--format=%B', '-n1', revision, cwd=git_path) |
| 977 footer_map = get_commit_message_footer_map(git_log) | 719 footer_map = get_commit_message_footer_map(git_log) |
| 978 | 720 |
| 979 # Search for commit position metadata | 721 # Search for commit position metadata |
| 980 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or | 722 value = (footer_map.get(COMMIT_POSITION_FOOTER_KEY) or |
| 981 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY)) | 723 footer_map.get(COMMIT_ORIGINAL_POSITION_FOOTER_KEY)) |
| 982 if value: | 724 if value: |
| 983 return value | 725 return value |
| 984 | |
| 985 # Compose a commit position from 'git-svn' metadata | |
| 986 value = footer_map.get(GIT_SVN_ID_FOOTER_KEY) | |
| 987 if value: | |
| 988 m = GIT_SVN_ID_RE.match(value) | |
| 989 if not m: | |
| 990 raise ValueError("Invalid 'git-svn' value: [%s]" % (value,)) | |
| 991 return get_commit_position_for_git_svn(m.group(1), m.group(2)) | |
| 992 return None | 726 return None |
| 993 | 727 |
| 994 | 728 |
| 995 def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): | 729 def parse_got_revision(gclient_output, got_revision_mapping): |
| 996 """Translate git gclient revision mapping to build properties. | 730 """Translate git gclient revision mapping to build properties.""" |
| 997 | |
| 998 If use_svn_revs is True, then translate git hashes in the revision mapping | |
| 999 to svn revision numbers. | |
| 1000 """ | |
| 1001 properties = {} | 731 properties = {} |
| 1002 solutions_output = { | 732 solutions_output = { |
| 1003 # Make sure path always ends with a single slash. | 733 # Make sure path always ends with a single slash. |
| 1004 '%s/' % path.rstrip('/') : solution_output for path, solution_output | 734 '%s/' % path.rstrip('/') : solution_output for path, solution_output |
| 1005 in gclient_output['solutions'].iteritems() | 735 in gclient_output['solutions'].iteritems() |
| 1006 } | 736 } |
| 1007 for dir_name, property_name in got_revision_mapping.iteritems(): | 737 for dir_name, property_name in got_revision_mapping.iteritems(): |
| 1008 # Make sure dir_name always ends with a single slash. | 738 # Make sure dir_name always ends with a single slash. |
| 1009 dir_name = '%s/' % dir_name.rstrip('/') | 739 dir_name = '%s/' % dir_name.rstrip('/') |
| 1010 if dir_name not in solutions_output: | 740 if dir_name not in solutions_output: |
| 1011 continue | 741 continue |
| 1012 solution_output = solutions_output[dir_name] | 742 solution_output = solutions_output[dir_name] |
| 1013 if solution_output.get('scm') is None: | 743 if solution_output.get('scm') is None: |
| 1014 # This is an ignored DEPS, so the output got_revision should be 'None'. | 744 # This is an ignored DEPS, so the output got_revision should be 'None'. |
| 1015 git_revision = revision = commit_position = None | 745 revision = commit_position = None |
| 1016 else: | 746 else: |
| 1017 # Since we are using .DEPS.git, everything had better be git. | 747 # Since we are using .DEPS.git, everything had better be git. |
| 1018 assert solution_output.get('scm') == 'git' | 748 assert solution_output.get('scm') == 'git' |
| 1019 git_revision = git('rev-parse', 'HEAD', cwd=dir_name).strip() | 749 revision = git('rev-parse', 'HEAD', cwd=dir_name).strip() |
| 1020 if use_svn_revs: | |
| 1021 revision = get_svn_rev(git_revision, dir_name) | |
| 1022 if not revision: | |
| 1023 revision = git_revision | |
| 1024 else: | |
| 1025 revision = git_revision | |
| 1026 commit_position = get_commit_position(dir_name) | 750 commit_position = get_commit_position(dir_name) |
| 1027 | 751 |
| 1028 properties[property_name] = revision | 752 properties[property_name] = revision |
| 1029 if revision != git_revision: | |
| 1030 properties['%s_git' % property_name] = git_revision | |
| 1031 if commit_position: | 753 if commit_position: |
| 1032 properties['%s_cp' % property_name] = commit_position | 754 properties['%s_cp' % property_name] = commit_position |
| 1033 | 755 |
| 1034 return properties | 756 return properties |
| 1035 | 757 |
| 1036 | 758 |
| 1037 def emit_json(out_file, did_run, gclient_output=None, **kwargs): | 759 def emit_json(out_file, did_run, gclient_output=None, **kwargs): |
| 1038 """Write run information into a JSON file.""" | 760 """Write run information into a JSON file.""" |
| 1039 output = {} | 761 output = {} |
| 1040 output.update(gclient_output if gclient_output else {}) | 762 output.update(gclient_output if gclient_output else {}) |
| 1041 output.update({'did_run': did_run}) | 763 output.update({'did_run': did_run}) |
| 1042 output.update(kwargs) | 764 output.update(kwargs) |
| 1043 with open(out_file, 'wb') as f: | 765 with open(out_file, 'wb') as f: |
| 1044 f.write(json.dumps(output, sort_keys=True)) | 766 f.write(json.dumps(output, sort_keys=True)) |
| 1045 | 767 |
| 1046 | 768 |
| 1047 def ensure_deps_revisions(deps_url_mapping, solutions, revisions): | 769 def ensure_deps_revisions(deps_url_mapping, solutions, revisions): |
| 1048 """Ensure correct DEPS revisions, ignores solutions.""" | 770 """Ensure correct DEPS revisions, ignores solutions.""" |
| 1049 for deps_name, deps_data in sorted(deps_url_mapping.items()): | 771 for deps_name, deps_data in sorted(deps_url_mapping.items()): |
| 1050 if deps_name.strip('/') in solutions: | 772 if deps_name.strip('/') in solutions: |
| 1051 # This has already been forced to the correct solution by git_checkout(). | 773 # This has already been forced to the correct solution by git_checkout(). |
| 1052 continue | 774 continue |
| 1053 revision = get_target_revision(deps_name, deps_data.get('url', None), | 775 revision = get_target_revision(deps_name, deps_data.get('url', None), |
| 1054 revisions) | 776 revisions) |
| 1055 if not revision: | 777 if not revision: |
| 1056 continue | 778 continue |
| 1057 # TODO(hinoka): Catch SVNRevisionNotFound error maybe? | |
| 1058 git('fetch', 'origin', cwd=deps_name) | 779 git('fetch', 'origin', cwd=deps_name) |
| 1059 force_revision(deps_name, revision) | 780 force_revision(deps_name, revision) |
| 1060 | 781 |
| 1061 | 782 |
| 1062 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only, | 783 def ensure_checkout(solutions, revisions, first_sln, target_os, target_os_only, |
| 1063 patch_root, issue, patchset, rietveld_server, | 784 patch_root, issue, patchset, rietveld_server, |
| 1064 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref, | 785 gerrit_repo, gerrit_ref, gerrit_rebase_patch_ref, |
| 1065 revision_mapping, apply_issue_email_file, | 786 revision_mapping, apply_issue_email_file, |
| 1066 apply_issue_key_file, buildspec, gyp_env, shallow, runhooks, | 787 apply_issue_key_file, gyp_env, shallow, runhooks, |
| 1067 refs, git_cache_dir, gerrit_reset): | 788 refs, git_cache_dir, gerrit_reset): |
| 1068 # Get a checkout of each solution, without DEPS or hooks. | 789 # Get a checkout of each solution, without DEPS or hooks. |
| 1069 # Calling git directly because there is no way to run Gclient without | 790 # Calling git directly because there is no way to run Gclient without |
| 1070 # invoking DEPS. | 791 # invoking DEPS. |
| 1071 print 'Fetching Git checkout' | 792 print 'Fetching Git checkout' |
| 1072 | 793 |
| 1073 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir) | 794 git_ref = git_checkout(solutions, revisions, shallow, refs, git_cache_dir) |
| 1074 | 795 |
| 1075 print '===Processing patch solutions===' | 796 print '===Processing patch solutions===' |
| 1076 already_patched = [] | 797 already_patched = [] |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1093 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, | 814 apply_gerrit_ref(gerrit_repo, gerrit_ref, patch_root, gerrit_reset, |
| 1094 gerrit_rebase_patch_ref) | 815 gerrit_rebase_patch_ref) |
| 1095 applied_gerrit_patch = True | 816 applied_gerrit_patch = True |
| 1096 | 817 |
| 1097 # Ensure our build/ directory is set up with the correct .gclient file. | 818 # Ensure our build/ directory is set up with the correct .gclient file. |
| 1098 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) | 819 gclient_configure(solutions, target_os, target_os_only, git_cache_dir) |
| 1099 | 820 |
| 1100 # Let gclient do the DEPS syncing. | 821 # Let gclient do the DEPS syncing. |
| 1101 # The branch-head refspec is a special case because its possible Chrome | 822 # The branch-head refspec is a special case because its possible Chrome |
| 1102 # src, which contains the branch-head refspecs, is DEPSed in. | 823 # src, which contains the branch-head refspecs, is DEPSed in. |
| 1103 gclient_output = gclient_sync(buildspec or BRANCH_HEADS_REFSPEC in refs, | 824 gclient_output = gclient_sync(BRANCH_HEADS_REFSPEC in refs, shallow) |
| 1104 shallow) | |
| 1105 | 825 |
| 1106 # Now that gclient_sync has finished, we should revert any .DEPS.git so that | 826 # Now that gclient_sync has finished, we should revert any .DEPS.git so that |
| 1107 # presubmit doesn't complain about it being modified. | 827 # presubmit doesn't complain about it being modified. |
| 1108 if (not buildspec and | 828 if git('ls-files', '.DEPS.git', cwd=first_sln).strip(): |
| 1109 git('ls-files', '.DEPS.git', cwd=first_sln).strip()): | |
| 1110 git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln) | 829 git('checkout', 'HEAD', '--', '.DEPS.git', cwd=first_sln) |
| 1111 | 830 |
| 1112 if buildspec and runhooks: | |
| 1113 # Run gclient runhooks if we're on an official builder. | |
| 1114 # TODO(hinoka): Remove this when the official builders run their own | |
| 1115 # runhooks step. | |
| 1116 gclient_runhooks(gyp_env) | |
| 1117 | |
| 1118 # Finally, ensure that all DEPS are pinned to the correct revision. | 831 # Finally, ensure that all DEPS are pinned to the correct revision. |
| 1119 dir_names = [sln['name'] for sln in solutions] | 832 dir_names = [sln['name'] for sln in solutions] |
| 1120 ensure_deps_revisions(gclient_output.get('solutions', {}), | 833 ensure_deps_revisions(gclient_output.get('solutions', {}), |
| 1121 dir_names, revisions) | 834 dir_names, revisions) |
| 1122 # Apply the rest of the patch here (sans DEPS) | 835 # Apply the rest of the patch here (sans DEPS) |
| 1123 if issue: | 836 if issue: |
| 1124 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, | 837 apply_rietveld_issue(issue, patchset, patch_root, rietveld_server, |
| 1125 revision_mapping, git_ref, apply_issue_email_file, | 838 revision_mapping, git_ref, apply_issue_email_file, |
| 1126 apply_issue_key_file, blacklist=already_patched) | 839 apply_issue_key_file, blacklist=already_patched) |
| 1127 elif gerrit_ref and not applied_gerrit_patch: | 840 elif gerrit_ref and not applied_gerrit_patch: |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1153 expanded_revisions.extend(revision.split(',')) | 866 expanded_revisions.extend(revision.split(',')) |
| 1154 for revision in expanded_revisions: | 867 for revision in expanded_revisions: |
| 1155 split_revision = revision.split('@') | 868 split_revision = revision.split('@') |
| 1156 if len(split_revision) == 1: | 869 if len(split_revision) == 1: |
| 1157 # This is just a plain revision, set it as the revision for root. | 870 # This is just a plain revision, set it as the revision for root. |
| 1158 results[root] = split_revision[0] | 871 results[root] = split_revision[0] |
| 1159 elif len(split_revision) == 2: | 872 elif len(split_revision) == 2: |
| 1160 # This is an alt_root@revision argument. | 873 # This is an alt_root@revision argument. |
| 1161 current_root, current_rev = split_revision | 874 current_root, current_rev = split_revision |
| 1162 | 875 |
| 1163 # We want to normalize svn/git urls into .git urls. | |
| 1164 parsed_root = urlparse.urlparse(current_root) | 876 parsed_root = urlparse.urlparse(current_root) |
| 1165 if parsed_root.scheme == 'svn': | 877 if parsed_root.scheme in ['http', 'https']: |
| 1166 if parsed_root.path in RECOGNIZED_PATHS: | 878 # We want to normalize git urls into .git urls. |
| 1167 normalized_root = RECOGNIZED_PATHS[parsed_root.path] | |
| 1168 else: | |
| 1169 print 'WARNING: SVN path %s not recognized, ignoring' % current_root | |
| 1170 continue | |
| 1171 elif parsed_root.scheme in ['http', 'https']: | |
| 1172 normalized_root = 'https://%s/%s' % (parsed_root.netloc, | 879 normalized_root = 'https://%s/%s' % (parsed_root.netloc, |
| 1173 parsed_root.path) | 880 parsed_root.path) |
| 1174 if not normalized_root.endswith('.git'): | 881 if not normalized_root.endswith('.git'): |
| 1175 normalized_root = '%s.git' % normalized_root | 882 normalized_root = '%s.git' % normalized_root |
| 1176 elif parsed_root.scheme: | 883 elif parsed_root.scheme: |
| 1177 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme | 884 print 'WARNING: Unrecognized scheme %s, ignoring' % parsed_root.scheme |
| 1178 continue | 885 continue |
| 1179 else: | 886 else: |
| 1180 # This is probably a local path. | 887 # This is probably a local path. |
| 1181 normalized_root = current_root.strip('/') | 888 normalized_root = current_root.strip('/') |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1214 parse.add_option('--specs', help='Gcilent spec.') | 921 parse.add_option('--specs', help='Gcilent spec.') |
| 1215 parse.add_option('-f', '--force', action='store_true', | 922 parse.add_option('-f', '--force', action='store_true', |
| 1216 help='Bypass check to see if we want to be run. ' | 923 help='Bypass check to see if we want to be run. ' |
| 1217 'Should ONLY be used locally or by smart recipes.') | 924 'Should ONLY be used locally or by smart recipes.') |
| 1218 parse.add_option('--revision_mapping', | 925 parse.add_option('--revision_mapping', |
| 1219 help='{"path/to/repo/": "property_name"}') | 926 help='{"path/to/repo/": "property_name"}') |
| 1220 parse.add_option('--revision_mapping_file', | 927 parse.add_option('--revision_mapping_file', |
| 1221 help=('Same as revision_mapping, except its a path to a json' | 928 help=('Same as revision_mapping, except its a path to a json' |
| 1222 ' file containing that format.')) | 929 ' file containing that format.')) |
| 1223 parse.add_option('--revision', action='append', default=[], | 930 parse.add_option('--revision', action='append', default=[], |
| 1224 help='Revision to check out. Can be an SVN revision number, ' | 931 help='Revision to check out. Can be any form of git ref. ' |
| 1225 'git hash, or any form of git ref. Can prepend ' | 932 'Can prepend root@<rev> to specify which repository, ' |
| 1226 'root@<rev> to specify which repository, where root ' | 933 'where root is either a filesystem path or git https ' |
| 1227 'is either a filesystem path, git https url, or ' | 934 'url. To specify Tip of Tree, set rev to HEAD. ') |
| 1228 'svn url. To specify Tip of Tree, set rev to HEAD.' | |
| 1229 'To specify a git branch and an SVN rev, <rev> can be ' | |
| 1230 'set to <branch>:<revision>.') | |
| 1231 parse.add_option('--output_manifest', action='store_true', | 935 parse.add_option('--output_manifest', action='store_true', |
| 1232 help=('Add manifest json to the json output.')) | 936 help=('Add manifest json to the json output.')) |
| 1233 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], | 937 parse.add_option('--slave_name', default=socket.getfqdn().split('.')[0], |
| 1234 help='Hostname of the current machine, ' | 938 help='Hostname of the current machine, ' |
| 1235 'used for determining whether or not to activate.') | 939 'used for determining whether or not to activate.') |
| 1236 parse.add_option('--build_dir', default=os.getcwd()) | 940 parse.add_option('--build_dir', default=os.getcwd()) |
| 1237 parse.add_option('--flag_file', default=path.join(os.getcwd(), | 941 parse.add_option('--flag_file', default=path.join(os.getcwd(), |
| 1238 'update.flag')) | 942 'update.flag')) |
| 1239 parse.add_option('--shallow', action='store_true', | 943 parse.add_option('--shallow', action='store_true', |
| 1240 help='Use shallow clones for cache repositories.') | 944 help='Use shallow clones for cache repositories.') |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1292 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') | 996 options.git_cache_dir = options.git_cache_dir.replace('\\', '\\\\') |
| 1293 | 997 |
| 1294 return options, args | 998 return options, args |
| 1295 | 999 |
| 1296 | 1000 |
| 1297 def prepare(options, git_slns, active): | 1001 def prepare(options, git_slns, active): |
| 1298 """Prepares the target folder before we checkout.""" | 1002 """Prepares the target folder before we checkout.""" |
| 1299 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] | 1003 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] |
| 1300 # If we're active now, but the flag file doesn't exist (we weren't active | 1004 # If we're active now, but the flag file doesn't exist (we weren't active |
| 1301 # last run) or vice versa, blow away all checkouts. | 1005 # last run) or vice versa, blow away all checkouts. |
| 1302 if bool(active) != bool(check_flag(options.flag_file)): | 1006 if options.clobber or (bool(active) != bool(check_flag(options.flag_file))): |
| 1303 ensure_no_checkout(dir_names, '*') | 1007 ensure_no_checkout(dir_names) |
| 1304 if options.output_json: | 1008 if options.output_json: |
| 1305 # Make sure we tell recipes that we didn't run if the script exits here. | 1009 # Make sure we tell recipes that we didn't run if the script exits here. |
| 1306 emit_json(options.output_json, did_run=active) | 1010 emit_json(options.output_json, did_run=active) |
| 1307 if active: | 1011 emit_flag(options.flag_file) |
| 1308 if options.clobber: | |
| 1309 ensure_no_checkout(dir_names, '*') | |
| 1310 else: | |
| 1311 ensure_no_checkout(dir_names, '.svn') | |
| 1312 emit_flag(options.flag_file) | |
| 1313 else: | |
| 1314 delete_flag(options.flag_file) | |
| 1315 raise Inactive # This is caught in main() and we exit cleanly. | |
| 1316 | 1012 |
| 1317 # Do a shallow checkout if the disk is less than 100GB. | 1013 # Do a shallow checkout if the disk is less than 100GB. |
| 1318 total_disk_space, free_disk_space = get_total_disk_space() | 1014 total_disk_space, free_disk_space = get_total_disk_space() |
| 1319 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) | 1015 total_disk_space_gb = int(total_disk_space / (1024 * 1024 * 1024)) |
| 1320 used_disk_space_gb = int((total_disk_space - free_disk_space) | 1016 used_disk_space_gb = int((total_disk_space - free_disk_space) |
| 1321 / (1024 * 1024 * 1024)) | 1017 / (1024 * 1024 * 1024)) |
| 1322 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) | 1018 percent_used = int(used_disk_space_gb * 100 / total_disk_space_gb) |
| 1323 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, | 1019 step_text = '[%dGB/%dGB used (%d%%)]' % (used_disk_space_gb, |
| 1324 total_disk_space_gb, | 1020 total_disk_space_gb, |
| 1325 percent_used) | 1021 percent_used) |
| 1326 if not options.output_json: | 1022 if not options.output_json: |
| 1327 print '@@@STEP_TEXT@%s@@@' % step_text | 1023 print '@@@STEP_TEXT@%s@@@' % step_text |
| 1328 if not options.shallow: | 1024 if not options.shallow: |
| 1329 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD | 1025 options.shallow = (total_disk_space < SHALLOW_CLONE_THRESHOLD |
| 1330 and not options.no_shallow) | 1026 and not options.no_shallow) |
| 1331 | 1027 |
| 1332 # The first solution is where the primary DEPS file resides. | 1028 # The first solution is where the primary DEPS file resides. |
| 1333 first_sln = dir_names[0] | 1029 first_sln = dir_names[0] |
| 1334 | 1030 |
| 1335 # Split all the revision specifications into a nice dict. | 1031 # Split all the revision specifications into a nice dict. |
| 1336 print 'Revisions: %s' % options.revision | 1032 print 'Revisions: %s' % options.revision |
| 1337 revisions = parse_revisions(options.revision, first_sln) | 1033 revisions = parse_revisions(options.revision, first_sln) |
| 1338 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) | 1034 print 'Fetching Git checkout at %s@%s' % (first_sln, revisions[first_sln]) |
| 1339 return revisions, step_text | 1035 return revisions, step_text |
| 1340 | 1036 |
| 1341 | 1037 |
| 1342 def checkout(options, git_slns, specs, buildspec, | 1038 def checkout(options, git_slns, specs, revisions, step_text): |
| 1343 svn_root, revisions, step_text): | |
| 1344 first_sln = git_slns[0]['name'] | 1039 first_sln = git_slns[0]['name'] |
| 1345 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] | 1040 dir_names = [sln.get('name') for sln in git_slns if 'name' in sln] |
| 1346 try: | 1041 try: |
| 1347 # Outer try is for catching patch failures and exiting gracefully. | 1042 # Outer try is for catching patch failures and exiting gracefully. |
| 1348 # Inner try is for catching gclient failures and retrying gracefully. | 1043 # Inner try is for catching gclient failures and retrying gracefully. |
| 1349 try: | 1044 try: |
| 1350 checkout_parameters = dict( | 1045 checkout_parameters = dict( |
| 1351 # First, pass in the base of what we want to check out. | 1046 # First, pass in the base of what we want to check out. |
| 1352 solutions=git_slns, | 1047 solutions=git_slns, |
| 1353 revisions=revisions, | 1048 revisions=revisions, |
| 1354 first_sln=first_sln, | 1049 first_sln=first_sln, |
| 1355 | 1050 |
| 1356 # Also, target os variables for gclient. | 1051 # Also, target os variables for gclient. |
| 1357 target_os=specs.get('target_os', []), | 1052 target_os=specs.get('target_os', []), |
| 1358 target_os_only=specs.get('target_os_only', False), | 1053 target_os_only=specs.get('target_os_only', False), |
| 1359 | 1054 |
| 1360 # Then, pass in information about how to patch. | 1055 # Then, pass in information about how to patch. |
| 1361 patch_root=options.patch_root, | 1056 patch_root=options.patch_root, |
| 1362 issue=options.issue, | 1057 issue=options.issue, |
| 1363 patchset=options.patchset, | 1058 patchset=options.patchset, |
| 1364 rietveld_server=options.rietveld_server, | 1059 rietveld_server=options.rietveld_server, |
| 1365 gerrit_repo=options.gerrit_repo, | 1060 gerrit_repo=options.gerrit_repo, |
| 1366 gerrit_ref=options.gerrit_ref, | 1061 gerrit_ref=options.gerrit_ref, |
| 1367 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, | 1062 gerrit_rebase_patch_ref=not options.gerrit_no_rebase_patch_ref, |
| 1368 revision_mapping=options.revision_mapping, | 1063 revision_mapping=options.revision_mapping, |
| 1369 apply_issue_email_file=options.apply_issue_email_file, | 1064 apply_issue_email_file=options.apply_issue_email_file, |
| 1370 apply_issue_key_file=options.apply_issue_key_file, | 1065 apply_issue_key_file=options.apply_issue_key_file, |
| 1371 | 1066 |
| 1372 # For official builders. | 1067 # For official builders. |
| 1373 buildspec=buildspec, | |
| 1374 gyp_env=options.gyp_env, | 1068 gyp_env=options.gyp_env, |
| 1375 runhooks=not options.no_runhooks, | 1069 runhooks=not options.no_runhooks, |
| 1376 | 1070 |
| 1377 # Finally, extra configurations such as shallowness of the clone. | 1071 # Finally, extra configurations such as shallowness of the clone. |
| 1378 shallow=options.shallow, | 1072 shallow=options.shallow, |
| 1379 refs=options.refs, | 1073 refs=options.refs, |
| 1380 git_cache_dir=options.git_cache_dir, | 1074 git_cache_dir=options.git_cache_dir, |
| 1381 gerrit_reset=not options.gerrit_no_reset) | 1075 gerrit_reset=not options.gerrit_no_reset) |
| 1382 gclient_output = ensure_checkout(**checkout_parameters) | 1076 gclient_output = ensure_checkout(**checkout_parameters) |
| 1383 except GclientSyncFailed: | 1077 except GclientSyncFailed: |
| 1384 print 'We failed gclient sync, lets delete the checkout and retry.' | 1078 print 'We failed gclient sync, lets delete the checkout and retry.' |
| 1385 ensure_no_checkout(dir_names, '*') | 1079 ensure_no_checkout(dir_names) |
| 1386 gclient_output = ensure_checkout(**checkout_parameters) | 1080 gclient_output = ensure_checkout(**checkout_parameters) |
| 1387 except PatchFailed as e: | 1081 except PatchFailed as e: |
| 1388 if options.output_json: | 1082 if options.output_json: |
| 1389 # Tell recipes information such as root, got_revision, etc. | 1083 # Tell recipes information such as root, got_revision, etc. |
| 1390 emit_json(options.output_json, | 1084 emit_json(options.output_json, |
| 1391 did_run=True, | 1085 did_run=True, |
| 1392 root=first_sln, | 1086 root=first_sln, |
| 1393 log_lines=[('patch error', e.output),], | 1087 log_lines=[('patch error', e.output),], |
| 1394 patch_apply_return_code=e.code, | 1088 patch_apply_return_code=e.code, |
| 1395 patch_root=options.patch_root, | 1089 patch_root=options.patch_root, |
| 1396 patch_failure=True, | 1090 patch_failure=True, |
| 1397 step_text='%s PATCH FAILED' % step_text, | 1091 step_text='%s PATCH FAILED' % step_text, |
| 1398 fixed_revisions=revisions) | 1092 fixed_revisions=revisions) |
| 1399 else: | 1093 else: |
| 1400 # If we're not on recipes, tell annotator about our got_revisions. | 1094 # If we're not on recipes, tell annotator about our got_revisions. |
| 1401 emit_log_lines('patch error', e.output) | 1095 emit_log_lines('patch error', e.output) |
| 1402 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text | 1096 print '@@@STEP_TEXT@%s PATCH FAILED@@@' % step_text |
| 1403 raise | 1097 raise |
| 1404 | 1098 |
| 1405 use_svn_rev = False | |
| 1406 | |
| 1407 # Take care of got_revisions outputs. | 1099 # Take care of got_revisions outputs. |
| 1408 revision_mapping = dict(GOT_REVISION_MAPPINGS.get(svn_root, {})) | 1100 revision_mapping = GOT_REVISION_MAPPINGS.get(git_slns[0]['url'], {}) |
| 1409 if options.revision_mapping: | 1101 if options.revision_mapping: |
| 1410 revision_mapping.update(options.revision_mapping) | 1102 revision_mapping.update(options.revision_mapping) |
| 1411 | 1103 |
| 1412 # If the repo is not in the default GOT_REVISION_MAPPINGS and no | 1104 # If the repo is not in the default GOT_REVISION_MAPPINGS and no |
| 1413 # revision_mapping were specified on the command line then | 1105 # revision_mapping were specified on the command line then |
| 1414 # default to setting 'got_revision' based on the first solution. | 1106 # default to setting 'got_revision' based on the first solution. |
| 1415 if not revision_mapping: | 1107 if not revision_mapping: |
| 1416 revision_mapping[first_sln] = 'got_revision' | 1108 revision_mapping[first_sln] = 'got_revision' |
| 1417 | 1109 |
| 1418 got_revisions = parse_got_revision(gclient_output, revision_mapping, | 1110 got_revisions = parse_got_revision(gclient_output, revision_mapping) |
| 1419 use_svn_rev) | |
| 1420 | 1111 |
| 1421 if not got_revisions: | 1112 if not got_revisions: |
| 1422 # TODO(hinoka): We should probably bail out here, but in the interest | 1113 # TODO(hinoka): We should probably bail out here, but in the interest |
| 1423 # of giving mis-configured bots some time to get fixed use a dummy | 1114 # of giving mis-configured bots some time to get fixed use a dummy |
| 1424 # revision here. | 1115 # revision here. |
| 1425 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' } | 1116 got_revisions = { 'got_revision': 'BOT_UPDATE_NO_REV_FOUND' } |
| 1426 #raise Exception('No got_revision(s) found in gclient output') | 1117 #raise Exception('No got_revision(s) found in gclient output') |
| 1427 | 1118 |
| 1428 if options.output_json: | 1119 if options.output_json: |
| 1429 manifest = create_manifest() if options.output_manifest else None | 1120 manifest = create_manifest() if options.output_manifest else None |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1467 | 1158 |
| 1468 # Check if this script should activate or not. | 1159 # Check if this script should activate or not. |
| 1469 active = True | 1160 active = True |
| 1470 | 1161 |
| 1471 # Print a helpful message to tell developers whats going on with this step. | 1162 # Print a helpful message to tell developers whats going on with this step. |
| 1472 print_debug_info() | 1163 print_debug_info() |
| 1473 | 1164 |
| 1474 # Parse, munipulate, and print the gclient solutions. | 1165 # Parse, munipulate, and print the gclient solutions. |
| 1475 specs = {} | 1166 specs = {} |
| 1476 exec(options.specs, specs) | 1167 exec(options.specs, specs) |
| 1477 svn_solutions = specs.get('solutions', []) | 1168 orig_solutions = specs.get('solutions', []) |
| 1478 git_slns, svn_root, buildspec = solutions_to_git(svn_solutions) | 1169 git_slns = modify_solutions(orig_solutions) |
| 1479 options.revision = maybe_ignore_revision(options.revision, buildspec) | |
| 1480 | 1170 |
| 1481 solutions_printer(git_slns) | 1171 solutions_printer(git_slns) |
| 1482 | 1172 |
| 1483 try: | 1173 try: |
| 1484 # Dun dun dun, the main part of bot_update. | 1174 # Dun dun dun, the main part of bot_update. |
| 1485 revisions, step_text = prepare(options, git_slns, active) | 1175 revisions, step_text = prepare(options, git_slns, active) |
| 1486 checkout(options, git_slns, specs, buildspec, svn_root, revisions, | 1176 checkout(options, git_slns, specs, revisions, step_text) |
| 1487 step_text) | |
| 1488 | 1177 |
| 1489 except Inactive: | |
| 1490 # Not active, should count as passing. | |
| 1491 pass | |
| 1492 except PatchFailed as e: | 1178 except PatchFailed as e: |
| 1493 emit_flag(options.flag_file) | 1179 emit_flag(options.flag_file) |
| 1494 # Return a specific non-zero exit code for patch failure (because it is | 1180 # Return a specific non-zero exit code for patch failure (because it is |
| 1495 # a failure), but make it different than other failures to distinguish | 1181 # a failure), but make it different than other failures to distinguish |
| 1496 # between infra failures (independent from patch author), and patch | 1182 # between infra failures (independent from patch author), and patch |
| 1497 # failures (that patch author can fix). However, PatchFailure due to | 1183 # failures (that patch author can fix). However, PatchFailure due to |
| 1498 # download patch failure is still an infra problem. | 1184 # download patch failure is still an infra problem. |
| 1499 if e.code == 3: | 1185 if e.code == 3: |
| 1500 # Patch download problem. | 1186 # Patch download problem. |
| 1501 return 87 | 1187 return 87 |
| 1502 # Genuine patch problem. | 1188 # Genuine patch problem. |
| 1503 return 88 | 1189 return 88 |
| 1504 except Exception: | 1190 except Exception: |
| 1505 # Unexpected failure. | 1191 # Unexpected failure. |
| 1506 emit_flag(options.flag_file) | 1192 emit_flag(options.flag_file) |
| 1507 raise | 1193 raise |
| 1508 else: | 1194 else: |
| 1509 emit_flag(options.flag_file) | 1195 emit_flag(options.flag_file) |
| 1510 | 1196 |
| 1511 | 1197 |
| 1512 if __name__ == '__main__': | 1198 if __name__ == '__main__': |
| 1513 sys.exit(main()) | 1199 sys.exit(main()) |
| OLD | NEW |