blob: 198dd94721151e3a6666d2ef28b24981a178c877 [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/core.dart';
import 'common/package_looping_command.dart';
import 'common/process_runner.dart';
import 'common/repository_package.dart';
const int _exitUnsupportedPlatform = 2;
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 {
/// Creates an instance of the linter command.
LintPodspecsCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
}) : super(packagesDir, processRunner: processRunner, platform: platform);
@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.';
@override
Future<void> initializeRun() async {
if (!platform.isMacOS) {
printError('This command is only supported on macOS');
throw ToolExit(_exitUnsupportedPlatform);
}
final ProcessResult result = await processRunner.run(
'which',
<String>['pod'],
workingDir: packagesDir,
logOnError: true,
);
if (result.exitCode != 0) {
printError('Unable to find "pod". Make sure it is in your path.');
throw ToolExit(_exitPodNotInstalled);
}
}
@override
Future<PackageResult> runForPackage(RepositoryPackage package) async {
final List<String> errors = <String>[];
final List<File> podspecs = await _podspecsToLint(package);
if (podspecs.isEmpty) {
return PackageResult.skip('No podspecs.');
}
for (final File podspec in podspecs) {
if (!await _lintPodspec(podspec)) {
errors.add(p.basename(podspec.path));
}
}
return errors.isEmpty
? PackageResult.success()
: PackageResult.fail(errors);
}
Future<List<File>> _podspecsToLint(RepositoryPackage package) async {
final List<File> podspecs =
await getFilesForPackage(package).where((File entity) {
final String filePath = entity.path;
return path.extension(filePath) == '.podspec';
}).toList();
podspecs.sort((File a, File b) => a.basename.compareTo(b.basename));
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 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 (libraryLint) '--use-libraries'
];
print('Running "pod ${arguments.join(' ')}"');
return processRunner.run('pod', arguments,
workingDir: packagesDir, stdoutEncoding: utf8, stderrEncoding: utf8);
}
}