@@ -355,7 +355,7 @@ class CocoaPods {
355355
356356 if (result.exitCode != 0 ) {
357357 invalidatePodInstallOutput (xcodeProject);
358- _diagnosePodInstallFailure (result);
358+ _diagnosePodInstallFailure (result, xcodeProject );
359359 throwToolExit ('Error running pod install' );
360360 } else if (xcodeProject.podfileLock.existsSync ()) {
361361 // Even if the Podfile.lock didn't change, update its modified date to now
@@ -367,7 +367,7 @@ class CocoaPods {
367367 }
368368 }
369369
370- void _diagnosePodInstallFailure (ProcessResult result) {
370+ void _diagnosePodInstallFailure (ProcessResult result, XcodeBasedProject xcodeProject ) {
371371 final Object ? stdout = result.stdout;
372372 final Object ? stderr = result.stderr;
373373 if (stdout is ! String || stderr is ! String ) {
@@ -397,7 +397,130 @@ class CocoaPods {
397397 ' sudo gem uninstall ffi && sudo gem install ffi -- --enable-libffi-alloc\n ' ,
398398 emphasis: true ,
399399 );
400+ } else if (stdout.contains ('required a higher minimum deployment target' )) {
401+ final ({String failingPod, String sourcePlugin, String podPluginSubdir})?
402+ podInfo = _parseMinDeploymentFailureInfo (stdout);
403+ if (podInfo != null ) {
404+ final String sourcePlugin = podInfo.sourcePlugin;
405+ // If the plugin's podfile has set its own minimum version correctly
406+ // based on the requirements of its dependencies the failing pod should
407+ // be the plugin itself, but if not they may be different (e.g., if
408+ // a plugin says its minimum iOS version is 11, but depends on a pod
409+ // with a minimum version of 12, then building for 11 will report that
410+ // pod as failing.)
411+ if (podInfo.failingPod == podInfo.sourcePlugin) {
412+ final Directory symlinksDir;
413+ final String podPlatformString;
414+ final String platformName;
415+ final String docsLink;
416+ if (xcodeProject is IosProject ) {
417+ symlinksDir = xcodeProject.symlinks;
418+ podPlatformString = 'ios' ;
419+ platformName = 'iOS' ;
420+ docsLink = 'https://docs.flutter.dev/deployment/ios' ;
421+ } else if (xcodeProject is MacOSProject ) {
422+ symlinksDir = xcodeProject.ephemeralDirectory.childDirectory ('.symlinks' );
423+ podPlatformString = 'osx' ;
424+ platformName = 'macOS' ;
425+ docsLink = 'https://docs.flutter.dev/deployment/macos' ;
426+ } else {
427+ return ;
428+ }
429+ final File podspec = symlinksDir
430+ .childDirectory ('plugins' )
431+ .childDirectory (sourcePlugin)
432+ .childDirectory (podInfo.podPluginSubdir)
433+ .childFile ('$sourcePlugin .podspec' );
434+ final String ? minDeploymentVersion = _findPodspecMinDeploymentVersion (
435+ podspec,
436+ podPlatformString
437+ );
438+ if (minDeploymentVersion != null ) {
439+ _logger.printError (
440+ 'Error: The plugin "$sourcePlugin " requires a higher minimum '
441+ '$platformName deployment version than your application is targeting.\n '
442+ "To build, increase your application's deployment target to at "
443+ 'least $minDeploymentVersion as described at $docsLink ' ,
444+ emphasis: true ,
445+ );
446+ } else {
447+ // If for some reason the min version can't be parsed out, provide
448+ // a less specific error message that still describes the problem,
449+ // but also requests filing a Flutter issue so the parsing in
450+ // _findPodspecMinDeploymentVersion can be improved.
451+ _logger.printError (
452+ 'Error: The plugin "$sourcePlugin " requires a higher minimum '
453+ '$platformName deployment version than your application is targeting.\n '
454+ "To build, increase your application's deployment target as "
455+ 'described at $docsLink \n\n '
456+ 'The minimum required version for "$sourcePlugin " could not be '
457+ 'determined. Please file an issue at '
458+ 'https://github.com/flutter/flutter/issues about this error message.' ,
459+ emphasis: true ,
460+ );
461+ }
462+ } else {
463+ // In theory this could find the failing pod's spec and parse out its
464+ // minimum deployment version, but finding that spec would add a lot
465+ // of complexity to handle a case that plugin authors should not
466+ // create, so this just provides the actionable step of following up
467+ // with the plugin developer.
468+ _logger.printError (
469+ 'Error: The pod "${podInfo .failingPod }" required by the plugin '
470+ '"$sourcePlugin " requires a higher minimum iOS deployment version '
471+ "than the plugin's reported minimum version.\n "
472+ 'To build, remove the plugin "$sourcePlugin ", or contact the plugin\' s '
473+ 'developers for assistance.' ,
474+ emphasis: true ,
475+ );
476+ }
477+ }
478+ }
479+ }
480+
481+ ({String failingPod, String sourcePlugin, String podPluginSubdir})?
482+ _parseMinDeploymentFailureInfo (String podInstallOutput) {
483+ final RegExp sourceLine = RegExp (r'\(from `.*\.symlinks/plugins/([^/]+)/([^/]+)`\)' );
484+ final RegExp dependencyLine = RegExp (r'Specs satisfying the `([^ ]+).*` dependency were found, '
485+ 'but they required a higher minimum deployment target' );
486+ final RegExpMatch ? sourceMatch = sourceLine.firstMatch (podInstallOutput);
487+ final RegExpMatch ? dependencyMatch = dependencyLine.firstMatch (podInstallOutput);
488+ if (sourceMatch == null || dependencyMatch == null ) {
489+ return null ;
490+ }
491+ return (
492+ failingPod: dependencyMatch.group (1 )! ,
493+ sourcePlugin: sourceMatch.group (1 )! ,
494+ podPluginSubdir: sourceMatch.group (2 )!
495+ );
496+ }
497+
498+ String ? _findPodspecMinDeploymentVersion (File podspec, String platformString) {
499+ if (! podspec.existsSync ()) {
500+ return null ;
400501 }
502+ // There are two ways the deployment target can be specified; see
503+ // https://guides.cocoapods.org/syntax/podspec.html#group_platform
504+ final RegExp platformPattern = RegExp (
505+ // Example: spec.platform = :osx, '10.8'
506+ // where "spec" is an arbitrary variable name.
507+ r'^\s*[a-zA-Z_]+\.platform\s*=\s*'
508+ ':$platformString '
509+ r'''\s*,\s*["']([^"']+)["']''' ,
510+ multiLine: true
511+ );
512+ final RegExp deploymentTargetPlatform = RegExp (
513+ // Example: spec.osx.deployment_target = '10.8'
514+ // where "spec" is an arbitrary variable name.
515+ r'^\s*[a-zA-Z_]+\.'
516+ '$platformString \\ .deployment_target'
517+ r'''\s*=\s*["']([^"']+)["']''' ,
518+ multiLine: true
519+ );
520+ final String podspecContents = podspec.readAsStringSync ();
521+ final RegExpMatch ? match = platformPattern.firstMatch (podspecContents) ??
522+ deploymentTargetPlatform.firstMatch (podspecContents);
523+ return match? .group (1 );
401524 }
402525
403526 bool _isFfiX86Error (String error) {
0 commit comments