Ian Hickson | 449f4a6 | 2019-11-27 15:04:02 -0800 | [diff] [blame] | 1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | import 'dart:async'; |
Zachary Anderson | ef146f6 | 2019-07-29 07:24:02 -0700 | [diff] [blame] | 6 | |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 7 | import 'package:json_rpc_2/json_rpc_2.dart' as rpc; |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 8 | import 'package:meta/meta.dart'; |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 9 | |
Alexandre Ardhuin | 8c043d0 | 2017-02-23 22:37:26 +0100 | [diff] [blame] | 10 | import 'asset.dart'; |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 11 | import 'base/context.dart'; |
Todd Volkert | 8bb2703 | 2017-01-06 16:51:44 -0800 | [diff] [blame] | 12 | import 'base/file_system.dart'; |
Todd Volkert | 016b5ab | 2017-01-09 08:37:00 -0800 | [diff] [blame] | 13 | import 'base/io.dart'; |
Emmanuel Garcia | 5df4b7d | 2019-11-20 18:51:25 -0800 | [diff] [blame] | 14 | import 'base/net.dart'; |
Chris Bracken | b5f763b | 2016-08-25 16:38:19 -0700 | [diff] [blame] | 15 | import 'build_info.dart'; |
Jacob Richman | f5f70f0 | 2018-10-23 10:09:18 -0700 | [diff] [blame] | 16 | import 'bundle.dart'; |
Alexander Aprelev | c5750cd | 2017-08-31 13:35:55 -0700 | [diff] [blame] | 17 | import 'compile.dart'; |
Jonah Williams | 91fd89e | 2019-01-25 16:16:26 -0800 | [diff] [blame] | 18 | import 'convert.dart' show base64, utf8; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 19 | import 'dart/package_map.dart'; |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 20 | import 'globals.dart' as globals; |
John McCutchan | cab7c8d | 2016-08-11 13:14:13 -0700 | [diff] [blame] | 21 | import 'vmservice.dart'; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 22 | |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 23 | class DevFSConfig { |
| 24 | /// Should DevFS assume that symlink targets are stable? |
| 25 | bool cacheSymlinks = false; |
John McCutchan | 45c95b2 | 2016-11-11 13:28:21 -0800 | [diff] [blame] | 26 | /// Should DevFS assume that there are no symlinks to directories? |
| 27 | bool noDirectorySymlinks = false; |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 28 | } |
| 29 | |
Jonah Williams | 0acd3e6 | 2019-04-25 15:51:08 -0700 | [diff] [blame] | 30 | DevFSConfig get devFSConfig => context.get<DevFSConfig>(); |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 31 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 32 | /// Common superclass for content copied to the device. |
| 33 | abstract class DevFSContent { |
Ian Hickson | efb45ea | 2017-09-27 16:13:48 -0700 | [diff] [blame] | 34 | /// Return true if this is the first time this method is called |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 35 | /// or if the entry has been modified since this method was last called. |
| 36 | bool get isModified; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 37 | |
jensjoha | 5ccd5a1 | 2018-01-31 16:39:58 +0100 | [diff] [blame] | 38 | /// Return true if this is the first time this method is called |
| 39 | /// or if the entry has been modified after the given time |
| 40 | /// or if the given time is null. |
| 41 | bool isModifiedAfter(DateTime time); |
| 42 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 43 | int get size; |
| 44 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 45 | Future<List<int>> contentsAsBytes(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 46 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 47 | Stream<List<int>> contentsAsStream(); |
| 48 | |
| 49 | Stream<List<int>> contentsAsCompressedStream() { |
| 50 | return contentsAsStream().cast<List<int>>().transform<List<int>>(gzip.encoder); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 51 | } |
P.Y. Laligand | d356b2d | 2017-03-23 09:42:58 -0700 | [diff] [blame] | 52 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 53 | /// Return the list of files this content depends on. |
| 54 | List<String> get fileDependencies => <String>[]; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 55 | } |
| 56 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 57 | // File content to be copied to the device. |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 58 | class DevFSFileContent extends DevFSContent { |
| 59 | DevFSFileContent(this.file); |
John McCutchan | b664473 | 2016-07-22 06:59:24 -0700 | [diff] [blame] | 60 | |
John McCutchan | 035afc2 | 2016-09-21 11:24:29 -0700 | [diff] [blame] | 61 | final FileSystemEntity file; |
Alexandre Ardhuin | adc7351 | 2019-11-19 07:57:42 +0100 | [diff] [blame] | 62 | File _linkTarget; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 63 | FileStat _fileStat; |
John McCutchan | b664473 | 2016-07-22 06:59:24 -0700 | [diff] [blame] | 64 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 65 | File _getFile() { |
| 66 | if (_linkTarget != null) { |
| 67 | return _linkTarget; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 68 | } |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 69 | if (file is Link) { |
| 70 | // The link target. |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 71 | return globals.fs.file(file.resolveSymbolicLinksSync()); |
John McCutchan | b11b2a1 | 2016-07-28 13:48:48 -0700 | [diff] [blame] | 72 | } |
Alexandre Ardhuin | adc7351 | 2019-11-19 07:57:42 +0100 | [diff] [blame] | 73 | return file as File; |
John McCutchan | b11b2a1 | 2016-07-28 13:48:48 -0700 | [diff] [blame] | 74 | } |
| 75 | |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 76 | void _stat() { |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 77 | if (_linkTarget != null) { |
| 78 | // Stat the cached symlink target. |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 79 | final FileStat fileStat = _linkTarget.statSync(); |
| 80 | if (fileStat.type == FileSystemEntityType.notFound) { |
| 81 | _linkTarget = null; |
| 82 | } else { |
| 83 | _fileStat = fileStat; |
| 84 | return; |
| 85 | } |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 86 | } |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 87 | final FileStat fileStat = file.statSync(); |
| 88 | _fileStat = fileStat.type == FileSystemEntityType.notFound ? null : fileStat; |
| 89 | if (_fileStat != null && _fileStat.type == FileSystemEntityType.link) { |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 90 | // Resolve, stat, and maybe cache the symlink target. |
Chris Bracken | 7a09316 | 2017-03-03 17:50:46 -0800 | [diff] [blame] | 91 | final String resolved = file.resolveSymbolicLinksSync(); |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 92 | final File linkTarget = globals.fs.file(resolved); |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 93 | // Stat the link target. |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 94 | final FileStat fileStat = linkTarget.statSync(); |
| 95 | if (fileStat.type == FileSystemEntityType.notFound) { |
| 96 | _fileStat = null; |
| 97 | _linkTarget = null; |
| 98 | } else if (devFSConfig.cacheSymlinks) { |
John McCutchan | 11ca0f1 | 2016-11-10 08:31:11 -0800 | [diff] [blame] | 99 | _linkTarget = linkTarget; |
| 100 | } |
John McCutchan | 035afc2 | 2016-09-21 11:24:29 -0700 | [diff] [blame] | 101 | } |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 102 | if (_fileStat == null) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 103 | globals.printError('Unable to get status of file "${file.path}": file not found.'); |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 104 | } |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 105 | } |
John McCutchan | b664473 | 2016-07-22 06:59:24 -0700 | [diff] [blame] | 106 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 107 | @override |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 108 | List<String> get fileDependencies => <String>[_getFile().path]; |
| 109 | |
| 110 | @override |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 111 | bool get isModified { |
Chris Bracken | 7a09316 | 2017-03-03 17:50:46 -0800 | [diff] [blame] | 112 | final FileStat _oldFileStat = _fileStat; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 113 | _stat(); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 114 | if (_oldFileStat == null && _fileStat == null) { |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 115 | return false; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 116 | } |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 117 | return _oldFileStat == null || _fileStat == null || _fileStat.modified.isAfter(_oldFileStat.modified); |
John McCutchan | 035afc2 | 2016-09-21 11:24:29 -0700 | [diff] [blame] | 118 | } |
| 119 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 120 | @override |
jensjoha | 5ccd5a1 | 2018-01-31 16:39:58 +0100 | [diff] [blame] | 121 | bool isModifiedAfter(DateTime time) { |
| 122 | final FileStat _oldFileStat = _fileStat; |
| 123 | _stat(); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 124 | if (_oldFileStat == null && _fileStat == null) { |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 125 | return false; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 126 | } |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 127 | return time == null |
| 128 | || _oldFileStat == null |
| 129 | || _fileStat == null |
| 130 | || _fileStat.modified.isAfter(time); |
jensjoha | 5ccd5a1 | 2018-01-31 16:39:58 +0100 | [diff] [blame] | 131 | } |
| 132 | |
| 133 | @override |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 134 | int get size { |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 135 | if (_fileStat == null) { |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 136 | _stat(); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 137 | } |
Greg Spencer | 485ed2f | 2018-10-09 14:33:47 -0700 | [diff] [blame] | 138 | // Can still be null if the file wasn't found. |
| 139 | return _fileStat?.size ?? 0; |
John McCutchan | b664473 | 2016-07-22 06:59:24 -0700 | [diff] [blame] | 140 | } |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 141 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 142 | @override |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 143 | Future<List<int>> contentsAsBytes() => _getFile().readAsBytes(); |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 144 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 145 | @override |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 146 | Stream<List<int>> contentsAsStream() => _getFile().openRead(); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 147 | } |
| 148 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 149 | /// Byte content to be copied to the device. |
| 150 | class DevFSByteContent extends DevFSContent { |
| 151 | DevFSByteContent(this._bytes); |
| 152 | |
| 153 | List<int> _bytes; |
| 154 | |
| 155 | bool _isModified = true; |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 156 | DateTime _modificationTime = DateTime.now(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 157 | |
| 158 | List<int> get bytes => _bytes; |
| 159 | |
Alexandre Ardhuin | c22812e | 2017-03-10 09:00:29 +0100 | [diff] [blame] | 160 | set bytes(List<int> value) { |
| 161 | _bytes = value; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 162 | _isModified = true; |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 163 | _modificationTime = DateTime.now(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 164 | } |
| 165 | |
Ian Hickson | efb45ea | 2017-09-27 16:13:48 -0700 | [diff] [blame] | 166 | /// Return true only once so that the content is written to the device only once. |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 167 | @override |
| 168 | bool get isModified { |
Chris Bracken | 7a09316 | 2017-03-03 17:50:46 -0800 | [diff] [blame] | 169 | final bool modified = _isModified; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 170 | _isModified = false; |
| 171 | return modified; |
| 172 | } |
| 173 | |
| 174 | @override |
jensjoha | 5ccd5a1 | 2018-01-31 16:39:58 +0100 | [diff] [blame] | 175 | bool isModifiedAfter(DateTime time) { |
| 176 | return time == null || _modificationTime.isAfter(time); |
| 177 | } |
| 178 | |
| 179 | @override |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 180 | int get size => _bytes.length; |
| 181 | |
| 182 | @override |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 183 | Future<List<int>> contentsAsBytes() async => _bytes; |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 184 | |
| 185 | @override |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 186 | Stream<List<int>> contentsAsStream() => |
| 187 | Stream<List<int>>.fromIterable(<List<int>>[_bytes]); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 188 | } |
| 189 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 190 | /// String content to be copied to the device. |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 191 | class DevFSStringContent extends DevFSByteContent { |
Alexandre Ardhuin | ef276ff | 2019-01-29 21:47:16 +0100 | [diff] [blame] | 192 | DevFSStringContent(String string) |
| 193 | : _string = string, |
| 194 | super(utf8.encode(string)); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 195 | |
| 196 | String _string; |
| 197 | |
| 198 | String get string => _string; |
| 199 | |
Alexandre Ardhuin | c22812e | 2017-03-10 09:00:29 +0100 | [diff] [blame] | 200 | set string(String value) { |
| 201 | _string = value; |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 202 | super.bytes = utf8.encode(_string); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 203 | } |
| 204 | |
| 205 | @override |
Alexandre Ardhuin | c22812e | 2017-03-10 09:00:29 +0100 | [diff] [blame] | 206 | set bytes(List<int> value) { |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 207 | string = utf8.decode(value); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 208 | } |
| 209 | } |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 210 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 211 | /// Abstract DevFS operations interface. |
| 212 | abstract class DevFSOperations { |
| 213 | Future<Uri> create(String fsName); |
| 214 | Future<dynamic> destroy(String fsName); |
| 215 | Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content); |
| 216 | } |
| 217 | |
| 218 | /// An implementation of [DevFSOperations] that speaks to the |
| 219 | /// vm service. |
| 220 | class ServiceProtocolDevFSOperations implements DevFSOperations { |
| 221 | ServiceProtocolDevFSOperations(this.vmService); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 222 | |
Alexandre Ardhuin | 2ea1d81 | 2018-10-04 07:28:07 +0200 | [diff] [blame] | 223 | final VMService vmService; |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 224 | |
| 225 | @override |
| 226 | Future<Uri> create(String fsName) async { |
| 227 | final Map<String, dynamic> response = await vmService.vm.createDevFS(fsName); |
Alexandre Ardhuin | adc7351 | 2019-11-19 07:57:42 +0100 | [diff] [blame] | 228 | return Uri.parse(response['uri'] as String); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | @override |
| 232 | Future<dynamic> destroy(String fsName) async { |
| 233 | await vmService.vm.deleteDevFS(fsName); |
| 234 | } |
| 235 | |
| 236 | @override |
| 237 | Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content) async { |
| 238 | List<int> bytes; |
| 239 | try { |
| 240 | bytes = await content.contentsAsBytes(); |
Zachary Anderson | 9de7787 | 2020-02-27 22:46:23 -0800 | [diff] [blame^] | 241 | } on Exception catch (e) { |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 242 | return e; |
| 243 | } |
| 244 | final String fileContents = base64.encode(bytes); |
| 245 | try { |
| 246 | return await vmService.vm.invokeRpcRaw( |
| 247 | '_writeDevFSFile', |
| 248 | params: <String, dynamic>{ |
| 249 | 'fsName': fsName, |
| 250 | 'uri': deviceUri.toString(), |
| 251 | 'fileContents': fileContents, |
| 252 | }, |
| 253 | ); |
Zachary Anderson | 9de7787 | 2020-02-27 22:46:23 -0800 | [diff] [blame^] | 254 | } on Exception catch (error) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 255 | globals.printTrace('DevFS: Failed to write $deviceUri: $error'); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 256 | } |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | class DevFSException implements Exception { |
| 261 | DevFSException(this.message, [this.error, this.stackTrace]); |
| 262 | final String message; |
| 263 | final dynamic error; |
| 264 | final StackTrace stackTrace; |
| 265 | } |
| 266 | |
| 267 | class _DevFSHttpWriter { |
| 268 | _DevFSHttpWriter(this.fsName, VMService serviceProtocol) |
Emmanuel Garcia | 5df4b7d | 2019-11-20 18:51:25 -0800 | [diff] [blame] | 269 | : httpAddress = serviceProtocol.httpAddress, |
| 270 | _client = (context.get<HttpClientFactory>() == null) |
| 271 | ? HttpClient() |
| 272 | : context.get<HttpClientFactory>()(); |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 273 | |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 274 | final String fsName; |
| 275 | final Uri httpAddress; |
Emmanuel Garcia | 5df4b7d | 2019-11-20 18:51:25 -0800 | [diff] [blame] | 276 | final HttpClient _client; |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 277 | |
| 278 | static const int kMaxInFlight = 6; |
| 279 | |
| 280 | int _inFlight = 0; |
Michael Goderbauer | 17057bb | 2017-03-01 10:11:56 -0800 | [diff] [blame] | 281 | Map<Uri, DevFSContent> _outstanding; |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 282 | Completer<void> _completer; |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 283 | |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 284 | Future<void> write(Map<Uri, DevFSContent> entries) async { |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 285 | _client.maxConnectionsPerHost = kMaxInFlight; |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 286 | _completer = Completer<void>(); |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 287 | _outstanding = Map<Uri, DevFSContent>.from(entries); |
Todd Volkert | b7ababe | 2017-07-17 09:30:56 -0700 | [diff] [blame] | 288 | _scheduleWrites(); |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 289 | await _completer.future; |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 290 | } |
| 291 | |
Todd Volkert | b7ababe | 2017-07-17 09:30:56 -0700 | [diff] [blame] | 292 | void _scheduleWrites() { |
Jason Simmons | 311cde9 | 2019-05-29 19:04:35 -0700 | [diff] [blame] | 293 | while ((_inFlight < kMaxInFlight) && (!_completer.isCompleted) && _outstanding.isNotEmpty) { |
Chris Bracken | 7a09316 | 2017-03-03 17:50:46 -0800 | [diff] [blame] | 294 | final Uri deviceUri = _outstanding.keys.first; |
| 295 | final DevFSContent content = _outstanding.remove(deviceUri); |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 296 | _startWrite(deviceUri, content, retry: 10); |
Jason Simmons | 311cde9 | 2019-05-29 19:04:35 -0700 | [diff] [blame] | 297 | _inFlight += 1; |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 298 | } |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 299 | if ((_inFlight == 0) && (!_completer.isCompleted) && _outstanding.isEmpty) { |
Jason Simmons | 311cde9 | 2019-05-29 19:04:35 -0700 | [diff] [blame] | 300 | _completer.complete(); |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 301 | } |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 302 | } |
| 303 | |
Jason Simmons | 311cde9 | 2019-05-29 19:04:35 -0700 | [diff] [blame] | 304 | Future<void> _startWrite( |
Michael Goderbauer | 17057bb | 2017-03-01 10:11:56 -0800 | [diff] [blame] | 305 | Uri deviceUri, |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 306 | DevFSContent content, { |
Todd Volkert | f903a1c | 2017-02-14 09:59:43 -0800 | [diff] [blame] | 307 | int retry = 0, |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 308 | }) async { |
| 309 | while(true) { |
| 310 | try { |
| 311 | final HttpClientRequest request = await _client.putUrl(httpAddress); |
| 312 | request.headers.removeAll(HttpHeaders.acceptEncodingHeader); |
| 313 | request.headers.add('dev_fs_name', fsName); |
| 314 | request.headers.add('dev_fs_uri_b64', base64.encode(utf8.encode('$deviceUri'))); |
| 315 | final Stream<List<int>> contents = content.contentsAsCompressedStream(); |
| 316 | await request.addStream(contents); |
| 317 | final HttpClientResponse response = await request.close(); |
| 318 | response.listen((_) => null, |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 319 | onError: (dynamic error) { globals.printTrace('error: $error'); }, |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 320 | cancelOnError: true); |
| 321 | break; |
Zachary Anderson | 9de7787 | 2020-02-27 22:46:23 -0800 | [diff] [blame^] | 322 | } on Exception catch (error, trace) { |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 323 | if (!_completer.isCompleted) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 324 | globals.printTrace('Error writing "$deviceUri" to DevFS: $error'); |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 325 | if (retry > 0) { |
| 326 | retry--; |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 327 | globals.printTrace('trying again in a few - $retry more attempts left'); |
Alexander Aprelev | 839fdbd | 2019-09-29 21:32:06 -0700 | [diff] [blame] | 328 | await Future<void>.delayed(const Duration(milliseconds: 500)); |
| 329 | continue; |
| 330 | } |
| 331 | _completer.completeError(error, trace); |
| 332 | } |
Todd Volkert | f903a1c | 2017-02-14 09:59:43 -0800 | [diff] [blame] | 333 | } |
Ryan Macnak | 2ac7d67 | 2016-10-25 10:40:54 -0700 | [diff] [blame] | 334 | } |
Jason Simmons | 311cde9 | 2019-05-29 19:04:35 -0700 | [diff] [blame] | 335 | _inFlight -= 1; |
| 336 | _scheduleWrites(); |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 337 | } |
| 338 | } |
| 339 | |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 340 | // Basic statistics for DevFS update operation. |
| 341 | class UpdateFSReport { |
Alexandre Ardhuin | bfa1d25 | 2019-03-23 00:02:21 +0100 | [diff] [blame] | 342 | UpdateFSReport({ |
| 343 | bool success = false, |
| 344 | int invalidatedSourcesCount = 0, |
| 345 | int syncedBytes = 0, |
| 346 | }) { |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 347 | _success = success; |
| 348 | _invalidatedSourcesCount = invalidatedSourcesCount; |
| 349 | _syncedBytes = syncedBytes; |
| 350 | } |
| 351 | |
| 352 | bool get success => _success; |
| 353 | int get invalidatedSourcesCount => _invalidatedSourcesCount; |
| 354 | int get syncedBytes => _syncedBytes; |
| 355 | |
Jonah Williams | cc51ad5 | 2019-11-07 20:13:02 -0800 | [diff] [blame] | 356 | /// JavaScript modules produced by the incremental compiler in `dartdevc` |
| 357 | /// mode. |
| 358 | /// |
| 359 | /// Only used for JavaScript compilation. |
| 360 | List<String> invalidatedModules; |
| 361 | |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 362 | void incorporateResults(UpdateFSReport report) { |
| 363 | if (!report._success) { |
| 364 | _success = false; |
| 365 | } |
| 366 | _invalidatedSourcesCount += report._invalidatedSourcesCount; |
| 367 | _syncedBytes += report._syncedBytes; |
Jonah Williams | cc51ad5 | 2019-11-07 20:13:02 -0800 | [diff] [blame] | 368 | invalidatedModules ??= report.invalidatedModules; |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 369 | } |
| 370 | |
| 371 | bool _success; |
| 372 | int _invalidatedSourcesCount; |
| 373 | int _syncedBytes; |
| 374 | } |
| 375 | |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 376 | class DevFS { |
Mikkel Nygaard Ravn | d4e5e1e | 2018-08-16 13:21:55 +0200 | [diff] [blame] | 377 | /// Create a [DevFS] named [fsName] for the local files in [rootDirectory]. |
Alexandre Ardhuin | ef276ff | 2019-01-29 21:47:16 +0100 | [diff] [blame] | 378 | DevFS( |
| 379 | VMService serviceProtocol, |
| 380 | this.fsName, |
| 381 | this.rootDirectory, { |
Alexandre Ardhuin | 387f885 | 2019-03-01 08:17:55 +0100 | [diff] [blame] | 382 | String packagesFilePath, |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 383 | }) : _operations = ServiceProtocolDevFSOperations(serviceProtocol), |
| 384 | _httpWriter = _DevFSHttpWriter(fsName, serviceProtocol), |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 385 | _packagesFilePath = packagesFilePath ?? globals.fs.path.join(rootDirectory.path, kPackagesFileName); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 386 | |
Alexandre Ardhuin | ef276ff | 2019-01-29 21:47:16 +0100 | [diff] [blame] | 387 | DevFS.operations( |
| 388 | this._operations, |
| 389 | this.fsName, |
| 390 | this.rootDirectory, { |
| 391 | String packagesFilePath, |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 392 | }) : _httpWriter = null, |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 393 | _packagesFilePath = packagesFilePath ?? globals.fs.path.join(rootDirectory.path, kPackagesFileName); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 394 | |
| 395 | final DevFSOperations _operations; |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 396 | final _DevFSHttpWriter _httpWriter; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 397 | final String fsName; |
| 398 | final Directory rootDirectory; |
Alexandre Ardhuin | c7408be | 2019-06-25 19:39:34 +0200 | [diff] [blame] | 399 | final String _packagesFilePath; |
Phil Quitslund | 802eca2 | 2019-03-06 11:05:16 -0800 | [diff] [blame] | 400 | final Set<String> assetPathsToEvict = <String>{}; |
Alexander Aprelev | 12c4e05 | 2019-03-20 21:58:15 -0700 | [diff] [blame] | 401 | List<Uri> sources = <Uri>[]; |
| 402 | DateTime lastCompiled; |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 403 | |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 404 | Uri _baseUri; |
| 405 | Uri get baseUri => _baseUri; |
| 406 | |
Ryan Macnak | 07a4b4c | 2017-10-12 15:58:40 -0700 | [diff] [blame] | 407 | Uri deviceUriToHostUri(Uri deviceUri) { |
| 408 | final String deviceUriString = deviceUri.toString(); |
| 409 | final String baseUriString = baseUri.toString(); |
| 410 | if (deviceUriString.startsWith(baseUriString)) { |
| 411 | final String deviceUriSuffix = deviceUriString.substring(baseUriString.length); |
| 412 | return rootDirectory.uri.resolve(deviceUriSuffix); |
| 413 | } |
| 414 | return deviceUri; |
| 415 | } |
| 416 | |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 417 | Future<Uri> create() async { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 418 | globals.printTrace('DevFS: Creating new filesystem on the device ($_baseUri)'); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 419 | try { |
| 420 | _baseUri = await _operations.create(fsName); |
| 421 | } on rpc.RpcException catch (rpcException) { |
| 422 | // 1001 is kFileSystemAlreadyExists in //dart/runtime/vm/json_stream.h |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 423 | if (rpcException.code != 1001) { |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 424 | rethrow; |
Zachary Anderson | e2340c6 | 2019-09-13 14:51:35 -0700 | [diff] [blame] | 425 | } |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 426 | globals.printTrace('DevFS: Creating failed. Destroying and trying again'); |
Zachary Anderson | 4d49066 | 2017-06-22 09:48:31 -0700 | [diff] [blame] | 427 | await destroy(); |
| 428 | _baseUri = await _operations.create(fsName); |
| 429 | } |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 430 | globals.printTrace('DevFS: Created new filesystem on the device ($_baseUri)'); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 431 | return _baseUri; |
| 432 | } |
| 433 | |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 434 | Future<void> destroy() async { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 435 | globals.printTrace('DevFS: Deleting filesystem on the device ($_baseUri)'); |
John McCutchan | 0ee7fab | 2017-03-30 10:03:42 -0700 | [diff] [blame] | 436 | await _operations.destroy(fsName); |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 437 | globals.printTrace('DevFS: Deleted filesystem on the device ($_baseUri)'); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 438 | } |
| 439 | |
Chris Bracken | 0e4770e | 2018-06-07 18:27:08 -0700 | [diff] [blame] | 440 | /// Updates files on the device. |
| 441 | /// |
| 442 | /// Returns the number of bytes synced. |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 443 | Future<UpdateFSReport> update({ |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 444 | @required String mainPath, |
Alexander Aprelev | c5750cd | 2017-08-31 13:35:55 -0700 | [diff] [blame] | 445 | String target, |
Todd Volkert | b7ababe | 2017-07-17 09:30:56 -0700 | [diff] [blame] | 446 | AssetBundle bundle, |
jensjoha | 5ccd5a1 | 2018-01-31 16:39:58 +0100 | [diff] [blame] | 447 | DateTime firstBuildTime, |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 448 | bool bundleFirstUpload = false, |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 449 | @required ResidentCompiler generator, |
Keerti Parthasarathy | 0263394 | 2018-03-06 14:41:50 -0800 | [diff] [blame] | 450 | String dillOutputPath, |
Jacob Richman | f5f70f0 | 2018-10-23 10:09:18 -0700 | [diff] [blame] | 451 | @required bool trackWidgetCreation, |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 452 | bool fullRestart = false, |
Alexander Aprelev | af74a72 | 2018-03-23 19:18:39 -0700 | [diff] [blame] | 453 | String projectRootPath, |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 454 | @required String pathToReload, |
Alexander Aprelev | 12c4e05 | 2019-03-20 21:58:15 -0700 | [diff] [blame] | 455 | @required List<Uri> invalidatedFiles, |
Jonah Williams | 81aa271 | 2019-12-09 21:31:34 -0800 | [diff] [blame] | 456 | bool skipAssets = false, |
Todd Volkert | b7ababe | 2017-07-17 09:30:56 -0700 | [diff] [blame] | 457 | }) async { |
Jacob Richman | f5f70f0 | 2018-10-23 10:09:18 -0700 | [diff] [blame] | 458 | assert(trackWidgetCreation != null); |
| 459 | assert(generator != null); |
Jonah Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 460 | final DateTime candidateCompileTime = DateTime.now(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 461 | |
Jonah Williams | 377f445 | 2019-03-21 09:03:28 -0700 | [diff] [blame] | 462 | // Update modified files |
| 463 | final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory()); |
| 464 | final Map<Uri, DevFSContent> dirtyEntries = <Uri, DevFSContent>{}; |
| 465 | |
| 466 | int syncedBytes = 0; |
Jonah Williams | 81aa271 | 2019-12-09 21:31:34 -0800 | [diff] [blame] | 467 | if (bundle != null && !skipAssets) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 468 | globals.printTrace('Scanning asset files'); |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 469 | // We write the assets into the AssetBundle working dir so that they |
| 470 | // are in the same location in DevFS and the iOS simulator. |
| 471 | final String assetDirectory = getAssetBuildDirectory(); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 472 | bundle.entries.forEach((String archivePath, DevFSContent content) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 473 | final Uri deviceUri = globals.fs.path.toUri(globals.fs.path.join(assetDirectory, archivePath)); |
Jonah Williams | 377f445 | 2019-03-21 09:03:28 -0700 | [diff] [blame] | 474 | if (deviceUri.path.startsWith(assetBuildDirPrefix)) { |
| 475 | archivePath = deviceUri.path.substring(assetBuildDirPrefix.length); |
| 476 | } |
| 477 | // Only update assets if they have been modified, or if this is the |
| 478 | // first upload of the asset bundle. |
| 479 | if (content.isModified || (bundleFirstUpload && archivePath != null)) { |
| 480 | dirtyEntries[deviceUri] = content; |
| 481 | syncedBytes += content.size; |
| 482 | if (archivePath != null && !bundleFirstUpload) { |
| 483 | assetPathsToEvict.add(archivePath); |
| 484 | } |
| 485 | } |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 486 | }); |
John McCutchan | b664473 | 2016-07-22 06:59:24 -0700 | [diff] [blame] | 487 | } |
Chris Bracken | 2ab4ed7 | 2018-09-04 16:47:57 -0700 | [diff] [blame] | 488 | if (fullRestart) { |
| 489 | generator.reset(); |
| 490 | } |
Jonah Williams | 53457c2 | 2020-02-26 08:06:03 -0800 | [diff] [blame] | 491 | // On a full restart, or on an initial compile for the attach based workflow, |
| 492 | // this will produce a full dill. Subsequent invocations will produce incremental |
| 493 | // dill files that depend on the invalidated files. |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 494 | globals.printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files'); |
Chris Bracken | 2ab4ed7 | 2018-09-04 16:47:57 -0700 | [diff] [blame] | 495 | final CompilerOutput compilerOutput = await generator.recompile( |
| 496 | mainPath, |
| 497 | invalidatedFiles, |
Jacob Richman | f5f70f0 | 2018-10-23 10:09:18 -0700 | [diff] [blame] | 498 | outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation), |
Chris Bracken | 2ab4ed7 | 2018-09-04 16:47:57 -0700 | [diff] [blame] | 499 | packagesFilePath : _packagesFilePath, |
| 500 | ); |
Jonah Williams | 5ec039d | 2019-08-15 09:08:19 -0700 | [diff] [blame] | 501 | if (compilerOutput == null || compilerOutput.errorCount > 0) { |
Jonah Williams | fc9f7de | 2019-03-21 13:59:38 -0700 | [diff] [blame] | 502 | return UpdateFSReport(success: false); |
| 503 | } |
Jonah Williams | 39f85f9 | 2019-10-02 12:46:33 -0700 | [diff] [blame] | 504 | // Only update the last compiled time if we successfully compiled. |
| 505 | lastCompiled = candidateCompileTime; |
Ben Konyi | e8b98f9 | 2019-03-19 20:01:03 -0700 | [diff] [blame] | 506 | // list of sources that needs to be monitored are in [compilerOutput.sources] |
Alexander Aprelev | 12c4e05 | 2019-03-20 21:58:15 -0700 | [diff] [blame] | 507 | sources = compilerOutput.sources; |
Ben Konyi | e8b98f9 | 2019-03-19 20:01:03 -0700 | [diff] [blame] | 508 | // |
Alexander Aprelev | 199422c | 2018-09-13 09:01:47 -0700 | [diff] [blame] | 509 | // Don't send full kernel file that would overwrite what VM already |
| 510 | // started loading from. |
| 511 | if (!bundleFirstUpload) { |
| 512 | final String compiledBinary = compilerOutput?.outputFilename; |
| 513 | if (compiledBinary != null && compiledBinary.isNotEmpty) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 514 | final Uri entryUri = globals.fs.path.toUri(projectRootPath != null |
| 515 | ? globals.fs.path.relative(pathToReload, from: projectRootPath) |
Alexander Aprelev | 199422c | 2018-09-13 09:01:47 -0700 | [diff] [blame] | 516 | : pathToReload, |
| 517 | ); |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 518 | final DevFSFileContent content = DevFSFileContent(globals.fs.file(compiledBinary)); |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 519 | syncedBytes += content.size; |
| 520 | dirtyEntries[entryUri] = content; |
Alexander Aprelev | af74a72 | 2018-03-23 19:18:39 -0700 | [diff] [blame] | 521 | } |
Alexander Aprelev | 35c4761 | 2017-09-13 07:33:52 -0700 | [diff] [blame] | 522 | } |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 523 | globals.printTrace('Updating files'); |
Alexandre Ardhuin | 69b6bb8 | 2017-03-02 07:17:30 +0100 | [diff] [blame] | 524 | if (dirtyEntries.isNotEmpty) { |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 525 | try { |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame] | 526 | await _httpWriter.write(dirtyEntries); |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 527 | } on SocketException catch (socketException, stackTrace) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 528 | globals.printTrace('DevFS sync failed. Lost connection to device: $socketException'); |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 529 | throw DevFSException('Lost connection to device.', socketException, stackTrace); |
Zachary Anderson | 9de7787 | 2020-02-27 22:46:23 -0800 | [diff] [blame^] | 530 | } on Exception catch (exception, stackTrace) { |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 531 | globals.printError('Could not update files on device: $exception'); |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 532 | throw DevFSException('Sync failed', exception, stackTrace); |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 533 | } |
John McCutchan | 487f28f | 2016-08-08 12:42:31 -0700 | [diff] [blame] | 534 | } |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 535 | globals.printTrace('DevFS: Sync finished'); |
Alexander Aprelev | 52bd2cc | 2018-12-27 09:53:24 -0800 | [diff] [blame] | 536 | return UpdateFSReport(success: true, syncedBytes: syncedBytes, |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 537 | invalidatedSourcesCount: invalidatedFiles.length); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 538 | } |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 539 | } |
Jonah Williams | cd803ac | 2019-03-15 15:02:45 -0700 | [diff] [blame] | 540 | |
Greg Spencer | a60bf8e | 2019-11-22 08:43:55 -0800 | [diff] [blame] | 541 | /// Converts a platform-specific file path to a platform-independent URL path. |
Jonah Williams | ee7a37f | 2020-01-06 11:04:20 -0800 | [diff] [blame] | 542 | String _asUriPath(String filePath) => globals.fs.path.toUri(filePath).path + '/'; |