-
Notifications
You must be signed in to change notification settings - Fork 232
pub run --v2 for use in dartdev #2435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,10 +29,15 @@ class RunCommand extends PubCommand { | |
argParser.addFlag('enable-asserts', help: 'Enable assert statements.'); | ||
argParser.addFlag('checked', abbr: 'c', hide: true); | ||
argParser.addOption('mode', help: 'Deprecated option', hide: true); | ||
// mode exposed for `dartdev run` to use as subprocess. | ||
argParser.addFlag('v2', hide: true); | ||
} | ||
|
||
@override | ||
Future run() async { | ||
if (argResults['v2']) { | ||
return await _runV2(); | ||
} | ||
if (argResults.rest.isEmpty) { | ||
usageException('Must specify an executable to run.'); | ||
} | ||
|
@@ -86,4 +91,99 @@ class RunCommand extends PubCommand { | |
}); | ||
await flushThenExit(exitCode); | ||
} | ||
|
||
/// Implement a v2 mode for use in `dartdev run`. | ||
/// | ||
/// Usage: `dartdev run [package[:command]]` | ||
/// | ||
/// If `package` is not given, defaults to current root package. | ||
/// If `command` is not given, defaults to name of `package`. | ||
/// If neither `package` or `command` is given and `command` with name of | ||
/// the current package doesn't exist we fallback to `'main'`. | ||
Comment on lines
+101
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the magic fallback to I worry that adding something new - What does the error message look like if you There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It only does the fallback if I think the reasoning was that if you make a console app that is not distributed on
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think as long as the error message if none off the fallbacks exist mentions I hope to avoid new things that would subtly encourage that pattern, but I'm fine with attempting to keep compatibility with the other places that were already encouraging that pattern. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What advantages does Flutter apps seem to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jonasfj you meant "bin/.dart does NOT exist, and" right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess my only concern is that I don't understand why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I never liked/understood that convention either. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Python has There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this is not the right place to take this discussion.
I guess it comes down to composability. - why would the name of the main library have to depend on the surrounding package? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is what I worry about, but maybe I'm imagining problems that don't exist. What I'm worried is that Regarding I don't think at this point its worth the churn to try to change it. |
||
/// | ||
/// Runs `bin/<command>.dart` from package `<package>`. If `<package>` is not | ||
/// mutable (local root package or path-dependency) a source snapshot will be | ||
/// cached in `.dart_tool/pub/bin/<package>/<command>.dart.snapshot.dart2`. | ||
Future _runV2() async { | ||
var package = entrypoint.root.name; | ||
var command = package; | ||
var args = <String>[]; | ||
|
||
if (argResults.rest.isNotEmpty) { | ||
if (argResults.rest[0].contains(RegExp(r'[/\\]'))) { | ||
usageException('[<package>[:command]] cannot contain "/" or "\\"'); | ||
} | ||
|
||
package = argResults.rest[0]; | ||
if (package.contains(':')) { | ||
final parts = package.split(':'); | ||
if (parts.length > 2) { | ||
usageException('[<package>[:command]] cannot contain multiple ":"'); | ||
} | ||
package = parts[0]; | ||
command = parts[1]; | ||
} else { | ||
command = package; | ||
} | ||
args = argResults.rest.skip(1).toList(); | ||
} | ||
|
||
String snapshotPath(String command) => p.join( | ||
entrypoint.cachePath, | ||
'bin', | ||
package, | ||
'$command.dart.snapshot.dart2', | ||
); | ||
|
||
// If snapshot exists, we strive to avoid using [entrypoint.packageGraph] | ||
// because this will load additional files. Instead we just run with the | ||
// snapshot. Note. that `pub get|upgrade` will purge snapshots. | ||
var snapshotExists = fileExists(snapshotPath(command)); | ||
|
||
// Don't ever compile snapshots for mutable packages, since their code may | ||
// change later on. Don't check if this the case if a snapshot already | ||
// exists. | ||
var useSnapshot = snapshotExists || | ||
(package != entrypoint.root.name && | ||
!entrypoint.packageGraph.isPackageMutable(package)); | ||
|
||
// If argResults.rest.isEmpty, package == command, and 'bin/$command.dart' | ||
// doesn't exist we use command = 'main' (if it exists). | ||
// We don't need to check this if a snapshot already exists. | ||
// This is a hack around the fact that we want 'dartdev run' to run either | ||
// `bin/<packageName>.dart` or `bin/main.dart`, because `bin/main.dart` is | ||
// a historical convention we've done in templates for a long time. | ||
if (!snapshotExists && argResults.rest.isEmpty && package == command) { | ||
final pkg = entrypoint.packageGraph.packages[package]; | ||
if (pkg == null) { | ||
usageException('No such package "$package"'); | ||
} | ||
if (!fileExists(pkg.path('bin', '$command.dart')) && | ||
fileExists(pkg.path('bin', 'main.dart'))) { | ||
command = 'main'; | ||
snapshotExists = fileExists(snapshotPath(command)); | ||
useSnapshot = snapshotExists || | ||
(package != entrypoint.root.name && | ||
!entrypoint.packageGraph.isPackageMutable(package)); | ||
} | ||
} | ||
|
||
return await flushThenExit(await runExecutable( | ||
entrypoint, | ||
package, | ||
'$command.dart', | ||
args, | ||
enableAsserts: argResults['enable-asserts'] || argResults['checked'], | ||
snapshotPath: useSnapshot ? snapshotPath(command) : null, | ||
recompile: () { | ||
final pkg = entrypoint.packageGraph.packages[package]; | ||
// The recompile function will only be called when [package] exists. | ||
assert(pkg != null); | ||
return entrypoint.precompileExecutable( | ||
package, | ||
pkg.path('bin', '$command.dart'), | ||
); | ||
}, | ||
)); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import '../../descriptor.dart' as d; | ||
import '../../test_pub.dart'; | ||
|
||
void main() { | ||
test('the spawned application can read line-by-line from stdin', () async { | ||
await d.dir(appPath, [ | ||
d.appPubspec(), | ||
d.dir('bin', [ | ||
d.file('script.dart', """ | ||
import 'dart:io'; | ||
|
||
main() { | ||
print("started"); | ||
var line1 = stdin.readLineSync(); | ||
print("between"); | ||
var line2 = stdin.readLineSync(); | ||
print(line1); | ||
print(line2); | ||
} | ||
""") | ||
]) | ||
]).create(); | ||
|
||
await pubGet(); | ||
var pub = await pubRunV2(args: ['myapp:script']); | ||
|
||
await expectLater(pub.stdout, emits('started')); | ||
pub.stdin.writeln('first'); | ||
await expectLater(pub.stdout, emits('between')); | ||
pub.stdin.writeln('second'); | ||
expect(pub.stdout, emits('first')); | ||
expect(pub.stdout, emits('second')); | ||
await pub.shouldExit(0); | ||
}); | ||
|
||
test('the spawned application can read streamed from stdin', () async { | ||
await d.dir(appPath, [ | ||
d.appPubspec(), | ||
d.dir('bin', [ | ||
d.file('script.dart', """ | ||
import 'dart:io'; | ||
|
||
main() { | ||
print("started"); | ||
stdin.listen(stdout.add); | ||
} | ||
""") | ||
]) | ||
]).create(); | ||
|
||
await pubGet(); | ||
var pub = await pubRunV2(args: ['myapp:script']); | ||
|
||
await expectLater(pub.stdout, emits('started')); | ||
pub.stdin.writeln('first'); | ||
await expectLater(pub.stdout, emits('first')); | ||
pub.stdin.writeln('second'); | ||
await expectLater(pub.stdout, emits('second')); | ||
pub.stdin.writeln('third'); | ||
await expectLater(pub.stdout, emits('third')); | ||
await pub.stdin.close(); | ||
await pub.shouldExit(0); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import 'package:pub/src/exit_codes.dart' as exit_codes; | ||
|
||
import '../../descriptor.dart' as d; | ||
import '../../test_pub.dart'; | ||
|
||
void main() { | ||
test('Errors if the script is in a non-immediate dependency.', () async { | ||
await d.dir('foo', [ | ||
d.libPubspec('foo', '1.0.0'), | ||
d.dir('bin', [d.file('bar.dart', "main() => print('foobar');")]) | ||
]).create(); | ||
|
||
await d.dir('bar', [ | ||
d.libPubspec('bar', '1.0.0', deps: { | ||
'foo': {'path': '../foo'} | ||
}) | ||
]).create(); | ||
|
||
await d.dir(appPath, [ | ||
d.appPubspec({ | ||
'bar': {'path': '../bar'} | ||
}) | ||
]).create(); | ||
|
||
await pubGet(); | ||
|
||
var pub = await pubRunV2(args: ['foo:script']); | ||
expect(pub.stderr, emits('Package "foo" is not an immediate dependency.')); | ||
expect(pub.stderr, | ||
emits('Cannot run executables in transitive dependencies.')); | ||
await pub.shouldExit(exit_codes.DATA); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import 'package:pub/src/exit_codes.dart' as exit_codes; | ||
|
||
import '../../descriptor.dart' as d; | ||
import '../../test_pub.dart'; | ||
|
||
void main() { | ||
test( | ||
'Errors if the executable is in a subdirectory in a ' | ||
'dependency.', () async { | ||
await d.dir('foo', [d.libPubspec('foo', '1.0.0')]).create(); | ||
|
||
await d.dir(appPath, [ | ||
d.appPubspec({ | ||
'foo': {'path': '../foo'} | ||
}) | ||
]).create(); | ||
|
||
await runPub(args: ['run', 'foo:sub/dir'], error: ''' | ||
Cannot run an executable in a subdirectory of a dependency. | ||
|
||
Usage: pub run <executable> [args...] | ||
-h, --help Print this usage information. | ||
--[no-]enable-asserts Enable assert statements. | ||
|
||
Run "pub help" to see global options. | ||
See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation. | ||
''', exitCode: exit_codes.USAGE); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
// Windows doesn't support sending signals. | ||
@TestOn('!windows') | ||
import 'dart:io'; | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import '../../descriptor.dart' as d; | ||
import '../../test_pub.dart'; | ||
|
||
const _catchableSignals = [ | ||
ProcessSignal.sighup, | ||
ProcessSignal.sigterm, | ||
ProcessSignal.sigusr1, | ||
ProcessSignal.sigusr2, | ||
ProcessSignal.sigwinch, | ||
]; | ||
|
||
const SCRIPT = """ | ||
import 'dart:io'; | ||
|
||
main() { | ||
ProcessSignal.SIGHUP.watch().first.then(print); | ||
ProcessSignal.SIGTERM.watch().first.then(print); | ||
ProcessSignal.SIGUSR1.watch().first.then(print); | ||
ProcessSignal.SIGUSR2.watch().first.then(print); | ||
ProcessSignal.SIGWINCH.watch().first.then(print); | ||
|
||
print("ready"); | ||
} | ||
"""; | ||
|
||
void main() { | ||
test('forwards signals to the inner script', () async { | ||
await d.dir(appPath, [ | ||
d.appPubspec(), | ||
d.dir('bin', [d.file('script.dart', SCRIPT)]) | ||
]).create(); | ||
|
||
await pubGet(); | ||
var pub = await pubRunV2(args: ['myapp:script']); | ||
|
||
await expectLater(pub.stdout, emits('ready')); | ||
for (var signal in _catchableSignals) { | ||
pub.signal(signal); | ||
await expectLater(pub.stdout, emits(signal.toString())); | ||
} | ||
|
||
await pub.kill(); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import '../../descriptor.dart' as d; | ||
import '../../test_pub.dart'; | ||
|
||
void main() { | ||
test('loads package imports in a dependency', () async { | ||
await d.dir('foo', [ | ||
d.libPubspec('foo', '1.0.0'), | ||
d.dir('lib', [d.file('foo.dart', "final value = 'foobar';")]), | ||
d.dir('bin', [ | ||
d.file('bar.dart', ''' | ||
import "package:foo/foo.dart"; | ||
|
||
main() => print(value); | ||
''') | ||
]) | ||
]).create(); | ||
|
||
await d.dir(appPath, [ | ||
d.appPubspec({ | ||
'foo': {'path': '../foo'} | ||
}) | ||
]).create(); | ||
|
||
await pubGet(); | ||
var pub = await pubRunV2(args: ['foo:bar']); | ||
expect(pub.stdout, emits('foobar')); | ||
await pub.shouldExit(); | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file | ||
// for details. All rights reserved. Use of this source code is governed by a | ||
// BSD-style license that can be found in the LICENSE file. | ||
|
||
import 'package:test/test.dart'; | ||
|
||
import 'package:pub/src/exit_codes.dart' as exit_codes; | ||
|
||
import '../../descriptor.dart' as d; | ||
import '../../test_pub.dart'; | ||
|
||
void main() { | ||
test('Errors if the script is in an unknown package.', () async { | ||
await d.dir(appPath, [d.appPubspec()]).create(); | ||
|
||
await pubGet(); | ||
var pub = await pubRunV2(args: ['foo:script']); | ||
expect( | ||
pub.stderr, | ||
emits('Could not find package "foo". Did you forget to add a ' | ||
'dependency?')); | ||
await pub.shouldExit(exit_codes.DATA); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we find a more descriptive name? Maybe
--as-subprocess
or something?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or we could say
--dartdev-mode