Skip to content

Commit

Permalink
Build arm64 FlutterEmbedder.framework and lipo the result (flutter#42233
Browse files Browse the repository at this point in the history
)

This PR modifies the mac_host_engine build config to build the
FlutterEmbedder.framework for arm64, and adds a generator that creates a
fat framework containing both the intel and arm64 dylibs. The fat
framework is uploaded to the gcs bucket instead of the thin intel-only
one.

The script to create the lipo'd embedder framework is different enough
from the desktop framework to have a separate script, but this still
triggers the pylint duplicate-code check. Due to a bug in our version of
pylint (pylint-dev/pylint#214), it isn't
possible to disable per-file, so this PR also disables the check
globally.
  • Loading branch information
zanderso authored May 23, 2023
1 parent e9d1310 commit 268c0e1
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 1 deletion.
1 change: 1 addition & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ docstring-quote = double
# multiple time (only on the command line, not in the configuration file where
# it should appear only once).
disable=
duplicate-code,
exec-used,
fixme,
missing-class-docstring,
Expand Down
20 changes: 19 additions & 1 deletion ci/builders/mac_host_engine.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
"type": "gcs",
"include_paths": [
"out/host_debug/zip_archives/darwin-x64/artifacts.zip",
"out/host_debug/zip_archives/darwin-x64/FlutterEmbedder.framework.zip",
"out/host_debug/zip_archives/dart-sdk-darwin-x64.zip"
],
"name": "host_debug",
Expand Down Expand Up @@ -214,6 +213,7 @@
"flutter/build/archives:archive_gen_snapshot",
"flutter/build/archives:artifacts",
"flutter/build/archives:dart_sdk_archive",
"flutter/build/archives:flutter_embedder_framework",
"flutter/shell/platform/darwin/macos:zip_macos_flutter_framework"
]
}
Expand Down Expand Up @@ -299,6 +299,19 @@
],
"generators": {
"tasks": [
{
"name": "Debug-FlutterEmbedder.framework",
"parameters": [
"--dst",
"out/debug/framework",
"--arm64-out-dir",
"out/mac_debug_arm64",
"--x64-out-dir",
"out/host_debug",
"--zip"
],
"script": "flutter/sky/tools/create_embedder_framework.py"
},
{
"name": "Release-FlutterMacOS.framework",
"parameters": [
Expand Down Expand Up @@ -390,6 +403,11 @@
]
},
"archives": [
{
"source": "out/debug/framework/FlutterEmbedder.framework.zip",
"destination": "darwin-x64/FlutterEmbedder.framework.zip",
"realm": "production"
},
{
"source": "out/release/framework/FlutterMacOS.dSYM.zip",
"destination": "darwin-x64-release/FlutterMacOS.dSYM.zip",
Expand Down
1 change: 1 addition & 0 deletions ci/licenses_golden/excluded_files
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@
../../../flutter/sky/packages/sky_engine/README.md
../../../flutter/sky/packages/sky_engine/lib/_embedder.yaml
../../../flutter/sky/packages/sky_engine/pubspec.yaml
../../../flutter/sky/tools/create_embedder_framework.py
../../../flutter/sky/tools/create_full_ios_framework.py
../../../flutter/sky/tools/create_ios_framework.py
../../../flutter/sky/tools/create_macos_framework.py
Expand Down
169 changes: 169 additions & 0 deletions sky/tools/create_embedder_framework.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
#!/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 argparse
import subprocess
import shutil
import sys
import os

buildroot_dir = os.path.abspath(
os.path.join(os.path.realpath(__file__), '..', '..', '..', '..')
)

DSYMUTIL = os.path.join(
os.path.dirname(__file__), '..', '..', '..', 'buildtools', 'mac-x64',
'clang', 'bin', 'dsymutil'
)

out_dir = os.path.join(buildroot_dir, 'out')


def main():
parser = argparse.ArgumentParser(
description='Creates FlutterEmbedder.framework for macOS'
)

parser.add_argument('--dst', type=str, required=True)
parser.add_argument('--arm64-out-dir', type=str, required=True)
parser.add_argument('--x64-out-dir', type=str, required=True)
parser.add_argument('--strip', action='store_true', default=False)
parser.add_argument('--dsym', action='store_true', default=False)
# TODO(godofredoc): Remove after recipes v2 have landed.
parser.add_argument('--zip', action='store_true', default=False)

args = parser.parse_args()

dst = (
args.dst
if os.path.isabs(args.dst) else os.path.join(buildroot_dir, 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)
)
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)
)

fat_framework = os.path.join(dst, 'FlutterEmbedder.framework')
arm64_framework = os.path.join(arm64_out_dir, 'FlutterEmbedder.framework')
x64_framework = os.path.join(x64_out_dir, 'FlutterEmbedder.framework')

arm64_dylib = os.path.join(arm64_framework, 'FlutterEmbedder')
x64_dylib = os.path.join(x64_framework, 'FlutterEmbedder')

if not os.path.isdir(arm64_framework):
print('Cannot find macOS arm64 Framework at %s' % arm64_framework)
return 1

if not os.path.isdir(x64_framework):
print('Cannot find macOS x64 Framework at %s' % x64_framework)
return 1

if not os.path.isfile(arm64_dylib):
print('Cannot find macOS arm64 dylib at %s' % arm64_dylib)
return 1

if not os.path.isfile(x64_dylib):
print('Cannot find macOS x64 dylib at %s' % x64_dylib)
return 1

if not os.path.isfile(DSYMUTIL):
print('Cannot find dsymutil at %s' % DSYMUTIL)
return 1

shutil.rmtree(fat_framework, True)
shutil.copytree(arm64_framework, fat_framework, symlinks=True)
regenerate_symlinks(fat_framework)

fat_framework_binary = os.path.join(
fat_framework, 'Versions', 'A', 'FlutterEmbedder'
)

# Create the arm64/x64 fat framework.
subprocess.check_call([
'lipo', arm64_dylib, x64_dylib, '-create', '-output', fat_framework_binary
])
process_framework(dst, args, fat_framework, fat_framework_binary)

return 0


def regenerate_symlinks(fat_framework):
"""Regenerates the symlinks structure.
Recipes V2 upload artifacts in CAS before integration and CAS follows symlinks.
This logic regenerates the symlinks in the expected structure.
"""
if os.path.islink(os.path.join(fat_framework, 'FlutterEmbedder')):
return
os.remove(os.path.join(fat_framework, 'FlutterEmbedder'))
shutil.rmtree(os.path.join(fat_framework, 'Headers'), True)
shutil.rmtree(os.path.join(fat_framework, 'Modules'), True)
shutil.rmtree(os.path.join(fat_framework, 'Resources'), True)
current_version_path = os.path.join(fat_framework, 'Versions', 'Current')
shutil.rmtree(current_version_path, True)
os.symlink('A', current_version_path)
os.symlink(
os.path.join('Versions', 'Current', 'FlutterEmbedder'),
os.path.join(fat_framework, 'FlutterEmbedder')
)
os.symlink(
os.path.join('Versions', 'Current', 'Headers'),
os.path.join(fat_framework, 'Headers')
)
os.symlink(
os.path.join('Versions', 'Current', 'Modules'),
os.path.join(fat_framework, 'Modules')
)
os.symlink(
os.path.join('Versions', 'Current', 'Resources'),
os.path.join(fat_framework, 'Resources')
)


def process_framework(dst, args, fat_framework, fat_framework_binary):
if args.dsym:
dsym_out = os.path.splitext(fat_framework)[0] + '.dSYM'
subprocess.check_call([DSYMUTIL, '-o', dsym_out, fat_framework_binary])
if args.zip:
dsym_dst = os.path.join(dst, 'FlutterEmbedder.dSYM')
subprocess.check_call([
'zip', '-r', '-y', 'FlutterEmbedder.dSYM.zip', '.'
],
cwd=dsym_dst)
dsym_final_src_path = os.path.join(dsym_dst, 'FlutterEmbedder.dSYM.zip')
dsym_final_dst_path = os.path.join(dst, 'FlutterEmbedder.dSYM.zip')
shutil.move(dsym_final_src_path, dsym_final_dst_path)

if args.strip:
# copy unstripped
unstripped_out = os.path.join(dst, 'FlutterEmbedder.unstripped')
shutil.copyfile(fat_framework_binary, unstripped_out)
subprocess.check_call(['strip', '-x', '-S', fat_framework_binary])

# Zip FlutterEmbedder.framework.
if args.zip:
framework_dst = os.path.join(dst, 'FlutterEmbedder.framework')
subprocess.check_call([
'zip',
'-r',
'-y',
'FlutterEmbedder.framework.zip',
'.',
],
cwd=framework_dst)
final_src_path = os.path.join(
framework_dst, 'FlutterEmbedder.framework.zip'
)
final_dst_path = os.path.join(dst, 'FlutterEmbedder.framework.zip')
shutil.move(final_src_path, final_dst_path)


if __name__ == '__main__':
sys.exit(main())

0 comments on commit 268c0e1

Please sign in to comment.