blob: 0cac09980c94540af88f56b3d2c0d83fd342b659 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. 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 'package:file/file.dart';
import 'package:platform/platform.dart';
import 'common/core.dart';
import 'common/package_looping_command.dart';
import 'common/plugin_utils.dart';
import 'common/process_runner.dart';
/// Key for APK.
const String _platformFlagApk = 'apk';
const int _exitNoPlatformFlags = 2;
/// A command to build the example applications for packages.
class BuildExamplesCommand extends PackageLoopingCommand {
/// Creates an instance of the build command.
BuildExamplesCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
Platform platform = const LocalPlatform(),
}) : super(packagesDir, processRunner: processRunner, platform: platform) {
argParser.addFlag(kPlatformLinux);
argParser.addFlag(kPlatformMacos);
argParser.addFlag(kPlatformWeb);
argParser.addFlag(kPlatformWindows);
argParser.addFlag(kPlatformIos);
argParser.addFlag(_platformFlagApk);
argParser.addOption(
kEnableExperiment,
defaultsTo: '',
help: 'Enables the given Dart SDK experiments.',
);
}
// Maps the switch this command uses to identify a platform to information
// about it.
static final Map<String, _PlatformDetails> _platforms =
<String, _PlatformDetails>{
_platformFlagApk: const _PlatformDetails(
'Android',
pluginPlatform: kPlatformAndroid,
flutterBuildType: 'apk',
),
kPlatformIos: const _PlatformDetails(
'iOS',
pluginPlatform: kPlatformIos,
flutterBuildType: 'ios',
extraBuildFlags: <String>['--no-codesign'],
),
kPlatformLinux: const _PlatformDetails(
'Linux',
pluginPlatform: kPlatformLinux,
flutterBuildType: 'linux',
),
kPlatformMacos: const _PlatformDetails(
'macOS',
pluginPlatform: kPlatformMacos,
flutterBuildType: 'macos',
),
kPlatformWeb: const _PlatformDetails(
'web',
pluginPlatform: kPlatformWeb,
flutterBuildType: 'web',
),
kPlatformWindows: const _PlatformDetails(
'Windows',
pluginPlatform: kPlatformWindows,
flutterBuildType: 'windows',
),
};
@override
final String name = 'build-examples';
@override
final String description =
'Builds all example apps (IPA for iOS and APK for Android).\n\n'
'This command requires "flutter" to be in your path.';
@override
Future<void> initializeRun() async {
final List<String> platformFlags = _platforms.keys.toList();
platformFlags.sort();
if (!platformFlags.any((String platform) => getBoolArg(platform))) {
printError(
'None of ${platformFlags.map((String platform) => '--$platform').join(', ')} '
'were specified. At least one platform must be provided.');
throw ToolExit(_exitNoPlatformFlags);
}
}
@override
Future<PackageResult> runForPackage(Directory package) async {
final List<String> errors = <String>[];
final Iterable<_PlatformDetails> requestedPlatforms = _platforms.entries
.where(
(MapEntry<String, _PlatformDetails> entry) => getBoolArg(entry.key))
.map((MapEntry<String, _PlatformDetails> entry) => entry.value);
final Set<_PlatformDetails> buildPlatforms = <_PlatformDetails>{};
final Set<_PlatformDetails> unsupportedPlatforms = <_PlatformDetails>{};
for (final _PlatformDetails platform in requestedPlatforms) {
if (pluginSupportsPlatform(platform.pluginPlatform, package)) {
buildPlatforms.add(platform);
} else {
unsupportedPlatforms.add(platform);
}
}
if (buildPlatforms.isEmpty) {
final String unsupported = requestedPlatforms.length == 1
? '${requestedPlatforms.first.label} is not supported'
: 'None of [${requestedPlatforms.map((_PlatformDetails p) => p.label).join(',')}] are supported';
return PackageResult.skip('$unsupported by this plugin');
}
print('Building for: '
'${buildPlatforms.map((_PlatformDetails platform) => platform.label).join(',')}');
if (unsupportedPlatforms.isNotEmpty) {
print('Skipping unsupported platform(s): '
'${unsupportedPlatforms.map((_PlatformDetails platform) => platform.label).join(',')}');
}
print('');
for (final Directory example in getExamplesForPlugin(package)) {
final String packageName =
getRelativePosixPath(example, from: packagesDir);
for (final _PlatformDetails platform in buildPlatforms) {
String buildPlatform = platform.label;
if (platform.label.toLowerCase() != platform.flutterBuildType) {
buildPlatform += ' (${platform.flutterBuildType})';
}
print('\nBUILDING $packageName for $buildPlatform');
if (!await _buildExample(example, platform.flutterBuildType,
extraBuildFlags: platform.extraBuildFlags)) {
errors.add('$packageName (${platform.label})');
}
}
}
return errors.isEmpty
? PackageResult.success()
: PackageResult.fail(errors);
}
Future<bool> _buildExample(
Directory example,
String flutterBuildType, {
List<String> extraBuildFlags = const <String>[],
}) async {
final String enableExperiment = getStringArg(kEnableExperiment);
final int exitCode = await processRunner.runAndStream(
flutterCommand,
<String>[
'build',
flutterBuildType,
...extraBuildFlags,
if (enableExperiment.isNotEmpty)
'--enable-experiment=$enableExperiment',
],
workingDir: example,
);
return exitCode == 0;
}
}
/// A collection of information related to a specific platform.
class _PlatformDetails {
const _PlatformDetails(
this.label, {
required this.pluginPlatform,
required this.flutterBuildType,
this.extraBuildFlags = const <String>[],
});
/// The name to use in output.
final String label;
/// The key in a pubspec's platform: entry.
final String pluginPlatform;
/// The `flutter build` build type.
final String flutterBuildType;
/// Any extra flags to pass to `flutter build`.
final List<String> extraBuildFlags;
}