#!/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 pnacl/git/.
It should be run from one of these subdirectories.  The tool sends
changes to the PNaCl toolchain trybots.

Example usage:

$ cd pnacl/git/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".
"""

import argparse
import base64
import os
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') and
      (parent3, parent2) != ('pnacl', 'git')):
    raise Exception(
        'Expected the Git repo (%r) to be under '
        'toolchain_build/src/ or pnacl/git/' % 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')
    rc = subprocess.call(['diff', '-urN', 'a', 'b'], cwd=temp_dir,
                         stdout=open(patch_file, 'w'))
    # 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',
        ]

    if options.dry_run:
      subprocess.check_call(['cat', patch_file])
      return

    # Send the patch to the trybots.  Keeping the cwd set to
    # pnacl/git/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:])

@@ -4146,32 +4146,76 @@
 -pnacl-x86_32',%0A
+        'nacl-toolchain-win7-pnacl-x86_64',%0A
         %5D%0A%0A    i

add Windows to set of bots used by try_git_change.py