getExecutableForCommand function

Future<DartExecutableWithPackageConfig> getExecutableForCommand(
  1. String descriptor, {
  2. @Deprecated('Snapshotting is now handled by dartdev.') bool allowSnapshot = false,
  3. String? root,
  4. String? pubCacheDir,
})

Returns the dart program/snapshot to invoke for running descriptor resolved according to the package configuration of the package at root (defaulting to the current working directory). Using the pub-cache at pubCacheDir (defaulting to the default pub cache).

The returned path will be relative to root.

Resolution:

descriptor is resolved as follows:

  • If <descriptor> is an existing file (resolved relative to root, either as a path or a file uri): return that file with a null packageConfig.

  • Otherwise if it looks like a file name (ends with '.dart' or contains a '/' or a r'') throw a CommandResolutionFailedException. (This is for more clear error messages).

  • Otherwise call Entrypoint.ensureUpToDate in the current directory to obtain a package config. If that fails, return a CommandResolutionFailedException.

  • Otherwise let <current> be the name of the innermost package containing root, and interpret descriptor as [<package>][:<command>].

    • If <package> is empty: default to the current package.
    • If <command> is empty, resolve it as bin/<package>.dart or bin/main.dart to the first that exists.

For example:

  • foo will resolve to foo:bin/foo.dart or foo:bin/main.dart.
  • :foo will resolve to <current>:bin/foo.dart.
  • The empty string and ":" both resolves to <current>:bin/<current>.dart or bin/<current>:main.dart.

If that doesn't resolve as an existing file, throw an exception.

Throws an CommandResolutionFailedException if the command is not found or if the entrypoint is not up to date (requires pub get) and a pub get fails.

Implementation

Future<DartExecutableWithPackageConfig> getExecutableForCommand(
  String descriptor, {
  @Deprecated('Snapshotting is now handled by dartdev.')
  bool allowSnapshot = false,
  String? root,
  String? pubCacheDir,
}) async {
  final rootOrCurrent = root ?? p.current;
  var asPath = descriptor;
  try {
    asPath = Uri.parse(descriptor).toFilePath();
  } catch (_) {
    // Consume input path will either be a valid path or a file uri
    // (e.g /directory/file.dart or file:///directory/file.dart). We will try
    // parsing it as a Uri, but if parsing failed for any reason (likely
    // because path is not a file Uri), `path` will be passed without
    // modification to the VM.
  }

  final asDirectFile = p.join(rootOrCurrent, asPath);
  if (fileExists(asDirectFile)) {
    return DartExecutableWithPackageConfig(
      executable: p.normalize(p.relative(asDirectFile, from: rootOrCurrent)),
      packageConfig: null,
    );
  } else if (_looksLikeFile(asPath)) {
    throw CommandResolutionFailedException._(
      'Could not find file `$descriptor`',
      CommandResolutionIssue.fileNotFound,
    );
  }
  final PackageConfig packageConfig;
  final String workspaceRootDir;
  try {
    final String workspaceRootRelativeToCwd;
    (
      packageConfig: packageConfig,
      rootDir: workspaceRootRelativeToCwd,
    ) = await Entrypoint.ensureUpToDate(
      rootOrCurrent,
      cache: SystemCache(rootDir: pubCacheDir),
    );
    workspaceRootDir = p.absolute(workspaceRootRelativeToCwd);
  } on ApplicationException catch (e) {
    throw CommandResolutionFailedException._(
      e.toString(),
      CommandResolutionIssue.pubGetFailed,
    );
  }
  // Find the first directory from [rootOrCurrent] to [workspaceRootDir] (both
  // inclusive) that contains a package from the package config.
  final packageConfigDir = p.join(
    workspaceRootDir,
    '.dart_tool',
    'package_config.json',
  );

  final rootPackageName =
      maxBy<(String, String), int>(
        packageConfig.packages.map((package) {
          final packageRootDir = p.canonicalize(
            package.resolvedRootDir(packageConfigDir),
          );
          if (p.equals(packageRootDir, rootOrCurrent) ||
              p.isWithin(packageRootDir, rootOrCurrent)) {
            return (package.name, packageRootDir);
          } else {
            return null;
          }
        }).nonNulls,
        (tuple) => tuple.$2.length,
      )?.$1;

  if (rootPackageName == null) {
    final packageConfigPath = p.join(
      workspaceRootDir,
      '.dart_tool',
      'package_config.json',
    );
    throw CommandResolutionFailedException._(
      '$packageConfigPath did not contain its own root package',
      CommandResolutionIssue.fileNotFound,
    );
  }
  final String command;
  final String package;
  if (descriptor.contains(':')) {
    final parts = descriptor.split(':');
    if (parts.length > 2) {
      throw CommandResolutionFailedException._(
        '[<package>[:command]] cannot contain multiple ":"',
        CommandResolutionIssue.parseError,
      );
    }
    final packageName = parts[0];
    package = packageName.isNotEmpty ? packageName : rootPackageName;
    command = parts[1];
  } else {
    package = descriptor.isNotEmpty ? descriptor : rootPackageName;
    command = package;
  }

  if (!packageConfig.packages.any((p) => p.name == package)) {
    throw CommandResolutionFailedException._(
      'Could not find package `$package` or file `$descriptor`',
      CommandResolutionIssue.packageNotFound,
    );
  }
  final executable = Executable(package, p.join('bin', '$command.dart'));
  final packageConfigPath = p.normalize(
    p.join(workspaceRootDir, '.dart_tool', 'package_config.json'),
  );
  final path = executable.resolve(packageConfig, packageConfigPath);
  if (!fileExists(p.join(rootOrCurrent, path))) {
    throw CommandResolutionFailedException._(
      'Could not find `bin${p.separator}$command.dart` in package `$package`.',
      CommandResolutionIssue.noBinaryFound,
    );
  }
  return DartExecutableWithPackageConfig(
    executable: p.normalize(path),
    packageConfig: p.relative(packageConfigPath, from: rootOrCurrent),
  );
}