parseImpellerCDepfileDependencies function

List<Uri> parseImpellerCDepfileDependencies(
  1. String depfileContents, {
  2. Uri? relativeTo,
  3. bool fileExists(
    1. String path
    )?,
})

Parses dependency paths from an impellerc depfile.

impellerc emits a Ninja-style single-line depfile: <target>: <dep1> <dep2> .... The target is ignored. Dependency paths are converted to file URIs; relative paths are resolved against relativeTo when it is provided.

impellerc writes paths without escaping, so a path containing a space (for example an SDK under Application Support) arrives split across several whitespace-separated tokens. When a token's path does not exist on disk, adjacent tokens are rejoined with single spaces until the combined path exists, recovering the original path. fileExists is the existence probe (overridable for tests); tokens that never combine into an existing path are kept as split, matching the old behavior. Escaped spaces (\ ) are also honored should impellerc start emitting them.

Implementation

List<Uri> parseImpellerCDepfileDependencies(
  String depfileContents, {
  Uri? relativeTo,
  bool Function(String path)? fileExists,
}) {
  final exists = fileExists ?? (path) => File(path).existsSync();
  final separator = RegExp(r':(?:\s|$)').firstMatch(depfileContents);
  if (separator == null) {
    return const [];
  }
  final dependencies = depfileContents.substring(separator.end).trim();
  if (dependencies.isEmpty) {
    return const [];
  }
  final tokens = dependencies
      .split(RegExp(r'(?<!\\)\s+'))
      .where((dependency) => dependency.isNotEmpty)
      .map((dependency) => dependency.replaceAll(r'\ ', ' '))
      .toList();

  String resolvePath(String token) => _isAbsoluteFilePath(token)
      ? token
      : (relativeTo?.resolveUri(Uri.file(token)).toFilePath() ?? token);

  final paths = <String>[];
  for (var i = 0; i < tokens.length;) {
    var token = tokens[i];
    var consumed = 1;
    if (!exists(resolvePath(token))) {
      // Try rejoining successive tokens; accept the first combination that
      // names a real file.
      var candidate = token;
      for (var j = i + 1; j < tokens.length; j++) {
        candidate = '$candidate ${tokens[j]}';
        if (exists(resolvePath(candidate))) {
          token = candidate;
          consumed = j - i + 1;
          break;
        }
      }
    }
    paths.add(token);
    i += consumed;
  }

  return [
    for (final path in paths)
      if (_isAbsoluteFilePath(path))
        Uri.file(path)
      else
        relativeTo?.resolveUri(Uri.file(path)) ?? Uri.file(path),
  ];
}