[flutter_plugin_tools] Migrate build-examples to new base command (#4087)
Switches build-examples to the new base command that handles the boilerplate of looping over target packages.
While modifying the command, also does some minor cleanup:
- Extracts a helper to reduce duplicated details of calling `flutter build`
- Switches the flag for iOS to `--ios` rather than `--ipa` since `ios` is what is actually passed to the build command
- iOS no longer defaults to on, so that it behaves like all the other platform flags
- Passing no platform flags is now an error rather than a silent pass, to ensure that we never accidentally have CI doing a no-op run without noticing.
- Rewords the logging slightly for the versions where the label for what is being built is a platform, not an artifact (which is now everything but Android).
Part of flutter/flutter#83413
diff --git a/.cirrus.yml b/.cirrus.yml
index 45f9442..4ad5e8e 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -220,7 +220,7 @@
- xcrun simctl list
- xcrun simctl create Flutter-iPhone com.apple.CoreSimulator.SimDeviceType.iPhone-11 com.apple.CoreSimulator.SimRuntime.iOS-14-5 | xargs xcrun simctl boot
build_script:
- - ./script/tool_runner.sh build-examples --ipa
+ - ./script/tool_runner.sh build-examples --ios
xctest_script:
- ./script/tool_runner.sh xctest --ios --ios-destination "platform=iOS Simulator,name=iPhone 11,OS=latest"
drive_script:
@@ -248,7 +248,7 @@
PATH: $PATH:/usr/local/bin
build_script:
- flutter config --enable-macos-desktop
- - ./script/tool_runner.sh build-examples --macos --no-ipa
+ - ./script/tool_runner.sh build-examples --macos
xctest_script:
- ./script/tool_runner.sh xctest --macos --exclude $PLUGINS_TO_EXCLUDE_MACOS_XCTESTS
drive_script:
diff --git a/script/tool/CHANGELOG.md b/script/tool/CHANGELOG.md
index 94514e3..a2716cb 100644
--- a/script/tool/CHANGELOG.md
+++ b/script/tool/CHANGELOG.md
@@ -5,6 +5,8 @@
compatibility.
- `xctest` now supports running macOS tests in addition to iOS
- **Breaking change**: it now requires an `--ios` and/or `--macos` flag.
+- **Breaking change**: `build-examples` for iOS now uses `--ios` rather than
+ `--ipa`.
- The tooling now runs in strong null-safe mode.
- `publish plugins` check against pub.dev to determine if a release should happen.
- Modified the output format of many commands
diff --git a/script/tool/lib/src/build_examples_command.dart b/script/tool/lib/src/build_examples_command.dart
index aff5ecb..c8280f4 100644
--- a/script/tool/lib/src/build_examples_command.dart
+++ b/script/tool/lib/src/build_examples_command.dart
@@ -3,36 +3,34 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:io' as io;
import 'package:file/file.dart';
import 'package:path/path.dart' as p;
import 'package:platform/platform.dart';
import 'common/core.dart';
-import 'common/plugin_command.dart';
+import 'common/package_looping_command.dart';
import 'common/plugin_utils.dart';
import 'common/process_runner.dart';
-/// Key for IPA.
-const String kIpa = 'ipa';
-
/// Key for APK.
-const String kApk = 'apk';
+const String _platformFlagApk = 'apk';
+
+const int _exitNoPlatformFlags = 2;
/// A command to build the example applications for packages.
-class BuildExamplesCommand extends PluginCommand {
+class BuildExamplesCommand extends PackageLoopingCommand {
/// Creates an instance of the build command.
BuildExamplesCommand(
Directory packagesDir, {
ProcessRunner processRunner = const ProcessRunner(),
}) : super(packagesDir, processRunner: processRunner) {
- argParser.addFlag(kPlatformLinux, defaultsTo: false);
- argParser.addFlag(kPlatformMacos, defaultsTo: false);
- argParser.addFlag(kPlatformWeb, defaultsTo: false);
- argParser.addFlag(kPlatformWindows, defaultsTo: false);
- argParser.addFlag(kIpa, defaultsTo: io.Platform.isMacOS);
- argParser.addFlag(kApk);
+ argParser.addFlag(kPlatformLinux);
+ argParser.addFlag(kPlatformMacos);
+ argParser.addFlag(kPlatformWeb);
+ argParser.addFlag(kPlatformWindows);
+ argParser.addFlag(kPlatformIos);
+ argParser.addFlag(_platformFlagApk);
argParser.addOption(
kEnableExperiment,
defaultsTo: '',
@@ -49,164 +47,125 @@
'This command requires "flutter" to be in your path.';
@override
- Future<void> run() async {
+ Future<void> initializeRun() async {
final List<String> platformSwitches = <String>[
- kApk,
- kIpa,
+ _platformFlagApk,
+ kPlatformIos,
kPlatformLinux,
kPlatformMacos,
kPlatformWeb,
kPlatformWindows,
];
if (!platformSwitches.any((String platform) => getBoolArg(platform))) {
- print(
+ printError(
'None of ${platformSwitches.map((String platform) => '--$platform').join(', ')} '
- 'were specified, so not building anything.');
- return;
+ 'were specified. At least one platform must be provided.');
+ throw ToolExit(_exitNoPlatformFlags);
}
+ }
+
+ @override
+ Future<List<String>> runForPackage(Directory package) async {
+ final List<String> errors = <String>[];
+
+ for (final Directory example in getExamplesForPlugin(package)) {
+ final String packageName =
+ p.relative(example.path, from: packagesDir.path);
+
+ if (getBoolArg(kPlatformLinux)) {
+ print('\nBUILDING $packageName for Linux');
+ if (isLinuxPlugin(package)) {
+ if (!await _buildExample(example, kPlatformLinux)) {
+ errors.add('$packageName (Linux)');
+ }
+ } else {
+ printSkip('Linux is not supported by this plugin');
+ }
+ }
+
+ if (getBoolArg(kPlatformMacos)) {
+ print('\nBUILDING $packageName for macOS');
+ if (isMacOsPlugin(package)) {
+ if (!await _buildExample(example, kPlatformMacos)) {
+ errors.add('$packageName (macOS)');
+ }
+ } else {
+ printSkip('macOS is not supported by this plugin');
+ }
+ }
+
+ if (getBoolArg(kPlatformWeb)) {
+ print('\nBUILDING $packageName for web');
+ if (isWebPlugin(package)) {
+ if (!await _buildExample(example, kPlatformWeb)) {
+ errors.add('$packageName (web)');
+ }
+ } else {
+ printSkip('Web is not supported by this plugin');
+ }
+ }
+
+ if (getBoolArg(kPlatformWindows)) {
+ print('\nBUILDING $packageName for Windows');
+ if (isWindowsPlugin(package)) {
+ if (!await _buildExample(example, kPlatformWindows)) {
+ errors.add('$packageName (Windows)');
+ }
+ } else {
+ printSkip('Windows is not supported by this plugin');
+ }
+ }
+
+ if (getBoolArg(kPlatformIos)) {
+ print('\nBUILDING $packageName for iOS');
+ if (isIosPlugin(package)) {
+ if (!await _buildExample(
+ example,
+ kPlatformIos,
+ extraBuildFlags: <String>['--no-codesign'],
+ )) {
+ errors.add('$packageName (iOS)');
+ }
+ } else {
+ printSkip('iOS is not supported by this plugin');
+ }
+ }
+
+ if (getBoolArg(_platformFlagApk)) {
+ print('\nBUILDING APK for $packageName');
+ if (isAndroidPlugin(package)) {
+ if (!await _buildExample(example, _platformFlagApk)) {
+ errors.add('$packageName (apk)');
+ }
+ } else {
+ printSkip('Android is not supported by this plugin');
+ }
+ }
+ }
+
+ return errors;
+ }
+
+ Future<bool> _buildExample(
+ Directory example,
+ String flutterBuildType, {
+ List<String> extraBuildFlags = const <String>[],
+ }) async {
final String flutterCommand =
const LocalPlatform().isWindows ? 'flutter.bat' : 'flutter';
-
final String enableExperiment = getStringArg(kEnableExperiment);
- final List<String> failingPackages = <String>[];
- await for (final Directory plugin in getPlugins()) {
- for (final Directory example in getExamplesForPlugin(plugin)) {
- final String packageName =
- p.relative(example.path, from: packagesDir.path);
-
- if (getBoolArg(kPlatformLinux)) {
- print('\nBUILDING Linux for $packageName');
- if (isLinuxPlugin(plugin)) {
- final int buildExitCode = await processRunner.runAndStream(
- flutterCommand,
- <String>[
- 'build',
- kPlatformLinux,
- if (enableExperiment.isNotEmpty)
- '--enable-experiment=$enableExperiment',
- ],
- workingDir: example);
- if (buildExitCode != 0) {
- failingPackages.add('$packageName (linux)');
- }
- } else {
- print('Linux is not supported by this plugin');
- }
- }
-
- if (getBoolArg(kPlatformMacos)) {
- print('\nBUILDING macOS for $packageName');
- if (isMacOsPlugin(plugin)) {
- final int exitCode = await processRunner.runAndStream(
- flutterCommand,
- <String>[
- 'build',
- kPlatformMacos,
- if (enableExperiment.isNotEmpty)
- '--enable-experiment=$enableExperiment',
- ],
- workingDir: example);
- if (exitCode != 0) {
- failingPackages.add('$packageName (macos)');
- }
- } else {
- print('macOS is not supported by this plugin');
- }
- }
-
- if (getBoolArg(kPlatformWeb)) {
- print('\nBUILDING web for $packageName');
- if (isWebPlugin(plugin)) {
- final int buildExitCode = await processRunner.runAndStream(
- flutterCommand,
- <String>[
- 'build',
- kPlatformWeb,
- if (enableExperiment.isNotEmpty)
- '--enable-experiment=$enableExperiment',
- ],
- workingDir: example);
- if (buildExitCode != 0) {
- failingPackages.add('$packageName (web)');
- }
- } else {
- print('Web is not supported by this plugin');
- }
- }
-
- if (getBoolArg(kPlatformWindows)) {
- print('\nBUILDING Windows for $packageName');
- if (isWindowsPlugin(plugin)) {
- final int buildExitCode = await processRunner.runAndStream(
- flutterCommand,
- <String>[
- 'build',
- kPlatformWindows,
- if (enableExperiment.isNotEmpty)
- '--enable-experiment=$enableExperiment',
- ],
- workingDir: example);
- if (buildExitCode != 0) {
- failingPackages.add('$packageName (windows)');
- }
- } else {
- print('Windows is not supported by this plugin');
- }
- }
-
- if (getBoolArg(kIpa)) {
- print('\nBUILDING IPA for $packageName');
- if (isIosPlugin(plugin)) {
- final int exitCode = await processRunner.runAndStream(
- flutterCommand,
- <String>[
- 'build',
- 'ios',
- '--no-codesign',
- if (enableExperiment.isNotEmpty)
- '--enable-experiment=$enableExperiment',
- ],
- workingDir: example);
- if (exitCode != 0) {
- failingPackages.add('$packageName (ipa)');
- }
- } else {
- print('iOS is not supported by this plugin');
- }
- }
-
- if (getBoolArg(kApk)) {
- print('\nBUILDING APK for $packageName');
- if (isAndroidPlugin(plugin)) {
- final int exitCode = await processRunner.runAndStream(
- flutterCommand,
- <String>[
- 'build',
- 'apk',
- if (enableExperiment.isNotEmpty)
- '--enable-experiment=$enableExperiment',
- ],
- workingDir: example);
- if (exitCode != 0) {
- failingPackages.add('$packageName (apk)');
- }
- } else {
- print('Android is not supported by this plugin');
- }
- }
- }
- }
- print('\n\n');
-
- if (failingPackages.isNotEmpty) {
- print('The following build are failing (see above for details):');
- for (final String package in failingPackages) {
- print(' * $package');
- }
- throw ToolExit(1);
- }
-
- print('All builds successful!');
+ final int exitCode = await processRunner.runAndStream(
+ flutterCommand,
+ <String>[
+ 'build',
+ flutterBuildType,
+ ...extraBuildFlags,
+ if (enableExperiment.isNotEmpty)
+ '--enable-experiment=$enableExperiment',
+ ],
+ workingDir: example,
+ );
+ return exitCode == 0;
}
}
diff --git a/script/tool/test/build_examples_command_test.dart b/script/tool/test/build_examples_command_test.dart
index 7fc9783..c6febdc 100644
--- a/script/tool/test/build_examples_command_test.dart
+++ b/script/tool/test/build_examples_command_test.dart
@@ -15,7 +15,7 @@
import 'util.dart';
void main() {
- group('test build_example_command', () {
+ group('build-example', () {
late FileSystem fileSystem;
late Directory packagesDir;
late CommandRunner<void> runner;
@@ -35,6 +35,13 @@
runner.addCommand(command);
});
+ test('fails if no plaform flags are passed', () async {
+ expect(
+ () => runCapturingPrint(runner, <String>['build-examples']),
+ throwsA(isA<ToolExit>()),
+ );
+ });
+
test('building for iOS when plugin is not set up for iOS results in no-op',
() async {
final Directory pluginDirectory = createFakePlugin('plugin', packagesDir,
@@ -43,18 +50,16 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--ipa', '--no-macos']);
+ final List<String> output =
+ await runCapturingPrint(runner, <String>['build-examples', '--ios']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING IPA for $packageName',
- 'iOS is not supported by this plugin',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<Matcher>[
+ contains('BUILDING $packageName for iOS'),
+ contains('iOS is not supported by this plugin'),
]),
);
@@ -78,21 +83,15 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- final List<String> output = await runCapturingPrint(runner, <String>[
- 'build-examples',
- '--ipa',
- '--no-macos',
- '--enable-experiment=exp1'
- ]);
+ final List<String> output = await runCapturingPrint(runner,
+ <String>['build-examples', '--ios', '--enable-experiment=exp1']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING IPA for $packageName',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<String>[
+ '\nBUILDING $packageName for iOS',
]),
);
@@ -123,17 +122,15 @@
pluginDirectory.childDirectory('example');
final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--linux']);
+ runner, <String>['build-examples', '--linux']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING Linux for $packageName',
- 'Linux is not supported by this plugin',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<Matcher>[
+ contains('BUILDING $packageName for Linux'),
+ contains('Linux is not supported by this plugin'),
]),
);
@@ -158,16 +155,14 @@
pluginDirectory.childDirectory('example');
final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--linux']);
+ runner, <String>['build-examples', '--linux']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING Linux for $packageName',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<String>[
+ '\nBUILDING $packageName for Linux',
]),
);
@@ -190,17 +185,15 @@
pluginDirectory.childDirectory('example');
final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--macos']);
+ runner, <String>['build-examples', '--macos']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING macOS for $packageName',
- 'macOS is not supported by this plugin',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<Matcher>[
+ contains('BUILDING $packageName for macOS'),
+ contains('macOS is not supported by this plugin'),
]),
);
@@ -226,16 +219,14 @@
pluginDirectory.childDirectory('example');
final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--macos']);
+ runner, <String>['build-examples', '--macos']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING macOS for $packageName',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<String>[
+ '\nBUILDING $packageName for macOS',
]),
);
@@ -256,18 +247,16 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--web']);
+ final List<String> output =
+ await runCapturingPrint(runner, <String>['build-examples', '--web']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING web for $packageName',
- 'Web is not supported by this plugin',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<Matcher>[
+ contains('BUILDING $packageName for web'),
+ contains('Web is not supported by this plugin'),
]),
);
@@ -292,17 +281,15 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--web']);
+ final List<String> output =
+ await runCapturingPrint(runner, <String>['build-examples', '--web']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING web for $packageName',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<String>[
+ '\nBUILDING $packageName for web',
]),
);
@@ -326,17 +313,15 @@
pluginDirectory.childDirectory('example');
final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--windows']);
+ runner, <String>['build-examples', '--windows']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING Windows for $packageName',
- 'Windows is not supported by this plugin',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<Matcher>[
+ contains('BUILDING $packageName for Windows'),
+ contains('Windows is not supported by this plugin'),
]),
);
@@ -361,16 +346,14 @@
pluginDirectory.childDirectory('example');
final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--no-ipa', '--windows']);
+ runner, <String>['build-examples', '--windows']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING Windows for $packageName',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<String>[
+ '\nBUILDING $packageName for Windows',
]),
);
@@ -393,18 +376,16 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- final List<String> output = await runCapturingPrint(
- runner, <String>['build-examples', '--apk', '--no-ipa']);
+ final List<String> output =
+ await runCapturingPrint(runner, <String>['build-examples', '--apk']);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
- '\nBUILDING APK for $packageName',
- 'Android is not supported by this plugin',
- '\n\n',
- 'All builds successful!',
+ containsAllInOrder(<Matcher>[
+ contains('\nBUILDING APK for $packageName'),
+ contains('Android is not supported by this plugin'),
]),
);
@@ -431,18 +412,14 @@
final List<String> output = await runCapturingPrint(runner, <String>[
'build-examples',
'--apk',
- '--no-ipa',
- '--no-macos',
]);
final String packageName =
p.relative(pluginExampleDirectory.path, from: packagesDir.path);
expect(
output,
- orderedEquals(<String>[
+ containsAllInOrder(<String>[
'\nBUILDING APK for $packageName',
- '\n\n',
- 'All builds successful!',
]),
);
@@ -469,13 +446,8 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- await runCapturingPrint(runner, <String>[
- 'build-examples',
- '--apk',
- '--no-ipa',
- '--no-macos',
- '--enable-experiment=exp1'
- ]);
+ await runCapturingPrint(runner,
+ <String>['build-examples', '--apk', '--enable-experiment=exp1']);
expect(
processRunner.recordedCalls,
@@ -502,12 +474,8 @@
final Directory pluginExampleDirectory =
pluginDirectory.childDirectory('example');
- await runCapturingPrint(runner, <String>[
- 'build-examples',
- '--ipa',
- '--no-macos',
- '--enable-experiment=exp1'
- ]);
+ await runCapturingPrint(runner,
+ <String>['build-examples', '--ios', '--enable-experiment=exp1']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[