// Copyright 2014 The Flutter 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 '../base/file_system.dart'; import '../globals.dart' as globals; /// A class for representing depfile formats. class Depfile { /// Create a [Depfile] from a list of [input] files and [output] files. const Depfile(this.inputs, this.outputs); /// Parse the depfile contents from [file]. /// /// If the syntax is invalid, returns an empty [Depfile]. factory Depfile.parse(File file) { final String contents = file.readAsStringSync(); final List<String> colonSeparated = contents.split(': '); if (colonSeparated.length != 2) { globals.printError('Invalid depfile: ${file.path}'); return const Depfile(<File>[], <File>[]); } final List<File> inputs = _processList(colonSeparated[1].trim()); final List<File> outputs = _processList(colonSeparated[0].trim()); return Depfile(inputs, outputs); } /// Parse the output of dart2js's used dependencies. /// /// The [file] contains a list of newline separated file URIs. The output /// file must be manually specified. factory Depfile.parseDart2js(File file, File output) { final List<File> inputs = <File>[]; for (final String rawUri in file.readAsLinesSync()) { if (rawUri.trim().isEmpty) { continue; } final Uri fileUri = Uri.tryParse(rawUri); if (fileUri == null) { continue; } if (fileUri.scheme != 'file') { continue; } inputs.add(globals.fs.file(fileUri)); } return Depfile(inputs, <File>[output]); } /// The input files for this depfile. final List<File> inputs; /// The output files for this depfile. final List<File> outputs; /// Given an [depfile] File, write the depfile contents. /// /// If either [inputs] or [outputs] is empty, ensures the file does not /// exist. void writeToFile(File depfile) { if (inputs.isEmpty || outputs.isEmpty) { if (depfile.existsSync()) { depfile.deleteSync(); } return; } final StringBuffer buffer = StringBuffer(); _writeFilesToBuffer(outputs, buffer); buffer.write(': '); _writeFilesToBuffer(inputs, buffer); depfile.writeAsStringSync(buffer.toString()); } void _writeFilesToBuffer(List<File> files, StringBuffer buffer) { for (final File outputFile in files) { if (globals.platform.isWindows) { // Paths in a depfile have to be escaped on windows. final String escapedPath = outputFile.path.replaceAll(r'\', r'\\'); buffer.write(' $escapedPath'); } else { buffer.write(' ${outputFile.path}'); } } } static final RegExp _separatorExpr = RegExp(r'([^\\]) '); static final RegExp _escapeExpr = RegExp(r'\\(.)'); static List<File> _processList(String rawText) { return rawText // Put every file on right-hand side on the separate line .replaceAllMapped(_separatorExpr, (Match match) => '${match.group(1)}\n') .split('\n') // Expand escape sequences, so that '\ ', for example,ß becomes ' ' .map<String>((String path) => path.replaceAllMapped(_escapeExpr, (Match match) => match.group(1)).trim()) .where((String path) => path.isNotEmpty) // The tool doesn't write duplicates to these lists. This call is an attempt to // be resillient to the outputs of other tools which write or user edits to depfiles. .toSet() .map((String path) => globals.fs.file(path)) .toList(); } }