@@ -6,7 +6,6 @@ import 'dart:convert';
66import 'dart:io' ;
77
88import 'package:file/file.dart' ;
9- import 'package:path/path.dart' as p;
109import 'package:platform/platform.dart' ;
1110
1211import 'common/core.dart' ;
@@ -20,23 +19,24 @@ const int _exitPodNotInstalled = 3;
2019/// Lint the CocoaPod podspecs and run unit tests.
2120///
2221/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint.
23- class LintPodspecsCommand extends PackageLoopingCommand {
22+ class PodspecCheckCommand extends PackageLoopingCommand {
2423 /// Creates an instance of the linter command.
25- LintPodspecsCommand (
24+ PodspecCheckCommand (
2625 Directory packagesDir, {
2726 ProcessRunner processRunner = const ProcessRunner (),
2827 Platform platform = const LocalPlatform (),
2928 }) : super (packagesDir, processRunner: processRunner, platform: platform);
3029
3130 @override
32- final String name = 'podspecs ' ;
31+ final String name = 'podspec-check ' ;
3332
3433 @override
35- List <String > get aliases => < String > ['podspec' ];
34+ List <String > get aliases => < String > ['podspec' , 'podspecs' ];
3635
3736 @override
3837 final String description =
39- 'Runs "pod lib lint" on all iOS and macOS plugin podspecs.\n\n '
38+ 'Runs "pod lib lint" on all iOS and macOS plugin podspecs, as well as '
39+ 'making sure the podspecs follow repository standards.\n\n '
4040 'This command requires "pod" and "flutter" to be in your path. Runs on macOS only.' ;
4141
4242 @override
@@ -69,9 +69,32 @@ class LintPodspecsCommand extends PackageLoopingCommand {
6969
7070 for (final File podspec in podspecs) {
7171 if (! await _lintPodspec (podspec)) {
72- errors.add (p. basename ( podspec.path) );
72+ errors.add (podspec.basename );
7373 }
7474 }
75+
76+ if (await _hasIOSSwiftCode (package)) {
77+ print ('iOS Swift code found, checking for search paths settings...' );
78+ for (final File podspec in podspecs) {
79+ if (_isPodspecMissingSearchPaths (podspec)) {
80+ const String workaroundBlock = r'''
81+ s.xcconfig = {
82+ 'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift',
83+ 'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift',
84+ }
85+ ''' ;
86+ final String path =
87+ getRelativePosixPath (podspec, from: package.directory);
88+ printError ('$path is missing seach path configuration. Any iOS '
89+ 'plugin implementation that contains Swift implementation code '
90+ 'needs to contain the following:\n\n '
91+ '$workaroundBlock \n '
92+ 'For more details, see https://github.com/flutter/flutter/issues/118418.' );
93+ errors.add (podspec.basename);
94+ }
95+ }
96+ }
97+
7598 return errors.isEmpty
7699 ? PackageResult .success ()
77100 : PackageResult .fail (errors);
@@ -92,7 +115,7 @@ class LintPodspecsCommand extends PackageLoopingCommand {
92115 // Do not run the static analyzer on plugins with known analyzer issues.
93116 final String podspecPath = podspec.path;
94117
95- final String podspecBasename = p .basename (podspecPath) ;
118+ final String podspecBasename = podspec .basename;
96119 print ('Linting $podspecBasename ' );
97120
98121 // Lint plugin as framework (use_frameworks!).
@@ -126,4 +149,46 @@ class LintPodspecsCommand extends PackageLoopingCommand {
126149 return processRunner.run ('pod' , arguments,
127150 workingDir: packagesDir, stdoutEncoding: utf8, stderrEncoding: utf8);
128151 }
152+
153+ /// Returns true if there is any iOS plugin implementation code written in
154+ /// Swift.
155+ Future <bool > _hasIOSSwiftCode (RepositoryPackage package) async {
156+ return getFilesForPackage (package).any ((File entity) {
157+ final String relativePath =
158+ getRelativePosixPath (entity, from: package.directory);
159+ // Ignore example code.
160+ if (relativePath.startsWith ('example/' )) {
161+ return false ;
162+ }
163+ final String filePath = entity.path;
164+ return path.extension (filePath) == '.swift' ;
165+ });
166+ }
167+
168+ /// Returns true if [podspec] could apply to iOS, but does not have the
169+ /// workaround for search paths that makes Swift plugins build correctly in
170+ /// Objective-C applications. See
171+ /// https://github.com/flutter/flutter/issues/118418 for context and details.
172+ ///
173+ /// This does not check that the plugin has Swift code, and thus whether the
174+ /// workaround is needed, only whether or not it is there.
175+ bool _isPodspecMissingSearchPaths (File podspec) {
176+ final String directory = podspec.parent.basename;
177+ // All macOS Flutter apps are Swift, so macOS-only podspecs don't need the
178+ // workaround. If it's anywhere other than macos/, err or the side of
179+ // assuming it's required.
180+ if (directory == 'macos' ) {
181+ return false ;
182+ }
183+
184+ // This errs on the side of being too strict, to minimize the chance of
185+ // accidental incorrect configuration. If we ever need more flexibility
186+ // due to a false negative we can adjust this as necessary.
187+ final RegExp workaround = RegExp (r'''
188+ \s*s\.(?:ios\.)?xcconfig = {[^}]*
189+ \s*'LIBRARY_SEARCH_PATHS' => '\$\(TOOLCHAIN_DIR\)/usr/lib/swift/\$\(PLATFORM_NAME\)/ \$\(SDKROOT\)/usr/lib/swift',
190+ \s*'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift',[^}]*
191+ \s*}''' , dotAll: true );
192+ return ! workaround.hasMatch (podspec.readAsStringSync ());
193+ }
129194}
0 commit comments