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'; | |
7 import 'dart:core' hide Resource; | 8 import 'dart:core' hide Resource; |
8 import 'dart:io' as io; | 9 import 'dart:io' as io; |
9 | 10 |
10 import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit; | 11 import 'package:analyzer/dart/ast/ast.dart' show CompilationUnit; |
11 import 'package:analyzer/dart/element/element.dart'; | 12 import 'package:analyzer/dart/element/element.dart'; |
12 import 'package:analyzer/file_system/file_system.dart'; | 13 import 'package:analyzer/file_system/file_system.dart'; |
13 import 'package:analyzer/file_system/physical_file_system.dart'; | 14 import 'package:analyzer/file_system/physical_file_system.dart'; |
14 import 'package:analyzer/src/generated/engine.dart'; | 15 import 'package:analyzer/src/generated/engine.dart'; |
15 import 'package:analyzer/src/generated/error.dart'; | 16 import 'package:analyzer/src/generated/error.dart'; |
16 import 'package:analyzer/src/generated/java_io.dart'; | 17 import 'package:analyzer/src/generated/java_io.dart'; |
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
274 'Illegal input file (must be "\$uri|\$path"): $sourceFile'); | 275 'Illegal input file (must be "\$uri|\$path"): $sourceFile'); |
275 return null; | 276 return null; |
276 } | 277 } |
277 Uri uri = Uri.parse(sourceFile.substring(0, pipeIndex)); | 278 Uri uri = Uri.parse(sourceFile.substring(0, pipeIndex)); |
278 String path = sourceFile.substring(pipeIndex + 1); | 279 String path = sourceFile.substring(pipeIndex + 1); |
279 uriToFileMap[uri] = new JavaFile(path); | 280 uriToFileMap[uri] = new JavaFile(path); |
280 } | 281 } |
281 return uriToFileMap; | 282 return uriToFileMap; |
282 } | 283 } |
283 } | 284 } |
285 | |
286 /** | |
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. | |
298 */ | |
299 abstract class WorkerConnection { | |
300 /** | |
301 * Read a new line. Block until a line is read. Return `null` if EOF. | |
302 */ | |
303 String readLineSync(); | |
304 | |
305 /** | |
306 * Write the given [json] as a new line to the stdout. | |
Paul Berry
2016/03/30 18:27:43
s/to the stdout/to the output/
(since this class
scheglov
2016/03/30 18:39:02
Done.
| |
307 */ | |
308 void writeJson(Map<String, Object> json); | |
309 } | |
310 | |
311 /** | |
312 * Persistent Bazel worker. | |
313 */ | |
314 class WorkerLoop { | |
315 final WorkerConnection connection; | |
316 | |
317 final StringBuffer errorBuffer = new StringBuffer(); | |
318 final StringBuffer outBuffer = new StringBuffer(); | |
319 | |
320 WorkerLoop(this.connection); | |
321 | |
322 factory WorkerLoop.std() { | |
323 WorkerConnection connection = new _StdWorkerConnection(); | |
324 return new WorkerLoop(connection); | |
325 } | |
326 | |
327 /** | |
328 * Performs analysis with given [options]. | |
329 */ | |
330 void analyze(CommandLineOptions options) { | |
331 new BuildMode(options, new AnalysisStats()).analyze(); | |
332 } | |
333 | |
334 /** | |
335 * Perform a single loop step. Return `true` if should exit the loop. | |
336 */ | |
337 bool performSingle() { | |
338 try { | |
339 WorkRequest request = _readRequest(); | |
340 if (request == null) { | |
341 return true; | |
342 } | |
343 // Prepare options. | |
344 CommandLineOptions options = | |
345 CommandLineOptions.parse(request.arguments, (String msg) { | |
346 throw new ArgumentError(msg); | |
347 }); | |
348 // Analyze and respond. | |
349 analyze(options); | |
350 String msg = _getErrorOutputBuffersText(); | |
351 _writeResponse(new WorkResponse(0, msg)); | |
352 } catch (e, st) { | |
353 String msg = _getErrorOutputBuffersText(); | |
354 msg += '$e \n $st'; | |
355 _writeResponse(new WorkResponse(15, msg)); | |
Paul Berry
2016/03/30 18:27:43
Can we make constants for these exit codes (0 abov
scheglov
2016/03/30 18:39:02
Done.
| |
356 } | |
357 return false; | |
358 } | |
359 | |
360 /** | |
361 * Run the worker loop. | |
362 */ | |
363 void run() { | |
364 errorSink = errorBuffer; | |
365 outSink = outBuffer; | |
366 exitHandler = (int exitCode) { | |
367 return throw new StateError('Exit called: $exitCode'); | |
368 }; | |
369 while (true) { | |
370 errorBuffer.clear(); | |
371 outBuffer.clear(); | |
372 bool shouldExit = performSingle(); | |
373 if (shouldExit) { | |
374 break; | |
375 } | |
376 } | |
377 } | |
378 | |
379 String _getErrorOutputBuffersText() { | |
380 String msg = ''; | |
381 if (errorBuffer.isNotEmpty) { | |
382 msg += errorBuffer.toString() + '\n'; | |
383 } | |
384 if (outBuffer.isNotEmpty) { | |
385 msg += outBuffer.toString() + '\n'; | |
386 } | |
387 return msg; | |
388 } | |
389 | |
390 /** | |
391 * Read a new [WorkRequest]. Return `null` if EOF. | |
392 * Throw [ArgumentError] if cannot be parsed. | |
393 */ | |
394 WorkRequest _readRequest() { | |
395 String line = connection.readLineSync(); | |
396 if (line == null) { | |
397 return null; | |
398 } | |
399 Object json = JSON.decode(line); | |
400 if (json is Map) { | |
401 return new WorkRequest.fromJson(json); | |
402 } else { | |
403 throw new ArgumentError('The request line is not a JSON object: $line'); | |
404 } | |
405 } | |
406 | |
407 void _writeResponse(WorkResponse response) { | |
408 Map<String, Object> json = response.toJson(); | |
409 connection.writeJson(json); | |
410 } | |
411 } | |
412 | |
413 /** | |
414 * Input file. | |
415 */ | |
416 class WorkInput implements WorkDataObject { | |
417 final String path; | |
418 final List<int> digest; | |
419 | |
420 WorkInput(this.path, this.digest); | |
421 | |
422 factory WorkInput.fromJson(Map<String, Object> json) { | |
423 // Parse path. | |
424 Object path2 = json['path']; | |
425 if (path2 == null) { | |
426 throw new ArgumentError('The field "path" is missing.'); | |
427 } | |
428 if (path2 is! String) { | |
429 throw new ArgumentError('The field "path" must be a string.'); | |
430 } | |
431 // Parse digest. | |
432 List<int> digest = const <int>[]; | |
433 { | |
434 Object digestJson = json['digest']; | |
435 if (digestJson != null) { | |
436 if (digestJson is List && digestJson.every((e) => e is int)) { | |
437 digest = digestJson; | |
438 } else { | |
439 throw new ArgumentError( | |
440 'The field "digest" should be a list of int.'); | |
441 } | |
442 } | |
443 } | |
444 // OK | |
445 return new WorkInput(path2, digest); | |
446 } | |
447 | |
448 @override | |
449 Map<String, Object> toJson() { | |
450 return <String, Object>{'path': path, 'digest': digest}; | |
Paul Berry
2016/03/30 18:27:43
If digest is null, I'd prefer to see it not includ
scheglov
2016/03/30 18:39:02
Done.
| |
451 } | |
452 } | |
453 | |
454 /** | |
455 * Single work unit that Bazel sends to the worker. | |
456 */ | |
457 class WorkRequest implements WorkDataObject { | |
458 /** | |
459 * Command line arguments for this request. | |
460 */ | |
461 final List<String> arguments; | |
462 | |
463 /** | |
464 * Input files that the worker is allowed to read during execution of this | |
465 * request. | |
466 */ | |
467 final List<WorkInput> inputs; | |
468 | |
469 WorkRequest(this.arguments, this.inputs); | |
470 | |
471 factory WorkRequest.fromJson(Map<String, Object> json) { | |
472 // Parse arguments. | |
473 List<String> arguments = const <String>[]; | |
474 { | |
475 Object argumentsJson = json['arguments']; | |
476 if (argumentsJson != null) { | |
477 if (argumentsJson is List && argumentsJson.every((e) => e is String)) { | |
478 arguments = argumentsJson; | |
479 } else { | |
480 throw new ArgumentError( | |
481 'The field "arguments" should be a list of strings.'); | |
482 } | |
483 } | |
484 } | |
485 // Parse inputs. | |
486 List<WorkInput> inputs = const <WorkInput>[]; | |
487 { | |
488 Object inputsJson = json['inputs']; | |
489 if (inputsJson != null) { | |
490 if (inputsJson is List && | |
491 inputsJson.every((e) { | |
492 return e is Map && e.keys.every((key) => key is String); | |
493 })) { | |
494 inputs = inputsJson | |
495 .map((Map input) => new WorkInput.fromJson(input)) | |
496 .toList(); | |
497 } else { | |
498 throw new ArgumentError( | |
499 'The field "inputs" should be a list of objects.'); | |
500 } | |
501 } | |
502 } | |
503 // No inputs. | |
504 if (arguments.isEmpty && inputs.isEmpty) { | |
505 throw new ArgumentError('Both "arguments" and "inputs" cannot be empty.'); | |
506 } | |
507 // OK | |
508 return new WorkRequest(arguments, inputs); | |
509 } | |
510 | |
511 @override | |
512 Map<String, Object> toJson() { | |
513 return <String, Object>{ | |
514 'arguments': arguments, | |
515 'inputs': inputs.map((input) => input.toJson()).toList() | |
516 }; | |
517 } | |
518 } | |
519 | |
520 /** | |
521 * Result that the worker sends back to Bazel when it finished its work on a | |
522 * [WorkRequest] message. | |
523 */ | |
524 class WorkResponse implements WorkDataObject { | |
525 final int exitCode; | |
526 final String output; | |
527 | |
528 WorkResponse(this.exitCode, this.output); | |
529 | |
530 @override | |
531 Map<String, Object> toJson() { | |
532 return <String, Object>{'exit_code': exitCode, 'output': output}; | |
533 } | |
534 } | |
535 | |
536 /** | |
537 * Default implementation of [WorkerConnection] that works with stdio. | |
538 */ | |
539 class _StdWorkerConnection implements WorkerConnection { | |
540 @override | |
541 String readLineSync() { | |
542 return io.stdin.readLineSync(); | |
543 } | |
544 | |
545 @override | |
546 void writeJson(Map<String, Object> json) { | |
547 io.stdout.writeln(JSON.encode(json)); | |
548 } | |
549 } | |
OLD | NEW |