[dartdev] Update dart install command to handle filesystem some errors
When running on a cloudtop, for example, the tmp space may be part of a
different filesystem than the user space where the package is being
registered. In this case a simple rename doesn't work, we have to copy
the files and delete the old one.
The script was also getting confused when a previous install failed. The
partial setup was leading to errors while trying to uninstall the old
verison.
Change-Id: I67f04547bc5279adc5f0d9f528cc9224c1f54e64
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/452000
Commit-Queue: Nate Biggs <natebiggs@google.com>
Reviewed-by: Daco Harkes <dacoharkes@google.com>
diff --git a/pkg/dartdev/lib/src/commands/install.dart b/pkg/dartdev/lib/src/commands/install.dart
index c78acc0..e37a562 100644
--- a/pkg/dartdev/lib/src/commands/install.dart
+++ b/pkg/dartdev/lib/src/commands/install.dart
@@ -291,6 +291,10 @@
}
} on PathAccessException {
_installException('Deletion failed. The application might be in use.');
+ } on PathNotFoundException {
+ print('Bundle not found when uninstalling. '
+ 'Earlier installation may have failed.');
+ // Continue installing
}
}
@@ -349,12 +353,42 @@
appBundleDirectory.directory.createSync(recursive: true);
final bundleDirectory =
Directory.fromUri(buildDirectory.uri.resolve('bundle/'));
- await bundleDirectory.rename(
- appBundleDirectory.directory.uri.resolve('bundle/').toFilePath());
+ await _renameSafe(
+ bundleDirectory, appBundleDirectory.directory.uri.resolve('bundle/'));
await helperPackageLockFile.copy(appBundleDirectory.pubspecLock.path);
await sourcePackagePubspecFile.copy(appBundleDirectory.pubspec.path);
}
+ /// This allows us to rename files across different filesystems.
+ ///
+ /// Tries to use the basic [Directory.rename] method but if that fails then
+ /// fall back to copying each entity and then deleting it.
+ Future<void> _renameSafe(Directory from, Uri to) async {
+ try {
+ await from.rename(to.toFilePath());
+ } on FileSystemException {
+ // The rename failed, possibly because `from` and `to` are on different
+ // filesystems. Fall back to copy and delete.
+ await _renameSafeCopyAndDelete(from, to);
+ }
+ }
+
+ Future<void> _renameSafeCopyAndDelete(Directory from, Uri to) async {
+ await Directory.fromUri(to).create(recursive: true);
+ await for (final child in from.list()) {
+ final newChildPath = to.resolve(p.relative(child.path, from: from.path));
+ if (child is File) {
+ await child.copy(newChildPath.toFilePath());
+ } else if (child is Directory) {
+ await _renameSafeCopyAndDelete(child, Uri.parse('$newChildPath/'));
+ } else {
+ await Link.fromUri(newChildPath)
+ .create(await (child as Link).resolveSymbolicLinks());
+ }
+ await child.delete(recursive: false);
+ }
+ }
+
void _installExecutablesOnPath(
DartBuildExecutables executables,
AppBundleDirectory appBundleDirectory,