Index: lib/src/executable.dart |
diff --git a/lib/src/executable.dart b/lib/src/executable.dart |
index 1b3646883375d5dd7b87423fdde9156dafcb5224..f692c026ecddec99dcd1767ca01d506080ddfc08 100644 |
--- a/lib/src/executable.dart |
+++ b/lib/src/executable.dart |
@@ -74,13 +74,11 @@ Future<int> runExecutable(Entrypoint entrypoint, String package, |
log.verbosity = log.Verbosity.WARNING; |
} |
- // Ignore a trailing extension. |
- if (p.extension(executable) == ".dart") { |
- executable = p.withoutExtension(executable); |
- } |
+ // Ensure that there's a trailing extension. |
+ if (p.extension(executable) != ".dart") executable += ".dart"; |
var localSnapshotPath = p.join(".pub", "bin", package, |
- "$executable.dart.snapshot"); |
+ "$executable.snapshot"); |
if (!isGlobal && fileExists(localSnapshotPath) && |
// Dependencies are only snapshotted in release mode, since that's the |
// default mode for them to run. We can't run them in a different mode |
@@ -92,16 +90,65 @@ Future<int> runExecutable(Entrypoint entrypoint, String package, |
// If the command has a path separator, then it's a path relative to the |
// root of the package. Otherwise, it's implicitly understood to be in |
// "bin". |
- var rootDir = "bin"; |
- var parts = p.split(executable); |
- if (parts.length > 1) { |
- assert(!isGlobal && package == entrypoint.root.name); |
- rootDir = parts.first; |
- } else { |
- executable = p.join("bin", executable); |
+ if (p.split(executable).length == 1) executable = p.join("bin", executable); |
+ |
+ var vmArgs = []; |
+ |
+ // Run in checked mode. |
+ // TODO(rnystrom): Make this configurable. |
+ vmArgs.add("--checked"); |
+ |
+ var executableUrl = await _executableUrl( |
+ entrypoint, package, executable, isGlobal: isGlobal, mode: mode); |
+ |
+ if (executableUrl == null) { |
+ var message = "Could not find ${log.bold(executable)}"; |
+ if (package != entrypoint.root.name) { |
+ message += " in package ${log.bold(package)}"; |
+ } |
+ log.error("$message."); |
+ return exit_codes.NO_INPUT; |
} |
- var assetPath = "${p.url.joinAll(p.split(executable))}.dart"; |
+ vmArgs.add(executableUrl.toString()); |
+ vmArgs.addAll(args); |
+ |
+ var process = await Process.start(Platform.executable, vmArgs); |
+ |
+ _forwardSignals(process); |
+ |
+ // Note: we're not using process.std___.pipe(std___) here because |
+ // that prevents pub from also writing to the output streams. |
+ process.stderr.listen(stderr.add); |
+ process.stdout.listen(stdout.add); |
+ stdin.listen(process.stdin.add); |
+ |
+ return process.exitCode; |
+} |
+ |
+/// Returns the URL the VM should use to load the executable at [path]. |
+/// |
+/// [path] must be relative to the root of [package]. If [path] doesn't exist, |
+/// returns `null`. |
+Future<Uri> _executableUrl(Entrypoint entrypoint, String package, String path, |
+ {bool isGlobal: false, BarbackMode mode}) async { |
+ assert(p.isRelative(path)); |
+ |
+ // If neither the executable nor any of its dependencies are transformed, |
+ // there's no need to spin up a barback server. Just run the VM directly |
+ // against the filesystem. |
+ // |
+ // TODO(nweiz): Once sdk#23369 is fixed, allow global executables to be run |
+ // (and snapshotted) from the filesystem using package specs. A spec can by |
+ // saved when activating the package. |
+ var packageGraph = await entrypoint.loadPackageGraph(); |
+ if (!isGlobal && !packageGraph.isPackageTransformed(package)) { |
+ var fullPath = packageGraph.packages[package].path(path); |
+ if (!fileExists(fullPath)) return null; |
+ return p.toUri(fullPath); |
+ } |
+ |
+ var assetPath = p.url.joinAll(p.split(path)); |
var id = new AssetId(package, assetPath); |
// TODO(nweiz): Use [packages] to only load assets from packages that the |
@@ -117,48 +164,24 @@ Future<int> runExecutable(Entrypoint entrypoint, String package, |
// Serve the entire root-most directory containing the entrypoint. That |
// ensures that, for example, things like `import '../../utils.dart';` |
// will work from within some deeply nested script. |
- server = await environment.serveDirectory(rootDir); |
+ server = await environment.serveDirectory(p.split(path).first); |
} else { |
+ assert(p.split(path).first == "bin"); |
+ |
// For other packages, always use the "bin" directory. |
server = await environment.servePackageBinDirectory(package); |
} |
try { |
await environment.barback.getAssetById(id); |
- } on AssetNotFoundException catch (error, stackTrace) { |
- var message = "Could not find ${log.bold(executable + ".dart")}"; |
- if (package != entrypoint.root.name) { |
- message += " in package ${log.bold(server.package)}"; |
- } |
- |
- log.error("$message."); |
- log.fine(new Chain.forTrace(stackTrace)); |
- return exit_codes.NO_INPUT; |
+ } on AssetNotFoundException catch (_) { |
+ return null; |
} |
- var vmArgs = []; |
- |
- // Run in checked mode. |
- // TODO(rnystrom): Make this configurable. |
- vmArgs.add("--checked"); |
- |
// Get the URL of the executable, relative to the server's root directory. |
var relativePath = p.url.relative(assetPath, |
from: p.url.joinAll(p.split(server.rootDirectory))); |
- vmArgs.add(server.url.resolve(relativePath).toString()); |
- vmArgs.addAll(args); |
- |
- var process = await Process.start(Platform.executable, vmArgs); |
- |
- _forwardSignals(process); |
- |
- // Note: we're not using process.std___.pipe(std___) here because |
- // that prevents pub from also writing to the output streams. |
- process.stderr.listen(stderr.add); |
- process.stdout.listen(stdout.add); |
- stdin.listen(process.stdin.add); |
- |
- return process.exitCode; |
+ return server.url.resolve(relativePath); |
} |
/// Runs the snapshot at [path] with [args] and hooks its stdout, stderr, and |