From f7d07abacffdbcd1d0e1b9f84cb87c49bf58d8f7 Mon Sep 17 00:00:00 2001 From: Nicholas Shahan Date: Wed, 17 Jan 2018 14:34:56 -0800 Subject: [PATCH 1/2] Try getting dependencies if needed to run `pub run build_runner` --- webdev/bin/webdev.dart | 6 ++ webdev/example/pubspec.yaml | 18 ++++- webdev/lib/src/command/build_command.dart | 6 +- .../command/build_runner_command_base.dart | 80 ++++++++++++++++++- webdev/lib/src/command/serve_command.dart | 6 +- webdev/pubspec.yaml | 17 +++- 6 files changed, 115 insertions(+), 18 deletions(-) diff --git a/webdev/bin/webdev.dart b/webdev/bin/webdev.dart index 6c28c16fa..484a4b412 100644 --- a/webdev/bin/webdev.dart +++ b/webdev/bin/webdev.dart @@ -1,7 +1,13 @@ import 'dart:async'; import 'package:webdev/webdev.dart'; +import 'package:build_runner/src/logging/std_io_logging.dart'; +import 'package:logging/logging.dart'; Future main(List args) async { + // TODO(nshahan) See if build_runner wants to expose their listener. + // Using log listener from build_runner to match output format. + final logListener = Logger.root.onRecord.listen(stdIOLogListener); await webdevCommandRunner().run(args); + await logListener?.cancel(); } diff --git a/webdev/example/pubspec.yaml b/webdev/example/pubspec.yaml index 5e4f3719d..69ec14596 100644 --- a/webdev/example/pubspec.yaml +++ b/webdev/example/pubspec.yaml @@ -14,12 +14,22 @@ dev_dependencies: path: ../ ############################################################################## # Temporary until build_runner exposes a function to generate it's script. - build_runner: - git: - url: https://github.com/dart-lang/build.git - path: build_runner + build_runner: ^0.7.5+1 ############################################################################## dependency_overrides: # Necessary with angular: ^5.0.0-alpha+1 dependency. analyzer: ^0.31.0-alpha.1 + ############################################################################## + # Temporary until updated packages are published. + build: + git: + url: https://github.com/dart-lang/build.git + ref: ffbba7fc74a1262c958a63fb31c146995f0a3a99 + path: build + build_runner: + git: + url: https://github.com/dart-lang/build.git + ref: ffbba7fc74a1262c958a63fb31c146995f0a3a99 + path: build_runner + ############################################################################## diff --git a/webdev/lib/src/command/build_command.dart b/webdev/lib/src/command/build_command.dart index ac1b646ca..7f71059e7 100644 --- a/webdev/lib/src/command/build_command.dart +++ b/webdev/lib/src/command/build_command.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:isolate'; import 'build_runner_command_base.dart'; @@ -15,9 +14,6 @@ class BuildCommand extends BuildRunnerCommandBase { Future run() async { final arguments = ['build']; arguments.addAll(argResults.arguments); - var exitPort = new ReceivePort(); - await Isolate.spawnUri(await buildRunnerScript, arguments, null, - onExit: exitPort.sendPort, automaticPackageResolution: true); - await exitPort.first; + await runBuildRunner(arguments); } } diff --git a/webdev/lib/src/command/build_runner_command_base.dart b/webdev/lib/src/command/build_runner_command_base.dart index 88f2fc0eb..326ce8ec6 100644 --- a/webdev/lib/src/command/build_runner_command_base.dart +++ b/webdev/lib/src/command/build_runner_command_base.dart @@ -1,7 +1,12 @@ import 'dart:async'; import 'dart:io'; +import 'dart:isolate'; import 'package:args/command_runner.dart'; +import 'package:build_runner/src/logging/logging.dart'; +import 'package:logging/logging.dart'; + +var _logger = new Logger('webdev'); /// Extend to get a command with the arguments common to all build_runner /// commands. @@ -19,13 +24,82 @@ abstract class BuildRunnerCommandBase extends Command { help: 'Enables verbose logging.'); } - Future get buildRunnerScript async { + /// Runs `pub run build_runner` with [arguments] as an isolate. + /// + /// Attempts to run `pub get` and `pub upgrade` if needed and will retry + /// running build_runner after. + Future runBuildRunner(List arguments) async { + var exitPort = new ReceivePort(); + try { + await Isolate.spawnUri(await _buildRunnerScript, arguments, null, + onExit: exitPort.sendPort, automaticPackageResolution: true); + await exitPort.first; + } on _PubDependenciesError catch (_) { + exitPort.close(); + } + } + + Future get _buildRunnerScript async { + return await logTimedAsync( + _logger, 'Generating build script', _runBuildRunnerGenerate); + } + + /// Generates a build script and returns it's location. + /// + /// Will attempt to run `pub get` or `pub update` if needed. + Future _runBuildRunnerGenerate() async { // TODO(nshahan) build_runner will expose this as a function call that will // be imported to avoid running a binary in a transitive dependency with // pub run. final executable = 'pub'; final arguments = ['run', 'build_runner', 'generate-build-script']; - final results = await Process.run(executable, arguments); - return new Uri.file(results.stdout.toString().trim()); + var result = await Process.run(executable, arguments); + + if (_needPubGet(result)) { + _logger.warning('Generating build script failed.'); + result = await logTimedAsync(_logger, 'Getting dependencies', _runPubGet); + + // Try running build_runner again. + result = await Process.run(executable, arguments); + + if (_needPubGet(result)) { + _logger.warning('Getting dependencies failed.'); + result = await logTimedAsync( + _logger, 'Upgrading dependencies', _runPubUpgrade); + + if (result.exitCode != 0) { + _logger.severe( + 'Could not retrieve dependencies. webdev must be able to run `pub' + ' get`.' + '\nPlease fix the dependencies and retry.' + '\n\n${result.stderr}'); + throw new _PubDependenciesError(); + } + + // Try running build_runner one last time. + result = await Process.run(executable, arguments); + } + } + + return new Uri.file(result.stdout.toString().trim()); + } + + /// Checks [result] for errors to see if a `pub get` is needed. + bool _needPubGet(ProcessResult result) => + result.exitCode != 0 && + result.stderr.toString().contains('please run "pub get"'); + + /// Runs `pub get` as a Process. + Future _runPubGet() async { + return await Process.run('pub', ['get', '--no-precompile']); + } + + /// Runs `pub upgrade` as a Process. + Future _runPubUpgrade() async { + return await Process.run('pub', ['upgrade', '--no-precompile']); } } + +/// Private error class used to report back an error when attempting to run +/// `pub get` and `pub upgrade`. +class _PubDependenciesError extends Error {} diff --git a/webdev/lib/src/command/serve_command.dart b/webdev/lib/src/command/serve_command.dart index a8ba022b7..5e74d21ec 100644 --- a/webdev/lib/src/command/serve_command.dart +++ b/webdev/lib/src/command/serve_command.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:isolate'; import 'build_runner_command_base.dart'; @@ -24,9 +23,6 @@ class ServeCommand extends BuildRunnerCommandBase { Future run() async { final arguments = ['serve']; arguments.addAll(argResults.arguments); - var exitPort = new ReceivePort(); - await Isolate.spawnUri(await buildRunnerScript, arguments, null, - onExit: exitPort.sendPort, automaticPackageResolution: true); - await exitPort.first; + await runBuildRunner(arguments); } } diff --git a/webdev/pubspec.yaml b/webdev/pubspec.yaml index 5796813f4..ed0cc0e87 100644 --- a/webdev/pubspec.yaml +++ b/webdev/pubspec.yaml @@ -12,7 +12,22 @@ dependencies: git: url: https://github.com/dart-lang/build.git path: build_runner - build_web_compilers: ^0.1.1 + build_web_compilers: ^0.2.0 dev_dependencies: test: "^0.12.0" + +################################################################################ +# Temporary until updated packages are published. +dependency_overrides: + build: + git: + url: https://github.com/dart-lang/build.git + ref: ffbba7fc74a1262c958a63fb31c146995f0a3a99 + path: build + build_runner: + git: + url: https://github.com/dart-lang/build.git + ref: ffbba7fc74a1262c958a63fb31c146995f0a3a99 + path: build_runner +################################################################################ From 90d9069f837c59f492dc0bd6ddb904cc7eb04ae0 Mon Sep 17 00:00:00 2001 From: Nicholas Shahan Date: Wed, 17 Jan 2018 16:31:16 -0800 Subject: [PATCH 2/2] Allow errors from the isolate to print Based on https://github.com/dart-lang/build/commit/400b58d --- webdev/lib/src/command/build_runner_command_base.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/webdev/lib/src/command/build_runner_command_base.dart b/webdev/lib/src/command/build_runner_command_base.dart index 326ce8ec6..cd63e4476 100644 --- a/webdev/lib/src/command/build_runner_command_base.dart +++ b/webdev/lib/src/command/build_runner_command_base.dart @@ -30,12 +30,19 @@ abstract class BuildRunnerCommandBase extends Command { /// running build_runner after. Future runBuildRunner(List arguments) async { var exitPort = new ReceivePort(); + var errorPort = new ReceivePort(); + var errorListener = + errorPort.listen((e) => stderr.writeAll(e as List, '\n')); try { await Isolate.spawnUri(await _buildRunnerScript, arguments, null, - onExit: exitPort.sendPort, automaticPackageResolution: true); + onExit: exitPort.sendPort, + onError: errorPort.sendPort, + automaticPackageResolution: true); await exitPort.first; } on _PubDependenciesError catch (_) { exitPort.close(); + } finally { + await errorListener.cancel(); } }