@@ -6,7 +6,6 @@ import 'dart:convert';
6
6
import 'dart:io' ;
7
7
8
8
import 'package:file/file.dart' ;
9
- import 'package:path/path.dart' as p;
10
9
import 'package:platform/platform.dart' ;
11
10
12
11
import 'common/core.dart' ;
@@ -20,23 +19,24 @@ const int _exitPodNotInstalled = 3;
20
19
/// Lint the CocoaPod podspecs and run unit tests.
21
20
///
22
21
/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint.
23
- class LintPodspecsCommand extends PackageLoopingCommand {
22
+ class PodspecCheckCommand extends PackageLoopingCommand {
24
23
/// Creates an instance of the linter command.
25
- LintPodspecsCommand (
24
+ PodspecCheckCommand (
26
25
Directory packagesDir, {
27
26
ProcessRunner processRunner = const ProcessRunner (),
28
27
Platform platform = const LocalPlatform (),
29
28
}) : super (packagesDir, processRunner: processRunner, platform: platform);
30
29
31
30
@override
32
- final String name = 'podspecs ' ;
31
+ final String name = 'podspec-check ' ;
33
32
34
33
@override
35
- List <String > get aliases => < String > ['podspec' ];
34
+ List <String > get aliases => < String > ['podspec' , 'podspecs' ];
36
35
37
36
@override
38
37
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 '
40
40
'This command requires "pod" and "flutter" to be in your path. Runs on macOS only.' ;
41
41
42
42
@override
@@ -69,9 +69,32 @@ class LintPodspecsCommand extends PackageLoopingCommand {
69
69
70
70
for (final File podspec in podspecs) {
71
71
if (! await _lintPodspec (podspec)) {
72
- errors.add (p. basename ( podspec.path) );
72
+ errors.add (podspec.basename );
73
73
}
74
74
}
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
+
75
98
return errors.isEmpty
76
99
? PackageResult .success ()
77
100
: PackageResult .fail (errors);
@@ -92,7 +115,7 @@ class LintPodspecsCommand extends PackageLoopingCommand {
92
115
// Do not run the static analyzer on plugins with known analyzer issues.
93
116
final String podspecPath = podspec.path;
94
117
95
- final String podspecBasename = p .basename (podspecPath) ;
118
+ final String podspecBasename = podspec .basename;
96
119
print ('Linting $podspecBasename ' );
97
120
98
121
// Lint plugin as framework (use_frameworks!).
@@ -126,4 +149,46 @@ class LintPodspecsCommand extends PackageLoopingCommand {
126
149
return processRunner.run ('pod' , arguments,
127
150
workingDir: packagesDir, stdoutEncoding: utf8, stderrEncoding: utf8);
128
151
}
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
+ }
129
194
}
0 commit comments