Skip to content

Commit e59d9a8

Browse files
authored
Reland "Added --dart-flags option to flutter run (flutter#33924)" (flutter#34181)
Reland "Added --dart-flags option to flutter run (flutter#33924)" This reverts commit 587687e.
1 parent 7cc7161 commit e59d9a8

File tree

7 files changed

+218
-3
lines changed

7 files changed

+218
-3
lines changed

packages/flutter_tools/lib/src/android/android_device.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,8 @@ class AndroidDevice extends Device {
491491
cmd.addAll(<String>['--ez', 'start-paused', 'true']);
492492
if (debuggingOptions.disableServiceAuthCodes)
493493
cmd.addAll(<String>['--ez', 'disable-service-auth-codes', 'true']);
494+
if (debuggingOptions.dartFlags.isNotEmpty)
495+
cmd.addAll(<String>['--es', 'dart-flags', debuggingOptions.dartFlags]);
494496
if (debuggingOptions.useTestFonts)
495497
cmd.addAll(<String>['--ez', 'use-test-fonts', 'true']);
496498
if (debuggingOptions.verboseSystemLogs) {

packages/flutter_tools/lib/src/commands/run.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import 'dart:async';
66

7+
import 'package:args/command_runner.dart';
8+
79
import '../base/common.dart';
810
import '../base/file_system.dart';
911
import '../base/time.dart';
@@ -130,6 +132,16 @@ class RunCommand extends RunCommandBase {
130132
defaultsTo: true,
131133
help: 'If necessary, build the app before running.',
132134
)
135+
..addOption('dart-flags',
136+
hide: !verboseHelp,
137+
help: 'Pass a list of comma separated flags to the Dart instance at '
138+
'application startup. Flags passed through this option must be '
139+
'present on the whitelist defined within the Flutter engine. If '
140+
'a non-whitelisted flag is encountered, the process will be '
141+
'terminated immediately.\n\n'
142+
'This flag is not available on the stable channel and is only '
143+
'applied in debug and profile modes. This option should only '
144+
'be used for experiments and should not be used by typical users.')
133145
..addOption('use-application-binary',
134146
hide: !verboseHelp,
135147
help: 'Specify a pre-built application binary to use when running.',
@@ -294,6 +306,7 @@ class RunCommand extends RunCommandBase {
294306
buildInfo,
295307
startPaused: argResults['start-paused'],
296308
disableServiceAuthCodes: argResults['disable-service-auth-codes'],
309+
dartFlags: argResults['dart-flags'] ?? '',
297310
useTestFonts: argResults['use-test-fonts'],
298311
enableSoftwareRendering: argResults['enable-software-rendering'],
299312
skiaDeterministicRendering: argResults['skia-deterministic-rendering'],
@@ -350,6 +363,11 @@ class RunCommand extends RunCommandBase {
350363
);
351364
}
352365

366+
if (argResults['dart-flags'] != null && FlutterVersion.instance.isStable) {
367+
throw UsageException('--dart-flags is not available on the stable '
368+
'channel.', null);
369+
}
370+
353371
for (Device device in devices) {
354372
if (await device.isLocalEmulator) {
355373
if (await device.supportsHardwareRendering) {

packages/flutter_tools/lib/src/device.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ class DebuggingOptions {
377377
this.buildInfo, {
378378
this.startPaused = false,
379379
this.disableServiceAuthCodes = false,
380+
this.dartFlags = '',
380381
this.enableSoftwareRendering = false,
381382
this.skiaDeterministicRendering = false,
382383
this.traceSkia = false,
@@ -391,6 +392,7 @@ class DebuggingOptions {
391392
: debuggingEnabled = false,
392393
useTestFonts = false,
393394
startPaused = false,
395+
dartFlags = '',
394396
disableServiceAuthCodes = false,
395397
enableSoftwareRendering = false,
396398
skiaDeterministicRendering = false,
@@ -404,6 +406,7 @@ class DebuggingOptions {
404406

405407
final BuildInfo buildInfo;
406408
final bool startPaused;
409+
final String dartFlags;
407410
final bool disableServiceAuthCodes;
408411
final bool enableSoftwareRendering;
409412
final bool skiaDeterministicRendering;

packages/flutter_tools/lib/src/ios/devices.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,11 @@ class IOSDevice extends Device {
283283
if (debuggingOptions.disableServiceAuthCodes)
284284
launchArguments.add('--disable-service-auth-codes');
285285

286+
if (debuggingOptions.dartFlags.isNotEmpty) {
287+
final String dartFlags = debuggingOptions.dartFlags;
288+
launchArguments.add('--dart-flags="$dartFlags"');
289+
}
290+
286291
if (debuggingOptions.useTestFonts)
287292
launchArguments.add('--use-test-fonts');
288293

packages/flutter_tools/test/commands/run_test.dart

Lines changed: 173 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,36 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:args/command_runner.dart';
6+
import 'package:flutter_tools/src/application_package.dart';
57
import 'package:flutter_tools/src/base/common.dart';
68
import 'package:flutter_tools/src/build_info.dart';
9+
import 'package:flutter_tools/src/cache.dart';
710
import 'package:flutter_tools/src/commands/run.dart';
811
import 'package:flutter_tools/src/device.dart';
912
import 'package:flutter_tools/src/runner/flutter_command.dart';
13+
import 'package:flutter_tools/src/version.dart';
1014
import 'package:mockito/mockito.dart';
1115

1216
import '../src/common.dart';
1317
import '../src/context.dart';
1418
import '../src/mocks.dart';
1519

1620
void main() {
17-
final MockDeviceManager mockDeviceManager = MockDeviceManager();
18-
1921
group('run', () {
22+
MockApplicationPackageFactory mockApplicationPackageFactory;
23+
MockDeviceManager mockDeviceManager;
24+
MockFlutterVersion mockStableFlutterVersion;
25+
MockFlutterVersion mockUnstableFlutterVersion;
26+
27+
setUpAll(() {
28+
Cache.disableLocking();
29+
mockApplicationPackageFactory = MockApplicationPackageFactory();
30+
mockDeviceManager = MockDeviceManager();
31+
mockStableFlutterVersion = MockFlutterVersion(isStable: true);
32+
mockUnstableFlutterVersion = MockFlutterVersion(isStable: false);
33+
});
34+
2035
testUsingContext('fails when target not found', () async {
2136
final RunCommand command = RunCommand();
2237
applyMocksToCommand(command);
@@ -28,6 +43,92 @@ void main() {
2843
}
2944
});
3045

46+
47+
group('dart-flags option', () {
48+
setUpAll(() {
49+
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
50+
return Stream<Device>.fromIterable(<Device>[
51+
FakeDevice(),
52+
]);
53+
});
54+
});
55+
56+
RunCommand command;
57+
List<String> args;
58+
setUp(() {
59+
command = TestRunCommand();
60+
args = <String> [
61+
'run',
62+
'--dart-flags', '"--observe"',
63+
'--no-hot',
64+
];
65+
});
66+
67+
testUsingContext('is not available on stable channel', () async {
68+
// Stable branch.
69+
try {
70+
await createTestCommandRunner(command).run(args);
71+
fail('Expect exception');
72+
// ignore: unused_catch_clause
73+
} on UsageException catch(e) {
74+
// Not available while on stable branch.
75+
}
76+
}, overrides: <Type, Generator>{
77+
DeviceManager: () => mockDeviceManager,
78+
FlutterVersion: () => mockStableFlutterVersion,
79+
});
80+
81+
testUsingContext('is populated in debug mode', () async {
82+
// FakeDevice.startApp checks that --dart-flags doesn't get dropped and
83+
// throws ToolExit with FakeDevice.kSuccess if the flag is populated.
84+
try {
85+
await createTestCommandRunner(command).run(args);
86+
fail('Expect exception');
87+
} on ToolExit catch (e) {
88+
expect(e.exitCode, FakeDevice.kSuccess);
89+
}
90+
}, overrides: <Type, Generator>{
91+
ApplicationPackageFactory: () => mockApplicationPackageFactory,
92+
DeviceManager: () => mockDeviceManager,
93+
FlutterVersion: () => mockUnstableFlutterVersion,
94+
});
95+
96+
testUsingContext('is populated in profile mode', () async {
97+
args.add('--profile');
98+
99+
// FakeDevice.startApp checks that --dart-flags doesn't get dropped and
100+
// throws ToolExit with FakeDevice.kSuccess if the flag is populated.
101+
try {
102+
await createTestCommandRunner(command).run(args);
103+
fail('Expect exception');
104+
} on ToolExit catch (e) {
105+
expect(e.exitCode, FakeDevice.kSuccess);
106+
}
107+
}, overrides: <Type, Generator>{
108+
ApplicationPackageFactory: () => mockApplicationPackageFactory,
109+
DeviceManager: () => mockDeviceManager,
110+
FlutterVersion: () => mockUnstableFlutterVersion,
111+
});
112+
113+
testUsingContext('is not populated in release mode', () async {
114+
args.add('--release');
115+
116+
// FakeDevice.startApp checks that --dart-flags *does* get dropped and
117+
// throws ToolExit with FakeDevice.kSuccess if the flag is set to the
118+
// empty string.
119+
try {
120+
await createTestCommandRunner(command).run(args);
121+
fail('Expect exception');
122+
} on ToolExit catch (e) {
123+
expect(e.exitCode, FakeDevice.kSuccess);
124+
}
125+
}, overrides: <Type, Generator>{
126+
ApplicationPackageFactory: () => mockApplicationPackageFactory,
127+
DeviceManager: () => mockDeviceManager,
128+
FlutterVersion: () => mockUnstableFlutterVersion,
129+
});
130+
});
131+
31132
testUsingContext('should only request artifacts corresponding to connected devices', () async {
32133
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
33134
return Stream<Device>.fromIterable(<Device>[
@@ -89,3 +190,73 @@ class MockDevice extends Mock implements Device {
89190
@override
90191
Future<TargetPlatform> get targetPlatform async => _targetPlatform;
91192
}
193+
194+
class TestRunCommand extends RunCommand {
195+
@override
196+
// ignore: must_call_super
197+
Future<void> validateCommand() async {
198+
devices = await deviceManager.getDevices().toList();
199+
}
200+
}
201+
202+
class MockStableFlutterVersion extends MockFlutterVersion {
203+
@override
204+
bool get isStable => true;
205+
}
206+
207+
class FakeDevice extends Fake implements Device {
208+
static const int kSuccess = 1;
209+
static const int kFailure = -1;
210+
final TargetPlatform _targetPlatform = TargetPlatform.ios;
211+
212+
void _throwToolExit(int code) => throwToolExit(null, exitCode: code);
213+
214+
@override
215+
Future<bool> get isLocalEmulator => Future<bool>.value(false);
216+
217+
@override
218+
bool get supportsHotReload => false;
219+
220+
@override
221+
Future<String> get sdkNameAndVersion => Future<String>.value('');
222+
223+
@override
224+
DeviceLogReader getLogReader({ ApplicationPackage app }) {
225+
return MockDeviceLogReader();
226+
}
227+
228+
@override
229+
String get name => 'FakeDevice';
230+
231+
@override
232+
Future<TargetPlatform> get targetPlatform async => _targetPlatform;
233+
234+
@override
235+
Future<LaunchResult> startApp(
236+
ApplicationPackage package, {
237+
String mainPath,
238+
String route,
239+
DebuggingOptions debuggingOptions,
240+
Map<String, dynamic> platformArgs,
241+
bool prebuiltApplication = false,
242+
bool usesTerminalUi = true,
243+
bool ipv6 = false,
244+
}) async {
245+
final String dartFlags = debuggingOptions.dartFlags;
246+
// In release mode, --dart-flags should be set to the empty string and
247+
// provided flags should be dropped. In debug and profile modes,
248+
// --dart-flags should not be empty.
249+
if (debuggingOptions.buildInfo.isRelease) {
250+
if (dartFlags.isNotEmpty) {
251+
_throwToolExit(kFailure);
252+
}
253+
_throwToolExit(kSuccess);
254+
} else {
255+
if (dartFlags.isEmpty) {
256+
_throwToolExit(kFailure);
257+
}
258+
_throwToolExit(kSuccess);
259+
}
260+
return null;
261+
}
262+
}

packages/flutter_tools/test/src/context.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,12 @@ class MockXcodeProjectInterpreter implements XcodeProjectInterpreter {
332332
}
333333

334334
class MockFlutterVersion extends Mock implements FlutterVersion {
335+
MockFlutterVersion({bool isStable = false}) : _isStable = isStable;
336+
337+
final bool _isStable;
338+
335339
@override
336-
bool get isStable => false;
340+
bool get isStable => _isStable;
337341
}
338342

339343
class MockClock extends Mock implements SystemClock {}

packages/flutter_tools/test/src/mocks.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ class MockApplicationPackageStore extends ApplicationPackageStore {
3737
);
3838
}
3939

40+
class MockApplicationPackageFactory extends Mock implements ApplicationPackageFactory {
41+
final MockApplicationPackageStore _store = MockApplicationPackageStore();
42+
43+
@override
44+
Future<ApplicationPackage> getPackageForPlatform(
45+
TargetPlatform platform, {
46+
File applicationBinary,
47+
}) async {
48+
return _store.getPackageForPlatform(platform);
49+
}
50+
}
51+
4052
/// An SDK installation with several SDK levels (19, 22, 23).
4153
class MockAndroidSdk extends Mock implements AndroidSdk {
4254
static Directory createSdkDirectory({

0 commit comments

Comments
 (0)