Clean up macOS framework creation scripts (flutter/engine#54658)

This refactors `create_fat_macos_framework`, `_regenerate_symlinks` and
`_set_framework_permissions` to be more generic and not hardcode "FlutterMacOS"
as a framework name. Further, it reuses several utility functions from the iOS
code in `sky_utils` to improve readability and eliminate duplication.

This is refactoring prior to embedding dSYMs in FlutterMacOS.xcframework in a
followup patch.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Chris Bracken 2024-08-20 17:46:15 -07:00 committed by GitHub
parent 950352ab04
commit d18ba04282
2 changed files with 41 additions and 22 deletions

View File

@ -47,12 +47,12 @@ def main():
print('Cannot find macOS x64 Framework at %s' % x64_framework) print('Cannot find macOS x64 Framework at %s' % x64_framework)
return 1 return 1
arm64_dylib = os.path.join(arm64_framework, 'FlutterMacOS') arm64_dylib = sky_utils.get_mac_framework_dylib_path(arm64_framework)
if not os.path.isfile(arm64_dylib): if not os.path.isfile(arm64_dylib):
print('Cannot find macOS arm64 dylib at %s' % arm64_dylib) print('Cannot find macOS arm64 dylib at %s' % arm64_dylib)
return 1 return 1
x64_dylib = os.path.join(x64_framework, 'FlutterMacOS') x64_dylib = sky_utils.get_mac_framework_dylib_path(x64_framework)
if not os.path.isfile(x64_dylib): if not os.path.isfile(x64_dylib):
print('Cannot find macOS x64 dylib at %s' % x64_dylib) print('Cannot find macOS x64 dylib at %s' % x64_dylib)
return 1 return 1
@ -71,15 +71,16 @@ def main():
return 0 return 0
def process_framework(dst, args, fat_framework): def process_framework(dst, args, framework_path):
fat_framework_binary = os.path.join(fat_framework, 'Versions', 'A', 'FlutterMacOS') framework_binary = sky_utils.get_mac_framework_dylib_path(framework_path)
if args.dsym: if args.dsym:
dsym_out = os.path.splitext(fat_framework)[0] + '.dSYM' dsym_out = os.path.join(dst, 'FlutterMacOS.dSYM')
sky_utils.extract_dsym(fat_framework_binary, dsym_out) sky_utils.extract_dsym(framework_binary, dsym_out)
if args.zip: if args.zip:
dsym_dst = os.path.join(dst, 'FlutterMacOS.dSYM') dsym_dst = os.path.join(dst, 'FlutterMacOS.dSYM')
sky_utils.create_zip(dsym_dst, 'FlutterMacOS.dSYM.zip', ['.']) sky_utils.create_zip(dsym_dst, 'FlutterMacOS.dSYM.zip', ['.'])
# Double zip to make it consistent with legacy artifacts. # Create a zip of just the contents of the dSYM, then create a zip of that zip.
# TODO(fujino): remove this once https://github.com/flutter/flutter/issues/125067 is resolved # TODO(fujino): remove this once https://github.com/flutter/flutter/issues/125067 is resolved
sky_utils.create_zip(dsym_dst, 'FlutterMacOS.dSYM_.zip', ['FlutterMacOS.dSYM.zip']) sky_utils.create_zip(dsym_dst, 'FlutterMacOS.dSYM_.zip', ['FlutterMacOS.dSYM.zip'])
@ -90,7 +91,7 @@ def process_framework(dst, args, fat_framework):
if args.strip: if args.strip:
unstripped_out = os.path.join(dst, 'FlutterMacOS.unstripped') unstripped_out = os.path.join(dst, 'FlutterMacOS.unstripped')
sky_utils.strip_binary(fat_framework_binary, unstripped_out) sky_utils.strip_binary(framework_binary, unstripped_out)
def zip_framework(dst): def zip_framework(dst):

View File

@ -119,36 +119,43 @@ def copy_tree(source_path, destination_path, symlinks=False):
def create_fat_macos_framework(fat_framework, arm64_framework, x64_framework): def create_fat_macos_framework(fat_framework, arm64_framework, x64_framework):
"""Creates a fat framework from two arm64 and x64 frameworks."""
# Clone the arm64 framework bundle as a starting point.
copy_tree(arm64_framework, fat_framework, symlinks=True) copy_tree(arm64_framework, fat_framework, symlinks=True)
_regenerate_symlinks(fat_framework) _regenerate_symlinks(fat_framework)
lipo([get_mac_framework_dylib_path(arm64_framework),
fat_framework_binary = os.path.join(fat_framework, 'Versions', 'A', 'FlutterMacOS') get_mac_framework_dylib_path(x64_framework)], get_mac_framework_dylib_path(fat_framework))
# Create the arm64/x64 fat framework.
arm64_dylib = os.path.join(arm64_framework, 'FlutterMacOS')
x64_dylib = os.path.join(x64_framework, 'FlutterMacOS')
lipo([arm64_dylib, x64_dylib], fat_framework_binary)
_set_framework_permissions(fat_framework) _set_framework_permissions(fat_framework)
def _regenerate_symlinks(framework_dir): def _regenerate_symlinks(framework_dir):
"""Regenerates the symlinks structure. """Regenerates the framework symlink structure.
Recipes V2 upload artifacts in CAS before integration and CAS follows symlinks. When building on the bots, the framework is produced in one shard, uploaded
This logic regenerates the symlinks in the expected structure. to LUCI's content-addressable storage cache (CAS), then pulled down in
another shard. When that happens, symlinks are dereferenced, resulting a
corrupted framework. This regenerates the expected symlink farm.
""" """
if os.path.islink(os.path.join(framework_dir, 'FlutterMacOS')): # If the dylib is symlinked, assume symlinks are all fine and bail out.
# The shutil.rmtree calls below only work on directories, and fail on symlinks.
framework_name = get_framework_name(framework_dir)
if os.path.islink(os.path.join(framework_dir, framework_name)):
return return
os.remove(os.path.join(framework_dir, 'FlutterMacOS'))
# Delete any existing files/directories.
os.remove(os.path.join(framework_dir, framework_name))
shutil.rmtree(os.path.join(framework_dir, 'Headers'), True) shutil.rmtree(os.path.join(framework_dir, 'Headers'), True)
shutil.rmtree(os.path.join(framework_dir, 'Modules'), True) shutil.rmtree(os.path.join(framework_dir, 'Modules'), True)
shutil.rmtree(os.path.join(framework_dir, 'Resources'), True) shutil.rmtree(os.path.join(framework_dir, 'Resources'), True)
current_version_path = os.path.join(framework_dir, 'Versions', 'Current') current_version_path = os.path.join(framework_dir, 'Versions', 'Current')
shutil.rmtree(current_version_path, True) shutil.rmtree(current_version_path, True)
# Recreate the expected framework symlinks.
os.symlink('A', current_version_path) os.symlink('A', current_version_path)
os.symlink( os.symlink(
os.path.join('Versions', 'Current', 'FlutterMacOS'), os.path.join('Versions', 'Current', framework_name),
os.path.join(framework_dir, 'FlutterMacOS') os.path.join(framework_dir, framework_name)
) )
os.symlink(os.path.join('Versions', 'Current', 'Headers'), os.path.join(framework_dir, 'Headers')) os.symlink(os.path.join('Versions', 'Current', 'Headers'), os.path.join(framework_dir, 'Headers'))
os.symlink(os.path.join('Versions', 'Current', 'Modules'), os.path.join(framework_dir, 'Modules')) os.symlink(os.path.join('Versions', 'Current', 'Modules'), os.path.join(framework_dir, 'Modules'))
@ -158,6 +165,7 @@ def _regenerate_symlinks(framework_dir):
def _set_framework_permissions(framework_dir): def _set_framework_permissions(framework_dir):
"""Sets framework contents to be world readable, and world executable if user-executable."""
# Make the framework readable and executable: u=rwx,go=rx. # Make the framework readable and executable: u=rwx,go=rx.
subprocess.check_call(['chmod', '755', framework_dir]) subprocess.check_call(['chmod', '755', framework_dir])
@ -190,6 +198,16 @@ def _dsymutil_path():
return buildroot_relative_path(dsymutil_path) return buildroot_relative_path(dsymutil_path)
def get_framework_name(framework_dir):
"""Returns Foo given /path/to/Foo.framework."""
return os.path.splitext(os.path.basename(framework_dir))[0]
def get_mac_framework_dylib_path(framework_dir):
"""Returns /path/to/Foo.framework/Versions/A/Foo given /path/to/Foo.framework."""
return os.path.join(framework_dir, 'Versions', 'A', get_framework_name(framework_dir))
def extract_dsym(binary_path, dsym_out_path): def extract_dsym(binary_path, dsym_out_path):
"""Extracts a dSYM bundle from the specified Mach-O binary.""" """Extracts a dSYM bundle from the specified Mach-O binary."""
arch_dir = 'mac-arm64' if platform.processor() == 'arm' else 'mac-x64' arch_dir = 'mac-arm64' if platform.processor() == 'arm' else 'mac-x64'