OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library analyzer_cli.src.build_mode; | 5 library analyzer_cli.src.build_mode; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io' as io; | 8 import 'dart:io' as io; |
9 | 9 |
10 import 'package:analyzer/error/error.dart'; | 10 import 'package:analyzer/error/error.dart'; |
(...skipping 11 matching lines...) Expand all Loading... |
22 import 'package:analyzer/src/summary/link.dart'; | 22 import 'package:analyzer/src/summary/link.dart'; |
23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; | 23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
24 import 'package:analyzer/src/summary/summarize_ast.dart'; | 24 import 'package:analyzer/src/summary/summarize_ast.dart'; |
25 import 'package:analyzer/src/summary/summarize_elements.dart'; | 25 import 'package:analyzer/src/summary/summarize_elements.dart'; |
26 import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk; | 26 import 'package:analyzer/src/summary/summary_sdk.dart' show SummaryBasedDartSdk; |
27 import 'package:analyzer_cli/src/driver.dart'; | 27 import 'package:analyzer_cli/src/driver.dart'; |
28 import 'package:analyzer_cli/src/error_formatter.dart'; | 28 import 'package:analyzer_cli/src/error_formatter.dart'; |
29 import 'package:analyzer_cli/src/error_severity.dart'; | 29 import 'package:analyzer_cli/src/error_severity.dart'; |
30 import 'package:analyzer_cli/src/options.dart'; | 30 import 'package:analyzer_cli/src/options.dart'; |
31 import 'package:bazel_worker/bazel_worker.dart'; | 31 import 'package:bazel_worker/bazel_worker.dart'; |
| 32 import 'package:collection/collection.dart'; |
| 33 import 'package:convert/convert.dart'; |
32 import 'package:front_end/src/base/performace_logger.dart'; | 34 import 'package:front_end/src/base/performace_logger.dart'; |
33 import 'package:front_end/src/byte_store/byte_store.dart'; | 35 import 'package:front_end/src/byte_store/byte_store.dart'; |
| 36 import 'package:front_end/src/byte_store/cache.dart'; |
34 | 37 |
35 /** | 38 /** |
36 * Persistent Bazel worker. | 39 * Persistent Bazel worker. |
37 */ | 40 */ |
38 class AnalyzerWorkerLoop extends AsyncWorkerLoop { | 41 class AnalyzerWorkerLoop extends AsyncWorkerLoop { |
39 final ResourceProvider resourceProvider; | 42 final ResourceProvider resourceProvider; |
40 final PerformanceLog logger = new PerformanceLog(null); | 43 final PerformanceLog logger = new PerformanceLog(null); |
41 final String dartSdkPath; | 44 final String dartSdkPath; |
| 45 WorkerPackageBundleCache packageBundleCache; |
42 | 46 |
43 final StringBuffer errorBuffer = new StringBuffer(); | 47 final StringBuffer errorBuffer = new StringBuffer(); |
44 final StringBuffer outBuffer = new StringBuffer(); | 48 final StringBuffer outBuffer = new StringBuffer(); |
45 | 49 |
46 AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection, | 50 AnalyzerWorkerLoop(this.resourceProvider, AsyncWorkerConnection connection, |
47 {this.dartSdkPath}) | 51 {this.dartSdkPath}) |
48 : super(connection: connection); | 52 : super(connection: connection) { |
| 53 packageBundleCache = new WorkerPackageBundleCache( |
| 54 resourceProvider, logger, 256 * 1024 * 1024); |
| 55 } |
49 | 56 |
50 factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider, | 57 factory AnalyzerWorkerLoop.std(ResourceProvider resourceProvider, |
51 {io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) { | 58 {io.Stdin stdinStream, io.Stdout stdoutStream, String dartSdkPath}) { |
52 AsyncWorkerConnection connection = new StdAsyncWorkerConnection( | 59 AsyncWorkerConnection connection = new StdAsyncWorkerConnection( |
53 inputStream: stdinStream, outputStream: stdoutStream); | 60 inputStream: stdinStream, outputStream: stdoutStream); |
54 return new AnalyzerWorkerLoop(resourceProvider, connection, | 61 return new AnalyzerWorkerLoop(resourceProvider, connection, |
55 dartSdkPath: dartSdkPath); | 62 dartSdkPath: dartSdkPath); |
56 } | 63 } |
57 | 64 |
58 /** | 65 /** |
59 * Performs analysis with given [options]. | 66 * Performs analysis with given [options]. |
60 */ | 67 */ |
61 Future<Null> analyze(CommandLineOptions options) async { | 68 Future<Null> analyze( |
| 69 CommandLineOptions options, Map<String, WorkerInput> inputs) async { |
| 70 var packageBundleProvider = |
| 71 new WorkerPackageBundleProvider(packageBundleCache, inputs); |
62 var buildMode = new BuildMode( | 72 var buildMode = new BuildMode( |
63 resourceProvider, options, new AnalysisStats(), | 73 resourceProvider, options, new AnalysisStats(), |
64 logger: logger); | 74 logger: logger, packageBundleProvider: packageBundleProvider); |
65 await buildMode.analyze(); | 75 await buildMode.analyze(); |
66 AnalysisEngine.instance.clearCaches(); | 76 AnalysisEngine.instance.clearCaches(); |
67 } | 77 } |
68 | 78 |
69 /** | 79 /** |
70 * Perform a single loop step. | 80 * Perform a single loop step. |
71 */ | 81 */ |
72 @override | 82 @override |
73 Future<WorkResponse> performRequest(WorkRequest request) async { | 83 Future<WorkResponse> performRequest(WorkRequest request) async { |
74 return logger.run('Perform request', () async { | 84 return logger.run('Perform request', () async { |
75 errorBuffer.clear(); | 85 errorBuffer.clear(); |
76 outBuffer.clear(); | 86 outBuffer.clear(); |
77 try { | 87 try { |
| 88 // Prepare inputs with their digests. |
| 89 Map<String, WorkerInput> inputs = {}; |
| 90 for (var input in request.inputs) { |
| 91 inputs[input.path] = new WorkerInput(input.path, input.digest); |
| 92 } |
| 93 |
78 // Add in the dart-sdk argument if `dartSdkPath` is not null, | 94 // Add in the dart-sdk argument if `dartSdkPath` is not null, |
79 // otherwise it will try to find the currently installed sdk. | 95 // otherwise it will try to find the currently installed sdk. |
80 var arguments = request.arguments.toList(); | 96 var arguments = request.arguments.toList(); |
81 if (dartSdkPath != null && | 97 if (dartSdkPath != null && |
82 !arguments.any((arg) => arg.startsWith('--dart-sdk'))) { | 98 !arguments.any((arg) => arg.startsWith('--dart-sdk'))) { |
83 arguments.add('--dart-sdk=$dartSdkPath'); | 99 arguments.add('--dart-sdk=$dartSdkPath'); |
84 } | 100 } |
| 101 |
85 // Prepare options. | 102 // Prepare options. |
86 CommandLineOptions options = | 103 CommandLineOptions options = |
87 CommandLineOptions.parse(arguments, printAndFail: (String msg) { | 104 CommandLineOptions.parse(arguments, printAndFail: (String msg) { |
88 throw new ArgumentError(msg); | 105 throw new ArgumentError(msg); |
89 }); | 106 }); |
| 107 |
90 // Analyze and respond. | 108 // Analyze and respond. |
91 await analyze(options); | 109 await analyze(options, inputs); |
92 String msg = _getErrorOutputBuffersText(); | 110 String msg = _getErrorOutputBuffersText(); |
93 return new WorkResponse() | 111 return new WorkResponse() |
94 ..exitCode = EXIT_CODE_OK | 112 ..exitCode = EXIT_CODE_OK |
95 ..output = msg; | 113 ..output = msg; |
96 } catch (e, st) { | 114 } catch (e, st) { |
97 String msg = _getErrorOutputBuffersText(); | 115 String msg = _getErrorOutputBuffersText(); |
98 msg += '$e\n$st'; | 116 msg += '$e\n$st'; |
99 return new WorkResponse() | 117 return new WorkResponse() |
100 ..exitCode = EXIT_CODE_ERROR | 118 ..exitCode = EXIT_CODE_ERROR |
101 ..output = msg; | 119 ..output = msg; |
(...skipping 27 matching lines...) Expand all Loading... |
129 } | 147 } |
130 | 148 |
131 /** | 149 /** |
132 * Analyzer used when the "--build-mode" option is supplied. | 150 * Analyzer used when the "--build-mode" option is supplied. |
133 */ | 151 */ |
134 class BuildMode { | 152 class BuildMode { |
135 final ResourceProvider resourceProvider; | 153 final ResourceProvider resourceProvider; |
136 final CommandLineOptions options; | 154 final CommandLineOptions options; |
137 final AnalysisStats stats; | 155 final AnalysisStats stats; |
138 final PerformanceLog logger; | 156 final PerformanceLog logger; |
| 157 final PackageBundleProvider packageBundleProvider; |
139 | 158 |
140 SummaryDataStore summaryDataStore; | 159 SummaryDataStore summaryDataStore; |
141 AnalysisOptions analysisOptions; | 160 AnalysisOptions analysisOptions; |
142 Map<Uri, File> uriToFileMap; | 161 Map<Uri, File> uriToFileMap; |
143 final List<Source> explicitSources = <Source>[]; | 162 final List<Source> explicitSources = <Source>[]; |
144 final List<PackageBundle> unlinkedBundles = <PackageBundle>[]; | 163 final List<PackageBundle> unlinkedBundles = <PackageBundle>[]; |
145 | 164 |
146 AnalysisDriver analysisDriver; | 165 AnalysisDriver analysisDriver; |
147 | 166 |
148 PackageBundleAssembler assembler; | 167 PackageBundleAssembler assembler; |
149 final Set<Source> processedSources = new Set<Source>(); | 168 final Set<Source> processedSources = new Set<Source>(); |
150 final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{}; | 169 final Map<String, UnlinkedUnit> uriToUnit = <String, UnlinkedUnit>{}; |
151 | 170 |
152 BuildMode(this.resourceProvider, this.options, this.stats, | 171 BuildMode(this.resourceProvider, this.options, this.stats, |
153 {PerformanceLog logger}) | 172 {PerformanceLog logger, PackageBundleProvider packageBundleProvider}) |
154 : logger = logger ?? new PerformanceLog(null); | 173 : logger = logger ?? new PerformanceLog(null), |
| 174 packageBundleProvider = packageBundleProvider ?? |
| 175 new DirectPackageBundleProvider(resourceProvider); |
155 | 176 |
156 bool get _shouldOutputSummary => | 177 bool get _shouldOutputSummary => |
157 options.buildSummaryOutput != null || | 178 options.buildSummaryOutput != null || |
158 options.buildSummaryOutputSemantic != null; | 179 options.buildSummaryOutputSemantic != null; |
159 | 180 |
160 /** | 181 /** |
161 * Perform package analysis according to the given [options]. | 182 * Perform package analysis according to the given [options]. |
162 */ | 183 */ |
163 Future<ErrorSeverity> analyze() async { | 184 Future<ErrorSeverity> analyze() async { |
164 return await logger.runAsync('Analyze', () async { | 185 return await logger.runAsync('Analyze', () async { |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
296 } | 317 } |
297 return maxSeverity; | 318 return maxSeverity; |
298 } | 319 } |
299 | 320 |
300 void _createAnalysisDriver() { | 321 void _createAnalysisDriver() { |
301 // Read the summaries. | 322 // Read the summaries. |
302 summaryDataStore = new SummaryDataStore(<String>[]); | 323 summaryDataStore = new SummaryDataStore(<String>[]); |
303 | 324 |
304 // Adds a bundle at `path` to `summaryDataStore`. | 325 // Adds a bundle at `path` to `summaryDataStore`. |
305 PackageBundle addBundle(String path) { | 326 PackageBundle addBundle(String path) { |
306 var bundle = | 327 PackageBundle bundle = packageBundleProvider.get(path); |
307 new PackageBundle.fromBuffer(new io.File(path).readAsBytesSync()); | |
308 summaryDataStore.addBundle(path, bundle); | 328 summaryDataStore.addBundle(path, bundle); |
309 return bundle; | 329 return bundle; |
310 } | 330 } |
311 | 331 |
312 int numInputs = options.buildSummaryInputs.length + | 332 int numInputs = options.buildSummaryInputs.length + |
313 options.buildSummaryUnlinkedInputs.length; | 333 options.buildSummaryUnlinkedInputs.length; |
314 logger.run('Add $numInputs input summaries', () { | 334 logger.run('Add $numInputs input summaries', () { |
315 for (var path in options.buildSummaryInputs) { | 335 for (var path in options.buildSummaryInputs) { |
316 var bundle = addBundle(path); | 336 var bundle = addBundle(path); |
317 if (bundle.linkedLibraryUris.isEmpty && | 337 if (bundle.linkedLibraryUris.isEmpty && |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
461 StringSink sink = options.machineFormat ? errorSink : outSink; | 481 StringSink sink = options.machineFormat ? errorSink : outSink; |
462 sink.write(buffer); | 482 sink.write(buffer); |
463 } else { | 483 } else { |
464 new io.File(outputPath).writeAsStringSync(buffer.toString()); | 484 new io.File(outputPath).writeAsStringSync(buffer.toString()); |
465 } | 485 } |
466 }); | 486 }); |
467 } | 487 } |
468 } | 488 } |
469 | 489 |
470 /** | 490 /** |
| 491 * [PackageBundleProvider] that always reads from the [ResourceProvider]. |
| 492 */ |
| 493 class DirectPackageBundleProvider implements PackageBundleProvider { |
| 494 final ResourceProvider resourceProvider; |
| 495 |
| 496 DirectPackageBundleProvider(this.resourceProvider); |
| 497 |
| 498 @override |
| 499 PackageBundle get(String path) { |
| 500 var bytes = new io.File(path).readAsBytesSync(); |
| 501 return new PackageBundle.fromBuffer(bytes); |
| 502 } |
| 503 } |
| 504 |
| 505 /** |
471 * Instances of the class [ExplicitSourceResolver] map URIs to files on disk | 506 * Instances of the class [ExplicitSourceResolver] map URIs to files on disk |
472 * using a fixed mapping provided at construction time. | 507 * using a fixed mapping provided at construction time. |
473 */ | 508 */ |
474 class ExplicitSourceResolver extends UriResolver { | 509 class ExplicitSourceResolver extends UriResolver { |
475 final Map<Uri, File> uriToFileMap; | 510 final Map<Uri, File> uriToFileMap; |
476 final Map<String, Uri> pathToUriMap; | 511 final Map<String, Uri> pathToUriMap; |
477 | 512 |
478 /** | 513 /** |
479 * Construct an [ExplicitSourceResolver] based on the given [uriToFileMap]. | 514 * Construct an [ExplicitSourceResolver] based on the given [uriToFileMap]. |
480 */ | 515 */ |
(...skipping 22 matching lines...) Expand all Loading... |
503 * Build the inverse mapping of [uriToSourceMap]. | 538 * Build the inverse mapping of [uriToSourceMap]. |
504 */ | 539 */ |
505 static Map<String, Uri> _computePathToUriMap(Map<Uri, File> uriToSourceMap) { | 540 static Map<String, Uri> _computePathToUriMap(Map<Uri, File> uriToSourceMap) { |
506 Map<String, Uri> pathToUriMap = <String, Uri>{}; | 541 Map<String, Uri> pathToUriMap = <String, Uri>{}; |
507 uriToSourceMap.forEach((Uri uri, File file) { | 542 uriToSourceMap.forEach((Uri uri, File file) { |
508 pathToUriMap[file.path] = uri; | 543 pathToUriMap[file.path] = uri; |
509 }); | 544 }); |
510 return pathToUriMap; | 545 return pathToUriMap; |
511 } | 546 } |
512 } | 547 } |
| 548 |
| 549 /** |
| 550 * Provider for [PackageBundle]s by file paths. |
| 551 */ |
| 552 abstract class PackageBundleProvider { |
| 553 /** |
| 554 * Return the [PackageBundle] for the file with the given [path]. |
| 555 */ |
| 556 PackageBundle get(String path); |
| 557 } |
| 558 |
| 559 /** |
| 560 * Worker input. |
| 561 * |
| 562 * Bazel does not specify the format of the digest, so we cannot assume that |
| 563 * the digest itself is enough to uniquely identify inputs. So, we use a pair |
| 564 * of path + digest. |
| 565 */ |
| 566 class WorkerInput { |
| 567 static const _digestEquality = const ListEquality<int>(); |
| 568 |
| 569 final String path; |
| 570 final List<int> digest; |
| 571 |
| 572 WorkerInput(this.path, this.digest); |
| 573 |
| 574 @override |
| 575 int get hashCode => _digestEquality.hash(digest); |
| 576 |
| 577 @override |
| 578 bool operator ==(Object other) { |
| 579 return other is WorkerInput && |
| 580 other.path == path && |
| 581 _digestEquality.equals(other.digest, digest); |
| 582 } |
| 583 |
| 584 @override |
| 585 String toString() => '$path @ ${hex.encode(digest)}'; |
| 586 } |
| 587 |
| 588 /** |
| 589 * Value object for [WorkerPackageBundleCache]. |
| 590 */ |
| 591 class WorkerPackageBundle { |
| 592 final List<int> bytes; |
| 593 final PackageBundle bundle; |
| 594 |
| 595 WorkerPackageBundle(this.bytes, this.bundle); |
| 596 |
| 597 /** |
| 598 * Approximation of a bundle size in memory. |
| 599 */ |
| 600 int get size => bytes.length * 3; |
| 601 } |
| 602 |
| 603 /** |
| 604 * Cache of [PackageBundle]s. |
| 605 */ |
| 606 class WorkerPackageBundleCache { |
| 607 final ResourceProvider resourceProvider; |
| 608 final PerformanceLog logger; |
| 609 final Cache<WorkerInput, WorkerPackageBundle> _cache; |
| 610 |
| 611 WorkerPackageBundleCache(this.resourceProvider, this.logger, int maxSizeBytes) |
| 612 : _cache = new Cache<WorkerInput, WorkerPackageBundle>( |
| 613 maxSizeBytes, (value) => value.size); |
| 614 |
| 615 /** |
| 616 * Get the [PackageBundle] from the file with the given [path] in the context |
| 617 * of the given worker [inputs]. |
| 618 */ |
| 619 PackageBundle get(Map<String, WorkerInput> inputs, String path) { |
| 620 WorkerInput input = inputs[path]; |
| 621 |
| 622 // The input must be not null, otherwise we're not expected to read |
| 623 // this file, but we check anyway to be safe. |
| 624 if (input == null) { |
| 625 logger.writeln('Read $path outside of the inputs.'); |
| 626 var bytes = resourceProvider.getFile(path).readAsBytesSync(); |
| 627 return new PackageBundle.fromBuffer(bytes); |
| 628 } |
| 629 |
| 630 return _cache.get(input, () { |
| 631 logger.writeln('Read $input.'); |
| 632 var bytes = resourceProvider.getFile(path).readAsBytesSync(); |
| 633 var bundle = new PackageBundle.fromBuffer(bytes); |
| 634 return new WorkerPackageBundle(bytes, bundle); |
| 635 }).bundle; |
| 636 } |
| 637 } |
| 638 |
| 639 /** |
| 640 * [PackageBundleProvider] that reads from [WorkerPackageBundleCache] using |
| 641 * the request specific [inputs]. |
| 642 */ |
| 643 class WorkerPackageBundleProvider implements PackageBundleProvider { |
| 644 final WorkerPackageBundleCache cache; |
| 645 final Map<String, WorkerInput> inputs; |
| 646 |
| 647 WorkerPackageBundleProvider(this.cache, this.inputs); |
| 648 |
| 649 @override |
| 650 PackageBundle get(String path) { |
| 651 return cache.get(inputs, path); |
| 652 } |
| 653 } |
OLD | NEW |