| 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:convert'; | |
| 8 import 'dart:core' hide Resource; | 7 import 'dart:core' hide Resource; |
| 9 import 'dart:io' as io; | 8 import 'dart:io' as io; |
| 10 | 9 |
| 10 import 'package:protobuf/protobuf.dart'; |
| 11 |
| 11 import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit; | 12 import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit; |
| 12 import 'package:analyzer/dart/element/element.dart'; | 13 import 'package:analyzer/dart/element/element.dart'; |
| 13 import 'package:analyzer/file_system/file_system.dart'; | 14 import 'package:analyzer/file_system/file_system.dart'; |
| 14 import 'package:analyzer/file_system/physical_file_system.dart'; | 15 import 'package:analyzer/file_system/physical_file_system.dart'; |
| 15 import 'package:analyzer/src/generated/engine.dart'; | 16 import 'package:analyzer/src/generated/engine.dart'; |
| 16 import 'package:analyzer/src/generated/error.dart'; | 17 import 'package:analyzer/src/generated/error.dart'; |
| 17 import 'package:analyzer/src/generated/java_io.dart'; | 18 import 'package:analyzer/src/generated/java_io.dart'; |
| 18 import 'package:analyzer/src/generated/sdk_io.dart'; | 19 import 'package:analyzer/src/generated/sdk_io.dart'; |
| 19 import 'package:analyzer/src/generated/source.dart'; | 20 import 'package:analyzer/src/generated/source.dart'; |
| 20 import 'package:analyzer/src/generated/source_io.dart'; | 21 import 'package:analyzer/src/generated/source_io.dart'; |
| 21 import 'package:analyzer/src/summary/format.dart'; | 22 import 'package:analyzer/src/summary/format.dart'; |
| 22 import 'package:analyzer/src/summary/idl.dart'; | 23 import 'package:analyzer/src/summary/idl.dart'; |
| 23 import 'package:analyzer/src/summary/package_bundle_reader.dart'; | 24 import 'package:analyzer/src/summary/package_bundle_reader.dart'; |
| 24 import 'package:analyzer/src/summary/prelink.dart'; | 25 import 'package:analyzer/src/summary/prelink.dart'; |
| 25 import 'package:analyzer/src/summary/summarize_ast.dart'; | 26 import 'package:analyzer/src/summary/summarize_ast.dart'; |
| 26 import 'package:analyzer/src/summary/summarize_elements.dart'; | 27 import 'package:analyzer/src/summary/summarize_elements.dart'; |
| 27 import 'package:analyzer/task/dart.dart'; | 28 import 'package:analyzer/task/dart.dart'; |
| 28 import 'package:analyzer_cli/src/analyzer_impl.dart'; | 29 import 'package:analyzer_cli/src/analyzer_impl.dart'; |
| 29 import 'package:analyzer_cli/src/driver.dart'; | 30 import 'package:analyzer_cli/src/driver.dart'; |
| 30 import 'package:analyzer_cli/src/error_formatter.dart'; | 31 import 'package:analyzer_cli/src/error_formatter.dart'; |
| 31 import 'package:analyzer_cli/src/options.dart'; | 32 import 'package:analyzer_cli/src/options.dart'; |
| 32 | 33 |
| 34 import 'message_grouper.dart'; |
| 35 import 'worker_protocol.pb.dart'; |
| 36 |
| 33 /** | 37 /** |
| 34 * Analyzer used when the "--build-mode" option is supplied. | 38 * Analyzer used when the "--build-mode" option is supplied. |
| 35 */ | 39 */ |
| 36 class BuildMode { | 40 class BuildMode { |
| 37 final CommandLineOptions options; | 41 final CommandLineOptions options; |
| 38 final AnalysisStats stats; | 42 final AnalysisStats stats; |
| 39 | 43 |
| 40 final ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE; | 44 final ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE; |
| 41 SummaryDataStore summaryDataStore; | 45 SummaryDataStore summaryDataStore; |
| 42 InternalAnalysisContext context; | 46 InternalAnalysisContext context; |
| (...skipping 234 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 277 } | 281 } |
| 278 Uri uri = Uri.parse(sourceFile.substring(0, pipeIndex)); | 282 Uri uri = Uri.parse(sourceFile.substring(0, pipeIndex)); |
| 279 String path = sourceFile.substring(pipeIndex + 1); | 283 String path = sourceFile.substring(pipeIndex + 1); |
| 280 uriToFileMap[uri] = new JavaFile(path); | 284 uriToFileMap[uri] = new JavaFile(path); |
| 281 } | 285 } |
| 282 return uriToFileMap; | 286 return uriToFileMap; |
| 283 } | 287 } |
| 284 } | 288 } |
| 285 | 289 |
| 286 /** | 290 /** |
| 287 * Interface that every worker related data object has. | |
| 288 */ | |
| 289 abstract class WorkDataObject { | |
| 290 /** | |
| 291 * Translate the data in this class into a JSON map. | |
| 292 */ | |
| 293 Map<String, Object> toJson(); | |
| 294 } | |
| 295 | |
| 296 /** | |
| 297 * Connection between a worker and input / output. | 291 * Connection between a worker and input / output. |
| 298 */ | 292 */ |
| 299 abstract class WorkerConnection { | 293 abstract class WorkerConnection { |
| 300 /** | 294 /** |
| 301 * Read a new line. Block until a line is read. Return `null` if EOF. | 295 * Read a new [WorkRequest]. Returns [null] when there are no more requests. |
| 302 */ | 296 */ |
| 303 String readLineSync(); | 297 WorkRequest readRequest(); |
| 304 | 298 |
| 305 /** | 299 /** |
| 306 * Write the given [json] as a new line to the output. | 300 * Write the given [response] as bytes to the output. |
| 307 */ | 301 */ |
| 308 void writeJson(Map<String, Object> json); | 302 void writeResponse(WorkResponse response); |
| 309 } | 303 } |
| 310 | 304 |
| 311 /** | 305 /** |
| 312 * Persistent Bazel worker. | 306 * Persistent Bazel worker. |
| 313 */ | 307 */ |
| 314 class WorkerLoop { | 308 class WorkerLoop { |
| 315 static const int EXIT_CODE_OK = 0; | 309 static const int EXIT_CODE_OK = 0; |
| 316 static const int EXIT_CODE_ERROR = 15; | 310 static const int EXIT_CODE_ERROR = 15; |
| 317 | 311 |
| 318 final WorkerConnection connection; | 312 final WorkerConnection connection; |
| 319 | 313 |
| 320 final StringBuffer errorBuffer = new StringBuffer(); | 314 final StringBuffer errorBuffer = new StringBuffer(); |
| 321 final StringBuffer outBuffer = new StringBuffer(); | 315 final StringBuffer outBuffer = new StringBuffer(); |
| 322 | 316 |
| 323 WorkerLoop(this.connection); | 317 WorkerLoop(this.connection); |
| 324 | 318 |
| 325 factory WorkerLoop.std() { | 319 factory WorkerLoop.std({io.Stdin stdinStream, io.Stdout stdoutStream}) { |
| 326 WorkerConnection connection = new _StdWorkerConnection(); | 320 stdinStream ??= io.stdin; |
| 321 stdoutStream ??= io.stdout; |
| 322 WorkerConnection connection = |
| 323 new StdWorkerConnection(stdinStream, stdoutStream); |
| 327 return new WorkerLoop(connection); | 324 return new WorkerLoop(connection); |
| 328 } | 325 } |
| 329 | 326 |
| 330 /** | 327 /** |
| 331 * Performs analysis with given [options]. | 328 * Performs analysis with given [options]. |
| 332 */ | 329 */ |
| 333 void analyze(CommandLineOptions options) { | 330 void analyze(CommandLineOptions options) { |
| 334 new BuildMode(options, new AnalysisStats()).analyze(); | 331 new BuildMode(options, new AnalysisStats()).analyze(); |
| 335 } | 332 } |
| 336 | 333 |
| 337 /** | 334 /** |
| 338 * Perform a single loop step. Return `true` if should exit the loop. | 335 * Perform a single loop step. Return `true` if should exit the loop. |
| 339 */ | 336 */ |
| 340 bool performSingle() { | 337 bool performSingle() { |
| 341 try { | 338 try { |
| 342 WorkRequest request = _readRequest(); | 339 WorkRequest request = connection.readRequest(); |
| 343 if (request == null) { | 340 if (request == null) { |
| 344 return true; | 341 return true; |
| 345 } | 342 } |
| 346 // Prepare options. | 343 // Prepare options. |
| 347 CommandLineOptions options = | 344 CommandLineOptions options = |
| 348 CommandLineOptions.parse(request.arguments, (String msg) { | 345 CommandLineOptions.parse(request.arguments, (String msg) { |
| 349 throw new ArgumentError(msg); | 346 throw new ArgumentError(msg); |
| 350 }); | 347 }); |
| 351 // Analyze and respond. | 348 // Analyze and respond. |
| 352 analyze(options); | 349 analyze(options); |
| 353 String msg = _getErrorOutputBuffersText(); | 350 String msg = _getErrorOutputBuffersText(); |
| 354 _writeResponse(new WorkResponse(EXIT_CODE_OK, msg)); | 351 connection.writeResponse(new WorkResponse() |
| 352 ..exitCode = EXIT_CODE_OK |
| 353 ..output = msg); |
| 355 } catch (e, st) { | 354 } catch (e, st) { |
| 356 String msg = _getErrorOutputBuffersText(); | 355 String msg = _getErrorOutputBuffersText(); |
| 357 msg += '$e \n $st'; | 356 msg += '$e \n $st'; |
| 358 _writeResponse(new WorkResponse(EXIT_CODE_ERROR, msg)); | 357 connection.writeResponse(new WorkResponse() |
| 358 ..exitCode = EXIT_CODE_ERROR |
| 359 ..output = msg); |
| 359 } | 360 } |
| 360 return false; | 361 return false; |
| 361 } | 362 } |
| 362 | 363 |
| 363 /** | 364 /** |
| 364 * Run the worker loop. | 365 * Run the worker loop. |
| 365 */ | 366 */ |
| 366 void run() { | 367 void run() { |
| 367 errorSink = errorBuffer; | 368 errorSink = errorBuffer; |
| 368 outSink = outBuffer; | 369 outSink = outBuffer; |
| (...skipping 13 matching lines...) Expand all Loading... |
| 382 String _getErrorOutputBuffersText() { | 383 String _getErrorOutputBuffersText() { |
| 383 String msg = ''; | 384 String msg = ''; |
| 384 if (errorBuffer.isNotEmpty) { | 385 if (errorBuffer.isNotEmpty) { |
| 385 msg += errorBuffer.toString() + '\n'; | 386 msg += errorBuffer.toString() + '\n'; |
| 386 } | 387 } |
| 387 if (outBuffer.isNotEmpty) { | 388 if (outBuffer.isNotEmpty) { |
| 388 msg += outBuffer.toString() + '\n'; | 389 msg += outBuffer.toString() + '\n'; |
| 389 } | 390 } |
| 390 return msg; | 391 return msg; |
| 391 } | 392 } |
| 392 | |
| 393 /** | |
| 394 * Read a new [WorkRequest]. Return `null` if EOF. | |
| 395 * Throw [ArgumentError] if cannot be parsed. | |
| 396 */ | |
| 397 WorkRequest _readRequest() { | |
| 398 String line = connection.readLineSync(); | |
| 399 if (line == null) { | |
| 400 return null; | |
| 401 } | |
| 402 Object json = JSON.decode(line); | |
| 403 if (json is Map) { | |
| 404 return new WorkRequest.fromJson(json); | |
| 405 } else { | |
| 406 throw new ArgumentError('The request line is not a JSON object: $line'); | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 void _writeResponse(WorkResponse response) { | |
| 411 Map<String, Object> json = response.toJson(); | |
| 412 connection.writeJson(json); | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 /** | |
| 417 * Input file. | |
| 418 */ | |
| 419 class WorkInput implements WorkDataObject { | |
| 420 final String path; | |
| 421 final List<int> digest; | |
| 422 | |
| 423 WorkInput(this.path, this.digest); | |
| 424 | |
| 425 factory WorkInput.fromJson(Map<String, Object> json) { | |
| 426 // Parse path. | |
| 427 Object path2 = json['path']; | |
| 428 if (path2 == null) { | |
| 429 throw new ArgumentError('The field "path" is missing.'); | |
| 430 } | |
| 431 if (path2 is! String) { | |
| 432 throw new ArgumentError('The field "path" must be a string.'); | |
| 433 } | |
| 434 // Parse digest. | |
| 435 List<int> digest = const <int>[]; | |
| 436 { | |
| 437 Object digestJson = json['digest']; | |
| 438 if (digestJson != null) { | |
| 439 if (digestJson is List && digestJson.every((e) => e is int)) { | |
| 440 digest = digestJson; | |
| 441 } else { | |
| 442 throw new ArgumentError( | |
| 443 'The field "digest" should be a list of int.'); | |
| 444 } | |
| 445 } | |
| 446 } | |
| 447 // OK | |
| 448 return new WorkInput(path2, digest); | |
| 449 } | |
| 450 | |
| 451 @override | |
| 452 Map<String, Object> toJson() { | |
| 453 Map<String, Object> json = <String, Object>{}; | |
| 454 if (path != null) { | |
| 455 json['path'] = path; | |
| 456 } | |
| 457 if (digest != null) { | |
| 458 json['digest'] = digest; | |
| 459 } | |
| 460 return json; | |
| 461 } | |
| 462 } | |
| 463 | |
| 464 /** | |
| 465 * Single work unit that Bazel sends to the worker. | |
| 466 */ | |
| 467 class WorkRequest implements WorkDataObject { | |
| 468 /** | |
| 469 * Command line arguments for this request. | |
| 470 */ | |
| 471 final List<String> arguments; | |
| 472 | |
| 473 /** | |
| 474 * Input files that the worker is allowed to read during execution of this | |
| 475 * request. | |
| 476 */ | |
| 477 final List<WorkInput> inputs; | |
| 478 | |
| 479 WorkRequest(this.arguments, this.inputs); | |
| 480 | |
| 481 factory WorkRequest.fromJson(Map<String, Object> json) { | |
| 482 // Parse arguments. | |
| 483 List<String> arguments = const <String>[]; | |
| 484 { | |
| 485 Object argumentsJson = json['arguments']; | |
| 486 if (argumentsJson != null) { | |
| 487 if (argumentsJson is List && argumentsJson.every((e) => e is String)) { | |
| 488 arguments = argumentsJson; | |
| 489 } else { | |
| 490 throw new ArgumentError( | |
| 491 'The field "arguments" should be a list of strings.'); | |
| 492 } | |
| 493 } | |
| 494 } | |
| 495 // Parse inputs. | |
| 496 List<WorkInput> inputs = const <WorkInput>[]; | |
| 497 { | |
| 498 Object inputsJson = json['inputs']; | |
| 499 if (inputsJson != null) { | |
| 500 if (inputsJson is List && | |
| 501 inputsJson.every((e) { | |
| 502 return e is Map && e.keys.every((key) => key is String); | |
| 503 })) { | |
| 504 inputs = inputsJson | |
| 505 .map((Map input) => new WorkInput.fromJson(input)) | |
| 506 .toList(); | |
| 507 } else { | |
| 508 throw new ArgumentError( | |
| 509 'The field "inputs" should be a list of objects.'); | |
| 510 } | |
| 511 } | |
| 512 } | |
| 513 // No inputs. | |
| 514 if (arguments.isEmpty && inputs.isEmpty) { | |
| 515 throw new ArgumentError('Both "arguments" and "inputs" cannot be empty.'); | |
| 516 } | |
| 517 // OK | |
| 518 return new WorkRequest(arguments, inputs); | |
| 519 } | |
| 520 | |
| 521 @override | |
| 522 Map<String, Object> toJson() { | |
| 523 Map<String, Object> json = <String, Object>{}; | |
| 524 if (arguments != null) { | |
| 525 json['arguments'] = arguments; | |
| 526 } | |
| 527 if (inputs != null) { | |
| 528 json['inputs'] = inputs.map((input) => input.toJson()).toList(); | |
| 529 } | |
| 530 return json; | |
| 531 } | |
| 532 } | |
| 533 | |
| 534 /** | |
| 535 * Result that the worker sends back to Bazel when it finished its work on a | |
| 536 * [WorkRequest] message. | |
| 537 */ | |
| 538 class WorkResponse implements WorkDataObject { | |
| 539 final int exitCode; | |
| 540 final String output; | |
| 541 | |
| 542 WorkResponse(this.exitCode, this.output); | |
| 543 | |
| 544 @override | |
| 545 Map<String, Object> toJson() { | |
| 546 Map<String, Object> json = <String, Object>{}; | |
| 547 if (exitCode != null) { | |
| 548 json['exit_code'] = exitCode; | |
| 549 } | |
| 550 if (output != null) { | |
| 551 json['output'] = output; | |
| 552 } | |
| 553 return json; | |
| 554 } | |
| 555 } | 393 } |
| 556 | 394 |
| 557 /** | 395 /** |
| 558 * Default implementation of [WorkerConnection] that works with stdio. | 396 * Default implementation of [WorkerConnection] that works with stdio. |
| 559 */ | 397 */ |
| 560 class _StdWorkerConnection implements WorkerConnection { | 398 class StdWorkerConnection implements WorkerConnection { |
| 399 final MessageGrouper _messageGrouper; |
| 400 final io.Stdout _stdoutStream; |
| 401 |
| 402 StdWorkerConnection(io.Stdin stdinStream, this._stdoutStream) |
| 403 : _messageGrouper = new MessageGrouper(stdinStream); |
| 404 |
| 561 @override | 405 @override |
| 562 String readLineSync() { | 406 WorkRequest readRequest() { |
| 563 return io.stdin.readLineSync(); | 407 var buffer = _messageGrouper.next; |
| 408 if (buffer == null) return null; |
| 409 |
| 410 return new WorkRequest.fromBuffer(buffer); |
| 564 } | 411 } |
| 565 | 412 |
| 566 @override | 413 @override |
| 567 void writeJson(Map<String, Object> json) { | 414 void writeResponse(WorkResponse response) { |
| 568 io.stdout.writeln(JSON.encode(json)); | 415 var responseBuffer = response.writeToBuffer(); |
| 416 |
| 417 var writer = new CodedBufferWriter(); |
| 418 writer.writeInt32NoTag(responseBuffer.length); |
| 419 writer.writeRawBytes(responseBuffer); |
| 420 |
| 421 _stdoutStream.add(writer.toBuffer()); |
| 569 } | 422 } |
| 570 } | 423 } |
| OLD | NEW |