OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library pub.executable; | 5 library pub.executable; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 | 9 |
10 import 'package:barback/barback.dart'; | 10 import 'package:barback/barback.dart'; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 'dependency?'); | 67 'dependency?'); |
68 } | 68 } |
69 } | 69 } |
70 | 70 |
71 // Unless the user overrides the verbosity, we want to filter out the | 71 // Unless the user overrides the verbosity, we want to filter out the |
72 // normal pub output shown while loading the environment. | 72 // normal pub output shown while loading the environment. |
73 if (log.verbosity == log.Verbosity.NORMAL) { | 73 if (log.verbosity == log.Verbosity.NORMAL) { |
74 log.verbosity = log.Verbosity.WARNING; | 74 log.verbosity = log.Verbosity.WARNING; |
75 } | 75 } |
76 | 76 |
77 // Ignore a trailing extension. | 77 // Ensure that there's a trailing extension. |
78 if (p.extension(executable) == ".dart") { | 78 if (p.extension(executable) != ".dart") executable += ".dart"; |
79 executable = p.withoutExtension(executable); | |
80 } | |
81 | 79 |
82 var localSnapshotPath = p.join(".pub", "bin", package, | 80 var localSnapshotPath = p.join(".pub", "bin", package, |
83 "$executable.dart.snapshot"); | 81 "$executable.snapshot"); |
84 if (!isGlobal && fileExists(localSnapshotPath) && | 82 if (!isGlobal && fileExists(localSnapshotPath) && |
85 // Dependencies are only snapshotted in release mode, since that's the | 83 // Dependencies are only snapshotted in release mode, since that's the |
86 // default mode for them to run. We can't run them in a different mode | 84 // default mode for them to run. We can't run them in a different mode |
87 // using the snapshot. | 85 // using the snapshot. |
88 mode == BarbackMode.RELEASE) { | 86 mode == BarbackMode.RELEASE) { |
89 return _runCachedExecutable(entrypoint, localSnapshotPath, args); | 87 return _runCachedExecutable(entrypoint, localSnapshotPath, args); |
90 } | 88 } |
91 | 89 |
92 // If the command has a path separator, then it's a path relative to the | 90 // If the command has a path separator, then it's a path relative to the |
93 // root of the package. Otherwise, it's implicitly understood to be in | 91 // root of the package. Otherwise, it's implicitly understood to be in |
94 // "bin". | 92 // "bin". |
95 var rootDir = "bin"; | 93 if (p.split(executable).length == 1) executable = p.join("bin", executable); |
96 var parts = p.split(executable); | 94 |
97 if (parts.length > 1) { | 95 var vmArgs = []; |
98 assert(!isGlobal && package == entrypoint.root.name); | 96 |
99 rootDir = parts.first; | 97 // Run in checked mode. |
100 } else { | 98 // TODO(rnystrom): Make this configurable. |
101 executable = p.join("bin", executable); | 99 vmArgs.add("--checked"); |
| 100 |
| 101 var executableUrl = await _executableUrl( |
| 102 entrypoint, package, executable, isGlobal: isGlobal, mode: mode); |
| 103 |
| 104 if (executableUrl == null) { |
| 105 var message = "Could not find ${log.bold(executable)}"; |
| 106 if (package != entrypoint.root.name) { |
| 107 message += " in package ${log.bold(package)}"; |
| 108 } |
| 109 log.error("$message."); |
| 110 return exit_codes.NO_INPUT; |
102 } | 111 } |
103 | 112 |
104 var assetPath = "${p.url.joinAll(p.split(executable))}.dart"; | 113 vmArgs.add(executableUrl.toString()); |
| 114 vmArgs.addAll(args); |
| 115 |
| 116 var process = await Process.start(Platform.executable, vmArgs); |
| 117 |
| 118 _forwardSignals(process); |
| 119 |
| 120 // Note: we're not using process.std___.pipe(std___) here because |
| 121 // that prevents pub from also writing to the output streams. |
| 122 process.stderr.listen(stderr.add); |
| 123 process.stdout.listen(stdout.add); |
| 124 stdin.listen(process.stdin.add); |
| 125 |
| 126 return process.exitCode; |
| 127 } |
| 128 |
| 129 /// Returns the URL the VM should use to load the executable at [path]. |
| 130 /// |
| 131 /// [path] must be relative to the root of [package]. If [path] doesn't exist, |
| 132 /// returns `null`. |
| 133 Future<Uri> _executableUrl(Entrypoint entrypoint, String package, String path, |
| 134 {bool isGlobal: false, BarbackMode mode}) async { |
| 135 assert(p.isRelative(path)); |
| 136 |
| 137 // If neither the executable nor any of its dependencies are transformed, |
| 138 // there's no need to spin up a barback server. Just run the VM directly |
| 139 // against the filesystem. |
| 140 // |
| 141 // TODO(nweiz): Once sdk#23369 is fixed, allow global executables to be run |
| 142 // (and snapshotted) from the filesystem using package specs. A spec can by |
| 143 // saved when activating the package. |
| 144 var packageGraph = await entrypoint.loadPackageGraph(); |
| 145 if (!isGlobal && !packageGraph.isPackageTransformed(package)) { |
| 146 var fullPath = packageGraph.packages[package].path(path); |
| 147 if (!fileExists(fullPath)) return null; |
| 148 return p.toUri(fullPath); |
| 149 } |
| 150 |
| 151 var assetPath = p.url.joinAll(p.split(path)); |
105 var id = new AssetId(package, assetPath); | 152 var id = new AssetId(package, assetPath); |
106 | 153 |
107 // TODO(nweiz): Use [packages] to only load assets from packages that the | 154 // TODO(nweiz): Use [packages] to only load assets from packages that the |
108 // executable might load. | 155 // executable might load. |
109 var environment = await AssetEnvironment.create(entrypoint, mode, | 156 var environment = await AssetEnvironment.create(entrypoint, mode, |
110 useDart2JS: false, entrypoints: [id]); | 157 useDart2JS: false, entrypoints: [id]); |
111 environment.barback.errors.listen((error) { | 158 environment.barback.errors.listen((error) { |
112 log.error(log.red("Build error:\n$error")); | 159 log.error(log.red("Build error:\n$error")); |
113 }); | 160 }); |
114 | 161 |
115 var server; | 162 var server; |
116 if (package == entrypoint.root.name) { | 163 if (package == entrypoint.root.name) { |
117 // Serve the entire root-most directory containing the entrypoint. That | 164 // Serve the entire root-most directory containing the entrypoint. That |
118 // ensures that, for example, things like `import '../../utils.dart';` | 165 // ensures that, for example, things like `import '../../utils.dart';` |
119 // will work from within some deeply nested script. | 166 // will work from within some deeply nested script. |
120 server = await environment.serveDirectory(rootDir); | 167 server = await environment.serveDirectory(p.split(path).first); |
121 } else { | 168 } else { |
| 169 assert(p.split(path).first == "bin"); |
| 170 |
122 // For other packages, always use the "bin" directory. | 171 // For other packages, always use the "bin" directory. |
123 server = await environment.servePackageBinDirectory(package); | 172 server = await environment.servePackageBinDirectory(package); |
124 } | 173 } |
125 | 174 |
126 try { | 175 try { |
127 await environment.barback.getAssetById(id); | 176 await environment.barback.getAssetById(id); |
128 } on AssetNotFoundException catch (error, stackTrace) { | 177 } on AssetNotFoundException catch (_) { |
129 var message = "Could not find ${log.bold(executable + ".dart")}"; | 178 return null; |
130 if (package != entrypoint.root.name) { | |
131 message += " in package ${log.bold(server.package)}"; | |
132 } | |
133 | |
134 log.error("$message."); | |
135 log.fine(new Chain.forTrace(stackTrace)); | |
136 return exit_codes.NO_INPUT; | |
137 } | 179 } |
138 | 180 |
139 var vmArgs = []; | |
140 | |
141 // Run in checked mode. | |
142 // TODO(rnystrom): Make this configurable. | |
143 vmArgs.add("--checked"); | |
144 | |
145 // Get the URL of the executable, relative to the server's root directory. | 181 // Get the URL of the executable, relative to the server's root directory. |
146 var relativePath = p.url.relative(assetPath, | 182 var relativePath = p.url.relative(assetPath, |
147 from: p.url.joinAll(p.split(server.rootDirectory))); | 183 from: p.url.joinAll(p.split(server.rootDirectory))); |
148 vmArgs.add(server.url.resolve(relativePath).toString()); | 184 return server.url.resolve(relativePath); |
149 vmArgs.addAll(args); | |
150 | |
151 var process = await Process.start(Platform.executable, vmArgs); | |
152 | |
153 _forwardSignals(process); | |
154 | |
155 // Note: we're not using process.std___.pipe(std___) here because | |
156 // that prevents pub from also writing to the output streams. | |
157 process.stderr.listen(stderr.add); | |
158 process.stdout.listen(stdout.add); | |
159 stdin.listen(process.stdin.add); | |
160 | |
161 return process.exitCode; | |
162 } | 185 } |
163 | 186 |
164 /// Runs the snapshot at [path] with [args] and hooks its stdout, stderr, and | 187 /// Runs the snapshot at [path] with [args] and hooks its stdout, stderr, and |
165 /// sdtin to this process's. | 188 /// sdtin to this process's. |
166 /// | 189 /// |
167 /// If [recompile] is passed, it's called if the snapshot is out-of-date. It's | 190 /// If [recompile] is passed, it's called if the snapshot is out-of-date. It's |
168 /// expected to regenerate a snapshot at [path], after which the snapshot will | 191 /// expected to regenerate a snapshot at [path], after which the snapshot will |
169 /// be re-run. It may return a Future. | 192 /// be re-run. It may return a Future. |
170 /// | 193 /// |
171 /// If [checked] is set, runs the snapshot in checked mode. | 194 /// If [checked] is set, runs the snapshot in checked mode. |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
231 } | 254 } |
232 | 255 |
233 /// Runs the executable snapshot at [snapshotPath]. | 256 /// Runs the executable snapshot at [snapshotPath]. |
234 Future<int> _runCachedExecutable(Entrypoint entrypoint, String snapshotPath, | 257 Future<int> _runCachedExecutable(Entrypoint entrypoint, String snapshotPath, |
235 List<String> args) { | 258 List<String> args) { |
236 return runSnapshot(snapshotPath, args, checked: true, recompile: () { | 259 return runSnapshot(snapshotPath, args, checked: true, recompile: () { |
237 log.fine("Precompiled executable is out of date."); | 260 log.fine("Precompiled executable is out of date."); |
238 return entrypoint.precompileExecutables(); | 261 return entrypoint.precompileExecutables(); |
239 }); | 262 }); |
240 } | 263 } |
OLD | NEW |