diff --git a/packages/flutter_tools/lib/src/build_system/depfile.dart b/packages/flutter_tools/lib/src/build_system/depfile.dart index a27e986e18..9d90194c93 100644 --- a/packages/flutter_tools/lib/src/build_system/depfile.dart +++ b/packages/flutter_tools/lib/src/build_system/depfile.dart @@ -65,16 +65,19 @@ class DepfileService { } void _writeFilesToBuffer(List files, StringBuffer buffer) { + final bool backslash = _fileSystem.path.style.separator == r'\'; for (final File outputFile in files) { - if (_fileSystem.path.style.separator == r'\') { - // backslashes and spaces in a depfile have to be escaped if the - // platform separator is a backslash. - final String path = outputFile.path.replaceAll(r'\', r'\\').replaceAll(r' ', r'\ '); - buffer.write(' $path'); + String path = _fileSystem.path.normalize(outputFile.path); + if (backslash) { + // Backslashes in a depfile have to be escaped if the platform separator is a backslash. + path = path.replaceAll(r'\', r'\\'); } else { - final String path = outputFile.path.replaceAll(r' ', r'\ '); - buffer.write(' $path'); + // Convert all path separators to forward slashes. + path = path.replaceAll(r'\', r'/'); } + // Escape spaces. + path = path.replaceAll(r' ', r'\ '); + buffer.write(' $path'); } } @@ -92,7 +95,8 @@ class DepfileService { // The tool doesn't write duplicates to these lists. This call is an attempt to // be resilient to the outputs of other tools which write or user edits to depfiles. .toSet() - .map(_fileSystem.file) + // Normalize the path before creating a file object. + .map((String path) => _fileSystem.file(_fileSystem.path.normalize(path))) .toList(); } } diff --git a/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart b/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart index f0efccaf5e..3855c40141 100644 --- a/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/depfile_test.dart @@ -17,6 +17,7 @@ void main() { fileSystem = MemoryFileSystem.test(); depfileService = DepfileService(logger: BufferLogger.test(), fileSystem: fileSystem); }); + testWithoutContext('Can parse depfile from file', () { final File depfileSource = fileSystem.file('example.d')..writeAsStringSync(''' a.txt: b.txt @@ -48,22 +49,34 @@ a.txt c.txt d.txt: b.txt }); testWithoutContext('Can parse depfile with windows file paths', () { - fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); - depfileService = DepfileService(logger: BufferLogger.test(), fileSystem: fileSystem); + final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); + final DepfileService depfileService = DepfileService( + logger: BufferLogger.test(), + fileSystem: fileSystem, + ); final File depfileSource = fileSystem.file('example.d')..writeAsStringSync(r''' -C:\\a.txt: C:\\b.txt +C:\\a1.txt C:\\a2/a3.txt: C:\\b1.txt C:\\b2/b3.txt '''); final Depfile depfile = depfileService.parse(depfileSource); - expect(depfile.inputs.single.path, r'C:\b.txt'); - expect(depfile.outputs.single.path, r'C:\a.txt'); + expect(depfile.inputs.map((File e) => e.path).toList(), [ + r'C:\b1.txt', + r'C:\b2\b3.txt', + ]); + expect(depfile.outputs.map((File e) => e.path).toList(), [ + r'C:\a1.txt', + r'C:\a2\a3.txt', + ]); }); testWithoutContext( 'Can escape depfile with windows file paths and spaces in directory names', () { - fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); - depfileService = DepfileService(logger: BufferLogger.test(), fileSystem: fileSystem); + final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows); + final DepfileService depfileService = DepfileService( + logger: BufferLogger.test(), + fileSystem: fileSystem, + ); final File inputFile = fileSystem.directory(r'Hello Flutter').childFile('a.txt').absolute ..createSync(recursive: true); @@ -73,8 +86,9 @@ C:\\a.txt: C:\\b.txt final File outputDepfile = fileSystem.file('depfile'); depfileService.writeToFile(depfile, outputDepfile); - expect(outputDepfile.readAsStringSync(), contains(r'C:\\Hello\ Flutter\\a.txt')); - expect(outputDepfile.readAsStringSync(), contains(r'C:\\Hello\ Flutter\\b.txt')); + final String output = outputDepfile.readAsStringSync(); + expect(output, contains(r'C:\\Hello\ Flutter\\a.txt')); + expect(output, contains(r'C:\\Hello\ Flutter\\b.txt')); }, ); @@ -88,8 +102,46 @@ C:\\a.txt: C:\\b.txt final File outputDepfile = fileSystem.file('depfile'); depfileService.writeToFile(depfile, outputDepfile); - expect(outputDepfile.readAsStringSync(), contains(r'/Hello\ Flutter/a.txt')); - expect(outputDepfile.readAsStringSync(), contains(r'/Hello\ Flutter/b.txt')); + final String output = outputDepfile.readAsStringSync(); + expect(output, contains(r'/Hello\ Flutter/a.txt')); + expect(output, contains(r'/Hello\ Flutter/b.txt')); + }); + + testWithoutContext('Can produce normalized paths', () { + final List<(FileSystemStyle style, String input, String output, List expects)> pairs = + <(FileSystemStyle style, String input, String output, List expects)>[ + ( + FileSystemStyle.posix, + r'Hello Flutter\a.txt', + r'Hello Flutter\b.txt', + [r'/Hello\ Flutter/a.txt', r'/Hello\ Flutter/b.txt'], + ), + ( + FileSystemStyle.windows, + r'Hello Flutter/a.txt', + r'Hello Flutter/b.txt', + [r'\\Hello\ Flutter\\a.txt', r'\\Hello\ Flutter\\b.txt'], + ), + ]; + + for (final (FileSystemStyle style, String input, String output, List expects) + in pairs) { + final FileSystem fileSystem = MemoryFileSystem.test(style: style); + final DepfileService depfileService = DepfileService( + logger: BufferLogger.test(), + fileSystem: fileSystem, + ); + final File inputFile = fileSystem.file(input).absolute..createSync(recursive: true); + final File outputFile = fileSystem.file(output).absolute..createSync(); + final Depfile depfile = Depfile([inputFile], [outputFile]); + final File outputDepfile = fileSystem.file('depfile'); + depfileService.writeToFile(depfile, outputDepfile); + + final String outputString = outputDepfile.readAsStringSync(); + for (final String path in expects) { + expect(outputString, contains(path)); + } + } }); testWithoutContext('Resilient to weird whitespace', () {