diff --git a/.packages b/.packages index cd51491172b..b88c3ba761f 100644 --- a/.packages +++ b/.packages @@ -33,6 +33,7 @@ dart2js_tools:pkg/dart2js_tools/lib dart2native:pkg/dart2native/lib dart_internal:pkg/dart_internal/lib dart_style:third_party/pkg_tested/dart_style/lib +dartdev:pkg/dartdev/lib dartdoc:third_party/pkg/dartdoc/lib dartfix:pkg/dartfix/lib dev_compiler:pkg/dev_compiler/lib diff --git a/pkg/dartdev/.gitignore b/pkg/dartdev/.gitignore new file mode 100644 index 00000000000..96cf44bc4f2 --- /dev/null +++ b/pkg/dartdev/.gitignore @@ -0,0 +1,10 @@ +.buildlog +.DS_Store +.idea +.project +.settings/ +build/ +pubspec.lock + +# Directory created by dartdoc +doc/api/ diff --git a/pkg/dartdev/CHANGELOG.md b/pkg/dartdev/CHANGELOG.md new file mode 100644 index 00000000000..c93158c95a2 --- /dev/null +++ b/pkg/dartdev/CHANGELOG.md @@ -0,0 +1,2 @@ +## 0.0.1 +* Initial commit adding `pkg/dartdev/` to the Dart SDK with initial package files \ No newline at end of file diff --git a/pkg/dartdev/LICENSE b/pkg/dartdev/LICENSE new file mode 100644 index 00000000000..18daf2b5bd9 --- /dev/null +++ b/pkg/dartdev/LICENSE @@ -0,0 +1,26 @@ +Copyright 2020, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pkg/dartdev/README.md b/pkg/dartdev/README.md new file mode 100644 index 00000000000..c1aeeeb17b5 --- /dev/null +++ b/pkg/dartdev/README.md @@ -0,0 +1,7 @@ +# dartdev + +A command-line utility for Dart development. + +## Docs + +This tool is currently under active development. \ No newline at end of file diff --git a/pkg/dartdev/analysis_options.yaml b/pkg/dartdev/analysis_options.yaml new file mode 100644 index 00000000000..84a5e26f95d --- /dev/null +++ b/pkg/dartdev/analysis_options.yaml @@ -0,0 +1 @@ +include: package:pedantic/analysis_options.1.8.0.yaml diff --git a/pkg/dartdev/bin/dartdev.dart b/pkg/dartdev/bin/dartdev.dart new file mode 100644 index 00000000000..db8472912ae --- /dev/null +++ b/pkg/dartdev/bin/dartdev.dart @@ -0,0 +1,25 @@ +// 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 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:dartdev/dartdev.dart'; + +/// The entry point for dartdev. +main(List args) async { + final runner = DartdevRunner(); + try { + dynamic result = await runner.run(args); + exit(result is int ? result : 0); + } catch (e) { + if (e is UsageException) { + stderr.writeln('$e'); + exit(64); + } else { + stderr.writeln('$e'); + exit(1); + } + } +} diff --git a/pkg/dartdev/lib/dartdev.dart b/pkg/dartdev/lib/dartdev.dart new file mode 100644 index 00000000000..84227995870 --- /dev/null +++ b/pkg/dartdev/lib/dartdev.dart @@ -0,0 +1,32 @@ +// 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:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:cli_util/cli_logging.dart'; + +import 'src/commands/format.dart'; +import 'src/core.dart'; + +class DartdevRunner extends CommandRunner { + static const String dartdevDescription = + 'A command-line utility for Dart development'; + + DartdevRunner() : super('dartdev', '$dartdevDescription.') { + argParser.addFlag('verbose', + abbr: 'v', negatable: false, help: 'Show verbose output.'); + + // The list of currently supported commands: + addCommand(FormatCommand()); + } + + @override + Future runCommand(ArgResults results) async { + isVerbose = results['verbose']; + + log = isVerbose ? Logger.verbose(ansi: ansi) : Logger.standard(ansi: ansi); + + return await super.runCommand(results); + } +} diff --git a/pkg/dartdev/lib/src/commands/format.dart b/pkg/dartdev/lib/src/commands/format.dart new file mode 100644 index 00000000000..97c2d7dd63b --- /dev/null +++ b/pkg/dartdev/lib/src/commands/format.dart @@ -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 '../core.dart'; +import '../sdk.dart'; + +class FormatCommand extends DartdevCommand { + FormatCommand() : super('format', 'Format one or more Dart files.') { + // TODO(jwren) add all options and flags + } + + @override + run() async { + // TODO(jwren) implement verbose in dart_style + // dartfmt doesn't have '-v' or '--verbose', so remove from the argument list + var args = List.from(argResults.arguments) + ..remove('-v') + ..remove('--verbose'); + var process = await startProcess(sdk.dartfmt, args); + routeToStdout(process); + return process.exitCode; + } +} diff --git a/pkg/dartdev/lib/src/core.dart b/pkg/dartdev/lib/src/core.dart new file mode 100644 index 00000000000..b937f198754 --- /dev/null +++ b/pkg/dartdev/lib/src/core.dart @@ -0,0 +1,82 @@ +// 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 'dart:convert'; +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:cli_util/cli_logging.dart'; + +Ansi ansi = Ansi(Ansi.terminalSupportsAnsi); +Logger log; +bool isVerbose = false; + +abstract class DartdevCommand extends Command { + final String _name; + final String _description; + + DartdevCommand(this._name, this._description); + + @override + String get name => _name; + + @override + String get description => _description; +} + +Future startProcess( + String executable, + List arguments, { + String cwd, +}) { + log.trace('$executable ${arguments.join(' ')}'); + return Process.start(executable, arguments, workingDirectory: cwd); +} + +void routeToStdout( + Process process, { + bool logToTrace = false, + void listener(String str), +}) { + if (isVerbose) { + _streamLineTransform(process.stdout, (String line) { + logToTrace ? log.trace(line.trimRight()) : log.stdout(line.trimRight()); + if (listener != null) listener(line); + }); + _streamLineTransform(process.stderr, (String line) { + log.stderr(line.trimRight()); + if (listener != null) listener(line); + }); + } else { + _streamLineTransform(process.stdout, (String line) { + logToTrace ? log.trace(line.trimRight()) : log.stdout(line.trimRight()); + if (listener != null) listener(line); + }); + + _streamLineTransform(process.stderr, (String line) { + log.stderr(line.trimRight()); + if (listener != null) listener(line); + }); + } +} + +void routeToStdoutStreaming(Process process) { + if (isVerbose) { + _streamLineTransform( + process.stdout, (line) => log.stdout(line.trimRight())); + _streamLineTransform( + process.stderr, (line) => log.stderr(line.trimRight())); + } else { + process.stdout.listen((List data) => stdout.add(data)); + _streamLineTransform( + process.stderr, (line) => log.stderr(line.trimRight())); + } +} + +void _streamLineTransform(Stream> stream, handler(String line)) { + stream + .transform(utf8.decoder) + .transform(const LineSplitter()) + .listen(handler); +} diff --git a/pkg/dartdev/lib/src/sdk.dart b/pkg/dartdev/lib/src/sdk.dart new file mode 100644 index 00000000000..73afe8d3284 --- /dev/null +++ b/pkg/dartdev/lib/src/sdk.dart @@ -0,0 +1,31 @@ +// 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 'dart:io'; + +import 'package:cli_util/cli_util.dart'; +import 'package:path/path.dart' as path; + +final Sdk sdk = Sdk(); + +/// A utility class for finding and referencing paths within the Dart SDK. +class Sdk { + final String dir; + + Sdk() : dir = getSdkPath(); + + String get dart => path.join(dir, 'bin', _exeName('dart')); + + String get dartanalyzer => path.join(dir, 'bin', _binName('dartanalyzer')); + + String get dartfmt => path.join(dir, 'bin', _binName('dartfmt')); + + String get pub => path.join(dir, 'bin', _binName('pub')); + + static String _binName(String base) => + Platform.isWindows ? '$base.bat' : base; + + static String _exeName(String base) => + Platform.isWindows ? '$base.exe' : base; +} diff --git a/pkg/dartdev/lib/src/utils.dart b/pkg/dartdev/lib/src/utils.dart new file mode 100644 index 00000000000..0918d816696 --- /dev/null +++ b/pkg/dartdev/lib/src/utils.dart @@ -0,0 +1,32 @@ +// 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 'dart:io'; + +import 'package:intl/intl.dart'; +import 'package:path/path.dart' as path; + +/// The directory used to store per-user settings for Dart tooling. +Directory getDartPrefsDirectory() { + return Directory(path.join(getUserHomeDir(), '.dart')); +} + +/// Return the user's home directory. +String getUserHomeDir() { + String envKey = Platform.operatingSystem == 'windows' ? 'APPDATA' : 'HOME'; + String value = Platform.environment[envKey]; + return value == null ? '.' : value; +} + +/// A typedef to represent a function taking no arguments and with no return +/// value. +typedef void VoidFunction(); + +final NumberFormat _numberFormat = NumberFormat.decimalPattern(); + +/// Convert the given number to a string using the current locale. +String formatNumber(int i) => _numberFormat.format(i); + +/// Emit the given word with the correct pluralization. +String pluralize(String word, int count) => count == 1 ? word : '${word}s'; diff --git a/pkg/dartdev/pubspec.yaml b/pkg/dartdev/pubspec.yaml new file mode 100644 index 00000000000..65aaee20d59 --- /dev/null +++ b/pkg/dartdev/pubspec.yaml @@ -0,0 +1,18 @@ +name: dartdev + +# This package is not intended to be published +publish_to: none + +environment: + sdk: '>=2.6.0 <3.0.0' +dependencies: + args: ^1.5.2 + cli_util: ^0.1.0 + intl: ^0.16.0 + path: ^1.6.2 + watcher: ^0.9.7+13 + yaml: ^2.2.0 + +dev_dependencies: + test: ^1.0.0 + pedantic: ^1.8.0 \ No newline at end of file diff --git a/pkg/dartdev/test/command_test.dart b/pkg/dartdev/test/command_test.dart new file mode 100644 index 00000000000..d49fec4dcf0 --- /dev/null +++ b/pkg/dartdev/test/command_test.dart @@ -0,0 +1,66 @@ +// 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 'dart:io'; + +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; + +// TODO(jwren) move the following test utilities to a separate file + +// Files that end in _test.dart must have a main method to satisfy the dart2js +// bots +void main() { + test('empty test for dart2js', () { + assert(true == true); + }); +} + +TestProject project({String mainSrc}) => TestProject(mainSrc: mainSrc); + +class TestProject { + Directory dir; + + static String get defaultProjectName => 'dartdev_temp'; + + String get name => defaultProjectName; + + TestProject({String mainSrc}) { + dir = Directory.systemTemp.createTempSync('dartdev'); + if (mainSrc != null) { + file('lib/main.dart', mainSrc); + } + file('pubspec.yaml', 'name: $name\ndev_dependencies:\n test: any\n'); + } + + void file(String name, String contents) { + var file = File(path.join(dir.path, name)); + file.parent.createSync(); + file.writeAsStringSync(contents); + } + + void dispose() { + dir.deleteSync(recursive: true); + } + + ProcessResult run(String command, [List args]) { + var arguments = [ + path.absolute(path.join(Directory.current.path, 'bin', 'dartdev.dart')), + command + ]; + if (args != null && args.isNotEmpty) { + arguments.addAll(args); + } + return Process.runSync( + Platform.resolvedExecutable, + arguments, + workingDirectory: dir.path, + ); + } + + File findFile(String name) { + var file = File(path.join(dir.path, name)); + return file.existsSync() ? file : null; + } +} diff --git a/pkg/dartdev/test/utils_test.dart b/pkg/dartdev/test/utils_test.dart new file mode 100644 index 00000000000..5850905ebee --- /dev/null +++ b/pkg/dartdev/test/utils_test.dart @@ -0,0 +1,22 @@ +// 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:dartdev/src/utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('pluralize', () { + test('zero', () { + expect(pluralize('cat', 0), 'cats'); + }); + + test('one', () { + expect(pluralize('cat', 1), 'cat'); + }); + + test('many', () { + expect(pluralize('cat', 2), 'cats'); + }); + }); +} diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index 1d2a195af5b..09243e8302a 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json @@ -2403,6 +2403,11 @@ "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer", "arguments": ["--fatal-warnings", "pkg/compiler"] }, + { + "name": "analyze pkg/dartdev", + "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer", + "arguments": ["--fatal-warnings", "pkg/dartdev"] + }, { "name": "analyze pkg/dart2js_tools", "script": "out/ReleaseX64/dart-sdk/bin/dartanalyzer",