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


6
import '../base/common.dart';
7
import '../base/file_system.dart';
8
import '../base/io.dart';
9
import '../base/logger.dart';
10
import '../dart/analysis.dart';
11
import 'analyze_base.dart';
12

13
class AnalyzeContinuously extends AnalyzeBase {
14
  AnalyzeContinuously(
15
    super.argResults,
16
    List<Directory> repoPackages, {
17 18 19 20 21 22
    required super.fileSystem,
    required super.logger,
    required super.terminal,
    required super.platform,
    required super.processManager,
    required super.artifacts,
23
    required super.suppressAnalytics,
24 25 26
  }) : super(
        repoPackages: repoPackages,
      );
27

28
  String? analysisTarget;
29
  bool firstAnalysis = true;
30
  Set<String> analyzedPaths = <String>{};
31
  Map<String, List<AnalysisError>> analysisErrors = <String, List<AnalysisError>>{};
32
  final Stopwatch analysisTimer = Stopwatch();
33
  int lastErrorCount = 0;
34
  Status? analysisStatus;
35 36

  @override
37
  Future<void> analyze() async {
38 39
    List<String> directories;

40
    if (isFlutterRepo) {
41
      final PackageDependencyTracker dependencies = PackageDependencyTracker();
42
      dependencies.checkForConflictingDependencies(repoPackages, dependencies);
43

44
      directories = <String>[flutterRoot];
45
      analysisTarget = 'Flutter repository';
46

47
      logger.printTrace('Analyzing Flutter repository:');
48
    } else {
49 50
      directories = <String>[fileSystem.currentDirectory.path];
      analysisTarget = fileSystem.currentDirectory.path;
51 52
    }

53 54 55
    final AnalysisServer server = AnalysisServer(
      sdkPath,
      directories,
56 57 58 59 60
      fileSystem: fileSystem,
      logger: logger,
      platform: platform,
      processManager: processManager,
      terminal: terminal,
61
      protocolTrafficLog: protocolTrafficLog,
62
      suppressAnalytics: suppressAnalytics,
63
    );
64 65 66 67
    server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
    server.onErrors.listen(_handleAnalysisErrors);

    await server.start();
68
    final int? exitCode = await server.onExit;
69

70
    final String message = 'Analysis server exited with code $exitCode.';
71
    if (exitCode != 0) {
72
      throwToolExit(message, exitCode: exitCode);
73
    }
74
    logger.printStatus(message);
75

76
    if (server.didServerErrorOccur) {
77
      throwToolExit('Server error(s) occurred.');
78
    }
79 80 81 82 83
  }

  void _handleAnalysisStatus(AnalysisServer server, bool isAnalyzing) {
    if (isAnalyzing) {
      analysisStatus?.cancel();
84
      if (!firstAnalysis) {
85
        logger.printStatus('\n');
86
      }
87
      analysisStatus = logger.startProgress('Analyzing $analysisTarget...');
88
      analyzedPaths.clear();
89
      analysisTimer.start();
90
    } else {
Devon Carew's avatar
Devon Carew committed
91
      analysisStatus?.stop();
92
      analysisStatus = null;
93 94
      analysisTimer.stop();

95
      logger.printStatus(terminal.clearScreen(), newline: false);
96 97

      // Remove errors for deleted files, sort, and print errors.
98 99
      final List<AnalysisError> sortedErrors = <AnalysisError>[];
      final List<String> pathsToRemove = <String>[];
100
      analysisErrors.forEach((String path, List<AnalysisError> errors) {
101
        if (fileSystem.isFileSync(path)) {
102
          sortedErrors.addAll(errors);
103
        } else {
104
          pathsToRemove.add(path);
105
        }
106
      });
107
      analysisErrors.removeWhere((String path, _) => pathsToRemove.contains(path));
108

109
      sortedErrors.sort();
110

111
      for (final AnalysisError error in sortedErrors) {
112
        logger.printStatus(error.toString());
113
        logger.printTrace('error code: ${error.code}');
114 115
      }

116
      dumpErrors(sortedErrors.map<String>((AnalysisError error) => error.toLegacyString()));
117

118
      final int issueCount = sortedErrors.length;
119
      final int issueDiff = issueCount - lastErrorCount;
120
      lastErrorCount = issueCount;
121 122 123 124 125 126 127
      final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
      final String errorsMessage = AnalyzeBase.generateErrorsMessage(
        issueCount: issueCount,
        issueDiff: issueDiff,
        files: analyzedPaths.length,
        seconds: seconds,
      );
128

129
      logger.printStatus(errorsMessage);
130 131

      if (firstAnalysis && isBenchmarking) {
132
        writeBenchmark(analysisTimer, issueCount);
133
        server.dispose().whenComplete(() { exit(issueCount > 0 ? 1 : 0); });
134 135 136 137 138 139 140
      }

      firstAnalysis = false;
    }
  }

  void _handleAnalysisErrors(FileAnalysisErrors fileErrors) {
141
    fileErrors.errors.removeWhere((AnalysisError error) => error.type == 'TODO');
142 143 144 145 146

    analyzedPaths.add(fileErrors.file);
    analysisErrors[fileErrors.file] = fileErrors.errors;
  }
}