1#!/usr/bin/env python 2# Copyright 2020 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"""Updates the Fuchsia SDK to the given revision. Should be used in a 'hooks_os' 6entry so that it only runs when .gclient's target_os includes 'fuchsia'.""" 7 8import argparse 9import itertools 10import logging 11import os 12import re 13import shutil 14import subprocess 15import sys 16import tarfile 17 18from common import GetHostOsFromPlatform, GetHostArchFromPlatform, \ 19 DIR_SOURCE_ROOT, IMAGES_ROOT 20from update_sdk import DownloadAndUnpackFromCloudStorage, \ 21 GetOverrideCloudStorageBucket, GetSdkHash, \ 22 MakeCleanDirectory, SDK_SIGNATURE_FILE 23 24 25def GetSdkSignature(sdk_hash, boot_images): 26 return 'gn:{sdk_hash}:{boot_images}:'.format(sdk_hash=sdk_hash, 27 boot_images=boot_images) 28 29 30def GetAllImages(boot_image_names): 31 if not boot_image_names: 32 return 33 34 all_device_types = ['generic', 'qemu'] 35 all_archs = ['x64', 'arm64'] 36 37 images_to_download = set() 38 39 for boot_image in boot_image_names.split(','): 40 components = boot_image.split('.') 41 if len(components) != 2: 42 continue 43 44 device_type, arch = components 45 device_images = all_device_types if device_type == '*' else [device_type] 46 arch_images = all_archs if arch == '*' else [arch] 47 images_to_download.update(itertools.product(device_images, arch_images)) 48 return images_to_download 49 50 51def DownloadSdkBootImages(bucket, sdk_hash, boot_image_names, image_root_dir): 52 images_to_download = GetAllImages(boot_image_names) 53 for image_to_download in images_to_download: 54 device_type = image_to_download[0] 55 arch = image_to_download[1] 56 image_output_dir = os.path.join(image_root_dir, arch, device_type) 57 if os.path.exists(image_output_dir): 58 continue 59 60 logging.info('Downloading Fuchsia boot images for %s.%s...' % 61 (device_type, arch)) 62 63 # Images use different formats. See fxbug.dev/85552. 64 if bucket == 'fuchsia-sdk' or device_type == "workstation": 65 type_arch_connector = '.' 66 else: 67 type_arch_connector = '-' 68 69 images_tarball_url = 'gs://{bucket}/development/{sdk_hash}/images/'\ 70 '{device_type}{type_arch_connector}{arch}.tgz'.format( 71 bucket=bucket, sdk_hash=sdk_hash, device_type=device_type, 72 type_arch_connector=type_arch_connector, arch=arch) 73 DownloadAndUnpackFromCloudStorage(images_tarball_url, image_output_dir) 74 75 76def GetNewSignature(sdk_hash, boot_images): 77 return GetSdkSignature(sdk_hash, boot_images) 78 79 80def main(): 81 parser = argparse.ArgumentParser() 82 parser.add_argument('--verbose', 83 '-v', 84 action='store_true', 85 help='Enable debug-level logging.') 86 parser.add_argument( 87 '--boot-images', 88 type=str, 89 required=True, 90 help='List of boot images to download, represented as a comma separated ' 91 'list. Wildcards are allowed. ') 92 parser.add_argument( 93 '--default-bucket', 94 type=str, 95 default='fuchsia', 96 help='The Google Cloud Storage bucket in which the Fuchsia images are ' 97 'stored. Entry in sdk-bucket.txt will override this flag.') 98 parser.add_argument( 99 '--image-root-dir', 100 default=IMAGES_ROOT, 101 help='Specify the root directory of the downloaded images. Optional') 102 args = parser.parse_args() 103 104 logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) 105 106 # If no boot images need to be downloaded, exit. 107 if not args.boot_images: 108 return 0 109 110 # Check whether there's SDK support for this platform. 111 GetHostOsFromPlatform() 112 113 # Use the bucket in sdk-bucket.txt if an entry exists. 114 # Otherwise use the default bucket. 115 bucket = GetOverrideCloudStorageBucket() or args.default_bucket 116 117 sdk_hash = GetSdkHash(bucket) 118 if not sdk_hash: 119 return 1 120 121 signature_filename = os.path.join(args.image_root_dir, SDK_SIGNATURE_FILE) 122 current_signature = (open(signature_filename, 'r').read().strip() 123 if os.path.exists(signature_filename) else '') 124 new_signature = GetNewSignature(sdk_hash, args.boot_images) 125 if current_signature != new_signature: 126 logging.info('Downloading Fuchsia images %s...' % sdk_hash) 127 MakeCleanDirectory(args.image_root_dir) 128 129 try: 130 DownloadSdkBootImages(bucket, sdk_hash, args.boot_images, 131 args.image_root_dir) 132 with open(signature_filename, 'w') as f: 133 f.write(new_signature) 134 135 except subprocess.CalledProcessError as e: 136 logging.error(("command '%s' failed with status %d.%s"), " ".join(e.cmd), 137 e.returncode, " Details: " + e.output if e.output else "") 138 139 return 0 140 141 142if __name__ == '__main__': 143 sys.exit(main()) 144