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

import 'dart:async';


8
import '../base/common.dart';
9
import '../base/file_system.dart';
10
import '../base/logger.dart';
11 12 13 14
import '../dart/analysis.dart';
import 'analyze_base.dart';

class AnalyzeOnce extends AnalyzeBase {
15
  AnalyzeOnce(
16
    super.argResults,
17 18
    List<String> repoRoots,
    List<Directory> repoPackages, {
19 20 21 22 23 24
    required super.fileSystem,
    required super.logger,
    required super.platform,
    required super.processManager,
    required super.terminal,
    required super.artifacts,
25
    required super.suppressAnalytics,
26
    this.workingDirectory,
27 28 29 30
  }) : super(
        repoRoots: repoRoots,
        repoPackages: repoPackages,
      );
31

32
  /// The working directory for testing analysis using dartanalyzer.
33
  final Directory? workingDirectory;
34 35

  @override
36
  Future<void> analyze() async {
37
    final String currentDirectory =
38
        (workingDirectory ?? fileSystem.currentDirectory).path;
39
    final Set<String> items = findDirectories(argResults, fileSystem);
40

41
    if (isFlutterRepo) {
42
      // check for conflicting dependencies
43
      final PackageDependencyTracker dependencies = PackageDependencyTracker();
44
      dependencies.checkForConflictingDependencies(repoPackages, dependencies);
45
      items.addAll(repoRoots);
46
      if (argResults.wasParsed('current-package') && (argResults['current-package'] as bool)) {
47
        items.add(currentDirectory);
48
      }
49
    } else {
50 51
      if ((argResults['current-package'] as bool) && items.isEmpty) {
        items.add(currentDirectory);
52
      }
53 54
    }

55
    if (items.isEmpty) {
56
      throwToolExit('Nothing to analyze.', exitCode: 0);
57
    }
58

59
    final Completer<void> analysisCompleter = Completer<void>();
60
    final List<AnalysisError> errors = <AnalysisError>[];
61

62
    final AnalysisServer server = AnalysisServer(
63
      sdkPath,
64
      items.toList(),
65 66 67 68 69
      fileSystem: fileSystem,
      platform: platform,
      logger: logger,
      processManager: processManager,
      terminal: terminal,
70
      protocolTrafficLog: protocolTrafficLog,
71
      suppressAnalytics: suppressAnalytics,
72
    );
73

74 75
    Stopwatch? timer;
    Status? progress;
76
    try {
77
      StreamSubscription<bool>? subscription;
78 79

      void handleAnalysisStatus(bool isAnalyzing) {
80 81 82 83 84
        if (!isAnalyzing) {
          analysisCompleter.complete();
          subscription?.cancel();
          subscription = null;
        }
85 86 87 88 89 90 91 92 93 94 95
      }

      subscription = server.onAnalyzing.listen((bool isAnalyzing) => handleAnalysisStatus(isAnalyzing));

      void handleAnalysisErrors(FileAnalysisErrors fileErrors) {
        fileErrors.errors.removeWhere((AnalysisError error) => error.type == 'TODO');

        errors.addAll(fileErrors.errors);
      }

      server.onErrors.listen(handleAnalysisErrors);
96 97 98

      await server.start();
      // Completing the future in the callback can't fail.
99
      unawaited(server.onExit.then<void>((int? exitCode) {
100
        if (!analysisCompleter.isCompleted) {
101 102 103 104 105 106
          analysisCompleter.completeError(
            // Include the last 20 lines of server output in exception message
            Exception(
              'analysis server exited with code $exitCode and output:\n${server.getLogs(20)}',
            ),
          );
107 108 109 110 111
        }
      }));

      // collect results
      timer = Stopwatch()..start();
112 113 114
      final String message = items.length > 1
          ? '${items.length} ${items.length == 1 ? 'item' : 'items'}'
          : fileSystem.path.basename(items.first);
115
      progress = argResults['preamble'] == true
116 117 118 119 120 121 122 123 124 125 126
          ? logger.startProgress(
            'Analyzing $message...',
          )
          : null;

      await analysisCompleter.future;
    } finally {
      await server.dispose();
      progress?.cancel();
      timer?.stop();
    }
127 128

    // emit benchmarks
129
    if (isBenchmarking) {
130
      writeBenchmark(timer, errors.length);
131
    }
132

133 134
    // --write
    dumpErrors(errors.map<String>((AnalysisError error) => error.toLegacyString()));
135

136
    // report errors
137
    if (errors.isNotEmpty && (argResults['preamble'] as bool)) {
138
      logger.printStatus('');
139
    }
140
    errors.sort();
141
    for (final AnalysisError error in errors) {
142
      logger.printStatus(error.toString(), hangingIndent: 7);
143
    }
144

145
    final int errorCount = errors.length;
146
    final String seconds = (timer.elapsedMilliseconds / 1000.0).toStringAsFixed(1);
147 148 149 150
    final String errorsMessage = AnalyzeBase.generateErrorsMessage(
      issueCount: errorCount,
      seconds: seconds,
    );
151

152
    if (errorCount > 0) {
153
      logger.printStatus('');
154
      throwToolExit(errorsMessage, exitCode: _isFatal(errors) ? 1 : 0);
155
    }
156

157 158
    if (argResults['congratulate'] as bool) {
      logger.printStatus(errorsMessage);
159 160
    }

161 162
    if (server.didServerErrorOccur) {
      throwToolExit('Server error(s) occurred. (ran in ${seconds}s)');
163
    }
164
  }
165 166 167 168 169 170 171

  bool _isFatal(List<AnalysisError> errors) {
    for (final AnalysisError error in errors) {
      final AnalysisSeverity severityLevel = error.writtenError.severityLevel;
      if (severityLevel == AnalysisSeverity.error) {
        return true;
      }
172
      if (severityLevel == AnalysisSeverity.warning && argResults['fatal-warnings'] as bool) {
173 174 175 176 177 178 179 180
        return true;
      }
      if (severityLevel == AnalysisSeverity.info && argResults['fatal-infos'] as bool) {
        return true;
      }
    }
    return false;
  }
181
}