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 sysroots for building chromium. 7""" 8 9# The sysroot is needed to ensure that binaries that get built will run on 10# the oldest stable version of Debian that we currently support. 11# This script can be run manually but is more often run as part of gclient 12# hooks. When run from hooks this script is a no-op on non-linux platforms. 13 14# The sysroot image could be constructed from scratch based on the current state 15# of the Debian archive but for consistency we use a pre-built root image (we 16# don't want upstream changes to Debian to effect the chromium build until we 17# choose to pull them in). The images will normally need to be rebuilt every 18# time chrome's build dependencies are changed but should also be updated 19# periodically to include upstream security fixes from Debian. 20 21from __future__ import print_function 22 23import hashlib 24import json 25import platform 26import optparse 27import os 28import re 29import shutil 30import subprocess 31import sys 32try: 33 # For Python 3.0 and later 34 from urllib.request import urlopen 35except ImportError: 36 # Fall back to Python 2's urllib2 37 from urllib2 import urlopen 38 39SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 40 41URL_PREFIX = 'https://commondatastorage.googleapis.com' 42URL_PATH = 'chrome-linux-sysroot/toolchain' 43 44VALID_ARCHS = ('arm', 'arm64', 'i386', 'amd64', 'mips', 'mips64el') 45 46ARCH_TRANSLATIONS = { 47 'x64': 'amd64', 48 'x86': 'i386', 49 'mipsel': 'mips', 50 'mips64': 'mips64el', 51} 52 53DEFAULT_TARGET_PLATFORM = 'sid' 54 55class Error(Exception): 56 pass 57 58 59def GetSha1(filename): 60 sha1 = hashlib.sha1() 61 with open(filename, 'rb') as f: 62 while True: 63 # Read in 1mb chunks, so it doesn't all have to be loaded into memory. 64 chunk = f.read(1024*1024) 65 if not chunk: 66 break 67 sha1.update(chunk) 68 return sha1.hexdigest() 69 70 71def main(args): 72 parser = optparse.OptionParser('usage: %prog [OPTIONS]', description=__doc__) 73 parser.add_option('--arch', 74 help='Sysroot architecture: %s' % ', '.join(VALID_ARCHS)) 75 parser.add_option('--all', action='store_true', 76 help='Install all sysroot images (useful when updating the' 77 ' images)') 78 parser.add_option('--print-hash', 79 help='Print the hash of the sysroot for the given arch.') 80 options, _ = parser.parse_args(args) 81 if not sys.platform.startswith('linux'): 82 return 0 83 84 if options.print_hash: 85 arch = options.print_hash 86 print(GetSysrootDict(DEFAULT_TARGET_PLATFORM, 87 ARCH_TRANSLATIONS.get(arch, arch))['Sha1Sum']) 88 return 0 89 if options.arch: 90 InstallSysroot(DEFAULT_TARGET_PLATFORM, 91 ARCH_TRANSLATIONS.get(options.arch, options.arch)) 92 elif options.all: 93 for arch in VALID_ARCHS: 94 InstallSysroot(DEFAULT_TARGET_PLATFORM, arch) 95 else: 96 print('You much specify one of the options.') 97 return 1 98 99 return 0 100 101 102def GetSysrootDict(target_platform, target_arch): 103 if target_arch not in VALID_ARCHS: 104 raise Error('Unknown architecture: %s' % target_arch) 105 106 sysroots_file = os.path.join(SCRIPT_DIR, 'sysroots.json') 107 sysroots = json.load(open(sysroots_file)) 108 sysroot_key = '%s_%s' % (target_platform, target_arch) 109 if sysroot_key not in sysroots: 110 raise Error('No sysroot for: %s %s' % (target_platform, target_arch)) 111 return sysroots[sysroot_key] 112 113 114def InstallSysroot(target_platform, target_arch): 115 sysroot_dict = GetSysrootDict(target_platform, target_arch) 116 tarball_filename = sysroot_dict['Tarball'] 117 tarball_sha1sum = sysroot_dict['Sha1Sum'] 118 # TODO(thestig) Consider putting this elsewhere to avoid having to recreate 119 # it on every build. 120 linux_dir = os.path.dirname(SCRIPT_DIR) 121 sysroot = os.path.join(linux_dir, sysroot_dict['SysrootDir']) 122 123 url = '%s/%s/%s/%s' % (URL_PREFIX, URL_PATH, tarball_sha1sum, 124 tarball_filename) 125 126 stamp = os.path.join(sysroot, '.stamp') 127 if os.path.exists(stamp): 128 with open(stamp) as s: 129 if s.read() == url: 130 return 131 132 print('Installing Debian %s %s root image: %s' % \ 133 (target_platform, target_arch, sysroot)) 134 if os.path.isdir(sysroot): 135 shutil.rmtree(sysroot) 136 os.mkdir(sysroot) 137 tarball = os.path.join(sysroot, tarball_filename) 138 print('Downloading %s' % url) 139 sys.stdout.flush() 140 sys.stderr.flush() 141 for _ in range(3): 142 try: 143 response = urlopen(url) 144 with open(tarball, "wb") as f: 145 f.write(response.read()) 146 break 147 except Exception: # Ignore exceptions. 148 pass 149 else: 150 raise Error('Failed to download %s' % url) 151 sha1sum = GetSha1(tarball) 152 if sha1sum != tarball_sha1sum: 153 raise Error('Tarball sha1sum is wrong.' 154 'Expected %s, actual: %s' % (tarball_sha1sum, sha1sum)) 155 subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot]) 156 os.remove(tarball) 157 158 with open(stamp, 'w') as s: 159 s.write(url) 160 161 162if __name__ == '__main__': 163 try: 164 sys.exit(main(sys.argv[1:])) 165 except Error as e: 166 sys.stderr.write(str(e) + '\n') 167 sys.exit(1) 168