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

Unified 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, 9 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 side-by-side diff with in-line comments
Download patch
« 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')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer_cli/lib/src/build_mode.dart
diff --git a/pkg/analyzer_cli/lib/src/build_mode.dart b/pkg/analyzer_cli/lib/src/build_mode.dart
index 800efff7a811d7e15678eecbcc0d3a178682847f..22562473d38804b0136364c79617a8efd2a4848e 100644
--- a/pkg/analyzer_cli/lib/src/build_mode.dart
+++ b/pkg/analyzer_cli/lib/src/build_mode.dart
@@ -4,6 +4,7 @@
library analyzer_cli.src.build_mode;
+import 'dart:convert';
import 'dart:core' hide Resource;
import 'dart:io' as io;
@@ -281,3 +282,289 @@ class BuildMode {
return uriToFileMap;
}
}
+
+/**
+ * Interface that every worker related data object has.
+ */
+abstract class WorkDataObject {
+ /**
+ * Translate the data in this class into a JSON map.
+ */
+ Map<String, Object> toJson();
+}
+
+/**
+ * Connection between a worker and input / output.
+ */
+abstract class WorkerConnection {
+ /**
+ * Read a new line. Block until a line is read. Return `null` if EOF.
+ */
+ String readLineSync();
+
+ /**
+ * Write the given [json] as a new line to the output.
+ */
+ void writeJson(Map<String, Object> json);
+}
+
+/**
+ * Persistent Bazel worker.
+ */
+class WorkerLoop {
+ static const int EXIT_CODE_OK = 0;
+ static const int EXIT_CODE_ERROR = 15;
+
+ final WorkerConnection connection;
+
+ final StringBuffer errorBuffer = new StringBuffer();
+ final StringBuffer outBuffer = new StringBuffer();
+
+ WorkerLoop(this.connection);
+
+ factory WorkerLoop.std() {
+ WorkerConnection connection = new _StdWorkerConnection();
+ return new WorkerLoop(connection);
+ }
+
+ /**
+ * Performs analysis with given [options].
+ */
+ void analyze(CommandLineOptions options) {
+ new BuildMode(options, new AnalysisStats()).analyze();
+ }
+
+ /**
+ * Perform a single loop step. Return `true` if should exit the loop.
+ */
+ bool performSingle() {
+ try {
+ WorkRequest request = _readRequest();
+ if (request == null) {
+ return true;
+ }
+ // Prepare options.
+ CommandLineOptions options =
+ CommandLineOptions.parse(request.arguments, (String msg) {
+ throw new ArgumentError(msg);
+ });
+ // Analyze and respond.
+ analyze(options);
+ String msg = _getErrorOutputBuffersText();
+ _writeResponse(new WorkResponse(EXIT_CODE_OK, msg));
+ } catch (e, st) {
+ String msg = _getErrorOutputBuffersText();
+ msg += '$e \n $st';
+ _writeResponse(new WorkResponse(EXIT_CODE_ERROR, msg));
+ }
+ return false;
+ }
+
+ /**
+ * Run the worker loop.
+ */
+ void run() {
+ errorSink = errorBuffer;
+ outSink = outBuffer;
+ exitHandler = (int exitCode) {
+ return throw new StateError('Exit called: $exitCode');
+ };
+ while (true) {
+ errorBuffer.clear();
+ outBuffer.clear();
+ bool shouldExit = performSingle();
+ if (shouldExit) {
+ break;
+ }
+ }
+ }
+
+ String _getErrorOutputBuffersText() {
+ String msg = '';
+ if (errorBuffer.isNotEmpty) {
+ msg += errorBuffer.toString() + '\n';
+ }
+ if (outBuffer.isNotEmpty) {
+ msg += outBuffer.toString() + '\n';
+ }
+ return msg;
+ }
+
+ /**
+ * Read a new [WorkRequest]. Return `null` if EOF.
+ * Throw [ArgumentError] if cannot be parsed.
+ */
+ WorkRequest _readRequest() {
+ String line = connection.readLineSync();
+ if (line == null) {
+ return null;
+ }
+ Object json = JSON.decode(line);
+ if (json is Map) {
+ return new WorkRequest.fromJson(json);
+ } else {
+ throw new ArgumentError('The request line is not a JSON object: $line');
+ }
+ }
+
+ void _writeResponse(WorkResponse response) {
+ Map<String, Object> json = response.toJson();
+ connection.writeJson(json);
+ }
+}
+
+/**
+ * Input file.
+ */
+class WorkInput implements WorkDataObject {
+ final String path;
+ final List<int> digest;
+
+ WorkInput(this.path, this.digest);
+
+ factory WorkInput.fromJson(Map<String, Object> json) {
+ // Parse path.
+ Object path2 = json['path'];
+ if (path2 == null) {
+ throw new ArgumentError('The field "path" is missing.');
+ }
+ if (path2 is! String) {
+ throw new ArgumentError('The field "path" must be a string.');
+ }
+ // Parse digest.
+ List<int> digest = const <int>[];
+ {
+ Object digestJson = json['digest'];
+ if (digestJson != null) {
+ if (digestJson is List && digestJson.every((e) => e is int)) {
+ digest = digestJson;
+ } else {
+ throw new ArgumentError(
+ 'The field "digest" should be a list of int.');
+ }
+ }
+ }
+ // OK
+ return new WorkInput(path2, digest);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ Map<String, Object> json = <String, Object>{};
+ if (path != null) {
+ json['path'] = path;
+ }
+ if (digest != null) {
+ json['digest'] = digest;
+ }
+ return json;
+ }
+}
+
+/**
+ * Single work unit that Bazel sends to the worker.
+ */
+class WorkRequest implements WorkDataObject {
+ /**
+ * Command line arguments for this request.
+ */
+ final List<String> arguments;
+
+ /**
+ * Input files that the worker is allowed to read during execution of this
+ * request.
+ */
+ final List<WorkInput> inputs;
+
+ WorkRequest(this.arguments, this.inputs);
+
+ factory WorkRequest.fromJson(Map<String, Object> json) {
+ // Parse arguments.
+ List<String> arguments = const <String>[];
+ {
+ Object argumentsJson = json['arguments'];
+ if (argumentsJson != null) {
+ if (argumentsJson is List && argumentsJson.every((e) => e is String)) {
+ arguments = argumentsJson;
+ } else {
+ throw new ArgumentError(
+ 'The field "arguments" should be a list of strings.');
+ }
+ }
+ }
+ // Parse inputs.
+ List<WorkInput> inputs = const <WorkInput>[];
+ {
+ Object inputsJson = json['inputs'];
+ if (inputsJson != null) {
+ if (inputsJson is List &&
+ inputsJson.every((e) {
+ return e is Map && e.keys.every((key) => key is String);
+ })) {
+ inputs = inputsJson
+ .map((Map input) => new WorkInput.fromJson(input))
+ .toList();
+ } else {
+ throw new ArgumentError(
+ 'The field "inputs" should be a list of objects.');
+ }
+ }
+ }
+ // No inputs.
+ if (arguments.isEmpty && inputs.isEmpty) {
+ throw new ArgumentError('Both "arguments" and "inputs" cannot be empty.');
+ }
+ // OK
+ return new WorkRequest(arguments, inputs);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ Map<String, Object> json = <String, Object>{};
+ if (arguments != null) {
+ json['arguments'] = arguments;
+ }
+ if (inputs != null) {
+ json['inputs'] = inputs.map((input) => input.toJson()).toList();
+ }
+ return json;
+ }
+}
+
+/**
+ * Result that the worker sends back to Bazel when it finished its work on a
+ * [WorkRequest] message.
+ */
+class WorkResponse implements WorkDataObject {
+ final int exitCode;
+ final String output;
+
+ WorkResponse(this.exitCode, this.output);
+
+ @override
+ Map<String, Object> toJson() {
+ Map<String, Object> json = <String, Object>{};
+ if (exitCode != null) {
+ json['exit_code'] = exitCode;
+ }
+ if (output != null) {
+ json['output'] = output;
+ }
+ return json;
+ }
+}
+
+/**
+ * Default implementation of [WorkerConnection] that works with stdio.
+ */
+class _StdWorkerConnection implements WorkerConnection {
+ @override
+ String readLineSync() {
+ return io.stdin.readLineSync();
+ }
+
+ @override
+ void writeJson(Map<String, Object> json) {
+ io.stdout.writeln(JSON.encode(json));
+ }
+}
« 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