analyze_continuously.dart 5.37 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
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:args/args.dart';
6 7
import 'package:meta/meta.dart';
import 'package:process/process.dart';
8

9
import '../artifacts.dart';
10
import '../base/common.dart';
11
import '../base/file_system.dart';
12
import '../base/io.dart';
13
import '../base/logger.dart';
14
import '../base/platform.dart';
15
import '../base/terminal.dart';
16
import '../dart/analysis.dart';
17
import 'analyze_base.dart';
18

19
class AnalyzeContinuously extends AnalyzeBase {
20 21 22 23
  AnalyzeContinuously(
    ArgResults argResults,
    List<String> repoRoots,
    List<Directory> repoPackages, {
24 25
    @required FileSystem fileSystem,
    @required Logger logger,
26
    @required Terminal terminal,
27 28
    @required Platform platform,
    @required ProcessManager processManager,
29
    @required Artifacts artifacts,
30 31 32 33 34 35 36 37 38
  }) : super(
        argResults,
        repoPackages: repoPackages,
        repoRoots: repoRoots,
        fileSystem: fileSystem,
        logger: logger,
        platform: platform,
        terminal: terminal,
        processManager: processManager,
39
        artifacts: artifacts,
40
      );
41

42 43
  String analysisTarget;
  bool firstAnalysis = true;
44
  Set<String> analyzedPaths = <String>{};
45 46 47 48 49 50
  Map<String, List<AnalysisError>> analysisErrors = <String, List<AnalysisError>>{};
  Stopwatch analysisTimer;
  int lastErrorCount = 0;
  Status analysisStatus;

  @override
51
  Future<void> analyze() async {
52 53
    List<String> directories;

54
    if (isFlutterRepo) {
55
      final PackageDependencyTracker dependencies = PackageDependencyTracker();
56
      dependencies.checkForConflictingDependencies(repoPackages, dependencies);
57 58

      directories = repoRoots;
59
      analysisTarget = 'Flutter repository';
60

61
      logger.printTrace('Analyzing Flutter repository:');
62
      for (final String projectPath in repoRoots) {
63
        logger.printTrace('  ${fileSystem.path.relative(projectPath)}');
64
      }
65
    } else {
66 67
      directories = <String>[fileSystem.currentDirectory.path];
      analysisTarget = fileSystem.currentDirectory.path;
68 69
    }

70 71 72
    final AnalysisServer server = AnalysisServer(
      sdkPath,
      directories,
73 74 75 76 77 78
      fileSystem: fileSystem,
      logger: logger,
      platform: platform,
      processManager: processManager,
      terminal: terminal,
    );
79 80 81 82 83 84
    server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
    server.onErrors.listen(_handleAnalysisErrors);

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

85
    final String message = 'Analysis server exited with code $exitCode.';
86
    if (exitCode != 0) {
87
      throwToolExit(message, exitCode: exitCode);
88
    }
89
    logger.printStatus(message);
90

91
    if (server.didServerErrorOccur) {
92
      throwToolExit('Server error(s) occurred.');
93
    }
94 95 96 97 98
  }

  void _handleAnalysisStatus(AnalysisServer server, bool isAnalyzing) {
    if (isAnalyzing) {
      analysisStatus?.cancel();
99
      if (!firstAnalysis) {
100
        logger.printStatus('\n');
101
      }
102
      analysisStatus = logger.startProgress('Analyzing $analysisTarget...');
103
      analyzedPaths.clear();
104
      analysisTimer = Stopwatch()..start();
105
    } else {
Devon Carew's avatar
Devon Carew committed
106
      analysisStatus?.stop();
107
      analysisStatus = null;
108 109
      analysisTimer.stop();

110
      logger.printStatus(terminal.clearScreen(), newline: false);
111 112

      // Remove errors for deleted files, sort, and print errors.
113
      final List<AnalysisError> errors = <AnalysisError>[];
114
      for (final String path in analysisErrors.keys.toList()) {
115
        if (fileSystem.isFileSync(path)) {
116
          errors.addAll(analysisErrors[path]);
117 118 119 120 121
        } else {
          analysisErrors.remove(path);
        }
      }

122 123 124
      int issueCount = errors.length;

      // count missing dartdocs
125 126
      final int undocumentedMembers = AnalyzeBase.countMissingDartDocs(errors);
      if (!isDartDocs) {
127 128 129 130
        errors.removeWhere((AnalysisError error) => error.code == 'public_member_api_docs');
        issueCount -= undocumentedMembers;
      }

131
      errors.sort();
132

133
      for (final AnalysisError error in errors) {
134
        logger.printStatus(error.toString());
135
        if (error.code != null) {
136
          logger.printTrace('error code: ${error.code}');
137
        }
138 139
      }

140
      dumpErrors(errors.map<String>((AnalysisError error) => error.toLegacyString()));
141

142
      final int issueDiff = issueCount - lastErrorCount;
143
      lastErrorCount = issueCount;
144
      final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
145
      final String dartDocMessage = AnalyzeBase.generateDartDocMessage(undocumentedMembers);
146 147 148 149 150 151 152 153
      final String errorsMessage = AnalyzeBase.generateErrorsMessage(
        issueCount: issueCount,
        issueDiff: issueDiff,
        files: analyzedPaths.length,
        seconds: seconds,
        undocumentedMembers: undocumentedMembers,
        dartDocMessage: dartDocMessage,
      );
154

155
      logger.printStatus(errorsMessage);
156 157

      if (firstAnalysis && isBenchmarking) {
158
        writeBenchmark(analysisTimer, issueCount, undocumentedMembers);
159
        server.dispose().whenComplete(() { exit(issueCount > 0 ? 1 : 0); });
160 161 162 163 164 165 166
      }

      firstAnalysis = false;
    }
  }

  void _handleAnalysisErrors(FileAnalysisErrors fileErrors) {
167
    fileErrors.errors.removeWhere((AnalysisError error) => error.type == 'TODO');
168 169 170 171 172

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