Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 9262f08

Browse files
[CP-stable][iOS] Bundle dSYM packages in Flutter.xcframework (#54513)
This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request) Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request. ### Issue Link: What is the link to the issue this cherry-pick is addressing? flutter/flutter#116493 Note this includes two PRs: #54414 #54458 ### Changelog Description: Bundle iOS framework debugging symbols (`Flutter.framework.dSYM`) in `Flutter.xcframework` in iOS release builds. This eliminates the need for manual download/bundling of Flutter debug symbols for release build crash log symbolication. As of Xcode 16, these symbols are required to be bundled with App Store archives. ### Impact Description: Without these bundled symbols, iOS App Store validation fails for Flutter apps under Xcode 16. ### Workaround: Is there a workaround for this issue? Users can follow the steps described in [docs/Crashes.md](https://github.com/flutter/engine/blob/main/docs/Crashes.md) to manually locate the engine SHA used in their Flutter app, download the Flutter.dSYM archive, and manually copy it into their app archive prior to App Store validation. This is very manual process with room for a lot of error. This process is not at all obvious or well-documented. ### Risk: What is the risk level of this cherry-pick? In the case where dSYM extraction fails, we'll know immediately since the build will fail. dSYMs can be verified using the steps below. ### Test Coverage: Are you confident that your fix is well-tested by automated tests? In this case the build itself will fail if dSYM extraction fails. Testing of bundling is covered in framework tool tests. ### Validation Steps: What are the steps to validate that this fix works? `artifacts.zip` can be checked at any commit on or after `c11fe483947c95553610ab59210af643f031f5f4`. For example https://storage.googleapis.com/flutter_infra_release/flutter/c11fe483947c95553610ab59210af643f031f5f4/ios-release/artifacts.zip Verify that Flutter.xcframework in this archive contains `ios-arm64/dSYMs/Flutter.framework.dSYM`.
1 parent 3ea260f commit 9262f08

File tree

2 files changed

+49
-17
lines changed

2 files changed

+49
-17
lines changed

sky/tools/create_full_ios_framework.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,20 @@ def create_framework( # pylint: disable=too-many-arguments
144144
print('Cannot find iOS simulator dylib at %s' % simulator_x64_dylib)
145145
return 1
146146

147+
# Compute dsym output paths, if enabled.
148+
framework_dsym = None
149+
simulator_dsym = None
150+
if args.dsym:
151+
framework_dsym = framework + '.dSYM'
152+
simulator_dsym = simulator_framework + '.dSYM'
153+
154+
# Emit the framework for physical devices.
147155
shutil.rmtree(framework, True)
148156
shutil.copytree(arm64_framework, framework)
149157
framework_binary = os.path.join(framework, 'Flutter')
150-
process_framework(args, dst, framework, framework_binary)
158+
process_framework(args, dst, framework_binary, framework_dsym)
151159

160+
# Emit the framework for simulators.
152161
if args.simulator_arm64_out_dir is not None:
153162
shutil.rmtree(simulator_framework, True)
154163
shutil.copytree(simulator_arm64_framework, simulator_framework)
@@ -160,22 +169,23 @@ def create_framework( # pylint: disable=too-many-arguments
160169
'lipo', simulator_x64_dylib, simulator_arm64_dylib, '-create', '-output',
161170
simulator_framework_binary
162171
])
163-
process_framework(args, dst, simulator_framework, simulator_framework_binary)
172+
process_framework(args, dst, simulator_framework_binary, simulator_dsym)
164173
else:
165174
simulator_framework = simulator_x64_framework
166175

167176
# Create XCFramework from the arm-only fat framework and the arm64/x64
168177
# simulator frameworks, or just the x64 simulator framework if only that one
169178
# exists.
170179
xcframeworks = [simulator_framework, framework]
171-
create_xcframework(location=dst, name='Flutter', frameworks=xcframeworks)
180+
dsyms = [simulator_dsym, framework_dsym] if args.dsym else None
181+
create_xcframework(location=dst, name='Flutter', frameworks=xcframeworks, dsyms=dsyms)
172182

173-
# Add the x64 simulator into the fat framework
183+
# Add the x64 simulator into the fat framework.
174184
subprocess.check_call([
175185
'lipo', arm64_dylib, simulator_x64_dylib, '-create', '-output', framework_binary
176186
])
177187

178-
process_framework(args, dst, framework, framework_binary)
188+
process_framework(args, dst, framework_binary, framework_dsym)
179189
return 0
180190

181191

@@ -209,23 +219,36 @@ def zip_archive(dst):
209219
'extension_safe/Flutter.xcframework',
210220
],
211221
cwd=dst)
212-
if os.path.exists(os.path.join(dst, 'Flutter.dSYM')):
222+
223+
# Generate Flutter.dSYM.zip for manual symbolification.
224+
#
225+
# Historically, the framework dSYM was named Flutter.dSYM, so in order to
226+
# remain backward-compatible with existing instructions in docs/Crashes.md
227+
# and existing tooling such as dart-lang/dart_ci, we rename back to that name
228+
#
229+
# TODO(cbracken): remove these archives and the upload steps once we bundle
230+
# dSYMs in app archives. https://github.com/flutter/flutter/issues/116493
231+
framework_dsym = os.path.join(dst, 'Flutter.framework.dSYM')
232+
if os.path.exists(framework_dsym):
233+
renamed_dsym = framework_dsym.replace('Flutter.framework.dSYM', 'Flutter.dSYM')
234+
os.rename(framework_dsym, renamed_dsym)
213235
subprocess.check_call(['zip', '-r', 'Flutter.dSYM.zip', 'Flutter.dSYM'], cwd=dst)
214236

215-
if os.path.exists(os.path.join(dst, 'extension_safe', 'Flutter.dSYM')):
237+
extension_safe_dsym = os.path.join(dst, 'extension_safe', 'Flutter.framework.dSYM')
238+
if os.path.exists(extension_safe_dsym):
239+
renamed_dsym = extension_safe_dsym.replace('Flutter.framework.dSYM', 'Flutter.dSYM')
240+
os.rename(extension_safe_dsym, renamed_dsym)
216241
subprocess.check_call(['zip', '-r', 'extension_safe_Flutter.dSYM.zip', 'Flutter.dSYM'], cwd=dst)
217242

218243

219-
def process_framework(args, dst, framework, framework_binary):
220-
if args.dsym:
221-
dsym_out = os.path.splitext(framework)[0] + '.dSYM'
222-
subprocess.check_call([DSYMUTIL, '-o', dsym_out, framework_binary])
244+
def process_framework(args, dst, framework_binary, dsym):
245+
if dsym:
246+
subprocess.check_call([DSYMUTIL, '-o', dsym, framework_binary])
223247

224248
if args.strip:
225249
# copy unstripped
226250
unstripped_out = os.path.join(dst, 'Flutter.unstripped')
227251
shutil.copyfile(framework_binary, unstripped_out)
228-
229252
subprocess.check_call(['strip', '-x', '-S', framework_binary])
230253

231254

sky/tools/create_xcframework.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,22 @@ def main():
2222
help='The framework paths used to create the XCFramework.',
2323
required=True
2424
)
25+
parser.add_argument(
26+
'--dsyms', nargs='+', help='The dSYM paths to be bundled in the XCFramework.', required=False
27+
)
2528
parser.add_argument('--name', help='Name of the XCFramework', type=str, required=True)
2629
parser.add_argument('--location', help='Output directory', type=str, required=True)
2730

2831
args = parser.parse_args()
2932

30-
create_xcframework(args.location, args.name, args.frameworks)
33+
create_xcframework(args.location, args.name, args.frameworks, args.dsyms)
34+
3135

36+
def create_xcframework(location, name, frameworks, dsyms=None):
37+
if dsyms and len(frameworks) != len(dsyms):
38+
print('Number of --dsyms must match number of --frameworks exactly.', file=sys.stderr)
39+
sys.exit(1)
3240

33-
def create_xcframework(location, name, frameworks):
3441
output_dir = os.path.abspath(location)
3542
output_xcframework = os.path.join(output_dir, '%s.xcframework' % name)
3643

@@ -45,11 +52,13 @@ def create_xcframework(location, name, frameworks):
4552
# -framework bar/baz.framework -output output/
4653
command = ['xcrun', 'xcodebuild', '-quiet', '-create-xcframework']
4754

48-
for framework in frameworks:
49-
command.extend(['-framework', os.path.abspath(framework)])
50-
5155
command.extend(['-output', output_xcframework])
5256

57+
for i in range(len(frameworks)): # pylint: disable=consider-using-enumerate
58+
command.extend(['-framework', os.path.abspath(frameworks[i])])
59+
if dsyms:
60+
command.extend(['-debug-symbols', os.path.abspath(dsyms[i])])
61+
5362
subprocess.check_call(command, stdout=open(os.devnull, 'w'))
5463

5564

0 commit comments

Comments
 (0)