diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index 22f71de36d258..608a44d710755 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -439,6 +439,7 @@ ../../../flutter/sky/tools/create_xcframework.py ../../../flutter/sky/tools/dist_dart_pkg.py ../../../flutter/sky/tools/install_framework_headers.py +../../../flutter/sky/tools/sky_utils.py ../../../flutter/testing ../../../flutter/third_party/.clang-tidy ../../../flutter/third_party/.gitignore diff --git a/sky/tools/create_ios_framework.py b/sky/tools/create_ios_framework.py index 7f43b4a7fe464..4a40e15a71367 100644 --- a/sky/tools/create_ios_framework.py +++ b/sky/tools/create_ios_framework.py @@ -9,19 +9,10 @@ import argparse import os -import platform -import shutil -import subprocess import sys from create_xcframework import create_xcframework # pylint: disable=import-error - -ARCH_SUBPATH = 'mac-arm64' if platform.processor() == 'arm' else 'mac-x64' -DSYMUTIL = os.path.join( - os.path.dirname(__file__), '..', '..', 'buildtools', ARCH_SUBPATH, 'clang', 'bin', 'dsymutil' -) - -buildroot_dir = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..', '..', '..')) +import sky_utils # pylint: disable=import-error def main(): @@ -42,25 +33,25 @@ def main(): args = parser.parse_args() - dst = (args.dst if os.path.isabs(args.dst) else os.path.join(buildroot_dir, args.dst)) + dst = (args.dst if os.path.isabs(args.dst) else sky_utils.buildroot_relative_path(args.dst)) arm64_out_dir = ( - args.arm64_out_dir - if os.path.isabs(args.arm64_out_dir) else os.path.join(buildroot_dir, args.arm64_out_dir) + args.arm64_out_dir if os.path.isabs(args.arm64_out_dir) else + sky_utils.buildroot_relative_path(args.arm64_out_dir) ) x64_out_dir = None if args.x64_out_dir: x64_out_dir = ( args.x64_out_dir - if os.path.isabs(args.x64_out_dir) else os.path.join(buildroot_dir, args.x64_out_dir) + if os.path.isabs(args.x64_out_dir) else sky_utils.buildroot_relative_path(args.x64_out_dir) ) simulator_x64_out_dir = None if args.simulator_x64_out_dir: simulator_x64_out_dir = ( args.simulator_x64_out_dir if os.path.isabs(args.simulator_x64_out_dir) else - os.path.join(buildroot_dir, args.simulator_x64_out_dir) + sky_utils.buildroot_relative_path(args.simulator_x64_out_dir) ) framework = os.path.join(dst, 'Flutter.framework') @@ -72,24 +63,15 @@ def main(): if args.simulator_arm64_out_dir: simulator_arm64_out_dir = ( args.simulator_arm64_out_dir if os.path.isabs(args.simulator_arm64_out_dir) else - os.path.join(buildroot_dir, args.simulator_arm64_out_dir) + sky_utils.buildroot_relative_path(args.simulator_arm64_out_dir) ) if args.simulator_arm64_out_dir is not None: simulator_arm64_framework = os.path.join(simulator_arm64_out_dir, 'Flutter.framework') - if not os.path.isdir(arm64_framework): - print('Cannot find iOS arm64 Framework at %s' % arm64_framework) - return 1 - - if not os.path.isdir(simulator_x64_framework): - print('Cannot find iOS x64 simulator Framework at %s' % simulator_framework) - return 1 - - if not os.path.isfile(DSYMUTIL): - print('Cannot find dsymutil at %s' % DSYMUTIL) - return 1 - + sky_utils.assert_directory(arm64_framework, 'iOS arm64 framework') + sky_utils.assert_directory(simulator_arm64_framework, 'iOS arm64 simulator framework') + sky_utils.assert_directory(simulator_x64_framework, 'iOS x64 simulator framework') create_framework( args, dst, framework, arm64_framework, simulator_framework, simulator_x64_framework, simulator_arm64_framework @@ -101,10 +83,18 @@ def main(): '%s_extension_safe' % simulator_x64_out_dir, '%s_extension_safe' % simulator_arm64_out_dir ) - generate_gen_snapshot(dst, x64_out_dir, arm64_out_dir) + # Copy gen_snapshot binary to destination directory. + if arm64_out_dir: + gen_snapshot = os.path.join(arm64_out_dir, 'gen_snapshot_arm64') + sky_utils.copy_binary(gen_snapshot, os.path.join(dst, 'gen_snapshot_arm64')) + if x64_out_dir: + gen_snapshot = os.path.join(x64_out_dir, 'gen_snapshot_x64') + sky_utils.copy_binary(gen_snapshot, os.path.join(dst, 'gen_snapshot_x64')) + zip_archive(dst) return 0 + def create_extension_safe_framework( # pylint: disable=too-many-arguments args, dst, arm64_out_dir, simulator_x64_out_dir, simulator_arm64_out_dir ): @@ -128,6 +118,7 @@ def create_extension_safe_framework( # pylint: disable=too-many-arguments ) return 0 + def create_framework( # pylint: disable=too-many-arguments args, dst, framework, arm64_framework, simulator_framework, simulator_x64_framework, simulator_arm64_framework @@ -135,13 +126,9 @@ def create_framework( # pylint: disable=too-many-arguments arm64_dylib = os.path.join(arm64_framework, 'Flutter') simulator_x64_dylib = os.path.join(simulator_x64_framework, 'Flutter') simulator_arm64_dylib = os.path.join(simulator_arm64_framework, 'Flutter') - if not os.path.isfile(arm64_dylib): - print('Cannot find iOS arm64 dylib at %s' % arm64_dylib) - return 1 - - if not os.path.isfile(simulator_x64_dylib): - print('Cannot find iOS simulator dylib at %s' % simulator_x64_dylib) - return 1 + sky_utils.assert_file(arm64_dylib, 'iOS arm64 dylib') + sky_utils.assert_file(simulator_arm64_dylib, 'iOS simulator arm64 dylib') + sky_utils.assert_file(simulator_x64_dylib, 'iOS simulator x64 dylib') # Compute dsym output paths, if enabled. framework_dsym = None @@ -151,23 +138,15 @@ def create_framework( # pylint: disable=too-many-arguments simulator_dsym = simulator_framework + '.dSYM' # Emit the framework for physical devices. - shutil.rmtree(framework, True) - shutil.copytree(arm64_framework, framework) + sky_utils.copy_tree(arm64_framework, framework) framework_binary = os.path.join(framework, 'Flutter') process_framework(args, dst, framework_binary, framework_dsym) # Emit the framework for simulators. if args.simulator_arm64_out_dir is not None: - shutil.rmtree(simulator_framework, True) - shutil.copytree(simulator_arm64_framework, simulator_framework) - + sky_utils.copy_tree(simulator_arm64_framework, simulator_framework) simulator_framework_binary = os.path.join(simulator_framework, 'Flutter') - - # Create the arm64/x64 simulator fat framework. - subprocess.check_call([ - 'lipo', simulator_x64_dylib, simulator_arm64_dylib, '-create', '-output', - simulator_framework_binary - ]) + sky_utils.lipo([simulator_x64_dylib, simulator_arm64_dylib], simulator_framework_binary) process_framework(args, dst, simulator_framework_binary, simulator_dsym) else: simulator_framework = simulator_x64_framework @@ -179,74 +158,41 @@ def create_framework( # pylint: disable=too-many-arguments dsyms = [simulator_dsym, framework_dsym] if args.dsym else None create_xcframework(location=dst, name='Flutter', frameworks=xcframeworks, dsyms=dsyms) - # Add the x64 simulator into the fat framework. - subprocess.check_call([ - 'lipo', arm64_dylib, simulator_x64_dylib, '-create', '-output', framework_binary - ]) - + sky_utils.lipo([arm64_dylib, simulator_x64_dylib], framework_binary) process_framework(args, dst, framework_binary, framework_dsym) return 0 -def embed_codesign_configuration(config_path, contents): - with open(config_path, 'w') as file: - file.write('\n'.join(contents) + '\n') - - def zip_archive(dst): - ios_file_with_entitlements = ['gen_snapshot_arm64'] - ios_file_without_entitlements = [ - 'Flutter.xcframework/ios-arm64/Flutter.framework/Flutter', - 'Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter', - 'extension_safe/Flutter.xcframework/ios-arm64/Flutter.framework/Flutter', - 'extension_safe/Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter' - ] - embed_codesign_configuration(os.path.join(dst, 'entitlements.txt'), ios_file_with_entitlements) - - embed_codesign_configuration( - os.path.join(dst, 'without_entitlements.txt'), ios_file_without_entitlements + sky_utils.write_codesign_config(os.path.join(dst, 'entitlements.txt'), ['gen_snapshot_arm64']) + + sky_utils.write_codesign_config( + os.path.join(dst, 'without_entitlements.txt'), [ + 'Flutter.xcframework/ios-arm64/Flutter.framework/Flutter', + 'Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter', + 'extension_safe/Flutter.xcframework/ios-arm64/Flutter.framework/Flutter', + 'extension_safe/Flutter.xcframework/ios-arm64_x86_64-simulator/Flutter.framework/Flutter' + ] ) - subprocess.check_call([ - 'zip', - '-r', - 'artifacts.zip', - 'gen_snapshot_arm64', - 'Flutter.xcframework', - 'entitlements.txt', - 'without_entitlements.txt', - 'extension_safe/Flutter.xcframework', - ], - cwd=dst) + sky_utils.create_zip( + dst, 'artifacts.zip', [ + 'gen_snapshot_arm64', + 'Flutter.xcframework', + 'entitlements.txt', + 'without_entitlements.txt', + 'extension_safe/Flutter.xcframework', + ] + ) def process_framework(args, dst, framework_binary, dsym): if dsym: - subprocess.check_call([DSYMUTIL, '-o', dsym, framework_binary]) + sky_utils.extract_dsym(framework_binary, dsym) if args.strip: - # copy unstripped unstripped_out = os.path.join(dst, 'Flutter.unstripped') - shutil.copyfile(framework_binary, unstripped_out) - subprocess.check_call(['strip', '-x', '-S', framework_binary]) - - -def generate_gen_snapshot(dst, x64_out_dir, arm64_out_dir): - if x64_out_dir: - x64_path = os.path.join(x64_out_dir, 'gen_snapshot_x64') - _generate_gen_snapshot(x64_path, os.path.join(dst, 'gen_snapshot_x64')) - - if arm64_out_dir: - arm64_path = os.path.join(arm64_out_dir, 'gen_snapshot_arm64') - _generate_gen_snapshot(arm64_path, os.path.join(dst, 'gen_snapshot_arm64')) - - -def _generate_gen_snapshot(gen_snapshot_path, destination): - if not os.path.isfile(gen_snapshot_path): - print('Cannot find gen_snapshot at %s' % gen_snapshot_path) - sys.exit(1) - - shutil.copy2(gen_snapshot_path, destination) + sky_utils.strip_binary(framework_binary, unstripped_copy_path=unstripped_out) if __name__ == '__main__': diff --git a/sky/tools/sky_utils.py b/sky/tools/sky_utils.py new file mode 100644 index 0000000000000..018890236c0ff --- /dev/null +++ b/sky/tools/sky_utils.py @@ -0,0 +1,89 @@ +#!/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. + +import os +import platform +import shutil +import subprocess +import sys + + +def assert_directory(path, what): + """Logs an error and exits with EX_NOINPUT if the specified directory doesn't exist.""" + if not os.path.isdir(path): + log_error('Cannot find %s at %s' % (what, path)) + sys.exit(os.EX_NOINPUT) + + +def assert_file(path, what): + """Logs an error and exits with EX_NOINPUT if the specified file doesn't exist.""" + if not os.path.isfile(path): + log_error('Cannot find %s at %s' % (what, path)) + sys.exit(os.EX_NOINPUT) + + +def buildroot_relative_path(path): + """Returns the absolute path to the specified buildroot-relative path.""" + buildroot_dir = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..', '..', '..')) + return os.path.join(buildroot_dir, path) + + +def copy_binary(source_path, destination_path): + """Copies a binary, preserving POSIX permissions.""" + assert_file(source_path, 'file to copy') + shutil.copy2(source_path, destination_path) + + +def copy_tree(source_path, destination_path): + """Performs a recursive copy of a directory. + If the destination path is present, it is deleted first.""" + assert_directory(source_path, 'directory to copy') + shutil.rmtree(destination_path, True) + shutil.copytree(source_path, destination_path) + + +def create_zip(cwd, zip_filename, paths): + """Creates a zip archive in cwd, containing a set of cwd-relative files.""" + subprocess.check_call(['zip', '-r', zip_filename] + paths, cwd=cwd) + + +def _dsymutil_path(): + """Returns the path to dsymutil within Flutter's clang toolchain.""" + arch_subpath = 'mac-arm64' if platform.processor() == 'arm' else 'mac-x64' + dsymutil_path = os.path.join('flutter', 'buildtools', arch_subpath, 'clang', 'bin', 'dsymutil') + return buildroot_relative_path(dsymutil_path) + + +def extract_dsym(binary_path, dsym_out_path): + """Extracts a dSYM bundle from the specified Mach-O binary.""" + arch_dir = 'mac-arm64' if platform.processor() == 'arm' else 'mac-x64' + dsymutil = buildroot_relative_path( + os.path.join('flutter', 'buildtools', arch_dir, 'clang', 'bin', 'dsymutil') + ) + subprocess.check_call([dsymutil, '-o', dsym_out_path, binary_path]) + + +def lipo(input_binaries, output_binary): + """Uses lipo to create a fat binary from a set of input binaries.""" + subprocess.check_call(['lipo'] + input_binaries + ['-create', '-output', output_binary]) + + +def log_error(message): + """Writes the message to stderr, followed by a newline.""" + print(message, file=sys.stderr) + + +def strip_binary(binary_path, unstripped_copy_path): + """Makes a copy of an unstripped binary, then strips symbols from the binary.""" + assert_file(binary_path, 'binary to strip') + shutil.copyfile(binary_path, unstripped_copy_path) + subprocess.check_call(['strip', '-x', '-S', binary_path]) + + +def write_codesign_config(output_path, paths): + """Writes an Apple codesign configuration file containing the specified paths.""" + with open(output_path, mode='w', encoding='utf-8') as file: + file.write('\n'.join(paths) + '\n')