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

[tool] Check for search paths in Swift plugins #6954

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ targets:
# TODO(stuartmorgan): Move this to ARM once google_maps_flutter has ARM
# support. `pod lint` makes a synthetic target that doesn't respect the
# pod's arch exclusions, so fails to build.
# When moving it, rename the task and file to check_podspecs
- name: Mac_x64 lint_podspecs
recipe: plugins/plugins
timeout: 30
Expand Down
4 changes: 2 additions & 2 deletions .ci/targets/mac_lint_podspecs.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
tasks:
- name: prepare tool
script: .ci/scripts/prepare_tool.sh
- name: lint iOS and macOS podspecs
- name: validate iOS and macOS podspecs
script: script/tool_runner.sh
args: ["podspecs"]
args: ["podspec-check"]
6 changes: 6 additions & 0 deletions script/tool/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.13.3

* Renames `podspecs` to `podspec-check`. The old name will continue to work.
* Adds validation of the Swift-in-Obj-C-projects workaround in the podspecs of
iOS plugin implementations that use Swift.

## 0.13.2+1

* Replaces deprecated `flutter format` with `dart format` in `format`
Expand Down
4 changes: 2 additions & 2 deletions script/tool/lib/src/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import 'fix_command.dart';
import 'format_command.dart';
import 'license_check_command.dart';
import 'lint_android_command.dart';
import 'lint_podspecs_command.dart';
import 'list_command.dart';
import 'make_deps_path_based_command.dart';
import 'native_test_command.dart';
import 'podspec_check_command.dart';
import 'publish_check_command.dart';
import 'publish_command.dart';
import 'pubspec_check_command.dart';
Expand Down Expand Up @@ -66,7 +66,7 @@ void main(List<String> args) {
..addCommand(FormatCommand(packagesDir))
..addCommand(LicenseCheckCommand(packagesDir))
..addCommand(LintAndroidCommand(packagesDir))
..addCommand(LintPodspecsCommand(packagesDir))
..addCommand(PodspecCheckCommand(packagesDir))
..addCommand(ListCommand(packagesDir))
..addCommand(NativeTestCommand(packagesDir))
..addCommand(MakeDepsPathBasedCommand(packagesDir))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import 'dart:convert';
import 'dart:io';

import 'package:file/file.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';

import 'common/core.dart';
Expand All @@ -20,23 +19,24 @@ const int _exitPodNotInstalled = 3;
/// Lint the CocoaPod podspecs and run unit tests.
///
/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint.
class LintPodspecsCommand extends PackageLoopingCommand {
class PodspecCheckCommand extends PackageLoopingCommand {
/// Creates an instance of the linter command.
LintPodspecsCommand(
PodspecCheckCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
}) : super(packagesDir, processRunner: processRunner, platform: platform);

@override
final String name = 'podspecs';
final String name = 'podspec-check';

@override
List<String> get aliases => <String>['podspec'];
List<String> get aliases => <String>['podspec', 'podspecs'];

@override
final String description =
'Runs "pod lib lint" on all iOS and macOS plugin podspecs.\n\n'
'Runs "pod lib lint" on all iOS and macOS plugin podspecs, as well as '
'making sure the podspecs follow repository standards.\n\n'
'This command requires "pod" and "flutter" to be in your path. Runs on macOS only.';

@override
Expand Down Expand Up @@ -69,9 +69,32 @@ class LintPodspecsCommand extends PackageLoopingCommand {

for (final File podspec in podspecs) {
if (!await _lintPodspec(podspec)) {
errors.add(p.basename(podspec.path));
errors.add(podspec.basename);
}
}

if (await _hasIOSSwiftCode(package)) {
print('iOS Swift code found, checking for search paths settings...');
for (final File podspec in podspecs) {
if (_isPodspecMissingSearchPaths(podspec)) {
const String workaroundBlock = r'''
s.xcconfig = {
'LIBRARY_SEARCH_PATHS' => '$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)/ $(SDKROOT)/usr/lib/swift',
'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift',
}
''';
final String path =
getRelativePosixPath(podspec, from: package.directory);
printError('$path is missing seach path configuration. Any iOS '
'plugin implementation that contains Swift implementation code '
'needs to contain the following:\n\n'
'$workaroundBlock\n'
'For more details, see https://github.com/flutter/flutter/issues/118418.');
errors.add(podspec.basename);
}
}
}

return errors.isEmpty
? PackageResult.success()
: PackageResult.fail(errors);
Expand All @@ -92,7 +115,7 @@ class LintPodspecsCommand extends PackageLoopingCommand {
// Do not run the static analyzer on plugins with known analyzer issues.
final String podspecPath = podspec.path;

final String podspecBasename = p.basename(podspecPath);
final String podspecBasename = podspec.basename;
print('Linting $podspecBasename');

// Lint plugin as framework (use_frameworks!).
Expand Down Expand Up @@ -126,4 +149,46 @@ class LintPodspecsCommand extends PackageLoopingCommand {
return processRunner.run('pod', arguments,
workingDir: packagesDir, stdoutEncoding: utf8, stderrEncoding: utf8);
}

/// Returns true if there is any iOS plugin implementation code written in
/// Swift.
Future<bool> _hasIOSSwiftCode(RepositoryPackage package) async {
return getFilesForPackage(package).any((File entity) {
final String relativePath =
getRelativePosixPath(entity, from: package.directory);
// Ignore example code.
if (relativePath.startsWith('example/')) {
return false;
}
final String filePath = entity.path;
return path.extension(filePath) == '.swift';
});
}

/// Returns true if [podspec] could apply to iOS, but does not have the
/// workaround for search paths that makes Swift plugins build correctly in
/// Objective-C applications. See
/// https://github.com/flutter/flutter/issues/118418 for context and details.
///
/// This does not check that the plugin has Swift code, and thus whether the
/// workaround is needed, only whether or not it is there.
bool _isPodspecMissingSearchPaths(File podspec) {
final String directory = podspec.parent.basename;
// All macOS Flutter apps are Swift, so macOS-only podspecs don't need the
// workaround. If it's anywhere other than macos/, err or the side of
// assuming it's required.
if (directory == 'macos') {
return false;
}

// This errs on the side of being too strict, to minimize the chance of
// accidental incorrect configuration. If we ever need more flexibility
// due to a false negative we can adjust this as necessary.
final RegExp workaround = RegExp(r'''
\s*s\.(?:ios\.)?xcconfig = {[^}]*
\s*'LIBRARY_SEARCH_PATHS' => '\$\(TOOLCHAIN_DIR\)/usr/lib/swift/\$\(PLATFORM_NAME\)/ \$\(SDKROOT\)/usr/lib/swift',
\s*'LD_RUNPATH_SEARCH_PATHS' => '/usr/lib/swift',[^}]*
\s*}''', dotAll: true);
return !workaround.hasMatch(podspec.readAsStringSync());
}
}
2 changes: 1 addition & 1 deletion script/tool/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: flutter_plugin_tools
description: Productivity utils for flutter/plugins and flutter/packages
repository: https://github.com/flutter/plugins/tree/main/script/tool
version: 0.13.2+1
version: 0.13.3

dependencies:
args: ^2.1.0
Expand Down
Loading