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