| # 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. |
| |
| import argparse |
| import os |
| import shutil |
| import subprocess |
| import sys |
| |
| from dataclasses import dataclass |
| |
| # Imports from //flutter/build/pyutil. |
| sys.path.insert(1, os.path.join(os.path.dirname(__file__), os.pardir)) |
| from pyutil.file_util import symlink |
| |
| # This script creates symlinks to Apple SDKs, Platforms, and host toolchain |
| # under //flutter/prebuilts. |
| PREBUILTS = os.path.realpath(os.path.join( |
| os.path.dirname(__file__), os.pardir, os.pardir, 'flutter', 'prebuilts', |
| )) |
| |
| # Supported SDKs. |
| SDKS = ['iphoneos', 'iphonesimulator', 'macosx'] |
| |
| |
| def parse_arguments(): |
| """Parses command-line arguments.""" |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| '--as-gclient-hook', |
| default=False, |
| action='store_true', |
| help='Whether the script is running as a gclient hook.', |
| ) |
| parser.add_argument( |
| '--print-paths', |
| default=False, |
| action='store_true', |
| help='Print the SDK paths in key=value form.', |
| ) |
| parser.add_argument( |
| '--symlink', |
| type=str, |
| help='Whether to create a symlink in the buildroot to the SDK.', |
| ) |
| parser.add_argument( |
| '--sdk', |
| choices=SDKS, |
| help='Which SDK to find.', |
| ) |
| return parser.parse_args() |
| |
| |
| def get_toolchain_path() -> str: |
| """Returns path for the host toolchain.""" |
| xcode_path = subprocess.check_output(['xcode-select', '-print-path'], timeout=300).decode('utf-8').strip() |
| return os.path.join(xcode_path, 'Toolchains/XcodeDefault.xctoolchain') |
| |
| |
| def get_platform_path(sdk) -> str: |
| """Returns the platform path for the specified SDK.""" |
| return subprocess.check_output(['xcrun', '--sdk', sdk, '--show-sdk-platform-path'], timeout=300).decode('utf-8').strip() |
| |
| |
| def get_sdk_path(sdk) -> str: |
| """Returns the SDK path for the specified SDK.""" |
| return subprocess.check_output(['xcrun', '--sdk', sdk, '--show-sdk-path'], timeout=300).decode('utf-8').strip() |
| |
| |
| @dataclass |
| class TargetSdk: |
| """A target-platform SDK.""" |
| name: str |
| platform_path: str |
| sdk_path: str |
| |
| |
| @dataclass |
| class SdkInfo: |
| """The host toolchain and all requested SDK paths.""" |
| toolchain_path: str |
| sdks: list |
| |
| |
| def get_sdk_info(sdks) -> SdkInfo: |
| """Collects paths for host toolchain and all specified SDKs.""" |
| sdk_info = SdkInfo(get_toolchain_path(), []) |
| for sdk in sdks: |
| platform_path = get_platform_path(sdk) |
| sdk_path = get_sdk_path(sdk) |
| sdk_info.sdks.append(TargetSdk(sdk, platform_path, sdk_path)) |
| return sdk_info |
| |
| |
| def print_paths(sdk_info) -> None: |
| """Prints all SDK paths in key=value from to stdout.""" |
| print('toolchain_path="%s"' % sdk_info.toolchain_path) |
| for sdk in sdk_info.sdks: |
| print('%s_platform_path="%s"' % (sdk.name, sdk.platform_path)) |
| print('%s_sdk_path="%s"' % (sdk.name, sdk.sdk_path)) |
| |
| |
| def create_symlink(target_dir, orig_path) -> str: |
| """Creates a symlink in target_dir that points to orig_path and has the same basename.""" |
| target_path = os.path.join(target_dir, os.path.basename(orig_path)) |
| symlink(orig_path, target_path) |
| return target_path |
| |
| |
| def create_symlinks(sdk_info, symlink_path, delete_existing_links) -> SdkInfo: |
| """ |
| Creates SDK symlinks under symlink_path. |
| Returns an SdkInfo with paths updated to use the symlinks instead of original paths. |
| """ |
| platforms_path = os.path.join(symlink_path, 'Platforms') |
| sdks_path = os.path.join(symlink_path, 'SDKs') |
| |
| if delete_existing_links: |
| # Remove any old files created by this script under the prebuilts dir. |
| if os.path.isdir(platforms_path): |
| shutil.rmtree(platforms_path) |
| if os.path.isdir(sdks_path): |
| shutil.rmtree(sdks_path) |
| |
| # Create toolchain symlink. |
| toolchain_path = create_symlink(sdks_path, sdk_info.toolchain_path) |
| symlink_sdk_info = SdkInfo(toolchain_path, []) |
| for sdk in sdk_info.sdks: |
| platform_path = create_symlink(platforms_path, sdk.platform_path) |
| sdk_path = create_symlink(sdks_path, sdk.sdk_path) |
| symlink_sdk_info.sdks.append(TargetSdk(sdk.name, platform_path, sdk_path)) |
| return symlink_sdk_info |
| |
| |
| def main(argv): |
| args = parse_arguments() |
| |
| # On CI, Xcode is not yet installed when gclient hooks are being run. |
| # This is because the version of Xcode that CI installs might depend on the |
| # contents of the repo, so the repo must be set up first, which includes |
| # running the gclient hooks. Instead, on CI, this script will be run during |
| # GN. |
| running_on_luci = os.environ.get('LUCI_CONTEXT') is not None |
| if running_on_luci and args.as_gclient_hook: |
| return 0 |
| |
| # Gather SDK paths. |
| sdks = [args.sdk] if args.sdk else SDKS |
| sdk_info = get_sdk_info(sdks) |
| |
| # For non-LUCI runs, default symlink_dir to the prebuilts dir. |
| symlink_dir = args.symlink |
| if not running_on_luci and symlink_dir is None: |
| symlink_dir = PREBUILTS |
| |
| # Create symlinks. |
| if symlink_dir: |
| sdk_info = create_symlinks(sdk_info, symlink_dir, args.as_gclient_hook) |
| |
| # Print paths to stdout. |
| if args.print_paths: |
| print_paths(sdk_info) |
| |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| if sys.platform != 'darwin': |
| raise Exception('This script only runs on Mac') |
| sys.exit(main(sys.argv)) |