#!/usr/bin/env python3 # Copyright 2013 The Flutter Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # The return code of this script will always be 0, even if there is an error, # unless the --fail-loudly flag is passed. import argparse import tarfile import json import os import shutil import subprocess import sys SRC_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) FUCHSIA_SDK_DIR = os.path.join(SRC_ROOT, 'fuchsia', 'sdk') FLUTTER_DIR = os.path.join(SRC_ROOT, 'flutter') # Prints to stderr. def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) def FileNameForSdkPath(sdk_path): return sdk_path.split('/')[-1] def DownloadFuchsiaSDKFromGCS(sdk_path, verbose): file = FileNameForSdkPath(sdk_path) url = 'https://storage.googleapis.com/fuchsia-artifacts/{}'.format(sdk_path) dest = os.path.join(FUCHSIA_SDK_DIR, file) if verbose: print('Fuchsia SDK url: "%s"' % url) print('Fuchsia SDK destination path: "%s"' % dest) if os.path.isfile(dest): os.unlink(dest) # Ensure destination folder exists. os.makedirs(FUCHSIA_SDK_DIR, exist_ok=True) curl_command = [ 'curl', '--retry', '3', '--continue-at', '-', '--location', '--output', dest, url, ] if verbose: print('Running: "%s"' % (' '.join(curl_command))) curl_result = subprocess.run( curl_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, ) if curl_result.returncode == 0 and verbose: print('curl output:stdout:\n{}\nstderr:\n{}'.format( curl_result.stdout, curl_result.stderr, )) elif curl_result.returncode != 0: eprint( 'Failed to download: stdout:\n{}\nstderr:\n{}'.format( curl_result.stdout, curl_result.stderr, ) ) return None return dest def OnErrorRmTree(func, path, exc_info): """ Error handler for ``shutil.rmtree``. If the error is due to an access error (read only file) it attempts to add write permission and then retries. If the error is for another reason it re-raises the error. Usage : ``shutil.rmtree(path, onerror=onerror)`` """ import stat # Is the error an access error? if not os.access(path, os.W_OK): os.chmod(path, stat.S_IWUSR) func(path) else: raise def ExtractGzipArchive(archive, host_os, verbose): sdk_dest = os.path.join(FUCHSIA_SDK_DIR, host_os) if os.path.isdir(sdk_dest): shutil.rmtree(sdk_dest, onerror=OnErrorRmTree) extract_dest = os.path.join(FUCHSIA_SDK_DIR, 'temp') if os.path.isdir(extract_dest): shutil.rmtree(extract_dest, onerror=OnErrorRmTree) os.makedirs(extract_dest, exist_ok=True) if verbose: print('Extracting "%s" to "%s"' % (archive, extract_dest)) with tarfile.open(archive, 'r') as z: z.extractall(extract_dest) shutil.move(extract_dest, sdk_dest) def Main(): parser = argparse.ArgumentParser() parser.add_argument( '--fail-loudly', action='store_true', default=False, help="Return an error code if a prebuilt couldn't be fetched and extracted" ) parser.add_argument( '--verbose', action='store_true', default='LUCI_CONTEXT' in os.environ, help='Emit verbose output' ) parser.add_argument('--host-os', help='The host os') parser.add_argument('--fuchsia-sdk-path', help='The path in gcs to the fuchsia sdk to download') args = parser.parse_args() fail_loudly = 1 if args.fail_loudly else 0 verbose = args.verbose host_os = args.host_os fuchsia_sdk_path = args.fuchsia_sdk_path if fuchsia_sdk_path is None: eprint('sdk_path can not be empty') return fail_loudly archive = DownloadFuchsiaSDKFromGCS(fuchsia_sdk_path, verbose) if archive is None: eprint('Failed to download SDK from %s' % fuchsia_sdk_path) return fail_loudly ExtractGzipArchive(archive, host_os, verbose) success = True return 0 if success else fail_loudly if __name__ == '__main__': sys.exit(Main())