Skip to content

Commit 71880b3

Browse files
authored
Daemon App Domain (#206)
* Daemon App Domain
1 parent 2d63f18 commit 71880b3

20 files changed

+730
-432
lines changed

dwds/lib/service.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,18 @@ class DebugService {
4545
final ChromeProxyService chromeProxyService;
4646
final String hostname;
4747
final ServiceExtensionRegistry serviceExtensionRegistry;
48-
4948
final int port;
5049
final HttpServer _server;
50+
5151
DebugService._(this.chromeProxyService, this.hostname, this.port,
5252
this.serviceExtensionRegistry, this._server);
5353

5454
Future<void> close() async {
5555
await _server.close();
5656
}
5757

58+
String get wsUri => 'ws://$hostname:$port';
59+
5860
static Future<DebugService> start(
5961
String hostname,
6062
ChromeConnection chromeConnection,

webdev/bin/webdev.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'dart:isolate';
99
import 'package:args/command_runner.dart';
1010
import 'package:io/ansi.dart';
1111
import 'package:io/io.dart';
12+
import 'package:webdev/src/command/configuration.dart';
1213
import 'package:webdev/src/webdev_command_runner.dart';
1314

1415
Future main(List<String> args) async {
@@ -43,6 +44,9 @@ Future main(List<String> args) async {
4344
print(red.wrap('$_boldApp failed with an unexpected exception.'));
4445
print(e.message);
4546
exitCode = ExitCode.software.code;
47+
} on InvalidConfiguration catch (e) {
48+
print(red.wrap('$_boldApp $e'));
49+
exitCode = ExitCode.config.code;
4650
}
4751
}
4852

webdev/lib/src/command/configuration.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class Configuration {
5353
final bool _requireBuildWebCompilers;
5454
final bool _verbose;
5555

56-
Configuration._({
56+
Configuration({
5757
int chromeDebugPort,
5858
bool debug,
5959
String hostname,
@@ -97,7 +97,7 @@ class Configuration {
9797

9898
/// Returns a new configuration with values updated from the parsed args.
9999
static Configuration fromArgs(ArgResults argResults) {
100-
var defaultConfiguration = Configuration._();
100+
var defaultConfiguration = Configuration();
101101
if (argResults == null) return defaultConfiguration;
102102

103103
var chromeDebugPort = argResults.options.contains(chromeDebugPortFlag)
@@ -143,7 +143,7 @@ class Configuration {
143143
'--$debugFlag.');
144144
}
145145

146-
return Configuration._(
146+
return Configuration(
147147
chromeDebugPort: chromeDebugPort,
148148
debug: debug,
149149
hostname: hostname,
@@ -160,4 +160,9 @@ class Configuration {
160160
class InvalidConfiguration implements Exception {
161161
final String details;
162162
InvalidConfiguration(this.details);
163+
164+
@override
165+
String toString() {
166+
return 'Invalid configuration: $details';
167+
}
163168
}

webdev/lib/src/command/daemon_command.dart

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import 'dart:io';
99
import 'package:args/command_runner.dart';
1010

1111
import '../daemon/daemon.dart';
12+
import '../serve/dev_workflow.dart';
13+
import '../serve/server_manager.dart';
14+
import '../serve/utils.dart';
15+
import 'configuration.dart';
16+
import 'shared.dart';
1217

1318
Stream<Map<String, dynamic>> get _stdinCommandStream => stdin
1419
.transform<String>(utf8.decoder)
@@ -42,8 +47,21 @@ class DaemonCommand extends Command<int> {
4247

4348
@override
4449
Future<int> run() async {
45-
var daemon = Daemon(_stdinCommandStream, _stdoutCommandResponse);
50+
var serveManagerCompleter = Completer<ServerManager>();
51+
var daemon = Daemon(_stdinCommandStream, _stdoutCommandResponse,
52+
serveManagerCompleter.future);
53+
var port = await findUnusedPort();
54+
var configuration = Configuration(launchInChrome: true);
55+
var pubspecLock = await readPubspecLock(configuration);
56+
var buildOptions = buildRunnerArgs(pubspecLock, configuration);
57+
var workflow = await DevWorkflow.start(
58+
configuration, buildOptions, {'web': port}, (level, message) {
59+
daemon.sendEvent(
60+
'daemon', 'logMessage', {'level': '$level', 'message': message});
61+
});
62+
serveManagerCompleter.complete(workflow.serverManager);
4663
await daemon.onExit;
64+
await workflow.shutDown();
4765
return 0;
4866
}
4967
}

webdev/lib/src/command/serve_command.dart

Lines changed: 8 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,9 @@ import 'dart:io';
77

88
import 'package:args/args.dart';
99
import 'package:args/command_runner.dart';
10-
import 'package:build_daemon/client.dart';
11-
import 'package:build_daemon/data/build_target.dart';
12-
import 'package:logging/logging.dart';
1310

14-
import '../serve/chrome.dart';
15-
import '../serve/daemon_client.dart';
16-
import '../serve/debugger/devtools.dart';
17-
import '../serve/server_manager.dart';
11+
import '../serve/dev_workflow.dart';
1812
import '../serve/utils.dart';
19-
import '../serve/webdev_server.dart';
2013
import 'configuration.dart';
2114
import 'shared.dart';
2215

@@ -46,10 +39,6 @@ Map<String, int> _parseDirectoryArgs(List<String> args) {
4639

4740
/// Command to run a server for local web development with the build daemon.
4841
class ServeCommand extends Command<int> {
49-
ServerManager _serverManager;
50-
Chrome _chrome;
51-
BuildDaemonClient _client;
52-
5342
@override
5443
final name = 'serve';
5544

@@ -100,101 +89,21 @@ class ServeCommand extends Command<int> {
10089
@override
10190
Future<int> run() async {
10291
Configuration configuration;
103-
try {
104-
configuration = Configuration.fromArgs(argResults);
105-
} on InvalidConfiguration catch (e) {
106-
colorLog(Level.SEVERE, 'Invalid configuration: ${e.details}\n\n');
107-
printUsage();
108-
return -1;
109-
}
110-
111-
var workingDirectory = Directory.current.path;
112-
113-
var directoryArgs = argResults.rest
114-
.where((arg) => arg.contains(':') || !arg.startsWith('--'))
115-
.toList();
116-
92+
configuration = Configuration.fromArgs(argResults);
11793
var pubspecLock = await readPubspecLock(configuration);
118-
11994
// Forward remaining arguments as Build Options to the Daemon.
12095
// This isn't documented. Should it be advertised?
12196
var buildOptions = buildRunnerArgs(pubspecLock, configuration)
12297
..addAll(argResults.rest
12398
.where((arg) => !arg.contains(':') || arg.startsWith('--'))
12499
.toList());
125-
126-
colorLog(Level.INFO, 'Connecting to the build daemon...');
127-
try {
128-
_client = await connectClient(
129-
workingDirectory,
130-
buildOptions,
131-
(serverLog) => writeServerLog(serverLog, configuration.verbose),
132-
);
133-
} on OptionsSkew {
134-
colorLog(
135-
Level.SEVERE,
136-
'\nIncompatible options with current running build daemon.\n\n'
137-
'Please stop other WebDev instances running in this directory '
138-
'before starting a new instance with these options.');
139-
// TODO(grouma) - Give an option to kill the running daemon.
140-
return -1;
141-
}
142-
143-
colorLog(Level.INFO, 'Registering build targets...');
100+
var directoryArgs = argResults.rest
101+
.where((arg) => arg.contains(':') || !arg.startsWith('--'))
102+
.toList();
144103
var targetPorts = _parseDirectoryArgs(directoryArgs);
145-
for (var target in targetPorts.keys) {
146-
_client.registerBuildTarget(DefaultBuildTarget((b) => b.target = target));
147-
}
148-
149-
var assetPort = daemonPort(workingDirectory);
150-
var serverOptions = Set<ServerOptions>();
151-
for (var target in targetPorts.keys) {
152-
serverOptions.add(ServerOptions(
153-
configuration,
154-
targetPorts[target],
155-
target,
156-
assetPort,
157-
));
158-
}
159-
160-
var devToolsCompleter = Completer<DevTools>();
161-
162-
_serverManager = ServerManager(
163-
serverOptions, _client.buildResults, devToolsCompleter.future);
164-
165-
colorLog(Level.INFO, 'Starting resource servers...');
166-
await _serverManager.start();
167-
168-
try {
169-
if (configuration.launchInChrome) {
170-
_chrome = await Chrome.start(_serverManager.uris,
171-
port: configuration.chromeDebugPort);
172-
} else if (configuration.chromeDebugPort != 0) {
173-
_chrome = await Chrome.fromExisting(configuration.chromeDebugPort);
174-
}
175-
176-
if (configuration.debug) {
177-
var devTools = await DevTools.start(configuration.hostname, _chrome);
178-
devToolsCompleter.complete(devTools);
179-
} else {
180-
devToolsCompleter.complete(null);
181-
}
182-
183-
colorLog(Level.INFO, 'Starting initial build...');
184-
_client.startBuild();
185-
await _client.finished;
186-
} on ChromeError catch (e) {
187-
colorLog(Level.SEVERE, e.details);
188-
} finally {
189-
await shutDown();
190-
}
191-
104+
var workflow = await DevWorkflow.start(
105+
configuration, buildOptions, targetPorts, colorLog);
106+
await workflow.done;
192107
return 0;
193108
}
194-
195-
Future<void> shutDown() async {
196-
await _client?.close();
197-
await _serverManager?.stop();
198-
await _chrome?.close();
199-
}
200109
}

webdev/lib/src/daemon/app_domain.dart

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
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.
4+
5+
import 'dart:async';
6+
import 'dart:io';
7+
8+
import 'package:dwds/service.dart';
9+
import 'package:uuid/uuid.dart';
10+
import 'package:webdev/src/serve/chrome.dart';
11+
import 'package:webdev/src/serve/server_manager.dart';
12+
13+
import '../serve/debugger/webdev_vm_client.dart';
14+
import 'daemon.dart';
15+
import 'domain.dart';
16+
import 'utilites.dart';
17+
18+
/// A collection of method and events relevant to the running application.
19+
class AppDomain extends Domain {
20+
final String _appId;
21+
22+
WebdevVmClient _webdevVmClient;
23+
DebugService _debugService;
24+
bool _isShutdown = false;
25+
26+
AppDomain(Daemon daemon, Future<ServerManager> futureServerManager)
27+
: _appId = Uuid().v1() as String,
28+
super(daemon, 'app') {
29+
registerHandler('restart', _restart);
30+
registerHandler('callServiceExtension', _callServiceExtension);
31+
registerHandler('stop', _stop);
32+
33+
futureServerManager.then((serverManager) async {
34+
if (_isShutdown) return;
35+
sendEvent('app.start', {
36+
'appId': _appId,
37+
'directory': Directory.current.path,
38+
'deviceId': 'chrome',
39+
'launchMode': 'run'
40+
});
41+
42+
// TODO(https://github.com/dart-lang/webdev/issues/202) - Embed the appID
43+
// in the WebServer.
44+
var server = serverManager.servers.firstWhere((s) => s.target == 'web');
45+
var devHandler = server.devHandler;
46+
await devHandler.connections.next;
47+
// TODO(https://github.com/dart-lang/webdev/issues/202) - Remove.
48+
await Future.delayed(Duration(seconds: 2));
49+
50+
var chrome = await Chrome.connectedInstance;
51+
// TODO(https://github.com/dart-lang/webdev/issues/202) - Run an eval to
52+
// get the appId.
53+
var appUrl = (await chrome.chromeConnection.getTabs())
54+
.firstWhere(
55+
(tab) => tab.url.startsWith('http://localhost:${server.port}'))
56+
.url;
57+
58+
sendEvent('daemon.logMessage',
59+
{'level': 'info', 'message': 'Connecting to $appUrl'});
60+
61+
_debugService = await server.devHandler
62+
.startDebugService(chrome.chromeConnection, appUrl);
63+
_webdevVmClient = await WebdevVmClient.create(_debugService);
64+
65+
sendEvent('app.debugPort', {
66+
'appId': _appId,
67+
'port': _debugService.port,
68+
'wsUri': _debugService.wsUri,
69+
});
70+
71+
// Shutdown could have been triggered while awaiting above.
72+
// ignore: invariant_booleans
73+
if (_isShutdown) dispose();
74+
75+
// TODO(grouma) - Add an event for when the application is started.
76+
});
77+
}
78+
79+
Future<String> _callServiceExtension(Map<String, dynamic> args) {
80+
throw UnimplementedError();
81+
}
82+
83+
Future<String> _restart(Map<String, dynamic> args) async {
84+
throw UnimplementedError();
85+
}
86+
87+
Future<bool> _stop(Map<String, dynamic> args) async {
88+
var appId = getStringArg(args, 'appId', required: true);
89+
if (_appId != appId) throw ArgumentError("app '$appId' not found");
90+
var chrome = await Chrome.connectedInstance;
91+
await chrome.close();
92+
return true;
93+
}
94+
95+
@override
96+
void dispose() {
97+
_isShutdown = true;
98+
_debugService?.close();
99+
_webdevVmClient?.close();
100+
}
101+
}

webdev/lib/src/daemon/daemon.dart

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import 'dart:async';
66
import 'dart:io';
77

8+
import '../serve/server_manager.dart';
9+
import 'app_domain.dart';
810
import 'daemon_domain.dart';
911
import 'domain.dart';
1012
import 'utilites.dart';
@@ -14,11 +16,15 @@ import 'utilites.dart';
1416
/// Listens for commands, routes them to the corresponding domain and provides
1517
/// the result.
1618
class Daemon {
17-
Daemon(Stream<Map<String, dynamic>> commandStream, this._sendCommand) {
19+
Daemon(
20+
Stream<Map<String, dynamic>> commandStream,
21+
this._sendCommand,
22+
Future<ServerManager> futureServerManager,
23+
) {
1824
_registerDomain(DaemonDomain(this));
25+
_registerDomain(AppDomain(this, futureServerManager));
1926

2027
// TODO(grouma) - complete these other domains.
21-
//_registerDomain(appDomain = AppDomain(this));
2228
//_registerDomain(deviceDomain = DeviceDomain(this));
2329
//_registerDomain(emulatorDomain = EmulatorDomain(this));
2430

@@ -79,6 +85,9 @@ class Daemon {
7985

8086
void send(Map<String, dynamic> map) => _sendCommand(map);
8187

88+
void sendEvent(String domain, String name, [dynamic args]) =>
89+
_domainMap[domain].sendEvent('$domain.$name', args);
90+
8291
void shutdown({dynamic error}) {
8392
_commandSubscription?.cancel();
8493
for (var domain in _domainMap.values) domain.dispose();

0 commit comments

Comments
 (0)