canonicalize paths in devfs (#8565)

diff --git a/packages/flutter_tools/lib/src/dart/dependencies.dart b/packages/flutter_tools/lib/src/dart/dependencies.dart
index 167378b..bfb8c16 100644
--- a/packages/flutter_tools/lib/src/dart/dependencies.dart
+++ b/packages/flutter_tools/lib/src/dart/dependencies.dart
@@ -24,6 +24,9 @@
   final String projectRootPath;
   final String packagesFilePath;
 
+  /// Returns a set of canonicalize paths.
+  ///
+  /// The paths have been canonicalize with `fs.path.canonicalize`.
   Set<String> build() {
     final String skySnapshotPath =
         Artifacts.instance.getArtifactPath(Artifact.skySnapshot);
@@ -37,7 +40,9 @@
 
     final String output = runSyncAndThrowStdErrOnError(args);
 
-    return new Set<String>.from(LineSplitter.split(output));
+    return new Set<String>.from(LineSplitter.split(output).map(
+        (String path) => fs.path.canonicalize(path))
+    );
   }
 }
 
@@ -91,7 +96,8 @@
     output = output.substring(splitIndex + 1);
     // Note: next line means we cannot process anything with spaces in the path
     //       because Makefiles don't support spaces in paths :(
-    final List<String> depsList = output.trim().split(' ');
-    return new Set<String>.from(depsList);
+    return new Set<String>.from(output.trim().split(' ').map(
+        (String path) => fs.path.canonicalize(path)
+    ));
   }
 }
diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart
index d9277c6..78b35eb 100644
--- a/packages/flutter_tools/lib/src/devfs.dart
+++ b/packages/flutter_tools/lib/src/devfs.dart
@@ -512,7 +512,8 @@
         final String relativePath =
             fs.path.relative(file.path, from: directory.path);
         final Uri deviceUri = directoryUriOnDevice.resolveUri(fs.path.toUri(relativePath));
-        if ((fileFilter != null) && !fileFilter.contains(file.absolute.path)) {
+        final String canonicalizeFilePath = fs.path.canonicalize(file.absolute.path);
+        if ((fileFilter != null) && !fileFilter.contains(canonicalizeFilePath)) {
           // Skip files that are not included in the filter.
           continue;
         }
diff --git a/packages/flutter_tools/test/dart_dependencies_test.dart b/packages/flutter_tools/test/dart_dependencies_test.dart
index 960574c..5f1989c 100644
--- a/packages/flutter_tools/test/dart_dependencies_test.dart
+++ b/packages/flutter_tools/test/dart_dependencies_test.dart
@@ -19,8 +19,8 @@
       final DartDependencySetBuilder builder =
           new DartDependencySetBuilder(mainPath, testPath, packagesPath);
       final Set<String> dependencies = builder.build();
-      expect(dependencies.contains(mainPath), isTrue);
-      expect(dependencies.contains(fs.path.join(testPath, 'foo.dart')), isTrue);
+      expect(dependencies.contains(fs.path.canonicalize(mainPath)), isTrue);
+      expect(dependencies.contains(fs.path.canonicalize(fs.path.join(testPath, 'foo.dart'))), isTrue);
     });
     testUsingContext('syntax_error', () {
       final String testPath = fs.path.join(dataPath, 'syntax_error');
diff --git a/packages/flutter_tools/test/devfs_test.dart b/packages/flutter_tools/test/devfs_test.dart
index c21070f..1549025 100644
--- a/packages/flutter_tools/test/devfs_test.dart
+++ b/packages/flutter_tools/test/devfs_test.dart
@@ -144,6 +144,31 @@
       expect(devFS.assetPathsToEvict, isEmpty);
       expect(bytes, 69);
     });
+    testUsingContext('add new package with double slashes in URI', () async {
+      String packageName = 'doubleslashpkg';
+      await _createPackage(packageName, 'somefile.txt', doubleSlash: true);
+
+      Set<String> fileFilter = new Set<String>();
+      List<Uri> pkgUris = <Uri>[fs.path.toUri(basePath)]..addAll(_packages.values);
+      for (Uri pkgUri in pkgUris) {
+        if (!pkgUri.isAbsolute) {
+          pkgUri = fs.path.toUri(fs.path.join(basePath, pkgUri.path));
+        }
+        fileFilter.addAll(fs.directory(pkgUri)
+            .listSync(recursive: true)
+            .where((FileSystemEntity file) => file is File)
+            .map((FileSystemEntity file) => fs.path.canonicalize(file.path))
+            .toList());
+      }
+
+      int bytes = await devFS.update(fileFilter: fileFilter);
+      devFSOperations.expectMessages(<String>[
+        'writeFile test .packages',
+        'writeFile test packages/doubleslashpkg/somefile.txt',
+      ]);
+      expect(devFS.assetPathsToEvict, isEmpty);
+      expect(bytes, 109);
+    });
     testUsingContext('add an asset bundle', () async {
       assetBundle.entries['a.txt'] = new DevFSStringContent('abc');
       final int bytes = await devFS.update(bundle: assetBundle, bundleDirty: true);
@@ -334,9 +359,15 @@
   }
 }
 
-Future<Null> _createPackage(String pkgName, String pkgFileName) async {
+Future<Null> _createPackage(String pkgName, String pkgFileName, { bool doubleSlash: false }) async {
   final Directory pkgTempDir = _newTempDir();
-  final File pkgFile = fs.file(fs.path.join(pkgTempDir.path, pkgName, 'lib', pkgFileName));
+  String pkgFilePath = fs.path.join(pkgTempDir.path, pkgName, 'lib', pkgFileName);
+  if (doubleSlash) {
+    // Force two separators into the path.
+    String doubleSlash = fs.path.separator + fs.path.separator;
+    pkgFilePath = pkgTempDir.path + doubleSlash  + fs.path.join(pkgName, 'lib', pkgFileName);
+  }
+  final File pkgFile = fs.file(pkgFilePath);
   await pkgFile.parent.create(recursive: true);
   pkgFile.writeAsBytesSync(<int>[11, 12, 13]);
   _packages[pkgName] = fs.path.toUri(pkgFile.parent.path);