blob: f146b2d4fbbe65bed8ecad63f48937449d58632c [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 'package:file/file.dart';
import 'package:platform/platform.dart';
import 'common/core.dart';
import 'common/gradle.dart';
import 'common/package_looping_command.dart';
import 'common/plugin_utils.dart';
import 'common/process_runner.dart';
import 'common/repository_package.dart';
/// Run 'gradlew lint'.
/// See
class LintAndroidCommand extends PackageLoopingCommand {
/// Creates an instance of the linter command.
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
}) : super(packagesDir, processRunner: processRunner, platform: platform);
final String name = 'lint-android';
final String description = 'Runs "gradlew lint" on Android plugins.\n\n'
'Requires the examples to have been build at least once before running.';
Future<PackageResult> runForPackage(RepositoryPackage package) async {
if (!pluginSupportsPlatform(platformAndroid, package,
requiredMode: PlatformSupport.inline)) {
return PackageResult.skip(
'Plugin does not have an Android implementation.');
bool failed = false;
for (final RepositoryPackage example in package.getExamples()) {
final GradleProject project = GradleProject(example,
processRunner: processRunner, platform: platform);
if (!project.isConfigured()) {
return<String>['Build examples before linting']);
final String packageName =;
// Only lint one build mode to avoid extra work.
// Only lint the plugin project itself, to avoid failing due to errors in
// dependencies.
// TODO(stuartmorgan): Consider adding an XML parser to read and summarize
// all results. Currently, only the first three errors will be shown
// inline, and the rest have to be checked via the CI-uploaded artifact.
final int exitCode = await project.runCommand('$packageName:lintDebug');
if (exitCode != 0) {
failed = true;
// In addition to running the Gradle lint step, also ensure that the
// example project is configured to build with javac lints enabled and
// treated as errors.
final List<String> gradleBuildContents = example
// The check here is intentionally somewhat loose, to allow for the
// possibility of variations (e.g., not using Xlint:all in some cases, or
// passing other arguments).
if (!gradleBuildContents.any(
(String line) => line.contains('project(":$packageName")')) ||
!gradleBuildContents.any((String line) =>
line.contains('options.compilerArgs') &&
line.contains('-Xlint') &&
line.contains('-Werror'))) {
failed = true;
printError('The example '
'${getRelativePosixPath(, from:} '
'is not configured to treat javac lints and warnings as errors. '
'Please add the following to its build.gradle:');
gradle.projectsEvaluated {
project(":${}") {
tasks.withType(JavaCompile) {
options.compilerArgs << "-Xlint:all" << "-Werror"
return failed ? : PackageResult.success();