Chromium Code Reviews| Index: scripts/slave/bot_update.py | 
| diff --git a/scripts/slave/bot_update.py b/scripts/slave/bot_update.py | 
| index 3cb62751fb07cd60f553d0beb2d44f04486e0e6a..19b9ce34b0e256606d7c000ed1bd3c590e68a875 100755 | 
| --- a/scripts/slave/bot_update.py | 
| +++ b/scripts/slave/bot_update.py | 
| @@ -224,6 +224,7 @@ def call(*args, **kwargs): | 
| kwargs['stdout'] = subprocess.PIPE | 
| kwargs['stderr'] = subprocess.STDOUT | 
| stdin_data = kwargs.pop('stdin_data', None) | 
| + tries = kwargs.pop('tries', RETRIES) | 
| if stdin_data: | 
| kwargs['stdin'] = subprocess.PIPE | 
| out = cStringIO.StringIO() | 
| @@ -231,7 +232,7 @@ def call(*args, **kwargs): | 
| env = copy.copy(os.environ) | 
| env.update(new_env) | 
| kwargs['env'] = env | 
| - for attempt in xrange(RETRIES): | 
| + for attempt in range(tries): | 
| 
 
iannucci
2014/05/08 22:17:01
hm... why not xrange?
 
Ryan Tseng
2014/05/09 20:59:42
"range" sounds cleaner (xrange isn't even a word!)
 
 | 
| attempt_msg = ' (retry #%d)' % attempt if attempt else '' | 
| if new_env: | 
| print '===Injecting Environment Variables===' | 
| @@ -269,7 +270,7 @@ def call(*args, **kwargs): | 
| raise SubprocessFailed('%s failed with code %d in %s after %d attempts.' % | 
| - (' '.join(args), code, os.getcwd(), RETRIES), code) | 
| + (' '.join(args), code, os.getcwd(), tries), code) | 
| def git(*args, **kwargs): | 
| @@ -578,12 +579,16 @@ def _last_commit_for_file(filename, repo_base): | 
| def need_to_run_deps2git(repo_base, deps_file, deps_git_file): | 
| """Checks to see if we need to run deps2git. | 
| - Returns True if there was a DEPS change after the last .DEPS.git update. | 
| + Returns True if there was a DEPS change after the last .DEPS.git update | 
| + or if DEPS has local modifications. | 
| """ | 
| print 'Checking if %s exists' % deps_git_file | 
| if not path.isfile(deps_git_file): | 
| # .DEPS.git doesn't exist but DEPS does? We probably want to generate one. | 
| - print 'it exists!' | 
| + print 'it doesn\'t exist!' | 
| + return True | 
| + | 
| + if git('diff', deps_file, cwd=repo_base).strip(): | 
| return True | 
| last_known_deps_ref = _last_commit_for_file(deps_file, repo_base) | 
| @@ -651,10 +656,10 @@ def ensure_deps2git(sln_dir, shallow): | 
| repo_base = path.join(os.getcwd(), sln_dir) | 
| deps_file = path.join(repo_base, 'DEPS') | 
| deps_git_file = path.join(repo_base, '.DEPS.git') | 
| - print 'Checking if %s is newer than %s' % (deps_file, deps_git_file) | 
| if not path.isfile(deps_file): | 
| return | 
| + print 'Checking if %s is newer than %s' % (deps_file, deps_git_file) | 
| if not need_to_run_deps2git(repo_base, deps_file, deps_git_file): | 
| return | 
| @@ -802,24 +807,102 @@ def _download(url): | 
| raise | 
| -def apply_issue_svn(root, patch_url): | 
| +def parse_diff(diff): | 
| + """Takes a unified diff and returns a list of diffed files and their diffs. | 
| + | 
| + The return format is a list of pairs of: | 
| + (<filename>, <diff contents>) | 
| + <diff contents> is inclusive of the diff line. | 
| + """ | 
| + result = [] | 
| + current_diff = '' | 
| + current_header = None | 
| + for line in diff.splitlines(): | 
| + # "diff" is for git style patches, and "Index: " is for SVN style patches. | 
| + if line.startswith('diff') or line.startswith('Index: '): | 
| + if current_header: | 
| + # If we are in a diff portion, then save the diff. | 
| + result.append((current_header, '%s\n' % current_diff)) | 
| + git_header_match = re.match(r'diff (?:--git )?(\S+) (\S+)', line) | 
| + svn_header_match = re.match(r'Index: (.*)', line) | 
| + | 
| + if git_header_match: | 
| + # First, see if its a git style header. | 
| + from_file = git_header_match.group(1) | 
| + to_file = git_header_match.group(2) | 
| + if from_file != to_file and from_file.startswith('a/'): | 
| + # Sometimes git prepends 'a/' and 'b/' in front of file paths. | 
| + from_file = from_file[2:] | 
| + current_header = from_file | 
| + | 
| + elif svn_header_match: | 
| + # Otherwise, check if its an SVN style header. | 
| + current_header = svn_header_match.group(1) | 
| + | 
| + else: | 
| + # Otherwise... I'm not really sure what to do with this. | 
| 
 
iannucci
2014/05/08 22:17:01
probably should abort loudly
 
Ryan Tseng
2014/05/09 20:59:42
Done.
 
 | 
| + current_header = line | 
| + | 
| + current_diff = '' | 
| + current_diff += '%s\n' % line | 
| + if current_header: | 
| + # We hit EOF, gotta save the last diff. | 
| + result.append((current_header, current_diff)) | 
| + return result | 
| + | 
| + | 
| +def get_svn_patch(patch_url): | 
| + """Fetch patch from patch_url, return list of (filename, diff)""" | 
| patch_data = call('svn', 'cat', patch_url) | 
| - call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward', | 
| - stdin_data=patch_data, cwd=root) | 
| + return parse_diff(patch_data) | 
| -def apply_issue_rietveld(issue, patchset, root, server, rev_map, revision): | 
| +def apply_svn_patch(patch_root, patches, whitelist=None, blacklist=None): | 
| + """Expects a list of (filename, diff), applies it on top of patch_root.""" | 
| + if whitelist: | 
| + patches = [(name, diff) for name, diff in patches if name in whitelist] | 
| + elif blacklist: | 
| + patches = [(name, diff) for name, diff in patches if name not in blacklist] | 
| + diffs = [diff for _, diff in patches] | 
| + patch = ''.join(diffs) | 
| + | 
| + if patch: | 
| + print '===Patching files===' | 
| + for filename, _ in patches: | 
| + print 'Patching %s' % filename | 
| + call(PATCH_TOOL, '-p0', '--remove-empty-files', '--force', '--forward', | 
| + stdin_data=patch, cwd=patch_root, tries=1) | 
| + | 
| + | 
| +def apply_rietveld_issue(issue, patchset, root, server, rev_map, revision, | 
| + whitelist=None, blacklist=None): | 
| apply_issue_bin = ('apply_issue.bat' if sys.platform.startswith('win') | 
| else 'apply_issue') | 
| - call(apply_issue_bin, | 
| - '--root_dir', root, | 
| - '--issue', issue, | 
| - '--patchset', patchset, | 
| - '--no-auth', | 
| - '--server', server, | 
| - '--base_ref', revision, | 
| - '--force', | 
| - '--ignore_deps') | 
| + cmd = [apply_issue_bin, | 
| + # The patch will be applied on top of this directory. | 
| + '--root_dir', root, | 
| + # Tell apply_issue how to fetch the patch. | 
| + '--issue', issue, | 
| + '--patchset', patchset, | 
| + '--no-auth', | 
| + '--server', server, | 
| + # Always run apply_issue.py, otherwise it would see update.flag | 
| + # and then bail out. | 
| + '--force', | 
| + # Don't run gclient sync when it sees a DEPS change. | 
| + '--ignore_deps', | 
| + # Don't commit the patch or add it to the index. | 
| + '--no_commit', | 
| + ] | 
| + if whitelist: | 
| + for item in whitelist: | 
| + cmd.extend(['--whitelist', item]) | 
| + elif blacklist: | 
| + for item in blacklist: | 
| + cmd.extend(['--blacklist', item]) | 
| + | 
| + # Only try once, since subsequent failures hide the real failure. | 
| + call(*cmd, tries=1) | 
| def check_flag(flag_file): | 
| @@ -840,14 +923,6 @@ def emit_flag(flag_file): | 
| f.write('Success!') | 
| -def get_real_git_hash(git_hash, dir_name): | 
| - """Return git_hash or the parent of git_hash if its a patched hash.""" | 
| - log = git('log', '-1', '--format=%B', cwd=dir_name).strip() | 
| - if 'committed patch' in log.lower(): | 
| - return git('log', '-1', '--format=%H', 'HEAD^', cwd=dir_name).strip() | 
| - return git_hash | 
| - | 
| - | 
| def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): | 
| """Translate git gclient revision mapping to build properties. | 
| @@ -866,7 +941,7 @@ def parse_got_revision(gclient_output, got_revision_mapping, use_svn_revs): | 
| else: | 
| # Since we are using .DEPS.git, everything had better be git. | 
| assert solution_output.get('scm') == 'git' | 
| - git_revision = get_real_git_hash(solution_output['revision'], dir_name) | 
| + git_revision = solution_output['revision'] | 
| if use_svn_revs: | 
| revision = get_svn_rev(git_revision, dir_name) | 
| if not revision: | 
| @@ -900,21 +975,23 @@ def ensure_checkout(solutions, revision, first_sln, target_os, target_os_only, | 
| print 'Fetching Git checkout' | 
| git_ref = git_checkout(solutions, revision, shallow) | 
| - # If either patch_url or issue is passed in, then we need to apply a patch. | 
| + patches = None | 
| if patch_url: | 
| - # patch_url takes precidence since its only passed in on gcl try/git try. | 
| - apply_issue_svn(root, patch_url) | 
| - elif issue: | 
| - apply_issue_rietveld(issue, patchset, root, rietveld_server, | 
| - revision_mapping, git_ref) | 
| + patches = get_svn_patch(patch_url) | 
| + | 
| + if root == first_sln: | 
| + # Only top level DEPS patching is supported right now. | 
| + if patches: | 
| + apply_svn_patch(root, patches, whitelist=['DEPS']) | 
| + elif issue: | 
| + apply_rietveld_issue(issue, patchset, root, rietveld_server, | 
| + revision_mapping, git_ref, whitelist=['DEPS']) | 
| + | 
| if buildspec_name: | 
| buildspecs2git(root, buildspec_name) | 
| - elif first_sln == root: | 
| - # Run deps2git if there is a DEPS commit after the last .DEPS.git commit. | 
| - # We only need to ensure deps2git if the root is not overridden, since | 
| - # if the root is overridden, it means we are working with a sub repository | 
| - # patch, which means its impossible for it to touch DEPS. | 
| + else: | 
| + # Run deps2git if there is a DEPS change after the last .DEPS.git commit. | 
| ensure_deps2git(root, shallow) | 
| # Ensure our build/ directory is set up with the correct .gclient file. | 
| @@ -927,6 +1004,14 @@ def ensure_checkout(solutions, revision, first_sln, target_os, target_os_only, | 
| # TODO(hinoka): Remove this when the official builders run their own | 
| # runhooks step. | 
| gclient_runhooks(gyp_env) | 
| + | 
| + # Apply the rest of the patch here (sans DEPS) | 
| + if patches: | 
| + apply_svn_patch(root, patches, blacklist=['DEPS']) | 
| + elif issue: | 
| + apply_rietveld_issue(issue, patchset, root, rietveld_server, | 
| + revision_mapping, git_ref, blacklist=['DEPS']) | 
| + | 
| return gclient_output |