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
0de69162
Commit
0de69162
authored
Jul 20, 2016
by
John McCutchan
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add --hot mode for flutter run
parent
8893e328
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
486 additions
and
220 deletions
+486
-220
android_device.dart
packages/flutter_tools/lib/src/android/android_device.dart
+25
-0
daemon.dart
packages/flutter_tools/lib/src/commands/daemon.dart
+3
-6
run.dart
packages/flutter_tools/lib/src/commands/run.dart
+19
-11
devfs.dart
packages/flutter_tools/lib/src/devfs.dart
+237
-0
device.dart
packages/flutter_tools/lib/src/device.dart
+13
-0
simulators.dart
packages/flutter_tools/lib/src/ios/simulators.dart
+6
-28
observatory.dart
packages/flutter_tools/lib/src/observatory.dart
+7
-25
run.dart
packages/flutter_tools/lib/src/run.dart
+83
-150
devfs_test.dart
packages/flutter_tools/test/devfs_test.dart
+59
-0
mocks.dart
packages/flutter_tools/test/src/mocks.dart
+34
-0
No files found.
packages/flutter_tools/lib/src/android/android_device.dart
View file @
0de69162
...
...
@@ -388,6 +388,31 @@ class AndroidDevice extends Device {
);
}
@override
bool
get
supportsHotMode
=>
true
;
@override
Future
<
bool
>
runFromFile
(
ApplicationPackage
package
,
String
scriptUri
,
String
packagesUri
)
async
{
AndroidApk
apk
=
package
;
List
<
String
>
cmd
=
adbCommandForDevice
(<
String
>[
'shell'
,
'am'
,
'start'
,
'-a'
,
'android.intent.action.RUN'
,
'-d'
,
_deviceBundlePath
,
'-f'
,
'0x20000000'
,
// FLAG_ACTIVITY_SINGLE_TOP
]);
cmd
.
addAll
(<
String
>[
'--es'
,
'file'
,
scriptUri
]);
cmd
.
addAll
(<
String
>[
'--es'
,
'packages'
,
packagesUri
]);
cmd
.
add
(
apk
.
launchActivity
);
String
result
=
runCheckedSync
(
cmd
);
if
(
result
.
contains
(
'Error: '
))
{
printError
(
result
.
trim
());
return
false
;
}
return
true
;
}
@override
bool
get
supportsRestart
=>
true
;
...
...
packages/flutter_tools/lib/src/commands/daemon.dart
View file @
0de69162
...
...
@@ -17,7 +17,6 @@ import '../ios/devices.dart';
import
'../ios/simulators.dart'
;
import
'../run.dart'
;
import
'../runner/flutter_command.dart'
;
import
'run.dart'
as
run
;
const
String
protocolVersion
=
'0.2.0'
;
...
...
@@ -292,7 +291,7 @@ class AppDomain extends Domain {
String
route
=
_getStringArg
(
args
,
'route'
);
String
mode
=
_getStringArg
(
args
,
'mode'
);
String
target
=
_getStringArg
(
args
,
'target'
);
bool
reloadSources
=
_getBoolArg
(
args
,
'reload-sources
'
);
bool
hotMode
=
_getBoolArg
(
args
,
'hot
'
);
Device
device
=
daemon
.
deviceDomain
.
_getDevice
(
deviceId
);
if
(
device
==
null
)
...
...
@@ -301,9 +300,6 @@ class AppDomain extends Domain {
if
(!
FileSystemEntity
.
isDirectorySync
(
projectDirectory
))
throw
"'
$projectDirectory
' does not exist"
;
if
(
reloadSources
!=
null
)
run
.
useReloadSources
=
reloadSources
;
BuildMode
buildMode
=
getBuildModeForName
(
mode
)
??
BuildMode
.
debug
;
DebuggingOptions
options
;
...
...
@@ -327,7 +323,8 @@ class AppDomain extends Domain {
device
,
target:
target
,
debuggingOptions:
options
,
usesTerminalUI:
false
usesTerminalUI:
false
,
hotMode:
hotMode
);
AppInstance
app
=
new
AppInstance
(
_getNextAppId
(),
runner
);
...
...
packages/flutter_tools/lib/src/commands/run.dart
View file @
0de69162
...
...
@@ -19,9 +19,6 @@ import 'build_apk.dart';
import
'install.dart'
;
import
'trace.dart'
;
/// Whether the user has passed the `--reload-sources` command-line option.
bool
useReloadSources
=
false
;
abstract
class
RunCommandBase
extends
FlutterCommand
{
RunCommandBase
()
{
addBuildModeFlags
(
defaultToRelease:
false
);
...
...
@@ -58,16 +55,16 @@ class RunCommand extends RunCommandBase {
argParser
.
addOption
(
'debug-port'
,
help:
'Listen to the given port for a debug connection (defaults to
$kDefaultObservatoryPort
).'
);
usesPubOption
();
argParser
.
addFlag
(
'resident'
,
defaultsTo:
true
,
help:
'Don
\'
t terminate the
\'
flutter run
\'
process after starting the application.'
);
// Hidden option to ship all the sources of the current project over to the
// embedder via the DevFS observatory API.
argParser
.
addFlag
(
'devfs'
,
negatable:
false
,
hide:
true
);
// Send the _reloadSource command to the VM.
argParser
.
addFlag
(
'reload-sources'
,
negatable:
true
,
defaultsTo:
false
,
hide:
true
);
// Option to enable hot reloading.
argParser
.
addFlag
(
'hot'
,
negatable:
false
,
defaultsTo:
false
,
help:
'Run with support for hot reloading.'
);
// Hidden option to enable a benchmarking mode. This will run the given
// application, measure the startup time and the app restart time, write the
...
...
@@ -122,14 +119,25 @@ class RunCommand extends RunCommandBase {
Cache
.
releaseLockEarly
();
useReloadSources
=
argResults
[
'reload-sources'
];
// Do some early error checks for hot mode.
bool
hotMode
=
argResults
[
'hot'
];
if
(
hotMode
)
{
if
(
getBuildMode
()
!=
BuildMode
.
debug
)
{
printError
(
'Hot mode only works with debug builds.'
);
return
1
;
}
if
(!
deviceForCommand
.
supportsHotMode
)
{
printError
(
'Hot mode is not supported by this device.'
);
return
1
;
}
}
if
(
argResults
[
'resident'
])
{
RunAndStayResident
runner
=
new
RunAndStayResident
(
deviceForCommand
,
target:
target
,
debuggingOptions:
options
,
useDevFS:
argResults
[
'devfs
'
]
hotMode:
argResults
[
'hot
'
]
);
return
runner
.
run
(
...
...
packages/flutter_tools/lib/src/devfs.dart
0 → 100644
View file @
0de69162
// Copyright 2016 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'
show
BASE64
,
UTF8
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'dart/package_map.dart'
;
import
'globals.dart'
;
import
'observatory.dart'
;
// A file that has been added to a DevFS.
class
DevFSEntry
{
DevFSEntry
(
this
.
devicePath
,
this
.
file
);
final
String
devicePath
;
final
File
file
;
FileStat
_fileStat
;
DateTime
get
lastModified
=>
_fileStat
?.
modified
;
bool
get
stillExists
{
_stat
();
return
_fileStat
.
type
!=
FileSystemEntityType
.
NOT_FOUND
;
}
bool
get
isModified
{
if
(
_fileStat
==
null
)
{
_stat
();
return
true
;
}
FileStat
_oldFileStat
=
_fileStat
;
_stat
();
return
_fileStat
.
modified
.
isAfter
(
_oldFileStat
.
modified
);
}
void
_stat
()
{
_fileStat
=
file
.
statSync
();
}
}
/// Abstract DevFS operations interface.
abstract
class
DevFSOperations
{
Future
<
Uri
>
create
(
String
fsName
);
Future
<
dynamic
>
destroy
(
String
fsName
);
Future
<
dynamic
>
writeFile
(
String
fsName
,
DevFSEntry
entry
);
Future
<
dynamic
>
writeSource
(
String
fsName
,
String
devicePath
,
String
contents
);
}
/// An implementation of [DevFSOperations] that speaks to the
/// service protocol.
class
ServiceProtocolDevFSOperations
implements
DevFSOperations
{
final
Observatory
serviceProtocol
;
ServiceProtocolDevFSOperations
(
this
.
serviceProtocol
);
@override
Future
<
Uri
>
create
(
String
fsName
)
async
{
Response
response
=
await
serviceProtocol
.
createDevFS
(
fsName
);
return
Uri
.
parse
(
response
[
'uri'
]);
}
@override
Future
<
dynamic
>
destroy
(
String
fsName
)
async
{
await
serviceProtocol
.
sendRequest
(
'_deleteDevFS'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
});
}
@override
Future
<
dynamic
>
writeFile
(
String
fsName
,
DevFSEntry
entry
)
async
{
List
<
int
>
bytes
;
try
{
bytes
=
await
entry
.
file
.
readAsBytes
();
}
catch
(
e
)
{
return
e
;
}
String
fileContents
=
BASE64
.
encode
(
bytes
);
return
await
serviceProtocol
.
sendRequest
(
'_writeDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'path'
:
entry
.
devicePath
,
'fileContents'
:
fileContents
});
}
@override
Future
<
dynamic
>
writeSource
(
String
fsName
,
String
devicePath
,
String
contents
)
async
{
String
fileContents
=
BASE64
.
encode
(
UTF8
.
encode
(
contents
));
return
await
serviceProtocol
.
sendRequest
(
'_writeDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'path'
:
devicePath
,
'fileContents'
:
fileContents
});
}
}
class
DevFS
{
/// Create a [DevFS] named [fsName] for the local files in [directory].
DevFS
(
Observatory
serviceProtocol
,
this
.
fsName
,
this
.
rootDirectory
)
:
_operations
=
new
ServiceProtocolDevFSOperations
(
serviceProtocol
);
DevFS
.
operations
(
this
.
_operations
,
this
.
fsName
,
this
.
rootDirectory
);
final
DevFSOperations
_operations
;
final
String
fsName
;
final
Directory
rootDirectory
;
final
Map
<
String
,
DevFSEntry
>
_entries
=
<
String
,
DevFSEntry
>{};
final
List
<
Future
<
Response
>>
_pendingWrites
=
new
List
<
Future
<
Response
>>();
Uri
_baseUri
;
Uri
get
baseUri
=>
_baseUri
;
Future
<
Uri
>
create
()
async
{
_baseUri
=
await
_operations
.
create
(
fsName
);
printTrace
(
'DevFS: Created new filesystem on the device (
$_baseUri
)'
);
return
_baseUri
;
}
Future
<
dynamic
>
destroy
()
async
{
printTrace
(
'DevFS: Deleted filesystem on the device (
$_baseUri
)'
);
return
await
_operations
.
destroy
(
fsName
);
}
Future
<
dynamic
>
update
()
async
{
printTrace
(
'DevFS: Starting sync from
$rootDirectory
'
);
// Send the root and lib directories.
Directory
directory
=
rootDirectory
;
_syncDirectory
(
directory
,
recursive:
true
);
String
packagesFilePath
=
path
.
join
(
rootDirectory
.
path
,
kPackagesFileName
);
StringBuffer
sb
;
// Send the packages.
if
(
FileSystemEntity
.
isFileSync
(
packagesFilePath
))
{
PackageMap
packageMap
=
new
PackageMap
(
kPackagesFileName
);
for
(
String
packageName
in
packageMap
.
map
.
keys
)
{
Uri
uri
=
packageMap
.
map
[
packageName
];
// Ignore self-references.
if
(
uri
.
toString
()
==
'lib/'
)
continue
;
Directory
directory
=
new
Directory
.
fromUri
(
uri
);
if
(
_syncDirectory
(
directory
,
directoryName:
'packages/
$packageName
'
,
recursive:
true
))
{
if
(
sb
==
null
)
{
sb
=
new
StringBuffer
();
}
sb
.
writeln
(
'
$packageName
:packages/
$packageName
'
);
}
}
}
printTrace
(
'DevFS: Waiting for sync of
${_pendingWrites.length}
files '
'to finish'
);
await
Future
.
wait
(
_pendingWrites
);
_pendingWrites
.
clear
();
if
(
sb
!=
null
)
{
await
_operations
.
writeSource
(
fsName
,
'.packages'
,
sb
.
toString
());
}
printTrace
(
'DevFS: Sync finished'
);
// NB: You must call flush after a printTrace if you want to be printed
// immediately.
logger
.
flush
();
}
void
_syncFile
(
String
devicePath
,
File
file
)
{
DevFSEntry
entry
=
_entries
[
devicePath
];
if
(
entry
==
null
)
{
// New file.
entry
=
new
DevFSEntry
(
devicePath
,
file
);
_entries
[
devicePath
]
=
entry
;
}
bool
needsWrite
=
entry
.
isModified
;
if
(
needsWrite
)
{
Future
<
dynamic
>
pendingWrite
=
_operations
.
writeFile
(
fsName
,
entry
);
if
(
pendingWrite
!=
null
)
{
_pendingWrites
.
add
(
pendingWrite
);
}
else
{
printTrace
(
'DevFS: Failed to sync "
$devicePath
"'
);
}
}
}
bool
_shouldIgnore
(
String
path
)
{
List
<
String
>
ignoredPrefixes
=
<
String
>[
'android/'
,
'build/'
,
'ios/'
,
'packages/analyzer'
];
for
(
String
ignoredPrefix
in
ignoredPrefixes
)
{
if
(
path
.
startsWith
(
ignoredPrefix
))
return
true
;
}
return
false
;
}
bool
_syncDirectory
(
Directory
directory
,
{
String
directoryName
,
bool
recursive:
false
,
bool
ignoreDotFiles:
true
})
{
String
prefix
=
directoryName
;
if
(
prefix
==
null
)
{
prefix
=
path
.
relative
(
directory
.
path
,
from:
rootDirectory
.
path
);
if
(
prefix
==
'.'
)
prefix
=
''
;
}
try
{
List
<
FileSystemEntity
>
files
=
directory
.
listSync
(
recursive:
recursive
,
followLinks:
false
);
for
(
FileSystemEntity
file
in
files
)
{
if
(
file
is
!
File
)
{
// Skip non-files.
continue
;
}
if
(
ignoreDotFiles
&&
path
.
basename
(
file
.
path
).
startsWith
(
'.'
))
{
// Skip dot files.
continue
;
}
final
String
devicePath
=
path
.
join
(
prefix
,
path
.
relative
(
file
.
path
,
from:
directory
.
path
));
if
(!
_shouldIgnore
(
devicePath
))
_syncFile
(
devicePath
,
file
);
}
}
catch
(
e
)
{
// Ignore directory and error.
return
false
;
}
return
true
;
}
}
packages/flutter_tools/lib/src/device.dart
View file @
0de69162
...
...
@@ -189,6 +189,19 @@ abstract class Device {
Map
<
String
,
dynamic
>
platformArgs
});
/// Does this device implement support for hot reloading / restarting?
bool
get
supportsHotMode
=>
false
;
/// Does this device need a DevFS to support hot mode?
bool
get
needsDevFS
=>
true
;
/// Run from a file. Necessary for hot mode.
Future
<
bool
>
runFromFile
(
ApplicationPackage
package
,
String
scriptUri
,
String
packagesUri
)
{
throw
'runFromFile unsupported'
;
}
bool
get
supportsRestart
=>
false
;
bool
get
restartSendsFrameworkInitEvent
=>
true
;
...
...
packages/flutter_tools/lib/src/ios/simulators.dart
View file @
0de69162
...
...
@@ -13,11 +13,9 @@ import '../application_package.dart';
import
'../base/context.dart'
;
import
'../base/process.dart'
;
import
'../build_info.dart'
;
import
'../commands/run.dart'
as
run
;
import
'../device.dart'
;
import
'../flx.dart'
as
flx
;
import
'../globals.dart'
;
import
'../observatory.dart'
;
import
'../protocol_discovery.dart'
;
import
'mac.dart'
;
...
...
@@ -361,6 +359,12 @@ class IOSSimulator extends Device {
@override
bool
get
isLocalEmulator
=>
true
;
@override
bool
get
supportsHotMode
=>
true
;
@override
bool
get
needsDevFS
=>
false
;
_IOSSimulatorLogReader
_logReader
;
_IOSSimulatorDevicePortForwarder
_portForwarder
;
...
...
@@ -575,32 +579,6 @@ class IOSSimulator extends Device {
return
(
await
flx
.
build
(
precompiledSnapshot:
true
))
==
0
;
}
@override
bool
get
supportsRestart
=>
run
.
useReloadSources
;
@override
bool
get
restartSendsFrameworkInitEvent
=>
false
;
@override
Future
<
bool
>
restartApp
(
ApplicationPackage
package
,
LaunchResult
result
,
{
String
mainPath
,
Observatory
observatory
})
async
{
if
(
observatory
.
firstIsolateId
==
null
)
throw
'Application isolate not found'
;
Event
result
=
await
observatory
.
reloadSources
(
observatory
.
firstIsolateId
);
dynamic
error
=
result
.
response
[
'reloadError'
];
if
(
error
!=
null
)
{
printError
(
'Error reloading application sources:
$error
'
);
return
false
;
}
else
{
await
observatory
.
flutterReassemble
(
observatory
.
firstIsolateId
);
return
true
;
}
}
@override
Future
<
bool
>
stopApp
(
ApplicationPackage
app
)
async
{
// Currently we don't have a way to stop an app running on iOS.
...
...
packages/flutter_tools/lib/src/observatory.dart
View file @
0de69162
...
...
@@ -9,6 +9,7 @@ import 'dart:io';
import
'package:json_rpc_2/json_rpc_2.dart'
as
rpc
;
import
'package:web_socket_channel/io.dart'
;
// TODO(johnmccutchan): Rename this class to ServiceProtocol or VmService.
class
Observatory
{
Observatory
.
_
(
this
.
peer
,
this
.
port
)
{
peer
.
registerMethod
(
'streamNotify'
,
(
rpc
.
Parameters
event
)
{
...
...
@@ -169,13 +170,13 @@ class Observatory {
});
}
// Write multiple files into a file system.
Future
<
Response
>
writeDevFSFiles
(
String
fsName
,
{
List
<
DevFSFile
>
files
})
{
assert
(
files
!=
null
);
return
sendRequest
(
'_writeDevFSFiles'
,
<
String
,
dynamic
>
{
// Read one file from a file system.
Future
<
List
<
int
>>
readDevFSFile
(
String
fsName
,
String
path
)
{
return
sendRequest
(
'_readDevFSFile'
,
<
String
,
dynamic
>
{
'fsName'
:
fsName
,
'files'
:
files
.
map
((
DevFSFile
file
)
=>
file
.
toJson
()).
toList
()
'path'
:
path
}).
then
((
Response
response
)
{
return
BASE64
.
decode
(
response
.
response
[
'fileContents'
]);
});
}
...
...
@@ -233,25 +234,6 @@ class Observatory {
}
}
abstract
class
DevFSFile
{
DevFSFile
(
this
.
path
);
final
String
path
;
List
<
int
>
getContents
();
List
<
String
>
toJson
()
=>
<
String
>[
path
,
BASE64
.
encode
(
getContents
())];
}
class
ByteDevFSFile
extends
DevFSFile
{
ByteDevFSFile
(
String
path
,
this
.
contents
):
super
(
path
);
final
List
<
int
>
contents
;
@override
List
<
int
>
getContents
()
=>
contents
;
}
class
Response
{
Response
(
this
.
response
);
...
...
packages/flutter_tools/lib/src/run.dart
View file @
0de69162
...
...
@@ -14,10 +14,10 @@ import 'build_info.dart';
import
'commands/build_apk.dart'
;
import
'commands/install.dart'
;
import
'commands/trace.dart'
;
import
'dart/package_map.dart'
;
import
'device.dart'
;
import
'globals.dart'
;
import
'observatory.dart'
;
import
'devfs.dart'
;
/// Given the value of the --target option, return the path of the Dart file
/// where the app's main function should be.
...
...
@@ -37,20 +37,20 @@ class RunAndStayResident {
this
.
target
,
this
.
debuggingOptions
,
this
.
usesTerminalUI
:
true
,
this
.
useDevFS
:
false
this
.
hotMode
:
false
});
final
Device
device
;
final
String
target
;
final
DebuggingOptions
debuggingOptions
;
final
bool
usesTerminalUI
;
final
bool
useDevFS
;
final
bool
hotMode
;
ApplicationPackage
_package
;
String
_mainPath
;
LaunchResult
_result
;
Completer
<
int
>
_exitCompleter
=
new
Completer
<
int
>();
final
Completer
<
int
>
_exitCompleter
=
new
Completer
<
int
>();
StreamSubscription
<
String
>
_loggingSubscription
;
Observatory
observatory
;
...
...
@@ -207,7 +207,15 @@ class RunAndStayResident {
if
(
debuggingOptions
.
debuggingEnabled
)
{
observatory
=
await
Observatory
.
connect
(
_result
.
observatoryPort
);
printTrace
(
'Connected to observatory port:
${_result.observatoryPort}
.'
);
if
(
hotMode
&&
device
.
needsDevFS
)
{
bool
result
=
await
_updateDevFS
();
if
(!
result
)
{
printError
(
'Could not perform initial file synchronization.'
);
return
3
;
}
printStatus
(
'Launching from sources.'
);
await
_launchFromDevFS
(
_package
,
_mainPath
);
}
observatory
.
populateIsolateInfo
();
observatory
.
onExtensionEvent
.
listen
((
Event
event
)
{
printTrace
(
event
.
toString
());
...
...
@@ -250,15 +258,17 @@ class RunAndStayResident {
// F1, help
_printHelp
();
}
else
if
(
lower
==
'r'
||
code
==
AnsiTerminal
.
KEY_F5
)
{
if
(
device
.
supportsRestart
)
{
// F5, restart
restart
();
if
(
hotMode
)
{
_reloadSources
();
}
else
{
if
(
device
.
supportsRestart
)
{
// F5, restart
restart
();
}
}
}
else
if
(
lower
==
'q'
||
code
==
AnsiTerminal
.
KEY_F10
)
{
// F10, exit
_stopApp
();
}
else
if
(
useDevFS
&&
lower
==
'd'
)
{
_updateDevFS
();
}
else
if
(
lower
==
'w'
)
{
_debugDumpApp
();
}
else
if
(
lower
==
't'
)
{
...
...
@@ -269,12 +279,14 @@ class RunAndStayResident {
ProcessSignal
.
SIGINT
.
watch
().
listen
((
ProcessSignal
signal
)
async
{
_resetTerminal
();
await
_cleanupDevFS
();
await
_stopLogger
();
await
_stopApp
();
exit
(
0
);
});
ProcessSignal
.
SIGTERM
.
watch
().
listen
((
ProcessSignal
signal
)
async
{
_resetTerminal
();
await
_cleanupDevFS
();
await
_stopLogger
();
await
_stopApp
();
exit
(
0
);
...
...
@@ -311,78 +323,84 @@ class RunAndStayResident {
observatory
.
flutterDebugDumpRenderTree
(
observatory
.
firstIsolateId
);
}
DevFS
devFS
;
Future
<
Null
>
_updateDevFS
()
async
{
if
(
devFS
==
null
)
{
devFS
=
new
DevFS
(
Directory
.
current
,
observatory
);
DevFS
_devFS
;
String
_devFSProjectRootPath
;
Future
<
bool
>
_updateDevFS
()
async
{
if
(
_devFS
==
null
)
{
Directory
directory
=
Directory
.
current
;
_devFSProjectRootPath
=
directory
.
path
;
String
fsName
=
path
.
basename
(
directory
.
path
);
_devFS
=
new
DevFS
(
observatory
,
fsName
,
directory
);
try
{
await
devFS
.
init
();
await
_devFS
.
create
();
}
catch
(
error
)
{
devFS
=
null
;
printError
(
'Error initializing
development client
:
$error
'
);
return
null
;
_
devFS
=
null
;
printError
(
'Error initializing
DevFS
:
$error
'
);
return
false
;
}
}
// Send the root and lib directories.
Directory
directory
=
Directory
.
current
;
_sendFiles
(
directory
,
''
,
_dartFiles
(
directory
.
listSync
()));
directory
=
new
Directory
(
'lib'
);
_sendFiles
(
directory
,
'lib'
,
_dartFiles
(
directory
.
listSync
(
recursive:
true
)));
// Send the packages.
if
(
FileSystemEntity
.
isFileSync
(
kPackagesFileName
))
{
PackageMap
packageMap
=
new
PackageMap
(
kPackagesFileName
);
for
(
String
packageName
in
packageMap
.
map
.
keys
)
{
Uri
uri
=
packageMap
.
map
[
packageName
];
// Ignore self-references.
if
(
uri
.
toString
()
==
'lib/'
)
continue
;
Directory
directory
=
new
Directory
.
fromUri
(
uri
);
if
(
directory
.
existsSync
())
{
_sendFiles
(
directory
,
'packages/
$packageName
'
,
_dartFiles
(
directory
.
listSync
(
recursive:
true
))
);
}
}
_exitCompleter
.
future
.
then
((
_
)
async
{
await
_cleanupDevFS
();
});
}
try
{
await
devFS
.
flush
();
}
catch
(
error
)
{
printError
(
'Error sending sources to the client device:
$error
'
);
}
printStatus
(
'DevFS: Updating files on device...'
);
await
_devFS
.
update
();
printStatus
(
'DevFS: Finished updating files on device...'
);
return
true
;
}
void
_sendFiles
(
Directory
base
,
String
prefix
,
List
<
File
>
files
)
{
String
basePath
=
base
.
path
;
for
(
File
file
in
files
)
{
String
devPath
=
file
.
path
.
substring
(
basePath
.
length
);
if
(
devPath
.
startsWith
(
'/'
))
devPath
=
devPath
.
substring
(
1
);
devFS
.
stageFile
(
prefix
.
isEmpty
?
devPath
:
'
$prefix
/
$devPath
'
,
file
);
Future
<
Null
>
_cleanupDevFS
()
async
{
if
(
_devFS
!=
null
)
{
// Cleanup the devFS.
await
_devFS
.
destroy
();
}
_devFS
=
null
;
}
List
<
File
>
_dartFiles
(
List
<
FileSystemEntity
>
entities
)
{
return
new
List
<
File
>.
from
(
entities
.
where
((
FileSystemEntity
entity
)
=>
entity
is
File
&&
entity
.
path
.
endsWith
(
'.dart'
)));
Future
<
Null
>
_launchFromDevFS
(
ApplicationPackage
package
,
String
mainScript
)
async
{
String
entryPath
=
path
.
relative
(
mainScript
,
from:
_devFSProjectRootPath
);
String
deviceEntryPath
=
_devFS
.
baseUri
.
resolve
(
entryPath
).
toFilePath
();
String
devicePackagesPath
=
_devFS
.
baseUri
.
resolve
(
'.packages'
).
toFilePath
();
await
device
.
runFromFile
(
package
,
deviceEntryPath
,
devicePackagesPath
);
}
Future
<
bool
>
_reloadSources
()
async
{
if
(
observatory
.
firstIsolateId
==
null
)
throw
'Application isolate not found'
;
if
(
_devFS
!=
null
)
{
await
_updateDevFS
();
}
Status
reloadStatus
=
logger
.
startProgress
(
'Performing hot reload'
);
Event
result
=
await
observatory
.
reloadSources
(
observatory
.
firstIsolateId
);
reloadStatus
.
stop
(
showElapsedTime:
true
);
dynamic
error
=
result
.
response
[
'reloadError'
];
if
(
error
!=
null
)
{
printError
(
'Error reloading application sources:
$error
'
);
return
false
;
}
Status
reassembleStatus
=
logger
.
startProgress
(
'Reassembling application'
);
await
observatory
.
flutterReassemble
(
observatory
.
firstIsolateId
);
reassembleStatus
.
stop
(
showElapsedTime:
true
);
return
true
;
}
void
_printHelp
()
{
String
restartText
=
device
.
supportsRestart
?
', "r" or F5 to restart the app,'
:
''
;
String
restartText
=
''
;
if
(
hotMode
)
{
restartText
=
', "r" or F5 to perform a hot reload of the app,'
;
}
else
if
(
device
.
supportsRestart
)
{
restartText
=
', "r" or F5 to restart the app,'
;
}
printStatus
(
'Type "h" or F1 for help
$restartText
and "q", F10, or ctrl-c to quit.'
);
printStatus
(
'Type "w" to print the widget hierarchy of the app, and "t" for the render tree.'
);
if
(
useDevFS
)
printStatus
(
'Type "d" to send modified project files to the the client
\'
s DevFS.'
);
}
Future
<
dynamic
>
_stopLogger
()
{
...
...
@@ -433,88 +451,3 @@ void writeRunBenchmarkFile(Stopwatch startTime, [Stopwatch restartTime]) {
new
File
(
benchmarkOut
).
writeAsStringSync
(
toPrettyJson
(
data
));
printStatus
(
'Run benchmark written to
$benchmarkOut
(
$data
).'
);
}
class
DevFS
{
DevFS
(
this
.
directory
,
this
.
observatory
)
{
fsName
=
path
.
basename
(
directory
.
path
);
}
final
Directory
directory
;
final
Observatory
observatory
;
String
fsName
;
String
uri
;
Map
<
String
,
_DevFSFileEntry
>
entries
=
<
String
,
_DevFSFileEntry
>{};
Future
<
Null
>
init
()
async
{
CreateDevFSResponse
response
=
await
observatory
.
createDevFS
(
fsName
);
uri
=
response
.
uri
;
}
void
stageFile
(
String
devPath
,
File
file
)
{
entries
.
putIfAbsent
(
devPath
,
()
=>
new
_DevFSFileEntry
(
devPath
,
file
));
}
/// Flush any modified files to the devfs.
Future
<
Null
>
flush
()
async
{
List
<
_DevFSFileEntry
>
toSend
=
entries
.
values
.
where
((
_DevFSFileEntry
entry
)
=>
entry
.
isModified
)
.
toList
();
for
(
_DevFSFileEntry
entry
in
toSend
)
{
printTrace
(
'sending to devfs:
${entry.devPath}
'
);
entry
.
updateLastModified
();
}
Status
status
=
logger
.
startProgress
(
'Sending
${toSend.length}
files...'
);
if
(
toSend
.
isEmpty
)
{
status
.
stop
(
showElapsedTime:
true
);
return
;
}
try
{
List
<
_DevFSFile
>
files
=
toSend
.
map
((
_DevFSFileEntry
entry
)
{
return
new
_DevFSFile
(
'/
${entry.devPath}
'
,
entry
.
file
);
}).
toList
();
// TODO(devoncarew): Batch this up in larger groups using writeDevFSFiles().
// The current implementation leaves dangling service protocol calls on a timeout.
await
Future
.
wait
(
files
.
map
((
_DevFSFile
file
)
{
return
observatory
.
writeDevFSFile
(
fsName
,
path:
file
.
path
,
fileContents:
file
.
getContents
()
);
})).
timeout
(
new
Duration
(
seconds:
10
));
}
finally
{
status
.
stop
(
showElapsedTime:
true
);
}
}
Future
<
List
<
String
>>
listDevFSFiles
()
=>
observatory
.
listDevFSFiles
(
fsName
);
}
class
_DevFSFileEntry
{
_DevFSFileEntry
(
this
.
devPath
,
this
.
file
);
final
String
devPath
;
final
File
file
;
DateTime
lastModified
;
bool
get
isModified
=>
lastModified
==
null
||
file
.
lastModifiedSync
().
isAfter
(
lastModified
);
void
updateLastModified
()
{
lastModified
=
file
.
lastModifiedSync
();
}
}
class
_DevFSFile
extends
DevFSFile
{
_DevFSFile
(
String
path
,
this
.
file
)
:
super
(
path
);
final
File
file
;
@override
List
<
int
>
getContents
()
=>
file
.
readAsBytesSync
();
}
packages/flutter_tools/test/devfs_test.dart
0 → 100644
View file @
0de69162
// Copyright 2016 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:io'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:path/path.dart'
as
path
;
import
'package:test/test.dart'
;
import
'src/context.dart'
;
import
'src/mocks.dart'
;
void
main
(
)
{
String
filePath
=
'bar/foo.txt'
;
String
filePath2
=
'foo/bar.txt'
;
Directory
tempDir
;
String
basePath
;
MockDevFSOperations
devFSOperations
=
new
MockDevFSOperations
();
DevFS
devFS
;
group
(
'devfs'
,
()
{
testUsingContext
(
'create local file system'
,
()
async
{
tempDir
=
Directory
.
systemTemp
.
createTempSync
();
basePath
=
tempDir
.
path
;
File
file
=
new
File
(
path
.
join
(
basePath
,
filePath
));
await
file
.
parent
.
create
(
recursive:
true
);
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
]);
});
testUsingContext
(
'create dev file system'
,
()
async
{
devFS
=
new
DevFS
.
operations
(
devFSOperations
,
'test'
,
tempDir
);
await
devFS
.
create
();
expect
(
devFSOperations
.
contains
(
'create test'
),
isTrue
);
});
testUsingContext
(
'populate dev file system'
,
()
async
{
await
devFS
.
update
();
expect
(
devFSOperations
.
contains
(
'writeFile test bar/foo.txt'
),
isTrue
);
});
testUsingContext
(
'modify existing file on local file system'
,
()
async
{
File
file
=
new
File
(
path
.
join
(
basePath
,
filePath
));
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
]);
});
testUsingContext
(
'update dev file system'
,
()
async
{
await
devFS
.
update
();
expect
(
devFSOperations
.
contains
(
'writeFile test bar/foo.txt'
),
isTrue
);
});
testUsingContext
(
'add new file to local file system'
,
()
async
{
File
file
=
new
File
(
path
.
join
(
basePath
,
filePath2
));
await
file
.
parent
.
create
(
recursive:
true
);
file
.
writeAsBytesSync
(<
int
>[
1
,
2
,
3
,
4
,
5
,
6
,
7
]);
});
testUsingContext
(
'update dev file system'
,
()
async
{
await
devFS
.
update
();
expect
(
devFSOperations
.
contains
(
'writeFile test foo/bar.txt'
),
isTrue
);
});
testUsingContext
(
'delete dev file system'
,
()
async
{
await
devFS
.
destroy
();
});
});
}
packages/flutter_tools/test/src/mocks.dart
View file @
0de69162
...
...
@@ -7,6 +7,7 @@ import 'dart:async';
import
'package:flutter_tools/src/android/android_device.dart'
;
import
'package:flutter_tools/src/application_package.dart'
;
import
'package:flutter_tools/src/build_info.dart'
;
import
'package:flutter_tools/src/devfs.dart'
;
import
'package:flutter_tools/src/device.dart'
;
import
'package:flutter_tools/src/ios/devices.dart'
;
import
'package:flutter_tools/src/ios/simulators.dart'
;
...
...
@@ -69,3 +70,36 @@ void applyMocksToCommand(FlutterCommand command) {
..
applicationPackages
=
new
MockApplicationPackageStore
()
..
commandValidator
=
()
=>
true
;
}
class
MockDevFSOperations
implements
DevFSOperations
{
final
List
<
String
>
messages
=
new
List
<
String
>();
bool
contains
(
String
match
)
{
bool
result
=
messages
.
contains
(
match
);
messages
.
clear
();
return
result
;
}
@override
Future
<
Uri
>
create
(
String
fsName
)
async
{
messages
.
add
(
'create
$fsName
'
);
return
Uri
.
parse
(
'file:///
$fsName
'
);
}
@override
Future
<
dynamic
>
destroy
(
String
fsName
)
async
{
messages
.
add
(
'destroy
$fsName
'
);
}
@override
Future
<
dynamic
>
writeFile
(
String
fsName
,
DevFSEntry
entry
)
async
{
messages
.
add
(
'writeFile
$fsName
${entry.devicePath}
'
);
}
@override
Future
<
dynamic
>
writeSource
(
String
fsName
,
String
devicePath
,
String
contents
)
async
{
messages
.
add
(
'writeSource
$fsName
$devicePath
'
);
}
}
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