analyze.dart 6.92 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
Hixie's avatar
Hixie committed
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:meta/meta.dart';
6
import 'package:process/process.dart';
7

8
import '../artifacts.dart';
9
import '../base/common.dart';
10
import '../base/file_system.dart';
11
import '../base/logger.dart';
12
import '../base/platform.dart';
13
import '../base/terminal.dart';
14
import '../project_validator.dart';
15
import '../runner/flutter_command.dart';
16
import 'analyze_base.dart';
17 18
import 'analyze_continuously.dart';
import 'analyze_once.dart';
19
import 'validate_project.dart';
Hixie's avatar
Hixie committed
20

21
class AnalyzeCommand extends FlutterCommand {
22 23 24
  AnalyzeCommand({
    bool verboseHelp = false,
    this.workingDirectory,
25 26 27 28 29 30
    required FileSystem fileSystem,
    required Platform platform,
    required Terminal terminal,
    required Logger logger,
    required ProcessManager processManager,
    required Artifacts artifacts,
31
    required List<ProjectValidator> allProjectValidators,
32 33
  }) : _artifacts = artifacts,
       _fileSystem = fileSystem,
34 35 36
       _processManager = processManager,
       _logger = logger,
       _terminal = terminal,
37
       _allProjectValidators = allProjectValidators,
38
       _platform = platform {
39 40 41 42 43 44 45 46
    argParser.addFlag('flutter-repo',
        negatable: false,
        help: 'Include all the examples and tests from the Flutter repository.',
        hide: !verboseHelp);
    argParser.addFlag('current-package',
        help: 'Analyze the current project, if applicable.', defaultsTo: true);
    argParser.addFlag('dartdocs',
        negatable: false,
47 48
        help: '(deprecated) List every public member that is lacking documentation. '
              'This command will be removed in a future version of Flutter.',
49 50 51 52 53 54
        hide: !verboseHelp);
    argParser.addFlag('watch',
        help: 'Run analysis continuously, watching the filesystem for changes.',
        negatable: false);
    argParser.addOption('write',
        valueHelp: 'file',
55
        help: 'Also output the results to a file. This is useful with "--watch" '
56
              'if you want a file to always contain the latest results.');
57 58 59 60
    argParser.addOption('dart-sdk',
        valueHelp: 'path-to-sdk',
        help: 'The path to the Dart SDK.',
        hide: !verboseHelp);
61 62 63 64 65
    argParser.addOption('protocol-traffic-log',
        valueHelp: 'path-to-protocol-traffic-log',
        help: 'The path to write the request and response protocol. This is '
              'only intended to be used for debugging the tooling.',
        hide: !verboseHelp);
66 67 68
    argParser.addFlag('suggestions',
        help: 'Show suggestions about the current flutter project.'
    );
69 70 71 72 73 74
    argParser.addFlag('machine',
        negatable: false,
        help: 'Dumps a JSON with a subset of relevant data about the tool, project, '
              'and environment.',
        hide: !verboseHelp,
    );
75 76

    // Hidden option to enable a benchmarking mode.
77 78 79 80
    argParser.addFlag('benchmark',
        negatable: false,
        hide: !verboseHelp,
        help: 'Also output the analysis time.');
81 82 83 84

    usesPubOption();

    // Not used by analyze --watch
85
    argParser.addFlag('congratulate',
86
        help: 'Show output even when there are no errors, warnings, hints, or lints. '
87
              'Ignored if "--watch" is specified.',
88 89 90 91
        defaultsTo: true);
    argParser.addFlag('preamble',
        defaultsTo: true,
        help: 'When analyzing the flutter repository, display the number of '
92
              'files that will be analyzed.\n'
93
              'Ignored if "--watch" is specified.');
94 95 96 97 98 99
    argParser.addFlag('fatal-infos',
        help: 'Treat info level issues as fatal.',
        defaultsTo: true);
    argParser.addFlag('fatal-warnings',
        help: 'Treat warning level issues as fatal.',
        defaultsTo: true);
Hixie's avatar
Hixie committed
100 101
  }

102
  /// The working directory for testing analysis using dartanalyzer.
103
  final Directory? workingDirectory;
104

105
  final Artifacts _artifacts;
106 107
  final FileSystem _fileSystem;
  final Logger _logger;
108
  final Terminal _terminal;
109 110
  final ProcessManager _processManager;
  final Platform _platform;
111
  final List<ProjectValidator> _allProjectValidators;
112

113
  @override
Ian Hickson's avatar
Ian Hickson committed
114
  String get name => 'analyze';
115 116

  @override
117
  String get description => "Analyze the project's Dart code.";
118

119 120 121
  @override
  String get category => FlutterCommandCategory.project;

122 123 124
  @visibleForTesting
  List<ProjectValidator> allProjectValidators() => _allProjectValidators;

125
  @override
126 127
  bool get shouldRunPub {
    // If they're not analyzing the current project.
128
    if (!boolArgDeprecated('current-package')) {
129
      return false;
130
    }
131

132
    // Or we're not in a project directory.
133
    if (!_fileSystem.file('pubspec.yaml').existsSync()) {
134
      return false;
135
    }
136

137 138 139 140 141
    // Don't run pub if asking for machine output.
    if (boolArg('machine') != null && boolArg('machine')!) {
      return false;
    }

142
    return super.shouldRunPub;
143 144
  }

145
  @override
146
  Future<FlutterCommandResult> runCommand() async {
147
    final bool? suggestionFlag = boolArg('suggestions');
148
    final bool machineFlag = boolArg('machine') ?? false;
149 150 151 152 153 154 155 156
    if (suggestionFlag != null && suggestionFlag == true) {
      final String directoryPath;
      final bool? watchFlag = boolArg('watch');
      if (watchFlag != null && watchFlag) {
        throwToolExit('flag --watch is not compatible with --suggestions');
      }
      if (workingDirectory == null) {
        final Set<String> items = findDirectories(argResults!, _fileSystem);
157 158 159 160 161 162 163
        if (items.isEmpty) { // user did not specify any path
          directoryPath = _fileSystem.currentDirectory.path;
          _logger.printTrace('Showing suggestions for current directory: $directoryPath');
        } else if (items.length > 1) { // if the user sends more than one path
          throwToolExit('The suggestions flag can process only one directory path');
        } else {
          directoryPath = items.first;
164 165 166 167 168 169 170 171 172
        }
      } else {
        directoryPath = workingDirectory!.path;
      }
      return ValidateProject(
        fileSystem: _fileSystem,
        logger: _logger,
        allProjectValidators: _allProjectValidators,
        userPath: directoryPath,
173
        processManager: _processManager,
174
        machine: machineFlag,
175 176
      ).run();
    } else if (boolArgDeprecated('watch')) {
177
      await AnalyzeContinuously(
178 179 180
        argResults!,
        runner!.getRepoRoots(),
        runner!.getRepoPackages(),
181
        fileSystem: _fileSystem,
182
        logger: _logger,
183 184 185
        platform: _platform,
        processManager: _processManager,
        terminal: _terminal,
186
        artifacts: _artifacts,
187
      ).analyze();
188
    } else {
189
      await AnalyzeOnce(
190 191 192
        argResults!,
        runner!.getRepoRoots(),
        runner!.getRepoPackages(),
193
        workingDirectory: workingDirectory,
194
        fileSystem: _fileSystem,
195
        logger: _logger,
196 197 198
        platform: _platform,
        processManager: _processManager,
        terminal: _terminal,
199
        artifacts: _artifacts,
200
      ).analyze();
201
    }
202
    return FlutterCommandResult.success();
203 204
  }
}