#!/usr/bin/env python # Copyright 2020 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. """Updates the Fuchsia SDK to the given revision. Should be used in a 'hooks_os' entry so that it only runs when .gclient's target_os includes 'fuchsia'.""" import argparse import itertools import logging import os import re import shutil import subprocess import sys import tarfile from common import GetHostOsFromPlatform, GetHostArchFromPlatform, \ DIR_SOURCE_ROOT, IMAGES_ROOT from update_sdk import DownloadAndUnpackFromCloudStorage, \ GetOverrideCloudStorageBucket, GetSdkHash, \ MakeCleanDirectory, SDK_SIGNATURE_FILE def GetSdkSignature(sdk_hash, boot_images): return 'gn:{sdk_hash}:{boot_images}:'.format(sdk_hash=sdk_hash, boot_images=boot_images) def GetAllImages(boot_image_names): if not boot_image_names: return all_device_types = ['generic', 'qemu'] all_archs = ['x64', 'arm64'] images_to_download = set() for boot_image in boot_image_names.split(','): components = boot_image.split('.') if len(components) != 2: continue device_type, arch = components device_images = all_device_types if device_type == '*' else [device_type] arch_images = all_archs if arch == '*' else [arch] images_to_download.update(itertools.product(device_images, arch_images)) return images_to_download def DownloadSdkBootImages(bucket, sdk_hash, boot_image_names, image_root_dir): images_to_download = GetAllImages(boot_image_names) for image_to_download in images_to_download: device_type = image_to_download[0] arch = image_to_download[1] image_output_dir = os.path.join(image_root_dir, arch, device_type) if os.path.exists(image_output_dir): continue logging.info('Downloading Fuchsia boot images for %s.%s...' % (device_type, arch)) if bucket == 'fuchsia-sdk': images_tarball_url = 'gs://{bucket}/development/{sdk_hash}/images/'\ '{device_type}.{arch}.tgz'.format( bucket=bucket, sdk_hash=sdk_hash, device_type=device_type, arch=arch) else: images_tarball_url = 'gs://{bucket}/development/{sdk_hash}/images/'\ '{device_type}-{arch}.tgz'.format( bucket=bucket, sdk_hash=sdk_hash, device_type=device_type, arch=arch) DownloadAndUnpackFromCloudStorage(images_tarball_url, image_output_dir) def GetNewSignature(sdk_hash, boot_images): return GetSdkSignature(sdk_hash, boot_images) def main(): parser = argparse.ArgumentParser() parser.add_argument('--verbose', '-v', action='store_true', help='Enable debug-level logging.') parser.add_argument( '--boot-images', type=str, required=True, help='List of boot images to download, represented as a comma separated ' 'list. Wildcards are allowed. ') parser.add_argument( '--default-bucket', type=str, default='fuchsia', help='The Google Cloud Storage bucket in which the Fuchsia images are ' 'stored. Entry in sdk-bucket.txt will override this flag.') parser.add_argument( '--image-root-dir', default=IMAGES_ROOT, help='Specify the root directory of the downloaded images. Optional') args = parser.parse_args() logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) # If no boot images need to be downloaded, exit. if not args.boot_images: return 0 # Check whether there's SDK support for this platform. GetHostOsFromPlatform() # Use the bucket in sdk-bucket.txt if an entry exists. # Otherwise use the default bucket. bucket = GetOverrideCloudStorageBucket() or args.default_bucket sdk_hash = GetSdkHash(bucket) if not sdk_hash: return 1 signature_filename = os.path.join(args.image_root_dir, SDK_SIGNATURE_FILE) current_signature = (open(signature_filename, 'r').read().strip() if os.path.exists(signature_filename) else '') new_signature = GetNewSignature(sdk_hash, args.boot_images) if current_signature != new_signature: logging.info('Downloading Fuchsia images %s...' % sdk_hash) MakeCleanDirectory(args.image_root_dir) try: DownloadSdkBootImages(bucket, sdk_hash, args.boot_images, args.image_root_dir) with open(signature_filename, 'w') as f: f.write(new_signature) except subprocess.CalledProcessError as e: logging.error(("command '%s' failed with status %d.%s"), " ".join(e.cmd), e.returncode, " Details: " + e.output if e.output else "") return 0 if __name__ == '__main__': sys.exit(main())