Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
856f04be
Commit
856f04be
authored
Nov 04, 2015
by
Devon Carew
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #169 from devoncarew/daemon
add a persistent daemon/server mode to sky_tools
parents
071ebda5
7d0a298b
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
354 additions
and
6 deletions
+354
-6
executable.dart
packages/flutter_tools/lib/executable.dart
+3
-1
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+214
-0
device.dart
packages/flutter_tools/lib/src/device.dart
+4
-2
all.dart
packages/flutter_tools/test/all.dart
+2
-0
daemon_test.dart
packages/flutter_tools/test/daemon_test.dart
+80
-0
init_test.dart
packages/flutter_tools/test/init_test.dart
+2
-2
list_test.dart
packages/flutter_tools/test/list_test.dart
+0
-1
daemon_client.dart
packages/flutter_tools/tool/daemon_client.dart
+49
-0
No files found.
packages/flutter_tools/lib/executable.dart
View file @
856f04be
...
...
@@ -11,6 +11,7 @@ import 'package:stack_trace/stack_trace.dart';
import
'src/commands/build.dart'
;
import
'src/commands/cache.dart'
;
import
'src/commands/daemon.dart'
;
import
'src/commands/flutter_command_runner.dart'
;
import
'src/commands/init.dart'
;
import
'src/commands/install.dart'
;
...
...
@@ -28,7 +29,7 @@ import 'src/process.dart';
/// This function is intended to be used from the [flutter] command line tool.
Future
main
(
List
<
String
>
args
)
async
{
// This level can be adjusted by users through the `--verbose` option.
Logger
.
root
.
level
=
Level
.
SEVERE
;
Logger
.
root
.
level
=
Level
.
WARNING
;
Logger
.
root
.
onRecord
.
listen
((
LogRecord
record
)
{
if
(
record
.
level
>=
Level
.
WARNING
)
{
stderr
.
writeln
(
record
.
message
);
...
...
@@ -44,6 +45,7 @@ Future main(List<String> args) async {
FlutterCommandRunner
runner
=
new
FlutterCommandRunner
()
..
addCommand
(
new
BuildCommand
())
..
addCommand
(
new
CacheCommand
())
..
addCommand
(
new
DaemonCommand
())
..
addCommand
(
new
InitCommand
())
..
addCommand
(
new
InstallCommand
())
..
addCommand
(
new
ListCommand
())
...
...
packages/flutter_tools/lib/src/commands/daemon.dart
0 → 100644
View file @
856f04be
// Copyright 2015 The Chromium Authors. 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:convert'
;
import
'dart:io'
;
import
'package:logging/logging.dart'
;
import
'flutter_command.dart'
;
import
'start.dart'
;
import
'stop.dart'
;
const
String
protocolVersion
=
'0.0.1'
;
/// A @domain annotation.
const
String
domain
=
'domain'
;
/// A domain @command annotation.
const
String
command
=
'command'
;
final
Logger
_logging
=
new
Logger
(
'sky_tools.daemon'
);
// TODO: Create a `device` domain in order to list devices and fire events when
// devices are added or removed.
// TODO: Is this the best name? Server? Daemon?
/// A server process command. This command will start up a long-lived server.
/// It reads JSON-RPC based commands from stdin, executes them, and returns
/// JSON-RPC based responses and events to stdout.
///
/// It can be shutdown with a `daemon.shutdown` command (or by killing the
/// process).
class
DaemonCommand
extends
FlutterCommand
{
final
String
name
=
'daemon'
;
final
String
description
=
'Run a persistent, JSON-RPC based server to communicate with devices.'
;
final
String
usageFooter
=
'
\n
This command is intended to be used by tooling environments that need '
'a programatic interface into launching Flutter applications.'
;
@override
Future
<
int
>
runInProject
()
async
{
print
(
'Starting device daemon...'
);
Stream
<
Map
>
commandStream
=
stdin
.
transform
(
UTF8
.
decoder
)
.
transform
(
const
LineSplitter
())
.
where
((
String
line
)
=>
line
.
startsWith
(
'[{'
)
&&
line
.
endsWith
(
'}]'
))
.
map
((
String
line
)
{
line
=
line
.
substring
(
1
,
line
.
length
-
1
);
return
JSON
.
decode
(
line
);
});
await
downloadApplicationPackagesAndConnectToDevices
();
Daemon
daemon
=
new
Daemon
(
commandStream
,
(
Map
command
)
{
stdout
.
writeln
(
'[
${JSON.encode(command)}
]'
);
},
daemonCommand:
this
);
return
daemon
.
onExit
;
}
}
typedef
void
DispatchComand
(
Map
command
);
typedef
Future
<
dynamic
>
CommandHandler
(
dynamic
args
);
class
Daemon
{
final
DispatchComand
sendCommand
;
final
DaemonCommand
daemonCommand
;
final
Completer
<
int
>
_onExitCompleter
=
new
Completer
();
final
Map
<
String
,
Domain
>
_domains
=
{};
Daemon
(
Stream
<
Map
>
commandStream
,
this
.
sendCommand
,
{
this
.
daemonCommand
})
{
// Set up domains.
_registerDomain
(
new
DaemonDomain
(
this
));
_registerDomain
(
new
AppDomain
(
this
));
// Start listening.
commandStream
.
listen
(
(
Map
command
)
=>
_handleCommand
(
command
),
onDone:
()
=>
_onExitCompleter
.
complete
(
0
)
);
}
void
_registerDomain
(
Domain
domain
)
{
_domains
[
domain
.
name
]
=
domain
;
}
Future
<
int
>
get
onExit
=>
_onExitCompleter
.
future
;
void
_handleCommand
(
Map
command
)
{
// {id, event, params}
var
id
=
command
[
'id'
];
if
(
id
==
null
)
{
_logging
.
severe
(
'no id for command:
${command}
'
);
return
;
}
try
{
String
event
=
command
[
'event'
];
if
(
event
.
indexOf
(
'.'
)
==
-
1
)
throw
'command not understood:
${event}
'
;
String
prefix
=
event
.
substring
(
0
,
event
.
indexOf
(
'.'
));
String
name
=
event
.
substring
(
event
.
indexOf
(
'.'
)
+
1
);
if
(
_domains
[
prefix
]
==
null
)
throw
'no domain for command:
${command}
'
;
_domains
[
prefix
].
handleEvent
(
name
,
id
,
command
[
'params'
]);
}
catch
(
error
,
trace
)
{
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
_logging
.
warning
(
'error handling
${command['event']}
'
,
error
,
trace
);
}
}
void
_send
(
Map
map
)
=>
sendCommand
(
map
);
void
shutdown
()
{
if
(!
_onExitCompleter
.
isCompleted
)
_onExitCompleter
.
complete
(
0
);
}
}
abstract
class
Domain
{
final
Daemon
daemon
;
final
String
name
;
final
Map
<
String
,
CommandHandler
>
_handlers
=
{};
Domain
(
this
.
daemon
,
this
.
name
);
void
registerHandler
(
String
name
,
CommandHandler
handler
)
{
_handlers
[
name
]
=
handler
;
}
String
toString
()
=>
name
;
void
handleEvent
(
String
name
,
dynamic
id
,
dynamic
args
)
{
new
Future
.
sync
(()
{
if
(
_handlers
.
containsKey
(
name
))
return
_handlers
[
name
](
args
);
throw
'command not understood:
${name}
'
;
}).
then
((
result
)
{
if
(
result
==
null
)
{
_send
({
'id'
:
id
});
}
else
{
_send
({
'id'
:
id
,
'result'
:
_toJsonable
(
result
)});
}
}).
catchError
((
error
,
trace
)
{
_send
({
'id'
:
id
,
'error'
:
_toJsonable
(
error
)});
_logging
.
warning
(
'error handling
${name}
'
,
error
,
trace
);
});
}
void
_send
(
Map
map
)
=>
daemon
.
_send
(
map
);
}
/// This domain responds to methods like [version] and [shutdown].
@domain
class
DaemonDomain
extends
Domain
{
DaemonDomain
(
Daemon
daemon
)
:
super
(
daemon
,
'daemon'
)
{
registerHandler
(
'version'
,
version
);
registerHandler
(
'shutdown'
,
shutdown
);
}
@command
Future
<
dynamic
>
version
(
dynamic
args
)
{
return
new
Future
.
value
(
protocolVersion
);
}
@command
Future
<
dynamic
>
shutdown
(
dynamic
args
)
{
Timer
.
run
(()
=>
daemon
.
shutdown
());
return
new
Future
.
value
();
}
}
/// This domain responds to methods like [start] and [stopAll].
///
/// It'll be extended to fire events for when applications start, stop, and
/// log data.
@domain
class
AppDomain
extends
Domain
{
AppDomain
(
Daemon
daemon
)
:
super
(
daemon
,
'app'
)
{
registerHandler
(
'start'
,
start
);
registerHandler
(
'stopAll'
,
stopAll
);
}
@command
Future
<
dynamic
>
start
(
dynamic
args
)
{
// TODO: Add the ability to pass args: target, http, checked
StartCommand
startComand
=
new
StartCommand
();
startComand
.
inheritFromParent
(
daemon
.
daemonCommand
);
return
startComand
.
runInProject
().
then
((
_
)
=>
null
);
}
@command
Future
<
bool
>
stopAll
(
dynamic
args
)
{
StopCommand
stopCommand
=
new
StopCommand
();
stopCommand
.
inheritFromParent
(
daemon
.
daemonCommand
);
return
stopCommand
.
stop
();
}
}
dynamic
_toJsonable
(
dynamic
obj
)
{
if
(
obj
is
String
||
obj
is
int
||
obj
is
bool
||
obj
is
Map
||
obj
is
List
||
obj
==
null
)
return
obj
;
return
'
${obj}
'
;
}
packages/flutter_tools/lib/src/device.dart
View file @
856f04be
...
...
@@ -886,8 +886,10 @@ class AndroidDevice extends Device {
'-v'
,
'tag'
,
// Only log the tag and the message
'-s'
,
'sky'
,
'chromium'
,
'sky:V'
,
'chromium:D'
,
'ActivityManager:W'
,
'*:F'
,
],
prefix:
'android: '
);
}
...
...
packages/flutter_tools/test/all.dart
View file @
856f04be
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import
'android_device_test.dart'
as
android_device_test
;
import
'daemon_test.dart'
as
daemon_test
;
import
'init_test.dart'
as
init_test
;
import
'install_test.dart'
as
install_test
;
import
'listen_test.dart'
as
listen_test
;
...
...
@@ -15,6 +16,7 @@ import 'trace_test.dart' as trace_test;
main
()
{
android_device_test
.
defineTests
();
daemon_test
.
defineTests
();
init_test
.
defineTests
();
install_test
.
defineTests
();
listen_test
.
defineTests
();
...
...
packages/flutter_tools/test/daemon_test.dart
0 → 100644
View file @
856f04be
// Copyright 2015 The Chromium Authors. 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
'package:mockito/mockito.dart'
;
import
'package:sky_tools/src/commands/daemon.dart'
;
import
'package:test/test.dart'
;
import
'src/mocks.dart'
;
main
()
=>
defineTests
();
defineTests
()
{
group
(
'daemon'
,
()
{
Daemon
daemon
;
tearDown
(()
{
if
(
daemon
!=
null
)
return
daemon
.
shutdown
();
});
test
(
'daemon.version'
,
()
async
{
StreamController
<
Map
>
commands
=
new
StreamController
();
StreamController
<
Map
>
responses
=
new
StreamController
();
daemon
=
new
Daemon
(
commands
.
stream
,
(
Map
result
)
=>
responses
.
add
(
result
)
);
commands
.
add
({
'id'
:
0
,
'event'
:
'daemon.version'
});
Map
response
=
await
responses
.
stream
.
first
;
expect
(
response
[
'id'
],
0
);
expect
(
response
[
'result'
],
isNotEmpty
);
expect
(
response
[
'result'
]
is
String
,
true
);
});
test
(
'daemon.shutdown'
,
()
async
{
StreamController
<
Map
>
commands
=
new
StreamController
();
StreamController
<
Map
>
responses
=
new
StreamController
();
daemon
=
new
Daemon
(
commands
.
stream
,
(
Map
result
)
=>
responses
.
add
(
result
)
);
commands
.
add
({
'id'
:
0
,
'event'
:
'daemon.shutdown'
});
return
daemon
.
onExit
.
then
((
int
code
)
{
expect
(
code
,
0
);
});
});
test
(
'daemon.stopAll'
,
()
async
{
DaemonCommand
command
=
new
DaemonCommand
();
applyMocksToCommand
(
command
);
StreamController
<
Map
>
commands
=
new
StreamController
();
StreamController
<
Map
>
responses
=
new
StreamController
();
daemon
=
new
Daemon
(
commands
.
stream
,
(
Map
result
)
=>
responses
.
add
(
result
),
daemonCommand:
command
);
MockDeviceStore
mockDevices
=
command
.
devices
;
when
(
mockDevices
.
android
.
isConnected
()).
thenReturn
(
true
);
when
(
mockDevices
.
android
.
stopApp
(
any
)).
thenReturn
(
true
);
when
(
mockDevices
.
iOS
.
isConnected
()).
thenReturn
(
false
);
when
(
mockDevices
.
iOS
.
stopApp
(
any
)).
thenReturn
(
false
);
when
(
mockDevices
.
iOSSimulator
.
isConnected
()).
thenReturn
(
false
);
when
(
mockDevices
.
iOSSimulator
.
stopApp
(
any
)).
thenReturn
(
false
);
commands
.
add
({
'id'
:
0
,
'event'
:
'app.stopAll'
});
Map
response
=
await
responses
.
stream
.
first
;
expect
(
response
[
'id'
],
0
);
expect
(
response
[
'result'
],
true
);
});
});
}
packages/flutter_tools/test/init_test.dart
View file @
856f04be
...
...
@@ -13,7 +13,7 @@ import 'package:test/test.dart';
main
()
=>
defineTests
();
defineTests
()
{
group
(
''
,
()
{
group
(
'
init
'
,
()
{
Directory
temp
;
setUp
(()
{
...
...
@@ -28,7 +28,7 @@ defineTests() {
// covered on the linux one.
if
(!
Platform
.
isWindows
)
{
// Verify that we create a project that is well-formed.
test
(
'
init
flutter-simple'
,
()
async
{
test
(
'flutter-simple'
,
()
async
{
InitCommand
command
=
new
InitCommand
();
CommandRunner
runner
=
new
CommandRunner
(
'test_flutter'
,
''
)
..
addCommand
(
command
);
...
...
packages/flutter_tools/test/list_test.dart
View file @
856f04be
...
...
@@ -36,7 +36,6 @@ defineTests() {
// Instead, cause the test to run the echo command.
when
(
mockDevices
.
iOSSimulator
.
xcrunPath
).
thenReturn
(
mockCommand
);
CommandRunner
runner
=
new
CommandRunner
(
'test_flutter'
,
''
)
..
addCommand
(
command
);
runner
.
run
([
'list'
]).
then
((
int
code
)
=>
expect
(
code
,
equals
(
0
)));
...
...
packages/flutter_tools/tool/daemon_client.dart
0 → 100644
View file @
856f04be
// Copyright 2015 The Chromium Authors. 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:convert'
;
import
'dart:io'
;
Process
daemon
;
main
()
async
{
daemon
=
await
Process
.
start
(
'dart'
,
[
'bin/sky_tools.dart'
,
'daemon'
]);
print
(
'daemon process started, pid:
${daemon.pid}
'
);
daemon
.
stdout
.
transform
(
UTF8
.
decoder
)
.
transform
(
const
LineSplitter
())
.
listen
((
String
line
)
=>
print
(
'<==
${line}
'
));
daemon
.
stderr
.
listen
((
data
)
=>
stderr
.
add
(
data
));
stdout
.
write
(
'> '
);
stdin
.
transform
(
UTF8
.
decoder
).
transform
(
const
LineSplitter
()).
listen
((
String
line
)
{
if
(
line
==
'version'
||
line
==
'v'
)
{
_send
({
'event'
:
'daemon.version'
});
}
else
if
(
line
==
'shutdown'
||
line
==
'q'
)
{
_send
({
'event'
:
'daemon.shutdown'
});
}
else
if
(
line
==
'start'
)
{
_send
({
'event'
:
'app.start'
});
}
else
if
(
line
==
'stopAll'
)
{
_send
({
'event'
:
'app.stopAll'
});
}
else
{
print
(
'command not understood:
${line}
'
);
}
stdout
.
write
(
'> '
);
});
daemon
.
exitCode
.
then
((
int
code
)
{
print
(
'daemon exiting (
${code}
)'
);
exit
(
code
);
});
}
int
id
=
0
;
void
_send
(
Map
map
)
{
map
[
'id'
]
=
id
++;
String
str
=
'[
${JSON.encode(map)}
]'
;
daemon
.
stdin
.
writeln
(
str
);
print
(
'==>
${str}
'
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment