blob: 2dd4379473bb61d16b4312fca077b72acafb6587 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2013 The Native Client Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
This tool will send a PNaCl Git change to the trybots for testing.
This tool is intended for testing changes to the PNaCl Git
repositories that are checked out under subdirectories of toolchain_build/src.
It should be run from one of these subdirectories. The tool sends
changes to the PNaCl toolchain trybots.
Example usage:
$ cd toolchain_build/src/llvm
$ git checkout -b my-change origin/master
$ ... make changes to LLVM
$ git commit -a
$ git cl upload
$ ../../try_git_change.py
The trybot results will appear on the Rietveld code review created by
"git cl upload".
"""
from __future__ import print_function
import argparse
import base64
import os
import re
import shutil
import subprocess
import sys
import tempfile
# Find the top-level directory of the Git checkout that the current
# directory is inside.
def FindGitDir():
path = os.getcwd()
while not os.path.exists(os.path.join(path, '.git')):
parent = os.path.dirname(path)
if parent == path:
raise Exception('Current directory is not inside a Git repo directory')
path = parent
return path
def WriteGitBundle(dest_dir, component_name):
patch_dir = os.path.join(dest_dir, 'pnacl', 'not_for_commit')
os.makedirs(patch_dir)
# Record the commit ID. Include the commit's subject line to give
# readable context, to make it easy to tell from the try job which
# Git change was tested.
subprocess.check_call(
['git', 'log', '--no-walk',
'--pretty=format:%H%n%ad%n%s', 'HEAD'],
stdout=open(os.path.join(patch_dir,
'%s_commit_id' % component_name), 'w'))
# Save the Git commits as a Git bundle.
proc = subprocess.Popen(['git', 'bundle', 'create', '-',
'origin/master..HEAD'],
stdout=subprocess.PIPE)
# Encode the binary bundle using base64 so that it can be included
# in a textual patch file.
with open(os.path.join(patch_dir, '%s_bundle.b64' % component_name),
'w') as bundle_file:
base64.encode(proc.stdout, bundle_file)
rc = proc.wait()
assert rc == 0, 'git bundle failed: %i' % rc
def Main(args):
parser = argparse.ArgumentParser(
epilog=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('-n', '--dry-run', action='store_true', default=False,
dest='dry_run',
help='Output the patch that would be sent to the '
'try server without sending it')
options = parser.parse_args(args)
# Find the name of the Git repo we're testing a change for (e.g. 'llvm').
repo_dir = FindGitDir()
parent_path1, component_name = os.path.split(repo_dir)
parent_path2, parent2 = os.path.split(parent_path1)
parent_path3, parent3 = os.path.split(parent_path2)
if ((parent3, parent2) != ('toolchain_build', 'src')):
raise Exception(
'Expected the Git repo (%r) to be under '
'toolchain_build/src/' % repo_dir)
print('Trying change to %r component' % component_name)
# Check that there are no uncommitted changes.
rc = subprocess.call(['git', 'diff', '--quiet', 'HEAD'])
if rc != 0:
raise Exception('There are local uncommitted changes to %r'
% component_name)
temp_dir = tempfile.mkdtemp(prefix='try_pnacl_git_change_')
try:
before_dir = os.path.join(temp_dir, 'a')
after_dir = os.path.join(temp_dir, 'b')
os.mkdir(before_dir)
os.mkdir(after_dir)
WriteGitBundle(after_dir, component_name)
patch_file = os.path.join(temp_dir, 'trybot_patch')
with open(patch_file, 'w') as f:
rc = subprocess.call(['diff', '-urN', 'a', 'b'], cwd=temp_dir, stdout=f)
# massage patch file to resemble one produced by 'git diff --no-prefix'
# which is how 'git try' normally generates patches. Unless we do this
# the code in bot_update.py fails to parse or apply the patch correctly.
with open(patch_file) as f:
patch_data = f.read()
patch_data = re.sub("^diff -urN", "diff --git", patch_data, flags=re.M)
patch_data = re.sub("^--- a/", "--- ", patch_data, flags=re.M)
patch_data = re.sub("^\+\+\+ b/", "+++ ", patch_data, flags=re.M)
with open(patch_file, 'w') as f:
f.write(patch_data)
# diff returns 1 when the patch is non-empty.
if rc != 1:
raise Exception('diff failed with exit status %i' % rc)
trybots = [
'nacl-toolchain-linux-pnacl-x86_64',
'nacl-toolchain-linux-pnacl-x86_32',
'nacl-toolchain-mac-pnacl-x86_32',
'nacl-toolchain-win7-pnacl-x86_64',
]
if options.dry_run:
subprocess.check_call(['cat', patch_file])
return
# Send the patch to the trybots. Keeping the cwd set to
# toolchain_build/src/FOO has the effect of sending the trybot results to
# the Rietveld code review for the change to FOO.
subprocess.check_call([
'git', 'try',
# Specify the patch.
'-p1', '--diff=%s' % patch_file,
# Directory that the trybot should apply the patch to.
'--root=native_client',
# SVN URL for trybot patch queue.
'--svn_repo=svn://svn.chromium.org/chrome-try/try-nacl',
'-b', ','.join(trybots)])
finally:
shutil.rmtree(temp_dir)
if __name__ == '__main__':
Main(sys.argv[1:])