From 7d1bfd1b6e2ab3d6d7c98d90153b0a77298eb746 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Thu, 8 Aug 2024 09:25:17 -0700 Subject: [PATCH 1/2] [iOS] Bundle dSYM packages in Flutter.xcframework (#54414) As of Xcode 16, App Store validation requires dSYMs for frameworks in app archives. Bundling dSYMs also significantly simplifies stack trace symbolification, so we should be doing this regardless. This adds both framework and simulator framework dSYMs to the Flutter.xcframework bundle. Issue: https://github.com/flutter/flutter/issues/116493 [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- sky/tools/create_full_ios_framework.py | 28 +++++++++++++++++--------- sky/tools/create_xcframework.py | 19 ++++++++++++----- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/sky/tools/create_full_ios_framework.py b/sky/tools/create_full_ios_framework.py index 1c3cc0d3bca06..5fad4cb0dfe3e 100644 --- a/sky/tools/create_full_ios_framework.py +++ b/sky/tools/create_full_ios_framework.py @@ -144,11 +144,20 @@ def create_framework( # pylint: disable=too-many-arguments print('Cannot find iOS simulator dylib at %s' % simulator_x64_dylib) return 1 + # Compute dsym output paths, if enabled. + framework_dsym = None + simulator_dsym = None + if args.dsym: + framework_dsym = os.path.splitext(framework)[0] + '.dSYM' + simulator_dsym = os.path.splitext(simulator_framework)[0] + '.dSYM' + + # Emit the framework for physical devices. shutil.rmtree(framework, True) shutil.copytree(arm64_framework, framework) framework_binary = os.path.join(framework, 'Flutter') - process_framework(args, dst, framework, framework_binary) + 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) @@ -160,7 +169,7 @@ def create_framework( # pylint: disable=too-many-arguments 'lipo', simulator_x64_dylib, simulator_arm64_dylib, '-create', '-output', simulator_framework_binary ]) - process_framework(args, dst, simulator_framework, simulator_framework_binary) + process_framework(args, dst, simulator_framework_binary, simulator_dsym) else: simulator_framework = simulator_x64_framework @@ -168,14 +177,15 @@ def create_framework( # pylint: disable=too-many-arguments # simulator frameworks, or just the x64 simulator framework if only that one # exists. xcframeworks = [simulator_framework, framework] - create_xcframework(location=dst, name='Flutter', frameworks=xcframeworks) + 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 + # Add the x64 simulator into the fat framework. subprocess.check_call([ 'lipo', arm64_dylib, simulator_x64_dylib, '-create', '-output', framework_binary ]) - process_framework(args, dst, framework, framework_binary) + process_framework(args, dst, framework_binary, framework_dsym) return 0 @@ -216,16 +226,14 @@ def zip_archive(dst): subprocess.check_call(['zip', '-r', 'extension_safe_Flutter.dSYM.zip', 'Flutter.dSYM'], cwd=dst) -def process_framework(args, dst, framework, framework_binary): - if args.dsym: - dsym_out = os.path.splitext(framework)[0] + '.dSYM' - subprocess.check_call([DSYMUTIL, '-o', dsym_out, framework_binary]) +def process_framework(args, dst, framework_binary, dsym): + if dsym: + subprocess.check_call([DSYMUTIL, '-o', dsym, framework_binary]) 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]) diff --git a/sky/tools/create_xcframework.py b/sky/tools/create_xcframework.py index 337bca458a52b..e2cd7ce18230a 100755 --- a/sky/tools/create_xcframework.py +++ b/sky/tools/create_xcframework.py @@ -22,15 +22,22 @@ def main(): help='The framework paths used to create the XCFramework.', required=True ) + parser.add_argument( + '--dsyms', nargs='+', help='The dSYM paths to be bundled in the XCFramework.', required=False + ) parser.add_argument('--name', help='Name of the XCFramework', type=str, required=True) parser.add_argument('--location', help='Output directory', type=str, required=True) args = parser.parse_args() - create_xcframework(args.location, args.name, args.frameworks) + create_xcframework(args.location, args.name, args.frameworks, args.dsyms) + +def create_xcframework(location, name, frameworks, dsyms=None): + if dsyms and len(frameworks) != len(dsyms): + print('Number of --dsyms must match number of --frameworks exactly.', file=sys.stderr) + sys.exit(1) -def create_xcframework(location, name, frameworks): output_dir = os.path.abspath(location) output_xcframework = os.path.join(output_dir, '%s.xcframework' % name) @@ -45,11 +52,13 @@ def create_xcframework(location, name, frameworks): # -framework bar/baz.framework -output output/ command = ['xcrun', 'xcodebuild', '-quiet', '-create-xcframework'] - for framework in frameworks: - command.extend(['-framework', os.path.abspath(framework)]) - command.extend(['-output', output_xcframework]) + for i in range(len(frameworks)): # pylint: disable=consider-using-enumerate + command.extend(['-framework', os.path.abspath(frameworks[i])]) + if dsyms: + command.extend(['-debug-symbols', os.path.abspath(dsyms[i])]) + subprocess.check_call(command, stdout=open(os.devnull, 'w')) From b6d9c5d0eba34cfadfd56cae9511f06200b63576 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Thu, 8 Aug 2024 17:48:38 -0700 Subject: [PATCH 2/2] [iOS] Rename Flutter.dSYM to Flutter.framework.dSYM (#54458) Renames our Flutter framework dSYM to `Flutter.framework.dSYM` for consistency with all other dSYM bundle names. In iOS release archives, all other dSYM files are: * `App.framework`: `App.framework.dSYM` * `Runner.app`: `Runner.app.dSYM` We continue to archive the dSYM to `Flutter.dSYM.zip` for backward compatibility with the existing instructions for manual symbolification in `docs/Crashes.md` and to remain compatible with dart-lang/dart-ci's symbolizer which expects `Flutter.dSYM` in [`Symbolizer._symbolizeIosFrames`][symbolizer]. Followup to: flutter/engine#54414 Issue: https://github.com/flutter/flutter/issues/116493 Motto: [Embrace the yak shave][yak_shave]. [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style --- sky/tools/create_full_ios_framework.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/sky/tools/create_full_ios_framework.py b/sky/tools/create_full_ios_framework.py index 5fad4cb0dfe3e..218ffc15ce5ff 100644 --- a/sky/tools/create_full_ios_framework.py +++ b/sky/tools/create_full_ios_framework.py @@ -148,8 +148,8 @@ def create_framework( # pylint: disable=too-many-arguments framework_dsym = None simulator_dsym = None if args.dsym: - framework_dsym = os.path.splitext(framework)[0] + '.dSYM' - simulator_dsym = os.path.splitext(simulator_framework)[0] + '.dSYM' + framework_dsym = framework + '.dSYM' + simulator_dsym = simulator_framework + '.dSYM' # Emit the framework for physical devices. shutil.rmtree(framework, True) @@ -219,10 +219,25 @@ def zip_archive(dst): 'extension_safe/Flutter.xcframework', ], cwd=dst) - if os.path.exists(os.path.join(dst, 'Flutter.dSYM')): + + # Generate Flutter.dSYM.zip for manual symbolification. + # + # Historically, the framework dSYM was named Flutter.dSYM, so in order to + # remain backward-compatible with existing instructions in docs/Crashes.md + # and existing tooling such as dart-lang/dart_ci, we rename back to that name + # + # TODO(cbracken): remove these archives and the upload steps once we bundle + # dSYMs in app archives. https://github.com/flutter/flutter/issues/116493 + framework_dsym = os.path.join(dst, 'Flutter.framework.dSYM') + if os.path.exists(framework_dsym): + renamed_dsym = framework_dsym.replace('Flutter.framework.dSYM', 'Flutter.dSYM') + os.rename(framework_dsym, renamed_dsym) subprocess.check_call(['zip', '-r', 'Flutter.dSYM.zip', 'Flutter.dSYM'], cwd=dst) - if os.path.exists(os.path.join(dst, 'extension_safe', 'Flutter.dSYM')): + extension_safe_dsym = os.path.join(dst, 'extension_safe', 'Flutter.framework.dSYM') + if os.path.exists(extension_safe_dsym): + renamed_dsym = extension_safe_dsym.replace('Flutter.framework.dSYM', 'Flutter.dSYM') + os.rename(extension_safe_dsym, renamed_dsym) subprocess.check_call(['zip', '-r', 'extension_safe_Flutter.dSYM.zip', 'Flutter.dSYM'], cwd=dst)