Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
148 changes: 47 additions & 101 deletions sky/tools/create_ios_framework.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand All @@ -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')
Expand All @@ -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
Expand All @@ -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
):
Expand All @@ -128,20 +118,17 @@ 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
):
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
Expand All @@ -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
Expand All @@ -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__':
Expand Down
89 changes: 89 additions & 0 deletions sky/tools/sky_utils.py
Original file line number Diff line number Diff line change
@@ -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')