Merge commit '1947a866a94d9d8546d1f88b3cdb80d4881e35bf' into 1.7.5-dev
diff --git a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
index b626e56..d7f3861 100644
--- a/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/ApplicationWriter.java
@@ -234,7 +234,8 @@
         buffer.putLong(inputChecksums.get(from.getType().descriptor.toASCIIString()));
       }
       CRC32 crc = new CRC32();
-      crc.update(buffer.array());
+      byte[] array = buffer.array();
+      crc.update(array, 0, array.length);
       synthesizedChecksums.put(clazz.getType().descriptor.toASCIIString(), crc.getValue());
     }
 
diff --git a/tools/r8_release.py b/tools/r8_release.py
new file mode 100755
index 0000000..b626832
--- /dev/null
+++ b/tools/r8_release.py
@@ -0,0 +1,255 @@
+#!/usr/bin/env python
+# Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+# for details. All rights reserved. Use of this source code is governed by a
+# BSD-style license that can be found in the LICENSE file.
+
+import argparse
+import datetime
+import os.path
+import re
+import shutil
+import subprocess
+import sys
+import tempfile
+import update_prebuilds_in_android
+import urllib
+import utils
+
+def update_prebuilds(hash, checkout):
+  update_prebuilds_in_android.main_download(hash, True, 'lib', checkout, '')
+
+
+def release_studio_or_aosp(path, options, git_base_message):
+  with utils.ChangedWorkingDirectory(path):
+    subprocess.call(['repo', 'abandon', 'update-r8'])
+    if not options.no_sync:
+      subprocess.check_call(['repo', 'sync'])
+
+    prebuilts_r8 = os.path.join(path, 'prebuilts', 'r8')
+
+    with utils.ChangedWorkingDirectory(prebuilts_r8):
+      subprocess.check_call(['repo', 'start', 'update-r8'])
+
+    update_prebuilds(options.hash, path)
+
+    with utils.ChangedWorkingDirectory(prebuilts_r8):
+      git_message = (git_base_message
+                     % (options.version, options.hash, options.hash))
+      subprocess.check_call(['git', 'commit', '-a', '-m', git_message])
+      process = subprocess.Popen(['repo', 'upload', '.', '--verify'],
+                                 stdin=subprocess.PIPE)
+      return process.communicate(input='y\n')[0]
+
+
+def prepare_aosp():
+  aosp = raw_input('Input the path for the AOSP checkout:\n')
+  assert os.path.exists(aosp), "Could not find AOSP path %s" % aosp
+
+  def release_aosp(options):
+    print "Releasing for AOSP"
+    git_base_message = """Update D8 and R8 to %s
+
+Version: master %s
+This build IS NOT suitable for preview or public release.
+
+Built here: go/r8-releases/raw/master/%s
+
+Test: TARGET_PRODUCT=aosp_arm64 m -j core-oj"""
+    return release_studio_or_aosp(aosp, options, git_base_message)
+
+  return release_aosp
+
+
+def prepare_studio():
+  studio = raw_input('Input the path for the STUDIO checkout:\n')
+  assert os.path.exists(studio), "Could not find STUDIO path %s" % studio
+
+  def release_studio(options):
+    print "Releasing for STUDIO"
+    git_base_message = """Update D8 R8 master to %s
+
+Version: master %s
+This is a development snapshot, it's fine to use for studio canary build, but
+not for BETA or release, for those we would need a release version of R8
+binaries.
+This build IS suitable for preview release but IS NOT suitable for public release.
+
+Built here: go/r8-releases/raw/master/%s/
+Test: ./gradlew check
+Bug: """
+    return release_studio_or_aosp(studio, options, git_base_message)
+
+  return release_studio
+
+
+def g4_cp(old, new, file):
+  subprocess.check_call('g4 cp {%s,%s}/%s' % (old, new, file), shell=True)
+
+
+def g4_open(file):
+  subprocess.check_call('g4 open %s' % file, shell=True)
+
+
+def g4_add(files):
+  subprocess.check_call(' '.join(['g4', 'add'] + files), shell=True)
+
+
+def g4_change(version, hash):
+  return subprocess.check_output(
+      'g4 change --desc "Update R8 to version %s %s"' % (version, hash),
+      shell=True)
+
+
+def sed(pattern, replace, path):
+  with open(path, "r") as sources:
+    lines = sources.readlines()
+  with open(path, "w") as sources:
+    for line in lines:
+      sources.write(re.sub(pattern, replace, line))
+
+
+def download_file(hash, file, dst):
+  urllib.urlretrieve(
+      ('http://storage.googleapis.com/r8-releases/raw/master/%s/%s'
+       % (hash, file)),
+      dst)
+
+
+def blaze_run(target):
+  return subprocess.check_output(
+      'blaze run %s' % target, shell=True, stderr=subprocess.STDOUT)
+
+
+def prepare_google3():
+  utils.check_prodacces()
+
+  # Check if an existing client exists.
+  if ':update-r8:' in subprocess.check_output('g4 myclients', shell=True):
+    print "Remove the existing 'update-r8' client before continuing."
+    sys.exit(1)
+
+  def release_google3(options):
+    print "Releasing for Google 3"
+
+    google3_base = subprocess.check_output(
+        ['p4', 'g4d', '-f', 'update-r8']).rstrip()
+    third_party_r8 = os.path.join(google3_base, 'third_party', 'java', 'r8')
+
+    # Check if new version folder is already created
+    today = datetime.date.today()
+    new_version='v%d%02d%02d' % (today.year, today.month, today.day)
+    new_version_path = os.path.join(third_party_r8, new_version)
+
+    if os.path.exists(new_version_path):
+      shutil.rmtree(new_version_path)
+
+    # Remove old version
+    old_versions = []
+    for name in os.listdir(third_party_r8):
+      if os.path.isdir(os.path.join(third_party_r8, name)):
+        old_versions.append(name)
+    old_versions.sort()
+
+    if len(old_versions) >= 2:
+      shutil.rmtree(os.path.join(third_party_r8, old_versions[0]))
+
+    # Take current version to copy from
+    old_version=old_versions[-1]
+
+    # Create new version
+    assert not os.path.exists(new_version_path)
+    os.mkdir(new_version_path)
+
+    with utils.ChangedWorkingDirectory(third_party_r8):
+      g4_cp(old_version, new_version, 'BUILD')
+      g4_cp(old_version, new_version, 'LICENSE')
+      g4_cp(old_version, new_version, 'METADATA')
+
+      with utils.ChangedWorkingDirectory(new_version_path):
+        # update METADATA
+        g4_open('METADATA')
+        sed(r'[a-z0-9]{40}',
+            options.hash,
+            os.path.join(new_version_path, 'METADATA'))
+        sed(r'\{ year.*\}',
+            ('{ year: %i month: %i day: %i }'
+             % (today.year, today.month, today.day))
+            , os.path.join(new_version_path, 'METADATA'))
+
+        # update BUILD (is not necessary from v20190923)
+        g4_open('BUILD')
+        sed(old_version, new_version, os.path.join(new_version_path, 'BUILD'))
+
+        # download files
+        download_file(options.hash, 'r8-full-exclude-deps.jar', 'r8.jar')
+        download_file(options.hash, 'r8-src.jar', 'r8-src.jar')
+        download_file(options.hash, 'r8lib-exclude-deps.jar', 'r8lib.jar')
+        download_file(
+            options.hash, 'r8lib-exclude-deps.jar.map', 'r8lib.jar.map')
+        g4_add(['r8.jar', 'r8-src.jar', 'r8lib.jar', 'r8lib.jar.map'])
+
+      subprocess.check_output('chmod u+w %s/*' % new_version, shell=True)
+
+      g4_open('BUILD')
+      sed(old_version, new_version, os.path.join(third_party_r8, 'BUILD'))
+
+    with utils.ChangedWorkingDirectory(google3_base):
+      blaze_result = blaze_run('//third_party/java/r8:d8 -- --version')
+
+      assert options.version in blaze_result
+      assert options.hash in blaze_result
+
+      return g4_change(new_version, options.hash)
+
+  return release_google3
+
+
+def parse_options():
+  result = argparse.ArgumentParser(description='Release r8')
+  result.add_argument('--targets',
+                      help='Where to release a new version.',
+                      choices=['all', 'aosp', 'studio', 'google3'],
+                      required=True,
+                      action='append')
+  result.add_argument('--version',
+                      required=True,
+                      help='The new version of R8 (e.g., 1.4.51)')
+  result.add_argument('--hash',
+                      required=True,
+                      help='The hash of the new R8 version')
+  result.add_argument('--no_sync',
+                      default=False,
+                      action='store_true',
+                      help='Do not sync repos before uploading')
+  args = result.parse_args()
+  if 'all' in args.targets:
+    args.targets = ['aosp', 'studio', 'google3']
+  return args
+
+
+def main():
+  args = parse_options()
+  targets_to_run = []
+  if 'google3' in args.targets:
+    targets_to_run.append(prepare_google3())
+  if 'studio' in args.targets:
+    targets_to_run.append(prepare_studio())
+  if 'aosp' in args.targets:
+    targets_to_run.append(prepare_aosp())
+
+  final_results = []
+  for target_closure in targets_to_run:
+    final_results.append(target_closure(args))
+
+  print '\n\n**************************************************************'
+  print 'PRINTING SUMMARY'
+  print '**************************************************************\n\n'
+
+  for result in final_results:
+    if result is not None:
+      print result
+
+
+if __name__ == '__main__':
+  sys.exit(main())
+
diff --git a/tools/update_prebuilds_in_android.py b/tools/update_prebuilds_in_android.py
index 118da7c..0cac61a 100755
--- a/tools/update_prebuilds_in_android.py
+++ b/tools/update_prebuilds_in_android.py
@@ -89,35 +89,41 @@
   if 'X-GUploader-Request-Result: success' not in str(result[1]):
     raise IOError('Failed to download ' + url)
 
-def Main():
-  args = parse_arguments()
+def main_download(hash, maps, targets, target_root, version):
+  jar_targets = JAR_TARGETS_MAP[targets]
+  final_targets = map((lambda t: t[0] + '.jar'), jar_targets) + OTHER_TARGETS
+  with utils.TempDir() as root:
+    for target in final_targets:
+      if hash:
+        download_hash(root, hash, target)
+        if maps and target not in OTHER_TARGETS:
+          download_hash(root, hash, target + '.map')
+      else:
+        assert version
+        download_version(root, version, target)
+        if maps and target not in OTHER_TARGETS:
+          download_version(root, version, target + '.map')
+    copy_jar_targets(root, target_root, jar_targets, maps)
+    copy_other_targets(root, target_root)
+
+def main_build(maps, max_memory_size, targets, target_root):
+  jar_targets = JAR_TARGETS_MAP[targets]
+  gradle_args = map(lambda t: t[0], jar_targets)
+  if max_memory_size:
+    gradle_args.append('-Dorg.gradle.jvmargs=-Xmx' + max_memory_size)
+  gradle.RunGradle(gradle_args)
+  copy_jar_targets(utils.LIBS, target_root, jar_targets, maps)
+  copy_other_targets(utils.GENERATED_LICENSE_DIR, target_root)
+
+def main(args):
   if args.maps and args.targets != 'lib':
     raise Exception("Use '--maps' only with '--targets lib.")
   target_root = args.android_root[0]
-  jar_targets = JAR_TARGETS_MAP[args.targets]
   if args.commit_hash == None and args.version == None:
-    gradle_args = map(lambda t: t[0], jar_targets)
-    if args.java_max_memory_size:
-      gradle_args.append('-Dorg.gradle.jvmargs=-Xmx' + args.java_max_memory_size)
-    gradle.RunGradle(gradle_args)
-    copy_jar_targets(utils.LIBS, target_root, jar_targets, args.maps)
-    copy_other_targets(utils.GENERATED_LICENSE_DIR, target_root)
+    main_build(args.maps, args.java_max_memory_size, args.targets, target_root)
   else:
     assert args.commit_hash == None or args.version == None
-    targets = map((lambda t: t[0] + '.jar'), jar_targets) + OTHER_TARGETS
-    with utils.TempDir() as root:
-      for target in targets:
-        if args.commit_hash:
-          download_hash(root, args.commit_hash, target)
-          if args.maps and target not in OTHER_TARGETS:
-            download_hash(root, args.commit_hash, target + '.map')
-        else:
-          assert args.version
-          download_version(root, args.version, target)
-          if args.maps and target not in OTHER_TARGETS:
-            download_version(root, args.version, target + '.map')
-      copy_jar_targets(root, target_root, jar_targets, args.maps)
-      copy_other_targets(root, target_root)
+    main_download(args.commit_hash, args.maps, args.targets, target_root, args.version)
 
 if __name__ == '__main__':
-  sys.exit(Main())
+  sys.exit(main(parse_arguments()))