blob: 08e45936dcb2d2e7287c715553197648c4f76cff [file] [log] [blame]
// Copyright (c) 2018, 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:async';
import 'dart:io' show Directory;
import 'package:args/args.dart';
import 'package:args/command_runner.dart';
import 'package:build_daemon/client.dart';
import 'package:build_daemon/data/build_status.dart';
import 'package:build_daemon/data/build_target.dart';
import 'package:build_daemon/data/server_log.dart';
import 'package:collection/collection.dart' show IterableExtension;
import 'package:logging/logging.dart' as logging;
import '../daemon_client.dart';
import '../logging.dart';
import '../pubspec.dart';
import 'configuration.dart';
import 'shared.dart';
/// Command to execute pub run build_runner build.
class BuildCommand extends Command<int> {
@override
final argParser = ArgParser(usageLineLength: lineLength);
@override
final name = 'build';
@override
final description = 'Run builders to build a package.';
BuildCommand() {
addSharedArgs(argParser, outputDefault: 'web:build');
}
@override
Future<int> run() async {
final extraArgs = argResults?.rest ?? [];
final unsupported = extraArgs.where((arg) => !arg.startsWith('-')).toList();
if (unsupported.isNotEmpty) {
throw UsageException(
'Arguments were provided that are not supported: '
'"${unsupported.join(' ')}".',
argParser.usage);
}
final validExtraArgs =
extraArgs.where((arg) => arg.startsWith('-')).toList();
final configuration = Configuration.fromArgs(argResults);
configureLogWriter(configuration.verbose);
List<String> arguments;
try {
await validatePubspecLock(configuration);
arguments = buildRunnerArgs(configuration)..addAll(validExtraArgs);
} on PackageException catch (e) {
logWriter(logging.Level.SEVERE, 'Pubspec errors: ',
error: '${e.details}');
rethrow;
}
try {
logWriter(logging.Level.INFO, 'Connecting to the build daemon...');
final client = await connectClient(
Directory.current.path,
arguments,
(serverLog) {
logWriter(toLoggingLevel(serverLog.level), serverLog.message,
error: serverLog.error,
loggerName: serverLog.loggerName,
stackTrace: serverLog.stackTrace);
},
);
OutputLocation? outputLocation;
final outputInput = configuration.outputInput;
if (configuration.outputPath != null) {
outputLocation = OutputLocation((b) => b
..output = configuration.outputPath
..useSymlinks = false
..hoist = outputInput != null && outputInput.isNotEmpty);
}
client.registerBuildTarget(DefaultBuildTarget((b) => b
..target = configuration.outputInput
..outputLocation = outputLocation?.toBuilder()));
client.startBuild();
var exitCode = 0;
var gotBuildStart = false;
await for (final result in client.buildResults) {
final targetResult = result.results.firstWhereOrNull(
(buildResult) => buildResult.target == configuration.outputInput);
if (targetResult == null) continue;
// We ignore any builds that happen before we get a `started` event,
// because those could be stale (from some other client).
gotBuildStart =
gotBuildStart || targetResult.status == BuildStatus.started;
if (!gotBuildStart) continue;
// Shouldn't happen, but being a bit defensive here.
if (targetResult.status == BuildStatus.started) continue;
if (targetResult.status == BuildStatus.failed) {
exitCode = 1;
}
final error = targetResult.error ?? '';
if (error.isNotEmpty) {
logWriter(logging.Level.SEVERE, error);
}
break;
}
await client.close();
return exitCode;
} on OptionsSkew catch (_) {
logWriter(
logging.Level.SEVERE,
'Incompatible options with current running build daemon.\n\n'
'Please stop other WebDev instances running in this directory '
'before starting a new instance with these options.\n\n');
return 1;
}
}
}