| # 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)) |