Unverified Commit 89807e68 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Ensure that flutter run/drive/test/update_packages only downloads required artifacts (#30075)

parent 0e0a4eee
...@@ -65,7 +65,7 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -65,7 +65,7 @@ class AnalyzeCommand extends FlutterCommand {
String get description => "Analyze the project's Dart code."; String get description => "Analyze the project's Dart code.";
@override @override
Set<DevelopmentArtifact> get requiredArtifacts => const <DevelopmentArtifact>{ Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
}; };
......
...@@ -16,7 +16,7 @@ import '../resident_runner.dart'; ...@@ -16,7 +16,7 @@ import '../resident_runner.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import 'build.dart'; import 'build.dart';
class BuildAotCommand extends BuildSubCommand { class BuildAotCommand extends BuildSubCommand with TargetPlatformBasedDevelopmentArtifacts {
BuildAotCommand() { BuildAotCommand() {
usesTargetOption(); usesTargetOption();
addBuildModeFlags(); addBuildModeFlags();
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import '../android/apk.dart'; import '../android/apk.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
import 'build.dart'; import 'build.dart';
class BuildApkCommand extends BuildSubCommand { class BuildApkCommand extends BuildSubCommand {
...@@ -34,6 +34,12 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -34,6 +34,12 @@ class BuildApkCommand extends BuildSubCommand {
@override @override
final String name = 'apk'; final String name = 'apk';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.android,
};
@override @override
final String description = 'Build an Android APK file from your app.\n\n' final String description = 'Build an Android APK file from your app.\n\n'
'This command can build debug and release versions of your application. \'debug\' builds support ' 'This command can build debug and release versions of your application. \'debug\' builds support '
......
...@@ -10,7 +10,7 @@ import '../base/utils.dart'; ...@@ -10,7 +10,7 @@ import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../globals.dart'; import '../globals.dart';
import '../ios/mac.dart'; import '../ios/mac.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
import 'build.dart'; import 'build.dart';
class BuildIOSCommand extends BuildSubCommand { class BuildIOSCommand extends BuildSubCommand {
...@@ -48,6 +48,12 @@ class BuildIOSCommand extends BuildSubCommand { ...@@ -48,6 +48,12 @@ class BuildIOSCommand extends BuildSubCommand {
@override @override
final String description = 'Build an iOS application bundle (Mac OS X host only).'; final String description = 'Build an iOS application bundle (Mac OS X host only).';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.iOS,
};
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final bool forSimulator = argResults['simulator']; final bool forSimulator = argResults['simulator'];
......
...@@ -20,7 +20,7 @@ class BuildWebCommand extends BuildSubCommand { ...@@ -20,7 +20,7 @@ class BuildWebCommand extends BuildSubCommand {
} }
@override @override
Set<DevelopmentArtifact> get requiredArtifacts => const <DevelopmentArtifact>{ Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.web, DevelopmentArtifact.web,
}; };
......
...@@ -31,6 +31,9 @@ class ChannelCommand extends FlutterCommand { ...@@ -31,6 +31,9 @@ class ChannelCommand extends FlutterCommand {
@override @override
String get invocation => '${runner.executableName} $name [<channel-name>]'; String get invocation => '${runner.executableName} $name [<channel-name>]';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
switch (argResults.rest.length) { switch (argResults.rest.length) {
......
...@@ -22,6 +22,9 @@ class CleanCommand extends FlutterCommand { ...@@ -22,6 +22,9 @@ class CleanCommand extends FlutterCommand {
@override @override
final String description = 'Delete the build/ and .dart_tool/ directories.'; final String description = 'Delete the build/ and .dart_tool/ directories.';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final FlutterProject flutterProject = await FlutterProject.current(); final FlutterProject flutterProject = await FlutterProject.current();
......
...@@ -44,6 +44,9 @@ class ConfigCommand extends FlutterCommand { ...@@ -44,6 +44,9 @@ class ConfigCommand extends FlutterCommand {
@override @override
bool get shouldUpdateCache => false; bool get shouldUpdateCache => false;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
@override @override
String get usageFooter { String get usageFooter {
// List all config settings. // List all config settings.
......
...@@ -39,6 +39,11 @@ class FormatCommand extends FlutterCommand { ...@@ -39,6 +39,11 @@ class FormatCommand extends FlutterCommand {
@override @override
final String description = 'Format one or more dart files.'; final String description = 'Format one or more dart files.';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
@override @override
String get invocation => '${runner.executableName} $name <one or more paths>'; String get invocation => '${runner.executableName} $name <one or more paths>';
......
...@@ -21,6 +21,11 @@ class GenerateCommand extends FlutterCommand { ...@@ -21,6 +21,11 @@ class GenerateCommand extends FlutterCommand {
@override @override
bool get hidden => true; bool get hidden => true;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
......
...@@ -42,6 +42,9 @@ class IdeConfigCommand extends FlutterCommand { ...@@ -42,6 +42,9 @@ class IdeConfigCommand extends FlutterCommand {
@override @override
final String name = 'ide-config'; final String name = 'ide-config';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
@override @override
final String description = 'Configure the IDE for use in the Flutter tree.\n\n' final String description = 'Configure the IDE for use in the Flutter tree.\n\n'
'If run on a Flutter tree that is already configured for the IDE, this ' 'If run on a Flutter tree that is already configured for the IDE, this '
......
...@@ -23,6 +23,9 @@ class InjectPluginsCommand extends FlutterCommand { ...@@ -23,6 +23,9 @@ class InjectPluginsCommand extends FlutterCommand {
@override @override
final bool hidden; final bool hidden;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final FlutterProject project = await FlutterProject.current(); final FlutterProject project = await FlutterProject.current();
......
...@@ -11,7 +11,7 @@ import '../device.dart'; ...@@ -11,7 +11,7 @@ import '../device.dart';
import '../globals.dart'; import '../globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
class InstallCommand extends FlutterCommand { class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
InstallCommand() { InstallCommand() {
requiresPubspecYaml(); requiresPubspecYaml();
} }
......
...@@ -26,6 +26,9 @@ class LogsCommand extends FlutterCommand { ...@@ -26,6 +26,9 @@ class LogsCommand extends FlutterCommand {
@override @override
final String description = 'Show log output for running Flutter apps.'; final String description = 'Show log output for running Flutter apps.';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
Device device; Device device;
@override @override
......
...@@ -27,6 +27,11 @@ class PackagesCommand extends FlutterCommand { ...@@ -27,6 +27,11 @@ class PackagesCommand extends FlutterCommand {
@override @override
final String description = 'Commands for managing Flutter packages.'; final String description = 'Commands for managing Flutter packages.';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
@override @override
Future<FlutterCommandResult> runCommand() async => null; Future<FlutterCommandResult> runCommand() async => null;
} }
......
...@@ -20,7 +20,7 @@ import '../runner/flutter_command.dart'; ...@@ -20,7 +20,7 @@ import '../runner/flutter_command.dart';
import '../tracing.dart'; import '../tracing.dart';
import 'daemon.dart'; import 'daemon.dart';
abstract class RunCommandBase extends FlutterCommand { abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
// Used by run and drive commands. // Used by run and drive commands.
RunCommandBase({ bool verboseHelp = false }) { RunCommandBase({ bool verboseHelp = false }) {
addBuildModeFlags(defaultToRelease: false, verboseHelp: verboseHelp); addBuildModeFlags(defaultToRelease: false, verboseHelp: verboseHelp);
...@@ -64,6 +64,7 @@ abstract class RunCommandBase extends FlutterCommand { ...@@ -64,6 +64,7 @@ abstract class RunCommandBase extends FlutterCommand {
} }
bool get traceStartup => argResults['trace-startup']; bool get traceStartup => argResults['trace-startup'];
String get route => argResults['route']; String get route => argResults['route'];
} }
......
...@@ -86,6 +86,11 @@ class TestCommand extends FastFlutterCommand { ...@@ -86,6 +86,11 @@ class TestCommand extends FastFlutterCommand {
valueHelp: 'jobs'); valueHelp: 'jobs');
} }
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
@override @override
String get name => 'test'; String get name => 'test';
......
...@@ -84,6 +84,11 @@ class UpdatePackagesCommand extends FlutterCommand { ...@@ -84,6 +84,11 @@ class UpdatePackagesCommand extends FlutterCommand {
@override @override
final bool hidden; final bool hidden;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
Future<void> _downloadCoverageData() async { Future<void> _downloadCoverageData() async {
final Status status = logger.startProgress( final Status status = logger.startProgress(
'Downloading lcov data for package:flutter...', 'Downloading lcov data for package:flutter...',
......
...@@ -36,6 +36,11 @@ class UpgradeCommand extends FlutterCommand { ...@@ -36,6 +36,11 @@ class UpgradeCommand extends FlutterCommand {
@override @override
bool get shouldUpdateCache => false; bool get shouldUpdateCache => false;
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final UpgradeCommandRunner upgradeCommandRunner = UpgradeCommandRunner(); final UpgradeCommandRunner upgradeCommandRunner = UpgradeCommandRunner();
......
...@@ -534,7 +534,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -534,7 +534,7 @@ abstract class FlutterCommand extends Command<void> {
// Populate the cache. We call this before pub get below so that the sky_engine // Populate the cache. We call this before pub get below so that the sky_engine
// package is available in the flutter cache for pub to find. // package is available in the flutter cache for pub to find.
if (shouldUpdateCache) { if (shouldUpdateCache) {
await cache.updateAll(requiredArtifacts); await cache.updateAll(await requiredArtifacts);
} }
if (shouldRunPub) { if (shouldRunPub) {
...@@ -557,7 +557,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -557,7 +557,7 @@ abstract class FlutterCommand extends Command<void> {
/// ///
/// Defaults to [DevelopmentArtifact.universal], /// Defaults to [DevelopmentArtifact.universal],
/// [DevelopmentArtifact.android], and [DevelopmentArtifact.iOS]. /// [DevelopmentArtifact.android], and [DevelopmentArtifact.iOS].
Set<DevelopmentArtifact> get requiredArtifacts => const <DevelopmentArtifact>{ Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.iOS, DevelopmentArtifact.iOS,
DevelopmentArtifact.android, DevelopmentArtifact.android,
...@@ -677,6 +677,90 @@ abstract class FlutterCommand extends Command<void> { ...@@ -677,6 +677,90 @@ abstract class FlutterCommand extends Command<void> {
ApplicationPackageStore applicationPackages; ApplicationPackageStore applicationPackages;
} }
/// A mixin which applies an implementation of [requiredArtifacts] that only
/// downloads artifacts corresponding to an attached device.
mixin DeviceBasedDevelopmentArtifacts on FlutterCommand {
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
// If there are no attached devices, use the default configuration.
// Otherwise, only add development artifacts which correspond to a
// connected device.
final List<Device> devices = await deviceManager.getDevices().toList();
if (devices.isEmpty) {
return super.requiredArtifacts;
}
final Set<DevelopmentArtifact> artifacts = <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
for (Device device in devices) {
final TargetPlatform targetPlatform = await device.targetPlatform;
switch (targetPlatform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
artifacts.add(DevelopmentArtifact.android);
break;
case TargetPlatform.web:
artifacts.add(DevelopmentArtifact.web);
break;
case TargetPlatform.ios:
artifacts.add(DevelopmentArtifact.iOS);
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.fuchsia:
case TargetPlatform.tester:
case TargetPlatform.windows_x64:
case TargetPlatform.linux_x64:
// No artifacts currently supported.
break;
}
}
return artifacts;
}
}
/// A mixin which applies an implementation of [requiredArtifacts] that only
/// downloads artifacts corresponding to a target device.
mixin TargetPlatformBasedDevelopmentArtifacts on FlutterCommand {
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
// If there is no specified target device, fallback to the default
// confiugration.
final String rawTargetPlatform = argResults['target-platform'];
final TargetPlatform targetPlatform = getTargetPlatformForName(rawTargetPlatform);
if (targetPlatform == null) {
return super.requiredArtifacts;
}
final Set<DevelopmentArtifact> artifacts = <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
switch (targetPlatform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_arm64:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
artifacts.add(DevelopmentArtifact.android);
break;
case TargetPlatform.web:
artifacts.add(DevelopmentArtifact.web);
break;
case TargetPlatform.ios:
artifacts.add(DevelopmentArtifact.iOS);
break;
case TargetPlatform.darwin_x64:
case TargetPlatform.fuchsia:
case TargetPlatform.tester:
case TargetPlatform.windows_x64:
case TargetPlatform.linux_x64:
// No artifacts currently supported.
break;
}
return artifacts;
}
}
/// A command which runs less analytics and checks to speed up startup time. /// A command which runs less analytics and checks to speed up startup time.
abstract class FastFlutterCommand extends FlutterCommand { abstract class FastFlutterCommand extends FlutterCommand {
@override @override
......
...@@ -2,8 +2,10 @@ ...@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/base/time.dart'; import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/usage.dart'; import 'package:flutter_tools/src/usage.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart';
...@@ -153,6 +155,81 @@ void main() { ...@@ -153,6 +155,81 @@ void main() {
SystemClock: () => clock, SystemClock: () => clock,
Usage: () => usage, Usage: () => usage,
}); });
});
group('Development artifacts', () {
final MockDeviceManager mockDeviceManager = MockDeviceManager();
testUsingContext('should only request artifacts corresponding to connected devices', () async {
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
MockDevice(TargetPlatform.android_arm),
]);
});
expect(await FakeDeviceBasedDevelopmentArtifacts().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.android,
}));
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
MockDevice(TargetPlatform.ios),
]);
});
expect(await FakeDeviceBasedDevelopmentArtifacts().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.iOS,
}));
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
MockDevice(TargetPlatform.ios),
MockDevice(TargetPlatform.android_arm),
]);
});
expect(await FakeDeviceBasedDevelopmentArtifacts().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.iOS,
DevelopmentArtifact.android,
}));
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Stream<Device>.fromIterable(<Device>[
MockDevice(TargetPlatform.web),
]);
});
expect(await FakeDeviceBasedDevelopmentArtifacts().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.web,
}));
}, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager,
});
}); });
} }
class MockDeviceManager extends Mock implements DeviceManager {}
class MockDevice extends Mock implements Device {
MockDevice(this._targetPlatform);
final TargetPlatform _targetPlatform;
@override
Future<TargetPlatform> get targetPlatform async => _targetPlatform;
}
class FakeDeviceBasedDevelopmentArtifacts extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
@override
String get description => null;
@override
String get name => null;
@override
Future<FlutterCommandResult> runCommand() {
return null;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment