1#!/usr/bin/env python 2# Copyright (c) 2013 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"""Install Debian Wheezy sysroots for building chromium. 7""" 8 9# The sysroot is needed to ensure that binaries will run on Debian Wheezy, 10# the oldest supported linux distribution. This script can be run manually but 11# is more often run as part of gclient hooks. When run from hooks this script 12# in a no-op on non-linux platforms. 13 14# The sysroot image could be constructed from scratch based on the current 15# state or Debian Wheezy but for consistency we currently use a pre-built root 16# image. The image will normally need to be rebuilt every time chrome's build 17# dependencies are changed. 18 19import hashlib 20import platform 21import optparse 22import os 23import re 24import shutil 25import subprocess 26import sys 27 28SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 29sys.path.append(os.path.dirname(os.path.dirname(SCRIPT_DIR))) 30import detect_host_arch 31import gyp_chromium 32import gyp_environment 33 34 35URL_PREFIX = 'https://commondatastorage.googleapis.com' 36URL_PATH = 'chrome-linux-sysroot/toolchain' 37REVISION_AMD64 = 'c52471d9dec240c8d0a88fa98aa1eefeee32e22f' 38REVISION_ARM = 'c52471d9dec240c8d0a88fa98aa1eefeee32e22f' 39REVISION_I386 = 'c52471d9dec240c8d0a88fa98aa1eefeee32e22f' 40REVISION_MIPS = 'c52471d9dec240c8d0a88fa98aa1eefeee32e22f' 41TARBALL_AMD64 = 'debian_wheezy_amd64_sysroot.tgz' 42TARBALL_ARM = 'debian_wheezy_arm_sysroot.tgz' 43TARBALL_I386 = 'debian_wheezy_i386_sysroot.tgz' 44TARBALL_MIPS = 'debian_wheezy_mips_sysroot.tgz' 45TARBALL_AMD64_SHA1SUM = 'ca4ed6e7c9e333b046be19d38584a11f6785eea6' 46TARBALL_ARM_SHA1SUM = '1fab0c2b1e93a933ddc593df3b43872b0ba5ded2' 47TARBALL_I386_SHA1SUM = '80c48c303319af2284e4a104c882d888af75ba81' 48TARBALL_MIPS_SHA1SUM = '01da32a35288627e05cfca193b7f3659531c6f7d' 49SYSROOT_DIR_AMD64 = 'debian_wheezy_amd64-sysroot' 50SYSROOT_DIR_ARM = 'debian_wheezy_arm-sysroot' 51SYSROOT_DIR_I386 = 'debian_wheezy_i386-sysroot' 52SYSROOT_DIR_MIPS = 'debian_wheezy_mips-sysroot' 53 54valid_archs = ('arm', 'i386', 'amd64', 'mips') 55 56 57class Error(Exception): 58 pass 59 60 61def GetSha1(filename): 62 sha1 = hashlib.sha1() 63 with open(filename, 'rb') as f: 64 while True: 65 # Read in 1mb chunks, so it doesn't all have to be loaded into memory. 66 chunk = f.read(1024*1024) 67 if not chunk: 68 break 69 sha1.update(chunk) 70 return sha1.hexdigest() 71 72 73def DetectHostArch(): 74 # Figure out host arch using build/detect_host_arch.py and 75 # set target_arch to host arch 76 detected_host_arch = detect_host_arch.HostArch() 77 if detected_host_arch == 'x64': 78 return 'amd64' 79 elif detected_host_arch == 'ia32': 80 return 'i386' 81 elif detected_host_arch == 'arm': 82 return 'arm' 83 elif detected_host_arch == 'mips': 84 return 'mips' 85 86 raise Error('Unrecognized host arch: %s' % detected_host_arch) 87 88 89def DetectTargetArch(): 90 """Attempt for determine target architecture. 91 92 This works by looking for target_arch in GYP_DEFINES. 93 """ 94 # TODO(agrieve): Make this script not depend on GYP_DEFINES so that it works 95 # with GN as well. 96 gyp_environment.SetEnvironment() 97 supplemental_includes = gyp_chromium.GetSupplementalFiles() 98 gyp_defines = gyp_chromium.GetGypVars(supplemental_includes) 99 target_arch = gyp_defines.get('target_arch') 100 if target_arch == 'x64': 101 return 'amd64' 102 elif target_arch == 'ia32': 103 return 'i386' 104 elif target_arch == 'arm': 105 return 'arm' 106 elif target_arch == 'arm64': 107 return 'arm64' 108 elif target_arch == 'mipsel': 109 return 'mips' 110 elif target_arch: 111 raise Error('Unrecognized target_arch: %s' % target_arch) 112 113 return None 114 115 116def InstallDefaultSysroots(): 117 """Install the default set of sysroot images. 118 119 This includes at least the sysroot for host architecture, and the 32-bit 120 sysroot for building the v8 snapshot image. It can also include the cross 121 compile sysroot for ARM/MIPS if cross compiling environment can be detected. 122 """ 123 host_arch = DetectHostArch() 124 InstallSysroot(host_arch) 125 126 if host_arch == 'amd64': 127 InstallSysroot('i386') 128 129 # Finally, if we can detect a non-standard target_arch such as ARM or 130 # MIPS, then install the sysroot too. 131 # Don't attampt to install arm64 since this is currently and android-only 132 # architecture. 133 target_arch = DetectTargetArch() 134 if target_arch and target_arch not in (host_arch, 'i386', 'arm64'): 135 InstallSysroot(target_arch) 136 137 138def main(args): 139 parser = optparse.OptionParser('usage: %prog [OPTIONS]', description=__doc__) 140 parser.add_option('--running-as-hook', action='store_true', 141 default=False, help='Used when running from gclient hooks.' 142 ' Installs default sysroot images.') 143 parser.add_option('--arch', type='choice', choices=valid_archs, 144 help='Sysroot architecture: %s' % ', '.join(valid_archs)) 145 options, _ = parser.parse_args(args) 146 if options.running_as_hook and not sys.platform.startswith('linux'): 147 return 0 148 149 if options.running_as_hook: 150 InstallDefaultSysroots() 151 else: 152 if not options.arch: 153 print 'You much specify either --arch or --running-as-hook' 154 return 1 155 InstallSysroot(options.arch) 156 157 return 0 158 159 160def InstallSysroot(target_arch): 161 # The sysroot directory should match the one specified in build/common.gypi. 162 # TODO(thestig) Consider putting this else where to avoid having to recreate 163 # it on every build. 164 linux_dir = os.path.dirname(SCRIPT_DIR) 165 if target_arch == 'amd64': 166 sysroot = os.path.join(linux_dir, SYSROOT_DIR_AMD64) 167 tarball_filename = TARBALL_AMD64 168 tarball_sha1sum = TARBALL_AMD64_SHA1SUM 169 revision = REVISION_AMD64 170 elif target_arch == 'arm': 171 sysroot = os.path.join(linux_dir, SYSROOT_DIR_ARM) 172 tarball_filename = TARBALL_ARM 173 tarball_sha1sum = TARBALL_ARM_SHA1SUM 174 revision = REVISION_ARM 175 elif target_arch == 'i386': 176 sysroot = os.path.join(linux_dir, SYSROOT_DIR_I386) 177 tarball_filename = TARBALL_I386 178 tarball_sha1sum = TARBALL_I386_SHA1SUM 179 revision = REVISION_I386 180 elif target_arch == 'mips': 181 sysroot = os.path.join(linux_dir, SYSROOT_DIR_MIPS) 182 tarball_filename = TARBALL_MIPS 183 tarball_sha1sum = TARBALL_MIPS_SHA1SUM 184 revision = REVISION_MIPS 185 else: 186 raise Error('Unknown architecture: %s' % target_arch) 187 188 url = '%s/%s/%s/%s' % (URL_PREFIX, URL_PATH, revision, tarball_filename) 189 190 stamp = os.path.join(sysroot, '.stamp') 191 if os.path.exists(stamp): 192 with open(stamp) as s: 193 if s.read() == url: 194 print 'Debian Wheezy %s root image already up-to-date: %s' % \ 195 (target_arch, sysroot) 196 return 197 198 print 'Installing Debian Wheezy %s root image: %s' % (target_arch, sysroot) 199 if os.path.isdir(sysroot): 200 shutil.rmtree(sysroot) 201 os.mkdir(sysroot) 202 tarball = os.path.join(sysroot, tarball_filename) 203 print 'Downloading %s' % url 204 sys.stdout.flush() 205 sys.stderr.flush() 206 subprocess.check_call( 207 ['curl', '--fail', '--retry', '3', '-L', url, '-o', tarball]) 208 sha1sum = GetSha1(tarball) 209 if sha1sum != tarball_sha1sum: 210 raise Error('Tarball sha1sum is wrong.' 211 'Expected %s, actual: %s' % (tarball_sha1sum, sha1sum)) 212 subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot]) 213 os.remove(tarball) 214 215 with open(stamp, 'w') as s: 216 s.write(url) 217 218 219if __name__ == '__main__': 220 try: 221 sys.exit(main(sys.argv[1:])) 222 except Error as e: 223 sys.stderr.write(str(e) + '\n') 224 sys.exit(1) 225