Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(171)

Side by Side Diff: pkg/analyzer_cli/lib/src/build_mode.dart

Issue 1848543002: Support --persistent_worker flag in --build-mode. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Fixes for review comments. Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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 output.
307 */
308 void writeJson(Map<String, Object> json);
309 }
310
311 /**
312 * Persistent Bazel worker.
313 */
314 class WorkerLoop {
315 static const int EXIT_CODE_OK = 0;
316 static const int EXIT_CODE_ERROR = 15;
317
318 final WorkerConnection connection;
319
320 final StringBuffer errorBuffer = new StringBuffer();
321 final StringBuffer outBuffer = new StringBuffer();
322
323 WorkerLoop(this.connection);
324
325 factory WorkerLoop.std() {
326 WorkerConnection connection = new _StdWorkerConnection();
327 return new WorkerLoop(connection);
328 }
329
330 /**
331 * Performs analysis with given [options].
332 */
333 void analyze(CommandLineOptions options) {
334 new BuildMode(options, new AnalysisStats()).analyze();
335 }
336
337 /**
338 * Perform a single loop step. Return `true` if should exit the loop.
339 */
340 bool performSingle() {
341 try {
342 WorkRequest request = _readRequest();
343 if (request == null) {
344 return true;
345 }
346 // Prepare options.
347 CommandLineOptions options =
348 CommandLineOptions.parse(request.arguments, (String msg) {
349 throw new ArgumentError(msg);
350 });
351 // Analyze and respond.
352 analyze(options);
353 String msg = _getErrorOutputBuffersText();
354 _writeResponse(new WorkResponse(EXIT_CODE_OK, msg));
355 } catch (e, st) {
356 String msg = _getErrorOutputBuffersText();
357 msg += '$e \n $st';
358 _writeResponse(new WorkResponse(EXIT_CODE_ERROR, msg));
359 }
360 return false;
361 }
362
363 /**
364 * Run the worker loop.
365 */
366 void run() {
367 errorSink = errorBuffer;
368 outSink = outBuffer;
369 exitHandler = (int exitCode) {
370 return throw new StateError('Exit called: $exitCode');
371 };
372 while (true) {
373 errorBuffer.clear();
374 outBuffer.clear();
375 bool shouldExit = performSingle();
376 if (shouldExit) {
377 break;
378 }
379 }
380 }
381
382 String _getErrorOutputBuffersText() {
383 String msg = '';
384 if (errorBuffer.isNotEmpty) {
385 msg += errorBuffer.toString() + '\n';
386 }
387 if (outBuffer.isNotEmpty) {
388 msg += outBuffer.toString() + '\n';
389 }
390 return msg;
391 }
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 }
556
557 /**
558 * Default implementation of [WorkerConnection] that works with stdio.
559 */
560 class _StdWorkerConnection implements WorkerConnection {
561 @override
562 String readLineSync() {
563 return io.stdin.readLineSync();
564 }
565
566 @override
567 void writeJson(Map<String, Object> json) {
568 io.stdout.writeln(JSON.encode(json));
569 }
570 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analyzer_cli/lib/src/driver.dart » ('j') | pkg/analyzer_cli/test/build_mode_test.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698