xref: /aosp_15_r20/external/openscreen/build/scripts/install-sysroot.py (revision 3f982cf4871df8771c9d4abe6e9a6f8d829b2736)
1#!/usr/bin/env python3
2
3# Copyright 2019 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""
8Install Debian sysroots for cross compiling Open Screen.
9"""
10
11# The sysroot is needed to ensure that binaries that get built will run on
12# the oldest stable version of Debian that we currently support.
13# This script can be run manually but is more often run as part of gclient
14# hooks. When run from hooks this script is a no-op on non-linux platforms.
15
16# The sysroot image could be constructed from scratch based on the current state
17# of the Debian archive but for consistency we use a pre-built root image (we
18# don't want upstream changes to Debian to affect the build until we
19# choose to pull them in). The sysroot images are stored in Chrome's common
20# data storage, and the sysroots.json file should be kept in sync with Chrome's
21# copy of it.
22
23from __future__ import print_function
24
25import hashlib
26import json
27import platform
28import argparse
29import os
30import re
31import shutil
32import subprocess
33import sys
34try:
35    # For Python 3.0 and later
36    from urllib.request import urlopen
37except ImportError:
38    # Fall back to Python 2's urllib2
39    from urllib2 import urlopen
40
41SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
42PARENT_DIR = os.path.dirname(SCRIPT_DIR)
43URL_PREFIX = 'https://storage.googleapis.com'
44URL_PATH = 'openscreen-sysroots'
45
46VALID_ARCHS = ('arm', 'arm64')
47VALID_PLATFORMS = ('stretch', 'sid')
48
49
50class Error(Exception):
51    pass
52
53
54def GetSha1(filename):
55    """Generates a SHA1 hash for validating download. Done in chunks to avoid
56    excess memory usage."""
57    BLOCKSIZE = 1024 * 1024
58    sha1 = hashlib.sha1()
59    with open(filename, 'rb') as f:
60        chunk = f.read(BLOCKSIZE)
61        while chunk:
62            sha1.update(chunk)
63            chunk = f.read(BLOCKSIZE)
64    return sha1.hexdigest()
65
66
67def GetSysrootDict(target_platform, target_arch):
68    """Gets the sysroot information for a given platform and arch from the
69       sysroots.json file."""
70    if target_arch not in VALID_ARCHS:
71        raise Error('Unknown architecture: %s' % target_arch)
72
73    sysroots_file = os.path.join(SCRIPT_DIR, 'sysroots.json')
74    sysroots = json.load(open(sysroots_file))
75    sysroot_key = '%s_%s' % (target_platform, target_arch)
76    if sysroot_key not in sysroots:
77        raise Error('No sysroot for: %s' % (sysroot_key))
78    return sysroots[sysroot_key]
79
80
81def DownloadFile(url, local_path):
82    """Uses urllib to download a remote file into local_path."""
83    for _ in range(3):
84        try:
85            response = urlopen(url)
86            with open(local_path, "wb") as f:
87                f.write(response.read())
88            break
89        except Exception:
90            pass
91    else:
92        raise Error('Failed to download %s' % url)
93
94def ValidateFile(local_path, expected_sum):
95    """Generates the SHA1 hash of a local file to compare with an expected
96       hashsum."""
97    sha1sum = GetSha1(local_path)
98    if sha1sum != expected_sum:
99        raise Error('Tarball sha1sum is wrong.'
100                    'Expected %s, actual: %s' % (expected_sum, sha1sum))
101
102def InstallSysroot(target_platform, target_arch):
103    """Downloads, validates, unpacks, and installs a sysroot image."""
104    sysroot_dict = GetSysrootDict(target_platform, target_arch)
105    tarball_filename = sysroot_dict['Tarball']
106    tarball_sha1sum = sysroot_dict['Sha1Sum']
107
108    sysroot = os.path.join(PARENT_DIR, sysroot_dict['SysrootDir'])
109
110    url = '%s/%s/%s/%s' % (URL_PREFIX, URL_PATH, tarball_sha1sum,
111                           tarball_filename)
112
113    stamp = os.path.join(sysroot, '.stamp')
114    if os.path.exists(stamp):
115        with open(stamp) as s:
116            if s.read() == url:
117                return
118
119    if os.path.isdir(sysroot):
120        shutil.rmtree(sysroot)
121    os.mkdir(sysroot)
122
123    tarball_path = os.path.join(sysroot, tarball_filename)
124    DownloadFile(url, tarball_path)
125    ValidateFile(tarball_path, tarball_sha1sum)
126    subprocess.check_call(['tar', 'xf', tarball_path, '-C', sysroot])
127    os.remove(tarball_path)
128
129    with open(stamp, 'w') as s:
130        s.write(url)
131
132
133def parse_args(args):
134    """Parses the passed in arguments into an object."""
135    p = argparse.ArgumentParser()
136    p.add_argument(
137        'arch',
138        help='Sysroot architecture: %s' % ', '.join(VALID_ARCHS))
139    p.add_argument(
140        'platform',
141        help='Sysroot platform: %s' % ', '.join(VALID_PLATFORMS))
142    p.add_argument(
143        '--print-hash', action="store_true",
144        help='Print the hash of the sysroot for the specified arch.')
145
146    return p.parse_args(args)
147
148
149def main(args):
150    if not (sys.platform.startswith('linux') or sys.platform == 'darwin'):
151        print('Unsupported platform. Only Linux and Mac OS X are supported.')
152        return 1
153
154    parsed_args = parse_args(args)
155    if parsed_args.print_hash:
156        print(GetSysrootDict(parsed_args.platform, parsed_args.arch)['Sha1Sum'])
157
158    InstallSysroot(parsed_args.platform, parsed_args.arch)
159    return 0
160
161
162if __name__ == '__main__':
163    try:
164        sys.exit(main(sys.argv[1:]))
165    except Error as e:
166        sys.stderr.write('Installing sysroot error: {}\n'.format(e))
167        sys.exit(1)
168