@@ -12,6 +12,7 @@ import '../../base/file_system.dart';
1212import '../../base/io.dart' ;
1313import '../../base/logger.dart' show Logger;
1414import '../../base/process.dart' ;
15+ import '../../base/version.dart' ;
1516import '../../build_info.dart' ;
1617import '../../darwin/darwin.dart' ;
1718import '../../devfs.dart' ;
@@ -486,6 +487,113 @@ class _IssueLaunchRootViewControllerAccess extends Target {
486487 List <Source > get outputs => < Source > [];
487488}
488489
490+ /// This target verifies that the Xcode project has an LLDB Init File set within
491+ /// at least one scheme.
492+ ///
493+ /// LLDB Init File is needed for debugging on physical iOS 26+ devices.
494+ class DebugIosLLDBInit extends Target {
495+ const DebugIosLLDBInit ();
496+
497+ @override
498+ String get name => 'debug_ios_lldb_init' ;
499+
500+ @override
501+ List <Source > get inputs => const < Source > [
502+ Source .pattern ('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/ios.dart' ),
503+ Source .pattern (
504+ '{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/darwin.dart' ,
505+ ),
506+ ];
507+
508+ @override
509+ List <Source > get outputs => < Source > [
510+ Source .fromProject ((FlutterProject project) => project.ios.lldbInitFile),
511+ ];
512+
513+ @override
514+ List <Target > get dependencies => < Target > [];
515+
516+ @override
517+ Future <void > build (Environment environment) async {
518+ final String ? sdkRoot = environment.defines[kSdkRoot];
519+ if (sdkRoot == null ) {
520+ throw MissingDefineException (kSdkRoot, name);
521+ }
522+ final EnvironmentType ? environmentType = environmentTypeFromSdkroot (
523+ sdkRoot,
524+ environment.fileSystem,
525+ );
526+
527+ // LLDB Init File is only required for physical devices in debug mode.
528+ if (environmentType != EnvironmentType .physical) {
529+ return ;
530+ }
531+
532+ final String ? targetDeviceVersionString = environment.defines[kTargetDeviceOSVersion];
533+ if (targetDeviceVersionString == null ) {
534+ // Skip if TARGET_DEVICE_OS_VERSION is not found. TARGET_DEVICE_OS_VERSION
535+ // is not set if "build ios-framework" is called, which builds the
536+ // DebugIosApplicationBundle directly rather than through flutter assemble.
537+ // If may also be null if the build is targeting multiple architectures.
538+ return ;
539+ }
540+
541+ final Version ? targetDeviceVersion = Version .parse (targetDeviceVersionString);
542+ if (targetDeviceVersion == null ) {
543+ environment.logger.printError (
544+ 'Failed to parse TARGET_DEVICE_OS_VERSION: $targetDeviceVersionString ' ,
545+ );
546+ return ;
547+ }
548+
549+ // LLDB Init File is only needed for iOS 26+.
550+ if (targetDeviceVersion < Version (26 , 0 , null )) {
551+ return ;
552+ }
553+
554+ final String ? srcRoot = environment.defines[kSrcRoot];
555+ if (srcRoot == null ) {
556+ environment.logger.printError ('Failed to find $srcRoot ' );
557+ return ;
558+ }
559+
560+ final Directory xcodeProjectDir = environment.fileSystem.directory (srcRoot);
561+ if (! xcodeProjectDir.existsSync ()) {
562+ environment.logger.printError ('Failed to find ${xcodeProjectDir .path }' );
563+ return ;
564+ }
565+
566+ // The scheme name is not available in Xcode Build Phases Run Scripts.
567+ // Instead, find all xcscheme files in the Xcode project (this may be the
568+ // Flutter Xcode project or an Add to App native Xcode project) and check
569+ // if any of them contain "customLLDBInitFile". If none have it set, print
570+ // a warning.
571+ // Also, this cannot check for a specific path/name for the LLDB Init File
572+ // since Flutter's LLDB Init file may be imported from within a user's
573+ // custom LLDB Init File.
574+ bool anyLLDBInitFound = false ;
575+ await for (final FileSystemEntity entity in xcodeProjectDir.list (recursive: true )) {
576+ if (environment.fileSystem.path.extension (entity.path) == '.xcscheme' && entity is File ) {
577+ if (entity.readAsStringSync ().contains ('customLLDBInitFile' )) {
578+ anyLLDBInitFound = true ;
579+ break ;
580+ }
581+ }
582+ }
583+ if (! anyLLDBInitFound) {
584+ final FlutterProject flutterProject = FlutterProject .fromDirectory (environment.projectDir);
585+ final String tab = flutterProject.isModule ? 'Use CocoaPods' : 'Use frameworks' ;
586+ printXcodeWarning (
587+ 'Debugging Flutter on new iOS versions requires an LLDB Init File. To '
588+ 'ensure debug mode works, please complete instructions found in '
589+ '"Embed a Flutter module in your iOS app > $tab > Set LLDB Init File" '
590+ 'section of https://docs.flutter.dev/to/ios-add-to-app-embed-setup.' ,
591+ );
592+ }
593+ return ;
594+ }
595+ }
596+
489597/// The base class for all iOS bundle targets.
490598///
491599/// This is responsible for setting up the basic App.framework structure, including:
@@ -641,7 +749,11 @@ class DebugIosApplicationBundle extends IosAssetBundle {
641749 ];
642750
643751 @override
644- List <Target > get dependencies => < Target > [const DebugUniversalFramework (), ...super .dependencies];
752+ List <Target > get dependencies => < Target > [
753+ const DebugUniversalFramework (),
754+ const DebugIosLLDBInit (),
755+ ...super .dependencies,
756+ ];
645757}
646758
647759/// IosAssetBundle with debug symbols, used for Profile and Release builds.
0 commit comments