Skip to content

Daemon App Domain #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dwds/lib/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,18 @@ class DebugService {
final ChromeProxyService chromeProxyService;
final String hostname;
final ServiceExtensionRegistry serviceExtensionRegistry;

final int port;
final HttpServer _server;

DebugService._(this.chromeProxyService, this.hostname, this.port,
this.serviceExtensionRegistry, this._server);

Future<void> close() async {
await _server.close();
}

String get wsUri => 'ws://$hostname:$port';

static Future<DebugService> start(
String hostname,
ChromeConnection chromeConnection,
Expand Down
4 changes: 4 additions & 0 deletions webdev/bin/webdev.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'dart:isolate';
import 'package:args/command_runner.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
import 'package:webdev/src/command/configuration.dart';
import 'package:webdev/src/webdev_command_runner.dart';

Future main(List<String> args) async {
Expand Down Expand Up @@ -43,6 +44,9 @@ Future main(List<String> args) async {
print(red.wrap('$_boldApp failed with an unexpected exception.'));
print(e.message);
exitCode = ExitCode.software.code;
} on InvalidConfiguration catch (e) {
print(red.wrap('$_boldApp $e'));
exitCode = ExitCode.config.code;
}
}

Expand Down
11 changes: 8 additions & 3 deletions webdev/lib/src/command/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class Configuration {
final bool _requireBuildWebCompilers;
final bool _verbose;

Configuration._({
Configuration({
int chromeDebugPort,
bool debug,
String hostname,
Expand Down Expand Up @@ -97,7 +97,7 @@ class Configuration {

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

var chromeDebugPort = argResults.options.contains(chromeDebugPortFlag)
Expand Down Expand Up @@ -143,7 +143,7 @@ class Configuration {
'--$debugFlag.');
}

return Configuration._(
return Configuration(
chromeDebugPort: chromeDebugPort,
debug: debug,
hostname: hostname,
Expand All @@ -160,4 +160,9 @@ class Configuration {
class InvalidConfiguration implements Exception {
final String details;
InvalidConfiguration(this.details);

@override
String toString() {
return 'Invalid configuration: $details';
}
}
20 changes: 19 additions & 1 deletion webdev/lib/src/command/daemon_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import 'dart:io';
import 'package:args/command_runner.dart';

import '../daemon/daemon.dart';
import '../serve/dev_workflow.dart';
import '../serve/server_manager.dart';
import '../serve/utils.dart';
import 'configuration.dart';
import 'shared.dart';

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

@override
Future<int> run() async {
var daemon = Daemon(_stdinCommandStream, _stdoutCommandResponse);
var serveManagerCompleter = Completer<ServerManager>();
var daemon = Daemon(_stdinCommandStream, _stdoutCommandResponse,
serveManagerCompleter.future);
var port = await findUnusedPort();
var configuration = Configuration(launchInChrome: true);
var pubspecLock = await readPubspecLock(configuration);
var buildOptions = buildRunnerArgs(pubspecLock, configuration);
var workflow = await DevWorkflow.start(
configuration, buildOptions, {'web': port}, (level, message) {
daemon.sendEvent(
'daemon', 'logMessage', {'level': '$level', 'message': message});
});
serveManagerCompleter.complete(workflow.serverManager);
await daemon.onExit;
await workflow.shutDown();
return 0;
}
}
107 changes: 8 additions & 99 deletions webdev/lib/src/command/serve_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,9 @@ import 'dart:io';

import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:build_daemon/client.dart';
import 'package:build_daemon/data/build_target.dart';
import 'package:logging/logging.dart';

import '../serve/chrome.dart';
import '../serve/daemon_client.dart';
import '../serve/debugger/devtools.dart';
import '../serve/server_manager.dart';
import '../serve/dev_workflow.dart';
import '../serve/utils.dart';
import '../serve/webdev_server.dart';
import 'configuration.dart';
import 'shared.dart';

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

/// Command to run a server for local web development with the build daemon.
class ServeCommand extends Command<int> {
ServerManager _serverManager;
Chrome _chrome;
BuildDaemonClient _client;

@override
final name = 'serve';

Expand Down Expand Up @@ -100,101 +89,21 @@ class ServeCommand extends Command<int> {
@override
Future<int> run() async {
Configuration configuration;
try {
configuration = Configuration.fromArgs(argResults);
} on InvalidConfiguration catch (e) {
colorLog(Level.SEVERE, 'Invalid configuration: ${e.details}\n\n');
printUsage();
return -1;
}

var workingDirectory = Directory.current.path;

var directoryArgs = argResults.rest
.where((arg) => arg.contains(':') || !arg.startsWith('--'))
.toList();

configuration = Configuration.fromArgs(argResults);
var pubspecLock = await readPubspecLock(configuration);

// Forward remaining arguments as Build Options to the Daemon.
// This isn't documented. Should it be advertised?
var buildOptions = buildRunnerArgs(pubspecLock, configuration)
..addAll(argResults.rest
.where((arg) => !arg.contains(':') || arg.startsWith('--'))
.toList());

colorLog(Level.INFO, 'Connecting to the build daemon...');
try {
_client = await connectClient(
workingDirectory,
buildOptions,
(serverLog) => writeServerLog(serverLog, configuration.verbose),
);
} on OptionsSkew {
colorLog(
Level.SEVERE,
'\nIncompatible options with current running build daemon.\n\n'
'Please stop other WebDev instances running in this directory '
'before starting a new instance with these options.');
// TODO(grouma) - Give an option to kill the running daemon.
return -1;
}

colorLog(Level.INFO, 'Registering build targets...');
var directoryArgs = argResults.rest
.where((arg) => arg.contains(':') || !arg.startsWith('--'))
.toList();
var targetPorts = _parseDirectoryArgs(directoryArgs);
for (var target in targetPorts.keys) {
_client.registerBuildTarget(DefaultBuildTarget((b) => b.target = target));
}

var assetPort = daemonPort(workingDirectory);
var serverOptions = Set<ServerOptions>();
for (var target in targetPorts.keys) {
serverOptions.add(ServerOptions(
configuration,
targetPorts[target],
target,
assetPort,
));
}

var devToolsCompleter = Completer<DevTools>();

_serverManager = ServerManager(
serverOptions, _client.buildResults, devToolsCompleter.future);

colorLog(Level.INFO, 'Starting resource servers...');
await _serverManager.start();

try {
if (configuration.launchInChrome) {
_chrome = await Chrome.start(_serverManager.uris,
port: configuration.chromeDebugPort);
} else if (configuration.chromeDebugPort != 0) {
_chrome = await Chrome.fromExisting(configuration.chromeDebugPort);
}

if (configuration.debug) {
var devTools = await DevTools.start(configuration.hostname, _chrome);
devToolsCompleter.complete(devTools);
} else {
devToolsCompleter.complete(null);
}

colorLog(Level.INFO, 'Starting initial build...');
_client.startBuild();
await _client.finished;
} on ChromeError catch (e) {
colorLog(Level.SEVERE, e.details);
} finally {
await shutDown();
}

var workflow = await DevWorkflow.start(
configuration, buildOptions, targetPorts, colorLog);
await workflow.done;
return 0;
}

Future<void> shutDown() async {
await _client?.close();
await _serverManager?.stop();
await _chrome?.close();
}
}
101 changes: 101 additions & 0 deletions webdev/lib/src/daemon/app_domain.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:io';

import 'package:dwds/service.dart';
import 'package:uuid/uuid.dart';
import 'package:webdev/src/serve/chrome.dart';
import 'package:webdev/src/serve/server_manager.dart';

import '../serve/debugger/webdev_vm_client.dart';
import 'daemon.dart';
import 'domain.dart';
import 'utilites.dart';

/// A collection of method and events relevant to the running application.
class AppDomain extends Domain {
final String _appId;

WebdevVmClient _webdevVmClient;
DebugService _debugService;
bool _isShutdown = false;

AppDomain(Daemon daemon, Future<ServerManager> futureServerManager)
: _appId = Uuid().v1() as String,
super(daemon, 'app') {
registerHandler('restart', _restart);
registerHandler('callServiceExtension', _callServiceExtension);
registerHandler('stop', _stop);

futureServerManager.then((serverManager) async {
if (_isShutdown) return;
sendEvent('app.start', {
'appId': _appId,
'directory': Directory.current.path,
'deviceId': 'chrome',
'launchMode': 'run'
});

// TODO(https://github.com/dart-lang/webdev/issues/202) - Embed the appID
// in the WebServer.
var server = serverManager.servers.firstWhere((s) => s.target == 'web');
var devHandler = server.devHandler;
await devHandler.connections.next;
// TODO(https://github.com/dart-lang/webdev/issues/202) - Remove.
await Future.delayed(Duration(seconds: 2));

var chrome = await Chrome.connectedInstance;
// TODO(https://github.com/dart-lang/webdev/issues/202) - Run an eval to
// get the appId.
var appUrl = (await chrome.chromeConnection.getTabs())
.firstWhere(
(tab) => tab.url.startsWith('http://localhost:${server.port}'))
.url;

sendEvent('daemon.logMessage',
{'level': 'info', 'message': 'Connecting to $appUrl'});

_debugService = await server.devHandler
.startDebugService(chrome.chromeConnection, appUrl);
_webdevVmClient = await WebdevVmClient.create(_debugService);

sendEvent('app.debugPort', {
'appId': _appId,
'port': _debugService.port,
'wsUri': _debugService.wsUri,
});

// Shutdown could have been triggered while awaiting above.
// ignore: invariant_booleans
if (_isShutdown) dispose();

// TODO(grouma) - Add an event for when the application is started.
});
}

Future<String> _callServiceExtension(Map<String, dynamic> args) {
throw UnimplementedError();
}

Future<String> _restart(Map<String, dynamic> args) async {
throw UnimplementedError();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could implement this as _callServiceExtension('hotRestart') now if you wanted in this PR (that is ultimately probably what it should be)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna wait for a follow up PR as I'd like to add some tests.

}

Future<bool> _stop(Map<String, dynamic> args) async {
var appId = getStringArg(args, 'appId', required: true);
if (_appId != appId) throw ArgumentError("app '$appId' not found");
var chrome = await Chrome.connectedInstance;
await chrome.close();
return true;
}

@override
void dispose() {
_isShutdown = true;
_debugService?.close();
_webdevVmClient?.close();
}
}
13 changes: 11 additions & 2 deletions webdev/lib/src/daemon/daemon.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:io';

import '../serve/server_manager.dart';
import 'app_domain.dart';
import 'daemon_domain.dart';
import 'domain.dart';
import 'utilites.dart';
Expand All @@ -14,11 +16,15 @@ import 'utilites.dart';
/// Listens for commands, routes them to the corresponding domain and provides
/// the result.
class Daemon {
Daemon(Stream<Map<String, dynamic>> commandStream, this._sendCommand) {
Daemon(
Stream<Map<String, dynamic>> commandStream,
this._sendCommand,
Future<ServerManager> futureServerManager,
) {
_registerDomain(DaemonDomain(this));
_registerDomain(AppDomain(this, futureServerManager));

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

Expand Down Expand Up @@ -79,6 +85,9 @@ class Daemon {

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

void sendEvent(String domain, String name, [dynamic args]) =>
_domainMap[domain].sendEvent('$domain.$name', args);

void shutdown({dynamic error}) {
_commandSubscription?.cancel();
for (var domain in _domainMap.values) domain.dispose();
Expand Down
Loading