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 _GetCurrentVersionFromManifest() -> Optional[str]: 58 if not os.path.exists(_VERSION_FILE): 59 return None 60 with open(_VERSION_FILE) as f: 61 try: 62 data = json.load(f) 63 except json.decoder.JSONDecodeError: 64 logging.warning('manifest.json is not at the JSON format and may be empty.') 65 return None 66 if 'id' not in data: 67 logging.warning('The key "id" does not exist in manifest.json') 68 return None 69 return data['id'] 70 71 72def main(): 73 parser = argparse.ArgumentParser() 74 parser.add_argument('--cipd-prefix', help='CIPD base directory for the SDK.') 75 parser.add_argument('--version', help='Specifies the SDK version.') 76 parser.add_argument('--verbose', 77 '-v', 78 action='store_true', 79 help='Enable debug-level logging.') 80 parser.add_argument( 81 '--file', 82 help='Specifies the sdk tar.gz file name without .tar.gz suffix', 83 default='core') 84 args = parser.parse_args() 85 86 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) 87 88 # Exit if there's no SDK support for this platform. 89 try: 90 host_plat = get_host_os() 91 except: 92 logging.warning('Fuchsia SDK is not supported on this platform.') 93 return 0 94 95 # TODO(crbug.com/326004432): Remove this once DEPS have been fixed not to 96 # include the "version:" prefix. 97 if args.version.startswith('version:'): 98 args.version = args.version[len('version:'):] 99 100 gcs_tarball_prefix = GetSDKOverrideGCSPath() 101 if not gcs_tarball_prefix: 102 # sdk_override contains the full path but not only the version id. But since 103 # the scenario is limited to dry-run, it's not worth complexity to extract 104 # the version id. 105 if args.version == _GetCurrentVersionFromManifest(): 106 return 0 107 108 make_clean_directory(SDK_ROOT) 109 110 # Download from CIPD if there is no override file. 111 if not gcs_tarball_prefix: 112 if not args.cipd_prefix: 113 parser.exit(1, '--cipd-prefix must be specified.') 114 if not args.version: 115 parser.exit(2, '--version must be specified.') 116 logging.info('Downloading SDK from CIPD...') 117 ensure_file = '%s%s-%s version:%s' % (args.cipd_prefix, host_plat, 118 _GetHostArch(), args.version) 119 subprocess.run(('cipd', 'ensure', '-ensure-file', '-', '-root', SDK_ROOT, 120 '-log-level', 'warning'), 121 check=True, 122 text=True, 123 input=ensure_file) 124 125 # Verify that the downloaded version matches the expected one. 126 downloaded_version = _GetCurrentVersionFromManifest() 127 if downloaded_version != args.version: 128 logging.error( 129 'SDK version after download does not match expected (downloaded:%s ' 130 'vs expected:%s)', downloaded_version, args.version) 131 return 3 132 else: 133 logging.info('Downloading SDK from GCS...') 134 DownloadAndUnpackFromCloudStorage( 135 f'{gcs_tarball_prefix}/{get_host_os()}-{_GetHostArch()}/' 136 f'{args.file}.tar.gz', SDK_ROOT) 137 138 # Build rules (e.g. fidl_library()) depend on updates to the top-level 139 # manifest to spot when to rebuild for an SDK update. Ensure that ninja 140 # sees that the SDK manifest has changed, regardless of the mtime set by 141 # the download & unpack steps above, by setting mtime to now. 142 # See crbug.com/1457463 143 os.utime(os.path.join(SDK_ROOT, 'meta', 'manifest.json'), None) 144 145 root_dir = os.path.dirname(os.path.realpath(__file__)) 146 build_def_cmd = [ 147 os.path.join(root_dir, 'gen_build_defs.py'), 148 ] 149 subprocess.run(build_def_cmd, check=True) 150 151 return 0 152 153 154if __name__ == '__main__': 155 sys.exit(main()) 156