blob: 364653bd13bab1a37cefdc0d35e2a96e55b9d487 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
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.dart';
/// Lint the CocoaPod podspecs and run unit tests.
///
/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint.
class LintPodspecsCommand extends PluginCommand {
/// Creates an instance of the linter command.
LintPodspecsCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
Print print = print,
}) : _platform = platform,
_print = print,
super(packagesDir, processRunner: processRunner) {
argParser.addMultiOption('skip',
help:
'Skip all linting for podspecs with this basename (example: federated plugins with placeholder podspecs)',
valueHelp: 'podspec_file_name');
argParser.addMultiOption('ignore-warnings',
help:
'Do not pass --allow-warnings flag to "pod lib lint" for podspecs with this basename (example: plugins with known warnings)',
valueHelp: 'podspec_file_name');
}
@override
final String name = 'podspecs';
@override
List<String> get aliases => <String>['podspec'];
@override
final String description =
'Runs "pod lib lint" on all iOS and macOS plugin podspecs.\n\n'
'This command requires "pod" and "flutter" to be in your path. Runs on macOS only.';
final Platform _platform;
final Print _print;
@override
Future<void> run() async {
if (!_platform.isMacOS) {
_print('Detected platform is not macOS, skipping podspec lint');
return;
}
await processRunner.run(
'which',
<String>['pod'],
workingDir: packagesDir,
exitOnError: true,
logOnError: true,
);
_print('Starting podspec lint test');
final List<String> failingPlugins = <String>[];
for (final File podspec in await _podspecsToLint()) {
if (!await _lintPodspec(podspec)) {
failingPlugins.add(p.basenameWithoutExtension(podspec.path));
}
}
_print('\n\n');
if (failingPlugins.isNotEmpty) {
_print('The following plugins have podspec errors (see above):');
for (final String plugin in failingPlugins) {
_print(' * $plugin');
}
throw ToolExit(1);
}
}
Future<List<File>> _podspecsToLint() async {
final List<File> podspecs = await getFiles().where((File entity) {
final String filePath = entity.path;
return p.extension(filePath) == '.podspec' &&
!getStringListArg('skip')
.contains(p.basenameWithoutExtension(filePath));
}).toList();
podspecs.sort(
(File a, File b) => p.basename(a.path).compareTo(p.basename(b.path)));
return podspecs;
}
Future<bool> _lintPodspec(File podspec) async {
// Do not run the static analyzer on plugins with known analyzer issues.
final String podspecPath = podspec.path;
final String podspecBasename = p.basename(podspecPath);
_print('Linting $podspecBasename');
// Lint plugin as framework (use_frameworks!).
final ProcessResult frameworkResult =
await _runPodLint(podspecPath, libraryLint: true);
_print(frameworkResult.stdout);
_print(frameworkResult.stderr);
// Lint plugin as library.
final ProcessResult libraryResult =
await _runPodLint(podspecPath, libraryLint: false);
_print(libraryResult.stdout);
_print(libraryResult.stderr);
return frameworkResult.exitCode == 0 && libraryResult.exitCode == 0;
}
Future<ProcessResult> _runPodLint(String podspecPath,
{required bool libraryLint}) async {
final bool allowWarnings = (getStringListArg('ignore-warnings'))
.contains(p.basenameWithoutExtension(podspecPath));
final List<String> arguments = <String>[
'lib',
'lint',
podspecPath,
'--configuration=Debug', // Release targets unsupported arm64 simulators. Use Debug to only build against targeted x86_64 simulator devices.
'--skip-tests',
'--use-modular-headers', // Flutter sets use_modular_headers! in its templates.
if (allowWarnings) '--allow-warnings',
if (libraryLint) '--use-libraries'
];
_print('Running "pod ${arguments.join(' ')}"');
return processRunner.run('pod', arguments,
workingDir: packagesDir, stdoutEncoding: utf8, stderrEncoding: utf8);
}
}