Skip to content

Commit 9973673

Browse files
Support flutter run --wasm and flutter drive --wasm. (#146231)
This adds support for adding the `--wasm` flag to `flutter run` and `flutter drive` * Emits errors if you attempt to use the skwasm renderer without the `--wasm` flag * Emits errors if you try to use `--wasm` when not using a web device * Uses the skwasm renderer by default if you pass `--wasm` and no `--web-renderer`
1 parent e2c8121 commit 9973673

29 files changed

+337
-64
lines changed

dev/bots/suite_runners/run_web_long_running_tests.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Future<void> webLongRunningTestsRunner(String flutterRoot) async {
3232
target: path.join('test_driver', 'failure.dart'),
3333
buildMode: buildMode,
3434
renderer: 'canvaskit',
35+
wasm: false,
3536
// This test intentionally fails and prints stack traces in the browser
3637
// logs. To avoid confusion, silence browser output.
3738
silenceBrowserOutput: true,
@@ -42,6 +43,17 @@ Future<void> webLongRunningTestsRunner(String flutterRoot) async {
4243
driver: path.join('test_driver', 'integration_test.dart'),
4344
buildMode: buildMode,
4445
renderer: 'canvaskit',
46+
wasm: false,
47+
expectWriteResponseFile: true,
48+
expectResponseFileContent: 'null',
49+
),
50+
() => _runFlutterDriverWebTest(
51+
testAppDirectory: path.join('packages', 'integration_test', 'example'),
52+
target: path.join('integration_test', 'example_test.dart'),
53+
driver: path.join('test_driver', 'integration_test.dart'),
54+
buildMode: buildMode,
55+
renderer: 'skwasm',
56+
wasm: true,
4557
expectWriteResponseFile: true,
4658
expectResponseFileContent: 'null',
4759
),
@@ -51,6 +63,7 @@ Future<void> webLongRunningTestsRunner(String flutterRoot) async {
5163
driver: path.join('test_driver', 'extended_integration_test.dart'),
5264
buildMode: buildMode,
5365
renderer: 'canvaskit',
66+
wasm: false,
5467
expectWriteResponseFile: true,
5568
expectResponseFileContent: '''
5669
{
@@ -97,6 +110,7 @@ Future<void> webLongRunningTestsRunner(String flutterRoot) async {
97110
() => _runWebE2eTest('capabilities_integration_canvaskit', buildMode: 'debug', renderer: 'auto'),
98111
() => _runWebE2eTest('capabilities_integration_canvaskit', buildMode: 'profile', renderer: 'canvaskit'),
99112
() => _runWebE2eTest('capabilities_integration_html', buildMode: 'release', renderer: 'html'),
113+
() => _runWebE2eTest('capabilities_integration_skwasm', buildMode: 'release', renderer: 'skwasm', wasm: true),
100114

101115
// This test doesn't do anything interesting w.r.t. rendering, so we don't run the full build mode x renderer matrix.
102116
// CacheWidth and CacheHeight are only currently supported in CanvasKit mode, so we don't run the test in HTML mode.
@@ -110,6 +124,7 @@ Future<void> webLongRunningTestsRunner(String flutterRoot) async {
110124
target: 'test_driver/smoke_web_engine.dart',
111125
buildMode: 'profile',
112126
renderer: 'auto',
127+
wasm: false,
113128
),
114129
() => _runGalleryE2eWebTest('debug'),
115130
() => _runGalleryE2eWebTest('debug', canvasKit: true),
@@ -192,12 +207,14 @@ Future<void> _runWebE2eTest(
192207
String name, {
193208
required String buildMode,
194209
required String renderer,
210+
bool wasm = false,
195211
}) async {
196212
await _runFlutterDriverWebTest(
197213
target: path.join('test_driver', '$name.dart'),
198214
buildMode: buildMode,
199215
renderer: renderer,
200216
testAppDirectory: path.join(flutterRoot, 'dev', 'integration_tests', 'web_e2e_tests'),
217+
wasm: wasm,
201218
);
202219
}
203220

@@ -206,6 +223,7 @@ Future<void> _runFlutterDriverWebTest({
206223
required String buildMode,
207224
required String renderer,
208225
required String testAppDirectory,
226+
required bool wasm,
209227
String? driver,
210228
bool expectFailure = false,
211229
bool silenceBrowserOutput = false,
@@ -235,6 +253,7 @@ Future<void> _runFlutterDriverWebTest({
235253
'web-server',
236254
'--$buildMode',
237255
'--web-renderer=$renderer',
256+
if (wasm) '--wasm',
238257
],
239258
expectNonZeroExit: expectFailure,
240259
workingDirectory: testAppDirectory,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
{{flutter_js}}
6+
{{flutter_build_config}}
7+
_flutter.loader.load({
8+
config: {
9+
// Use the local CanvasKit bundle instead of the CDN to reduce test flakiness.
10+
canvasKitBaseUrl: "/canvaskit/",
11+
},
12+
});

dev/integration_tests/non_nullable/web/index.html

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,6 @@
2222
<link rel="manifest" href="manifest.json">
2323
</head>
2424
<body>
25-
<!-- This script installs service_worker.js to provide PWA functionality to
26-
application. For more information, see:
27-
https://developers.google.com/web/fundamentals/primers/service-workers -->
28-
<script>
29-
if ('serviceWorker' in navigator) {
30-
window.addEventListener('load', function () {
31-
navigator.serviceWorker.register('flutter_service_worker.js');
32-
});
33-
}
34-
</script>
35-
<script src="main.dart.js" type="application/javascript"></script>
25+
<script src="flutter_bootstrap.js" async></script>
3626
</body>
3727
</html>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
{{flutter_js}}
6+
{{flutter_build_config}}
7+
_flutter.loader.load({
8+
config: {
9+
// Use the local CanvasKit bundle instead of the CDN to reduce test flakiness.
10+
canvasKitBaseUrl: "/canvaskit/",
11+
},
12+
});

dev/integration_tests/web_compile_tests/web/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
<title>Hello, World</title>
88
</head>
99
<body>
10-
<script src="main.dart.js"></script>
10+
<script src="flutter_bootstrap.js" async></script>
1111
</body>
1212
</html>

dev/integration_tests/web_e2e_tests/test_driver/capabilities_integration_canvaskit.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,7 @@ void main() {
1212
testWidgets('isCanvasKit returns true in CanvasKit mode', (WidgetTester tester) async {
1313
await tester.pumpAndSettle();
1414
expect(isCanvasKit, true);
15+
expect(isSkwasm, false);
16+
expect(isSkiaWeb, true);
1517
});
1618
}

dev/integration_tests/web_e2e_tests/test_driver/capabilities_integration_html.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ import 'package:integration_test/integration_test.dart';
88
void main() {
99
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
1010

11-
testWidgets('isCanvasKit returns false in HTML mode', (WidgetTester tester) async {
11+
testWidgets('capabilities are set properly in HTML mode', (WidgetTester tester) async {
1212
await tester.pumpAndSettle();
1313
expect(isCanvasKit, false);
14+
expect(isSkwasm, false);
15+
expect(isSkiaWeb, false);
1416
});
1517
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/foundation.dart';
6+
import 'package:flutter_test/flutter_test.dart';
7+
import 'package:integration_test/integration_test.dart';
8+
9+
void main() {
10+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
11+
12+
testWidgets('capabilities are set properly in Skwasm mode', (WidgetTester tester) async {
13+
await tester.pumpAndSettle();
14+
expect(isCanvasKit, false);
15+
expect(isSkwasm, true);
16+
expect(isSkiaWeb, true);
17+
});
18+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:integration_test/integration_test_driver.dart' as test;
6+
7+
Future<void> main() async => test.integrationDriver();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
{{flutter_js}}
6+
{{flutter_build_config}}
7+
_flutter.loader.load({
8+
config: {
9+
// Use the local CanvasKit bundle instead of the CDN to reduce test flakiness.
10+
canvasKitBaseUrl: "/canvaskit/",
11+
},
12+
});

examples/hello_world/web/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
<title>Hello, World</title>
88
</head>
99
<body>
10-
<script src="main.dart.js"></script>
10+
<script src="flutter_bootstrap.js" async></script>
1111
</body>
1212
</html>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class BuildWebCommand extends BuildSubCommand {
6464
help:
6565
'Sets the optimization level used for Dart compilation to JavaScript/Wasm.',
6666
defaultsTo: '${WebCompilerConfig.kDefaultOptimizationLevel}',
67-
allowed: const <String>['1', '2', '3', '4'],
67+
allowed: const <String>['0', '1', '2', '3', '4'],
6868
);
6969

7070
//

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

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import '../runner/flutter_command_runner.dart';
3030
import '../tracing.dart';
3131
import '../vmservice.dart';
3232
import '../web/compile.dart';
33+
import '../web/web_constants.dart';
3334
import '../web/web_runner.dart';
3435
import 'daemon.dart';
3536

@@ -179,7 +180,12 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
179180
hide: !verboseHelp,
180181
help: 'Uninstall previous versions of the app on the device '
181182
'before reinstalling. Currently only supported on iOS.',
182-
);
183+
)
184+
..addFlag(
185+
FlutterOptions.kWebWasmFlag,
186+
help: 'Compile to WebAssembly rather than JavaScript.\n$kWasmMoreInfo',
187+
negatable: false,
188+
);
183189
usesWebOptions(verboseHelp: verboseHelp);
184190
usesTargetOption();
185191
usesPortOptions(verboseHelp: verboseHelp);
@@ -227,6 +233,13 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
227233

228234
String? get traceAllowlist => stringArg('trace-allowlist');
229235

236+
bool get useWasm => boolArg(FlutterOptions.kWebWasmFlag);
237+
238+
WebRendererMode get webRenderer => WebRendererMode.fromCliOption(
239+
stringArg(FlutterOptions.kWebRendererFlag),
240+
useWasm: useWasm
241+
);
242+
230243
/// Create a debugging options instance for the current `run` or `drive` invocation.
231244
@visibleForTesting
232245
@protected
@@ -242,10 +255,6 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
242255
final Map<String, String> webHeaders = featureFlags.isWebEnabled
243256
? extractWebHeaders()
244257
: const <String, String>{};
245-
final String? webRendererString = stringArg('web-renderer');
246-
final WebRendererMode webRenderer = (webRendererString != null)
247-
? WebRendererMode.values.byName(webRendererString)
248-
: WebRendererMode.auto;
249258

250259
if (buildInfo.mode.isRelease) {
251260
return DebuggingOptions.disabled(
@@ -264,6 +273,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
264273
webBrowserFlags: webBrowserFlags,
265274
webHeaders: webHeaders,
266275
webRenderer: webRenderer,
276+
webUseWasm: useWasm,
267277
enableImpeller: enableImpeller,
268278
enableVulkanValidation: enableVulkanValidation,
269279
uninstallFirst: uninstallFirst,
@@ -314,6 +324,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
314324
webLaunchUrl: featureFlags.isWebEnabled ? stringArg('web-launch-url') : null,
315325
webHeaders: webHeaders,
316326
webRenderer: webRenderer,
327+
webUseWasm: useWasm,
317328
vmserviceOutFile: stringArg('vmservice-out-file'),
318329
fastStart: argParser.options.containsKey('fast-start')
319330
&& boolArg('fast-start')
@@ -630,12 +641,21 @@ class RunCommand extends RunCommandBase {
630641
if (devices!.any((Device device) => device is AndroidDevice)) {
631642
_deviceDeprecationBehavior = DeprecationBehavior.exit;
632643
}
644+
633645
// Only support "web mode" with a single web device due to resident runner
634646
// refactoring required otherwise.
635647
webMode = featureFlags.isWebEnabled &&
636648
devices!.length == 1 &&
637649
await devices!.single.targetPlatform == TargetPlatform.web_javascript;
638650

651+
if (useWasm && !webMode) {
652+
throwToolExit('--wasm is only supported on the web platform');
653+
}
654+
655+
if (webRenderer == WebRendererMode.skwasm && !useWasm) {
656+
throwToolExit('Skwasm renderer requires --wasm');
657+
}
658+
639659
final String? flavor = stringArg('flavor');
640660
final bool flavorsSupportedOnEveryDevice = devices!
641661
.every((Device device) => device.supportsFlavors);

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,11 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
330330
return super.verifyThenRunCommand(commandPath);
331331
}
332332

333+
WebRendererMode get webRenderer => WebRendererMode.fromCliOption(
334+
stringArg(FlutterOptions.kWebRendererFlag),
335+
useWasm: useWasm
336+
);
337+
333338
@override
334339
Future<FlutterCommandResult> runCommand() async {
335340
if (!globals.fs.isFileSync('pubspec.yaml')) {
@@ -388,10 +393,6 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
388393
);
389394
}
390395

391-
final String? webRendererString = stringArg('web-renderer');
392-
final WebRendererMode webRenderer = (webRendererString != null)
393-
? WebRendererMode.values.byName(webRendererString)
394-
: WebRendererMode.auto;
395396
final DebuggingOptions debuggingOptions = DebuggingOptions.enabled(
396397
buildInfo,
397398
startPaused: startPaused,
@@ -405,6 +406,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
405406
enableImpeller: ImpellerStatus.fromBool(argResults!['enable-impeller'] as bool?),
406407
debugLogsDirectoryPath: debugLogsDirectoryPath,
407408
webRenderer: webRenderer,
409+
webUseWasm: useWasm,
408410
);
409411

410412
String? testAssetDirectory;
@@ -511,6 +513,10 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
511513
throwToolExit('--wasm is only supported on the web platform');
512514
}
513515

516+
if (webRenderer == WebRendererMode.skwasm && !useWasm) {
517+
throwToolExit('Skwasm renderer requires --wasm');
518+
}
519+
514520
Device? integrationTestDevice;
515521
if (_isIntegrationTest) {
516522
integrationTestDevice = await findTargetDevice();
@@ -589,7 +595,6 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
589595
testAssetDirectory: testAssetDirectory,
590596
flutterProject: flutterProject,
591597
web: isWeb,
592-
useWasm: useWasm,
593598
randomSeed: stringArg('test-randomize-ordering-seed'),
594599
reporter: stringArg('reporter'),
595600
fileReporter: stringArg('file-reporter'),

packages/flutter_tools/lib/src/device.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,7 @@ class DebuggingOptions {
994994
this.webHeaders = const <String, String>{},
995995
this.webLaunchUrl,
996996
this.webRenderer = WebRendererMode.auto,
997+
this.webUseWasm = false,
997998
this.vmserviceOutFile,
998999
this.fastStart = false,
9991000
this.nullAssertions = false,
@@ -1024,6 +1025,7 @@ class DebuggingOptions {
10241025
this.webLaunchUrl,
10251026
this.webHeaders = const <String, String>{},
10261027
this.webRenderer = WebRendererMode.auto,
1028+
this.webUseWasm = false,
10271029
this.cacheSkSL = false,
10281030
this.traceAllowlist,
10291031
this.enableImpeller = ImpellerStatus.platformDefault,
@@ -1104,6 +1106,7 @@ class DebuggingOptions {
11041106
required this.webHeaders,
11051107
required this.webLaunchUrl,
11061108
required this.webRenderer,
1109+
required this.webUseWasm,
11071110
required this.vmserviceOutFile,
11081111
required this.fastStart,
11091112
required this.nullAssertions,
@@ -1191,6 +1194,9 @@ class DebuggingOptions {
11911194
/// Which web renderer to use for the debugging session
11921195
final WebRendererMode webRenderer;
11931196

1197+
/// Whether to compile to webassembly
1198+
final bool webUseWasm;
1199+
11941200
/// A file where the VM Service URL should be written after the application is started.
11951201
final String? vmserviceOutFile;
11961202
final bool fastStart;
@@ -1300,6 +1306,7 @@ class DebuggingOptions {
13001306
'webLaunchUrl': webLaunchUrl,
13011307
'webHeaders': webHeaders,
13021308
'webRenderer': webRenderer.name,
1309+
'webUseWasm': webUseWasm,
13031310
'vmserviceOutFile': vmserviceOutFile,
13041311
'fastStart': fastStart,
13051312
'nullAssertions': nullAssertions,
@@ -1356,6 +1363,7 @@ class DebuggingOptions {
13561363
webHeaders: (json['webHeaders']! as Map<dynamic, dynamic>).cast<String, String>(),
13571364
webLaunchUrl: json['webLaunchUrl'] as String?,
13581365
webRenderer: WebRendererMode.values.byName(json['webRenderer']! as String),
1366+
webUseWasm: json['webUseWasm']! as bool,
13591367
vmserviceOutFile: json['vmserviceOutFile'] as String?,
13601368
fastStart: json['fastStart']! as bool,
13611369
nullAssertions: json['nullAssertions']! as bool,

0 commit comments

Comments
 (0)