xref: /aosp_15_r20/external/cronet/build/fuchsia/update_sdk.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python3
2# Copyright 2022 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"""Check out the Fuchsia SDK from a given GCS path. Should be used in a
6'hooks_os' entry so that it only runs when .gclient's custom_vars includes
7'fuchsia'."""
8
9import argparse
10import json
11import logging
12import os
13import platform
14import subprocess
15import sys
16from typing import Optional
17
18from gcs_download import DownloadAndUnpackFromCloudStorage
19
20sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__),
21                                             'test')))
22
23from common import SDK_ROOT, get_host_os, make_clean_directory
24
25_VERSION_FILE = os.path.join(SDK_ROOT, 'meta', 'manifest.json')
26
27
28def _GetHostArch():
29  host_arch = platform.machine()
30  # platform.machine() returns AMD64 on 64-bit Windows.
31  if host_arch in ['x86_64', 'AMD64']:
32    return 'amd64'
33  elif host_arch == 'aarch64':
34    return 'arm64'
35  raise Exception('Unsupported host architecture: %s' % host_arch)
36
37
38def GetSDKOverrideGCSPath() -> Optional[str]:
39  """Fetches the sdk override path from a file or an environment variable.
40
41  Returns:
42    The override sdk location, stripped of white space.
43      Example: gs://fuchsia-artifacts/development/some-id/sdk
44  """
45  if os.getenv('FUCHSIA_SDK_OVERRIDE'):
46    return os.environ['FUCHSIA_SDK_OVERRIDE'].strip()
47
48  path = os.path.join(os.path.dirname(__file__), 'sdk_override.txt')
49
50  if os.path.isfile(path):
51    with open(path, 'r') as f:
52      return f.read().strip()
53
54  return None
55
56
57def _GetTarballPath(gcs_tarball_prefix: str) -> str:
58  """Get the full path to the sdk tarball on GCS"""
59  platform = get_host_os()
60  arch = _GetHostArch()
61  return f'{gcs_tarball_prefix}/{platform}-{arch}/core.tar.gz'
62
63
64def _GetCurrentVersionFromManifest() -> Optional[str]:
65  if not os.path.exists(_VERSION_FILE):
66    return None
67  with open(_VERSION_FILE) as f:
68    return json.load(f)['id']
69
70
71def main():
72  parser = argparse.ArgumentParser()
73  parser.add_argument('--cipd-prefix', help='CIPD base directory for the SDK.')
74  parser.add_argument('--version', help='Specifies the SDK version.')
75  parser.add_argument('--verbose',
76                      '-v',
77                      action='store_true',
78                      help='Enable debug-level logging.')
79  args = parser.parse_args()
80
81  logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO)
82
83  # Exit if there's no SDK support for this platform.
84  try:
85    host_plat = get_host_os()
86  except:
87    logging.warning('Fuchsia SDK is not supported on this platform.')
88    return 0
89
90  # TODO(crbug.com/326004432): Remove this once DEPS have been fixed not to
91  # include the "version:" prefix.
92  if args.version.startswith('version:'):
93    args.version = args.version[len('version:'):]
94
95  gcs_tarball_prefix = GetSDKOverrideGCSPath()
96  if not gcs_tarball_prefix:
97    # sdk_override contains the full path but not only the version id. But since
98    # the scenario is limited to dry-run, it's not worth complexity to extract
99    # the version id.
100    if args.version == _GetCurrentVersionFromManifest():
101      return 0
102
103  make_clean_directory(SDK_ROOT)
104
105  # Download from CIPD if there is no override file.
106  if not gcs_tarball_prefix:
107    if not args.cipd_prefix:
108      parser.exit(1, '--cipd-prefix must be specified.')
109    if not args.version:
110      parser.exit(2, '--version must be specified.')
111    logging.info('Downloading SDK from CIPD...')
112    ensure_file = '%s%s-%s version:%s' % (args.cipd_prefix, host_plat,
113                                          _GetHostArch(), args.version)
114    subprocess.run(('cipd', 'ensure', '-ensure-file', '-', '-root', SDK_ROOT,
115                    '-log-level', 'warning'),
116                   check=True,
117                   text=True,
118                   input=ensure_file)
119
120    # Verify that the downloaded version matches the expected one.
121    downloaded_version = _GetCurrentVersionFromManifest()
122    if downloaded_version != args.version:
123      logging.error(
124          'SDK version after download does not match expected (downloaded:%s '
125          'vs expected:%s)', downloaded_version, args.version)
126      return 3
127  else:
128    logging.info('Downloading SDK from GCS...')
129    DownloadAndUnpackFromCloudStorage(_GetTarballPath(gcs_tarball_prefix),
130                                      SDK_ROOT)
131
132  # Build rules (e.g. fidl_library()) depend on updates to the top-level
133  # manifest to spot when to rebuild for an SDK update. Ensure that ninja
134  # sees that the SDK manifest has changed, regardless of the mtime set by
135  # the download & unpack steps above, by setting mtime to now.
136  # See crbug.com/1457463
137  os.utime(os.path.join(SDK_ROOT, 'meta', 'manifest.json'), None)
138
139  root_dir = os.path.dirname(os.path.realpath(__file__))
140  build_def_cmd = [
141      os.path.join(root_dir, 'gen_build_defs.py'),
142  ]
143  subprocess.run(build_def_cmd, check=True)
144
145  return 0
146
147
148if __name__ == '__main__':
149  sys.exit(main())
150