Home | History | Annotate | Download | only in bin
      1 #!/usr/bin/env python
      2 # Copyright 2015 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Updates the 64 bit d8 binary for the current OS to match the v8 version used
      7 in the current version of the specified Chromium channel. If no channel is
      8 specified, we default to the 'stable' channel.
      9 
     10 This script assumes that git is installed and the computer meets all
     11 other prerequisites to build v8 normally (like having depot_tools installed and
     12 in $PATH).
     13 
     14 Example usage:
     15 $ tracing/bin/update_v8
     16 """
     17 
     18 import json
     19 import os
     20 import platform
     21 import re
     22 import shutil
     23 import subprocess
     24 import sys
     25 import tempfile
     26 import urllib2
     27 
     28 OMAHAPROXY_VERSION_MAP_URL = 'https://omahaproxy.appspot.com/all.json'
     29 
     30 V8_PATH = os.path.join(
     31     os.path.dirname(os.path.abspath(__file__)), os.path.pardir, 'third_party',
     32     'v8')
     33 V8_BINARY_PATH = os.path.join(V8_PATH, '{os}', '{arch}', 'd8')
     34 V8_README_PATH = os.path.join(V8_PATH, 'README.chromium')
     35 
     36 V8_CHECKOUT_BINARY_PATH = os.path.join(
     37     '{v8_root}', 'v8', 'out', 'Release', 'd8')
     38 V8_GENERATE_GYP_CMD = os.path.join('build', 'gyp_v8') + ' -Dtarget_arch={arch}'
     39 V8_COMPILE_CMD = 'ninja -C {0} d8'.format(os.path.join('out', 'Release'))
     40 V8_STRIP_CMD = 'strip -x {0}'.format(os.path.join('out', 'Release', 'd8'))
     41 
     42 VALID_CHANNEL_LIST = ['stable', 'canary', 'beta', 'dev']
     43 # Dict from the acceptable return values for Python's platform.system() to the
     44 # corresponding Chromium OS name.
     45 PYTHON_SYSTEM_TO_CHROME_OS = {
     46   'Linux': 'linux',
     47   'Windows': 'win',
     48   'Darwin': 'mac'
     49 }
     50 # Dict from the acceptable return values for Python's platform.machine() to the
     51 # corresponding ninja architecture name.
     52 PYTHON_MACHINE_TO_NINJA_ARCH = {
     53   'x86_64': 'x64'
     54 }
     55 
     56 def Main(args):
     57   if len(args) > 1:
     58     print('Usage: update_v8 [TARGET_CHANNEL]')
     59     return 1
     60 
     61   target_channel = args[0] if len(args) == 1 else 'stable'
     62   target_arch = platform.machine()
     63 
     64   if target_channel not in VALID_CHANNEL_LIST:
     65     print('Invalid target channel. Valid: {0}'.format(VALID_CHANNEL_LIST))
     66     return 1
     67 
     68   if platform.system() not in PYTHON_SYSTEM_TO_CHROME_OS:
     69     print('System not supported. Valid: {0}'.format(
     70         PYTHON_SYSTEM_TO_CHROME_OS.keys()))
     71     return 1
     72   target_os = PYTHON_SYSTEM_TO_CHROME_OS[platform.system()]
     73 
     74   if target_arch not in PYTHON_MACHINE_TO_NINJA_ARCH:
     75     print('Invalid target architecture. Valid: {0}'.format(
     76         PYTHON_MACHINE_TO_NINJA_ARCH.keys()))
     77     return 1
     78 
     79   v8_version = GetV8Version(target_os, target_channel)
     80   UpdateV8Binary(v8_version, target_os, target_arch)
     81   UpdateReadmeFile(v8_version, target_os)
     82 
     83   return 0
     84 
     85 def GetV8Version(target_os, target_channel):
     86   """Returns the v8 version that corresponds to the specified OS and channel."""
     87   # Fetch the current version map from omahaproxy.
     88   response = urllib2.urlopen(OMAHAPROXY_VERSION_MAP_URL)
     89   versions = json.loads(response.read())
     90 
     91   # Return the v8 version that corresponds to the target OS and channel in the
     92   # version map.
     93   v8_version = None
     94   for curr_os in versions:
     95     for curr_version in curr_os['versions']:
     96       if (curr_version['os'] == target_os and
     97           curr_version['channel'] == target_channel):
     98         return curr_version['v8_version']
     99 
    100 def UpdateV8Binary(v8_version, target_os, target_arch):
    101   """Updates the catapult V8 binary for the specified OS to be the specified V8
    102   version."""
    103   # Clone v8, checkout the version that corresponds to our target OS and target
    104   # channel, and build the d8 binary.
    105   with TempDir() as v8_checkout_path:
    106     with ChangeDirectory(v8_checkout_path):
    107       subprocess.check_call('fetch v8', shell=True)
    108       with ChangeDirectory('v8'):
    109         subprocess.check_call('git checkout {0}'.format(v8_version), shell=True)
    110 
    111         os.environ['GYP_DEFINES'] += ' v8_use_external_startup_data=0'
    112         os.environ['GYP_GENERATORS'] = 'ninja'
    113         ninja_arch = PYTHON_MACHINE_TO_NINJA_ARCH[target_arch]
    114         subprocess.check_call(
    115             V8_GENERATE_GYP_CMD.format(arch=ninja_arch), shell=True)
    116         subprocess.check_call(V8_COMPILE_CMD, shell=True)
    117         if target_os in ['linux', 'mac']:
    118           subprocess.check_call(V8_STRIP_CMD, shell=True)
    119 
    120     # Move the d8 binary into place.
    121     d8_src = V8_CHECKOUT_BINARY_PATH.format(v8_root=v8_checkout_path)
    122     d8_dst = V8_BINARY_PATH.format(os=target_os, arch=target_arch)
    123 
    124     shutil.move(d8_src, d8_dst)
    125 
    126 def UpdateReadmeFile(v8_version, target_os):
    127   """Updates the V8 version number in the V8 README.chromium file."""
    128   # Get the contents of the new README file with the replaced version number.
    129   new_readme_contents = ''
    130   with open(V8_README_PATH, 'r') as v8_readme:
    131     new_readme_contents = re.sub(r'[0-9\.]+ \({0}\)'.format(target_os),
    132                                  r'{0} ({1})'.format(v8_version, target_os),
    133                                  v8_readme.read())
    134 
    135   # Overwrite the old README file with the new one.
    136   with open(V8_README_PATH, 'w') as v8_readme:
    137     v8_readme.write(new_readme_contents)
    138 
    139 class ChangeDirectory:
    140   """A context manager that changes a directory while in scope."""
    141   def __init__(self, newPath):
    142     self.newPath = newPath
    143 
    144   def __enter__(self):
    145     self.oldPath = os.getcwd()
    146     os.chdir(self.newPath)
    147 
    148   def __exit__(self, etype, value, traceback):
    149     os.chdir(self.oldPath)
    150 
    151 class TempDir:
    152   """A context manager that creates a temporary directory while in scope."""
    153   def __enter__(self):
    154     self.path = tempfile.mkdtemp()
    155     print "creating {0}".format(self.path)
    156     return self.path
    157 
    158   def __exit__(self, etype, value, traceback):
    159     shutil.rmtree(self.path)
    160 
    161 if __name__ == '__main__':
    162   sys.exit(Main(sys.argv[1:]))
    163