• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
7in the current version of the specified Chromium channel. If no channel is
8specified, we default to the 'stable' channel.
9
10This script assumes that git is installed and the computer meets all
11other prerequisites to build v8 normally (like having depot_tools installed and
12in $PATH).
13
14Example usage:
15$ tracing/bin/update_v8
16"""
17
18import json
19import os
20import platform
21import re
22import shutil
23import subprocess
24import sys
25import tempfile
26import urllib2
27
28OMAHAPROXY_VERSION_MAP_URL = 'https://omahaproxy.appspot.com/all.json'
29
30V8_PATH = os.path.join(
31    os.path.dirname(os.path.abspath(__file__)), os.path.pardir, 'third_party',
32    'v8')
33V8_DST_PATH = os.path.join(V8_PATH, '{os}', '{arch}')
34V8_README_PATH = os.path.join(V8_PATH, 'README.chromium')
35
36V8_CHECKOUT_BINARY_PATH = os.path.join(
37    '{v8_root}', 'v8', 'out', 'Release', 'd8')
38V8_GENERATE_GYP_CMD = (sys.executable + ' ' +
39                       os.path.join('gypfiles', 'gyp_v8') +
40                       ' -Dtarget_arch={arch}')
41V8_COMPILE_CMD = 'ninja -j 100 -C {0} d8'.format(os.path.join('out', 'Release'))
42V8_STRIP_CMD = 'strip -x {0}'.format(os.path.join('out', 'Release', 'd8'))
43
44VALID_CHANNEL_LIST = ['stable', 'canary', 'beta', 'dev']
45# Dict from the acceptable return values for Python's platform.system() to the
46# corresponding Chromium OS name.
47PYTHON_SYSTEM_TO_CHROME_OS = {
48  'Linux': 'linux',
49  'Windows': 'win',
50  'Darwin': 'mac'
51}
52# Dict from the acceptable return values for Python's platform.machine() to the
53# corresponding ninja architecture name.
54PYTHON_MACHINE_TO_NINJA_ARCH = {
55  'x86_64': 'x64',
56  'AMD64': 'x64'
57}
58
59def Main(args):
60  if len(args) > 1:
61    print('Usage: update_v8 [TARGET_CHANNEL]')
62    return 1
63
64  target_channel = args[0] if len(args) == 1 else 'stable'
65  target_arch = platform.machine()
66
67  if target_channel not in VALID_CHANNEL_LIST:
68    print 'Invalid target channel %s. Valid: %s' % (
69        target_channel, VALID_CHANNEL_LIST)
70    return 1
71
72  if platform.system() not in PYTHON_SYSTEM_TO_CHROME_OS:
73    print 'System not supported %s. Valid: %s' % (
74        platform.system(), PYTHON_SYSTEM_TO_CHROME_OS)
75    return 1
76  target_os = PYTHON_SYSTEM_TO_CHROME_OS[platform.system()]
77
78  if target_arch not in PYTHON_MACHINE_TO_NINJA_ARCH:
79    print 'Invalid target architecture %s. Valid: %s' % (
80        target_arch, PYTHON_MACHINE_TO_NINJA_ARCH)
81    return 1
82
83  v8_version = GetV8Version(target_os, target_channel)
84  UpdateV8Binary(v8_version, target_os, target_arch)
85  UpdateReadmeFile(v8_version, target_os)
86
87  return 0
88
89def GetV8Version(target_os, target_channel):
90  """Returns the v8 version that corresponds to the specified OS and channel."""
91  # Fetch the current version map from omahaproxy.
92  response = urllib2.urlopen(OMAHAPROXY_VERSION_MAP_URL)
93  versions = json.loads(response.read())
94
95  # Return the v8 version that corresponds to the target OS and channel in the
96  # version map.
97  v8_version = None
98  for curr_os in versions:
99    for curr_version in curr_os['versions']:
100      if (curr_version['os'] == target_os and
101          curr_version['channel'] == target_channel):
102        return curr_version['v8_version']
103
104def _RunCommand(command):
105  print 'Run: %s' % command
106  subprocess.check_call(command, shell=True, stderr=sys.stderr,
107                        stdout=sys.stdout)
108
109def UpdateV8Binary(v8_version, target_os, target_arch):
110  """Updates the catapult V8 binary for the specified OS to be the specified V8
111  version."""
112  # Clone v8, checkout the version that corresponds to our target OS and target
113  # channel, and build the d8 binary.
114  with TempDir() as v8_checkout_path:
115    with ChangeDirectory(v8_checkout_path):
116      if 'DEPOT_TOOLS_WIN_TOOLCHAIN' not in os.environ:
117        # If the user doesn't specify that they're using the Googler Windows
118        # build toolchain, assume that they're not.
119        os.environ['DEPOT_TOOLS_WIN_TOOLCHAIN'] = '0'
120
121      _RunCommand('fetch v8')
122      with ChangeDirectory('v8'):
123        _RunCommand('git checkout {0}'.format(v8_version))
124        _RunCommand('gclient sync')
125        if not 'GYP_DEFINES' in os.environ:
126          os.environ['GYP_DEFINES'] = ''
127        os.environ['GYP_DEFINES'] += ' v8_use_external_startup_data=0'
128        os.environ['GYP_GENERATORS'] = 'ninja'
129        ninja_arch = PYTHON_MACHINE_TO_NINJA_ARCH[target_arch]
130        _RunCommand(
131            V8_GENERATE_GYP_CMD.format(arch=ninja_arch))
132        _RunCommand(V8_COMPILE_CMD)
133        if target_os in ['linux', 'mac']:
134          _RunCommand(V8_STRIP_CMD)
135
136    # Copy the d8 binary into place.
137    d8_bin_src = V8_CHECKOUT_BINARY_PATH.format(v8_root=v8_checkout_path)
138    d8_dst_dir = V8_DST_PATH.format(os=target_os, arch=target_arch)
139
140    # Append .exe extension on win
141    if target_os == 'win':
142      d8_bin_src += '.exe'
143    shutil.copy(d8_bin_src, d8_dst_dir)
144    # Also copy dll files on win
145    if target_os == 'win':
146      d8_dir_src = os.path.dirname(d8_bin_src)
147      for f in os.listdir(d8_dir_src):
148        if f.endswith('.dll'):
149          lib_path = os.path.join(d8_dir_src, f)
150          shutil.copy(lib_path, d8_dst_dir)
151
152def UpdateReadmeFile(v8_version, target_os):
153  """Updates the V8 version number in the V8 README.chromium file."""
154  # Get the contents of the new README file with the replaced version number.
155  new_readme_contents = ''
156  with open(V8_README_PATH, 'r') as v8_readme:
157    new_readme_contents = re.sub(r'[0-9\.]+ \({0}\)'.format(target_os),
158                                 r'{0} ({1})'.format(v8_version, target_os),
159                                 v8_readme.read())
160
161  # Overwrite the old README file with the new one.
162  with open(V8_README_PATH, 'w') as v8_readme:
163    v8_readme.write(new_readme_contents)
164
165class ChangeDirectory:
166  """A context manager that changes a directory while in scope."""
167  def __init__(self, newPath):
168    self.newPath = newPath
169
170  def __enter__(self):
171    self.oldPath = os.getcwd()
172    os.chdir(self.newPath)
173
174  def __exit__(self, etype, value, traceback):
175    os.chdir(self.oldPath)
176
177class TempDir:
178  """A context manager that creates a temporary directory while in scope."""
179  def __enter__(self):
180    self.path = tempfile.mkdtemp()
181    print "creating {0}".format(self.path)
182    return self.path
183
184  def __exit__(self, etype, value, traceback):
185    shutil.rmtree(self.path, ignore_errors=True)
186    if os.path.isdir(self.path):
187      print '%s still exists. You may want to delete it' % self.path
188
189if __name__ == '__main__':
190  sys.exit(Main(sys.argv[1:]))
191