Add update_build_tools.py for darwin.

Change-Id: I64267d906c3bacecfbc923fd973f8b96073a7575
diff --git a/update_build_tools.py b/update_build_tools.py
new file mode 100755
index 0000000..23e6e4b
--- /dev/null
+++ b/update_build_tools.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+#
+# Copyright 2014 Google Inc. All Rights Reserved.
+import argparse
+import os
+import re
+import shutil
+import subprocess
+import sys
+import zipfile
+parser = argparse.ArgumentParser(
+    description=('Update prebuilt android build tools'))
+parser.add_argument(
+    'buildId',
+    type=int,
+    nargs='?',
+    help='Build server build ID')
+parser.add_argument(
+    '--target',
+    default='sdk_phone_mips-sdk',
+    help='Download from the specified build server target')
+parser.add_argument(
+    '--from-local-dir',
+    dest='localDir',
+    help='Copy prebuilts from local directory instead of buildserver')
+parser.add_argument(
+    '--no-git',
+    action='store_true',
+    help='Do not create a git commit for the import')
+args = parser.parse_args()
+if not args.buildId and not args.localDir:
+    parser.error("you must specify either a build ID or --from-local-dir")
+    sys.exit(1)
+if not args.no_git:
+    # Make sure we don't overwrite any pending changes
+    try:
+        subprocess.check_call(['git', 'diff', '--quiet', '--', '**'])
+        subprocess.check_call(['git', 'diff', '--quiet', '--cached', '--', '**'])
+    except subprocess.CalledProcessError:
+        print >> sys.stderr, "FAIL: There are uncommitted changes here; please revert or stash"
+        sys.exit(1)
+def GetSSOCmdline(url):
+    return ['sso_client', '--location', '--request_timeout', '90', '--url', url]
+def SSOFetch(url):
+    return subprocess.check_output(GetSSOCmdline(url), stderr=subprocess.STDOUT)
+def FetchFile(srcFile, dstFile):
+    if not args.localDir:
+        fileURL = buildURL + '/' + srcFile
+        subprocess.check_call(' '.join(GetSSOCmdline(fileURL) + ['>', dstFile]), shell=True)
+    else:
+        shutil.copyfile(os.path.join(args.localDir, srcFile), dstFile)
+if not args.localDir:
+    # to start, find the build server branch we're downloading from
+    try:
+        branch = SSOFetch("http://android-build/buildbot-update?op=GET-BRANCH&bid=%d" % args.buildId)
+    except subprocess.CalledProcessError:
+        print >> sys.stderr, "FAIL: Unable to retrieve branch for build ID %d" % args.buildId
+        sys.exit(1)
+    buildURL = "http://android-build/builds/%s-mac-%s/%d" % (branch, args.target, args.buildId)
+    # fetch the list build artifacts
+    try:
+        listingText = SSOFetch(buildURL + "?output=text")
+    except subprocess.CalledProcessError:
+        print >> sys.stderr, "FAIL: Unable to retrieve listing for build ID %d" % args.buildId
+        sys.exit(1)
+    listing = listingText.strip().split('\n')
+else:
+    listing = os.listdir(args.localDir)
+buildToolsZipRE = re.compile(r'^sdk-repo-darwin-build-tools-.*\.zip$')
+buildToolsZipFile = None
+for fname in listing:
+    if buildToolsZipRE.match(fname):
+        buildToolsZipFile = fname
+        break
+if not buildToolsZipFile:
+    src = 'directory' if args.localDir else 'build'
+    print >> sys.stderr, "FAIL: The specified %s contains no platform ZIP" % src
+    sys.exit(1)
+try:
+    FetchFile(buildToolsZipFile, buildToolsZipFile)
+except:
+    print >> sys.stderr, "FAIL: Unable to fetch %s" % buildToolsZipFile
+    sys.exit(1)
+try:    # make sure we delete the downloaded ZIP
+    BUILD_TOOLS_DIRS = [
+        ('android-5.0/lib', 'lib'),
+        ('android-5.0/renderscript', 'renderscript'),
+    ]
+    executableMap = {}
+    with zipfile.ZipFile(buildToolsZipFile) as buildToolsZip:
+        files = filter(lambda x: x.count('/') == 1 and not x[-1] == '/', buildToolsZip.namelist())
+        for fileEntry in map(lambda x: (x, x[x.find('/')+1:]) , files):
+            executable = False
+            if os.path.exists(fileEntry[1]):
+                if os.path.isdir(fileEntry[1]):
+                    subprocess.check_call(['git', 'rm', '-rf', fileEntry[1]])
+                else:
+                    executable = os.access(fileEntry[1], os.X_OK)
+                    subprocess.check_call(['git', 'rm', fileEntry[1]])
+            with buildToolsZip.open(fileEntry[0], 'r') as infile:
+                with open(fileEntry[1], 'w+') as outfile:
+                    shutil.copyfileobj(infile, outfile)
+            if executable:
+                subprocess.check_call(['chmod', '+x', fileEntry[1]])
+            subprocess.check_call(['git', 'add', fileEntry[1]])
+        for dirEntry in BUILD_TOOLS_DIRS:
+            if os.path.exists(dirEntry[1]):
+                if os.path.isdir(dirEntry[1]):
+                    subprocess.check_call(['git', 'rm', '-rf', dirEntry[1]])
+                else:
+                    subprocess.check_call(['git', 'rm', dirEntry[1]])
+            # extract the new version of the directory
+            fileZipDir = dirEntry[0] + '/'
+            for fname in buildToolsZip.namelist():
+                if (fname[-1] != '/'
+                        and fname.startswith(dirEntry[0])):
+                    relativePath = fname[len(fileZipDir):]
+                    subdirs = os.path.join(dirEntry[1], os.path.dirname(relativePath))
+                    if not os.path.exists(subdirs):
+                        os.makedirs(subdirs)
+                    with buildToolsZip.open(fname, 'r') as infile:
+                        with open(os.path.join(dirEntry[1], relativePath), 'w+') as outfile:
+                            shutil.copyfileobj(infile, outfile)
+            subprocess.check_call(['git', 'add', dirEntry[1]])
+    # modify the source.properties
+    subprocess.check_call(['sed', '-i', 's/Pkg.Revision=/\/\/Pkg.Revision=/',
+            'source.properties'])
+    subprocess.check_call(' '.join(['echo', '-e',
+            '"Archive.Os=MACOSX\nPkg.Revision=22.0.0\nArchive.Arch=ANY"'] +
+            ['>>', 'source.properties']), shell=True)
+    subprocess.check_call(['git', 'add', 'source.properties'])
+    if not args.no_git:
+        # commit all changes
+        if not args.localDir:
+            # gather useful info
+            buildInfo = dict(
+                buildURL = buildURL,
+                buildId = args.buildId,
+                branch = branch,
+                buildToolsZipFile = buildToolsZipFile,
+            )
+            msg = """Import L Build Tools from {branch} build {buildId}
+{buildURL}/{buildToolsZipFile}
+source.properties has been modified to make this appear as API 22
+""".format(**buildInfo)
+        else:
+            msg = "DO NOT SUBMIT Import locally built android platform from %s" % args.localDir
+        subprocess.check_call(['git', 'commit', '-m', msg])
+        print 'Update successful.'
+        if not args.localDir:
+            print 'Be sure to upload this manually to gerrit.'
+finally:
+    # revert all stray files, including the downloaded zip
+    try:
+        with open(os.devnull, 'w') as bitbucket:
+            subprocess.check_call(['git', 'add', '-Af', '.'], stdout=bitbucket)
+            subprocess.check_call(
+                    ['git', 'commit', '-m', 'COMMIT TO REVERT - RESET ME!!!'], stdout=bitbucket)
+            subprocess.check_call(['git', 'reset', '--hard', 'HEAD~1'], stdout=bitbucket)
+    except subprocess.CalledProcessError:
+        print >> sys.stderr, "ERROR: Failed cleaning up, manual cleanup required!!!"
+