install-sysroot.py (5993B)
1 #!/usr/bin/env python3 2 # Copyright 2013 The Chromium Authors 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 21 # This script looks at sysroots.json next to it to find the name of a .tar.xz 22 # to download and the location to extract it to. The extracted sysroot could for 23 # example be in build/linux/debian_bullseye_amd64-sysroot/. 24 25 26 import glob 27 import hashlib 28 import json 29 import optparse 30 import os 31 import shutil 32 import subprocess 33 import sys 34 from urllib.request import urlopen 35 36 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 37 SRC_DIR = os.path.dirname(os.path.dirname(os.path.dirname(SCRIPT_DIR))) 38 39 VALID_ARCHS = ("amd64", "i386", "armhf", "arm64", "mipsel", "mips64el", 40 "ppc64el") 41 42 ARCH_TRANSLATIONS = { 43 "x64": "amd64", 44 "x86": "i386", 45 "arm": "armhf", 46 "mips": "mipsel", 47 "mips64": "mips64el", 48 "ppc64le": "ppc64el", 49 } 50 51 DEFAULT_SYSROOTS_PATH = os.path.join(os.path.relpath(SCRIPT_DIR, SRC_DIR), 52 "sysroots.json") 53 DEFAULT_TARGET_PLATFORM = "bullseye" 54 55 56 class Error(Exception): 57 pass 58 59 60 def GetSha256(filename): 61 sha1 = hashlib.sha256() 62 with open(filename, "rb") as f: 63 while True: 64 # Read in 1mb chunks, so it doesn't all have to be loaded into 65 # memory. 66 chunk = f.read(1024 * 1024) 67 if not chunk: 68 break 69 sha1.update(chunk) 70 return sha1.hexdigest() 71 72 73 def main(args): 74 parser = optparse.OptionParser("usage: %prog [OPTIONS]", 75 description=__doc__) 76 parser.add_option("--sysroots-json-path", 77 help="The location of sysroots.json file") 78 parser.add_option("--arch", 79 help="Sysroot architecture: %s" % ", ".join(VALID_ARCHS)) 80 parser.add_option( 81 "--all", 82 action="store_true", 83 help="Install all sysroot images (useful when updating the" 84 " images)", 85 ) 86 options, _ = parser.parse_args(args) 87 88 if options.sysroots_json_path: 89 sysroots_json_path = options.sysroots_json_path 90 else: 91 sysroots_json_path = DEFAULT_SYSROOTS_PATH 92 93 if options.arch: 94 InstallSysroot( 95 sysroots_json_path, 96 DEFAULT_TARGET_PLATFORM, 97 ARCH_TRANSLATIONS.get(options.arch, options.arch), 98 ) 99 elif options.all: 100 for arch in VALID_ARCHS: 101 InstallSysroot(sysroots_json_path, DEFAULT_TARGET_PLATFORM, arch) 102 else: 103 print("You much specify one of the options.") 104 return 1 105 106 return 0 107 108 109 def GetSysrootDict(sysroots_json_path, target_platform, target_arch): 110 if target_arch not in VALID_ARCHS: 111 raise Error("Unknown architecture: %s" % target_arch) 112 113 sysroots_file = os.path.join(SRC_DIR, sysroots_json_path) 114 sysroots = json.load(open(sysroots_file)) 115 sysroot_key = "%s_%s" % (target_platform, target_arch) 116 if sysroot_key not in sysroots: 117 raise Error("No sysroot for: %s %s" % (target_platform, target_arch)) 118 return sysroots[sysroot_key] 119 120 121 def InstallSysroot(sysroots_json_path, target_platform, target_arch): 122 sysroot_dict = GetSysrootDict(sysroots_json_path, target_platform, 123 target_arch) 124 tarball_filename = sysroot_dict["Tarball"] 125 tarball_sha256sum = sysroot_dict["Sha256Sum"] 126 url_prefix = sysroot_dict["URL"] 127 # TODO(thestig) Consider putting this elsewhere to avoid having to recreate 128 # it on every build. 129 linux_dir = os.path.dirname(SCRIPT_DIR) 130 sysroot = os.path.join(linux_dir, sysroot_dict["SysrootDir"]) 131 132 url = "%s/%s" % (url_prefix, tarball_sha256sum) 133 134 stamp = os.path.join(sysroot, ".stamp") 135 # This file is created by first class GCS deps. If this file exists, 136 # clear the entire directory and download with this script instead 137 if os.path.exists(stamp) and not glob.glob( 138 os.path.join(sysroot, ".*_is_first_class_gcs")): 139 with open(stamp) as s: 140 if s.read() == url: 141 return 142 143 print("Installing Debian %s %s root image: %s" % 144 (target_platform, target_arch, sysroot)) 145 if os.path.isdir(sysroot): 146 shutil.rmtree(sysroot) 147 os.mkdir(sysroot) 148 tarball = os.path.join(sysroot, tarball_filename) 149 print("Downloading %s" % url) 150 sys.stdout.flush() 151 sys.stderr.flush() 152 for _ in range(3): 153 try: 154 response = urlopen(url) 155 with open(tarball, "wb") as f: 156 f.write(response.read()) 157 break 158 except Exception: # Ignore exceptions. 159 pass 160 else: 161 raise Error("Failed to download %s" % url) 162 sha256sum = GetSha256(tarball) 163 if sha256sum != tarball_sha256sum: 164 raise Error("Tarball sha256sum is wrong." 165 "Expected %s, actual: %s" % (tarball_sha256sum, sha256sum)) 166 subprocess.check_call(["tar", "mxf", tarball, "-C", sysroot]) 167 os.remove(tarball) 168 169 with open(stamp, "w") as s: 170 s.write(url) 171 172 173 if __name__ == "__main__": 174 try: 175 sys.exit(main(sys.argv[1:])) 176 except Error as e: 177 sys.stderr.write(str(e) + "\n") 178 sys.exit(1)