Index: sdk/lib/_internal/pub/lib/src/command/build.dart |
diff --git a/sdk/lib/_internal/pub/lib/src/command/build.dart b/sdk/lib/_internal/pub/lib/src/command/build.dart |
index 6373935add5472496eeba5334d62fdcd3cfe6bf5..d57be726281aea24ff6d4ab48e08c8ac88b87c9f 100644 |
--- a/sdk/lib/_internal/pub/lib/src/command/build.dart |
+++ b/sdk/lib/_internal/pub/lib/src/command/build.dart |
@@ -33,10 +33,10 @@ class BuildCommand extends PubCommand { |
// TODO(nweiz): make this configurable. |
/// The path to the application's build output directory. |
- String get target => path.join(entrypoint.root.dir, 'build'); |
+ String get target => 'build'; |
/// The build mode. |
- BarbackMode get mode => new BarbackMode(commandOptions['mode']); |
+ BarbackMode get mode => new BarbackMode(commandOptions["mode"]); |
/// The number of files that have been built and written to disc so far. |
int builtFiles = 0; |
@@ -45,19 +45,26 @@ class BuildCommand extends PubCommand { |
final buildDirectories = new Set<String>(); |
BuildCommand() { |
- commandParser.addOption('mode', defaultsTo: BarbackMode.RELEASE.toString(), |
- help: 'Mode to run transformers in.'); |
+ commandParser.addOption("format", |
+ help: "How output should be displayed.", |
+ allowed: ["text", "json"], defaultsTo: "text"); |
- commandParser.addFlag('all', help: "Build all buildable directories.", |
+ commandParser.addOption("mode", defaultsTo: BarbackMode.RELEASE.toString(), |
+ help: "Mode to run transformers in."); |
+ |
+ commandParser.addFlag("all", help: "Build all buildable directories.", |
defaultsTo: false, negatable: false); |
} |
Future onRun() { |
- var exitCode = _parseBuildDirectories(); |
- if (exitCode != exit_codes.SUCCESS) return flushThenExit(exitCode); |
+ log.json.enabled = commandOptions["format"] == "json"; |
+ _parseBuildDirectories(); |
cleanDir(target); |
+ var errorsJson = []; |
+ var logJson = []; |
+ |
// Since this server will only be hit by the transformer loader and isn't |
// user-facing, just use an IPv4 address to avoid a weird bug on the |
// OS X buildbots. |
@@ -69,8 +76,23 @@ class BuildCommand extends PubCommand { |
// by getAllAssets(). |
environment.barback.errors.listen((error) { |
log.error(log.red("Build error:\n$error")); |
+ |
+ if (log.json.enabled) { |
+ // Wrap the error in a map in case we end up decorating it with more |
+ // properties later. |
+ errorsJson.add({ |
+ "error": error.toString() |
+ }); |
+ } |
}); |
+ // If we're using JSON output, the regular server logging is disabled. |
+ // Instead, we collect it here to include in the final JSON result. |
+ if (log.json.enabled) { |
+ environment.barback.log.listen( |
+ (entry) => logJson.add(_logEntryToJson(entry))); |
+ } |
+ |
return log.progress("Building ${entrypoint.root.name}", |
() => environment.barback.getAllAssets()).then((assets) { |
// Find all of the JS entrypoints we built. |
@@ -80,7 +102,15 @@ class BuildCommand extends PubCommand { |
return Future.wait(assets.map(_writeAsset)).then((_) { |
builtFiles += _copyBrowserJsFiles(dart2JSEntrypoints); |
- log.message("Built $builtFiles ${pluralize('file', builtFiles)}!"); |
+ log.message('Built $builtFiles ${pluralize('file', builtFiles)} ' |
+ 'to "$target".'); |
+ |
+ log.json.message({ |
+ "buildResult": "success", |
+ "outputDirectory": target, |
+ "numFiles": builtFiles, |
+ "log": logJson |
+ }); |
}); |
}); |
}).catchError((error) { |
@@ -89,6 +119,12 @@ class BuildCommand extends PubCommand { |
if (error is! BarbackException) throw error; |
log.error(log.red("Build failed.")); |
+ log.json.message({ |
+ "buildResult": "failure", |
+ "errors": errorsJson, |
+ "log": logJson |
+ }); |
+ |
return flushThenExit(exit_codes.DATA); |
}); |
} |
@@ -103,13 +139,12 @@ class BuildCommand extends PubCommand { |
/// |
/// Otherwise, all arguments should be the names of directories to include. |
/// |
- /// Returns the exit code of an error, or zero if it parsed correctly. |
- int _parseBuildDirectories() { |
+ /// Throws an exception if the arguments are invalid. |
+ void _parseBuildDirectories() { |
if (commandOptions["all"]) { |
if (commandOptions.rest.isNotEmpty) { |
- log.error( |
+ usageError( |
'Build directory names are not allowed if "--all" is passed.'); |
- return exit_codes.USAGE; |
} |
// Include every build directory that exists in the package. |
@@ -119,13 +154,12 @@ class BuildCommand extends PubCommand { |
if (allowed.isEmpty) { |
var buildDirs = toSentence(ordered(_allowedBuildDirectories.map( |
(name) => '"$name"'))); |
- log.error('There are no buildable directories.\n' |
+ dataError('There are no buildable directories.\n' |
'The supported directories are $buildDirs.'); |
- return exit_codes.DATA; |
} |
buildDirectories.addAll(allowed); |
- return exit_codes.SUCCESS; |
+ return; |
} |
buildDirectories.addAll(commandOptions.rest); |
@@ -144,9 +178,8 @@ class BuildCommand extends PubCommand { |
var names = toSentence(ordered(disallowed).map((name) => '"$name"')); |
var allowed = toSentence(ordered(_allowedBuildDirectories.map( |
(name) => '"$name"'))); |
- log.error('Unsupported build $dirs $names.\n' |
- 'The allowed directories are $allowed.'); |
- return exit_codes.USAGE; |
+ usageError('Unsupported build $dirs $names.\n' |
+ 'The allowed directories are $allowed.'); |
} |
// Make sure all of the build directories exist. |
@@ -154,15 +187,11 @@ class BuildCommand extends PubCommand { |
(dir) => !dirExists(path.join(entrypoint.root.dir, dir))); |
if (missing.length == 1) { |
- log.error('Directory "${missing.single}" does not exist.'); |
- return exit_codes.DATA; |
+ dataError('Directory "${missing.single}" does not exist.'); |
} else if (missing.isNotEmpty) { |
var names = toSentence(ordered(missing).map((name) => '"$name"')); |
- log.error('Directories $names do not exist.'); |
- return exit_codes.DATA; |
+ dataError('Directories $names do not exist.'); |
} |
- |
- return exit_codes.SUCCESS; |
} |
/// Writes [asset] to the appropriate build directory. |
@@ -277,7 +306,7 @@ class BuildCommand extends PubCommand { |
/// Ensures that the [name].js file is copied into [directory] in [target], |
/// under `packages/browser/`. |
void _addBrowserJs(String directory, String name) { |
- var jsPath = path.join( |
+ var jsPath = path.join(entrypoint.root.dir, |
target, directory, 'packages', 'browser', '$name.js'); |
ensureDir(path.dirname(jsPath)); |
@@ -286,4 +315,39 @@ class BuildCommand extends PubCommand { |
// directory. |
copyFile(path.join(entrypoint.packagesDir, 'browser', '$name.js'), jsPath); |
} |
+ |
+ /// Converts [entry] to a JSON object for use with JSON-formatted output. |
+ Map _logEntryToJson(LogEntry entry) { |
+ var data = { |
+ "level": entry.level.name, |
+ "transformer": { |
+ "name": entry.transform.transformer.toString(), |
+ "primaryInput": { |
+ "package": entry.transform.primaryId.package, |
+ "path": entry.transform.primaryId.path |
+ }, |
+ }, |
+ "assetId": { |
+ "package": entry.assetId.package, |
+ "path": entry.assetId.path |
+ }, |
+ "message": entry.message |
+ }; |
+ |
+ if (entry.span != null) { |
+ data["span"] = { |
+ "url": entry.span.sourceUrl, |
+ "start": { |
+ "line": entry.span.start.line, |
+ "column": entry.span.start.column |
+ }, |
+ "end": { |
+ "line": entry.span.end.line, |
+ "column": entry.span.end.column |
+ }, |
+ }; |
+ } |
+ |
+ return data; |
+ } |
} |