@@ -22,6 +22,7 @@ import '../../web/compile.dart';
2222import '../../web/file_generators/flutter_js.dart' as flutter_js;
2323import '../../web/file_generators/flutter_service_worker_js.dart' ;
2424import '../../web/file_generators/main_dart.dart' as main_dart;
25+ import '../../web/file_generators/wasm_bootstrap.dart' as wasm_bootstrap;
2526import '../build_system.dart' ;
2627import '../depfile.dart' ;
2728import '../exceptions.dart' ;
@@ -141,13 +142,11 @@ class WebEntrypointTarget extends Target {
141142}
142143
143144/// Compiles a web entry point with dart2js.
144- class Dart2JSTarget extends Target {
145- const Dart2JSTarget (this .webRenderer);
145+ abstract class Dart2WebTarget extends Target {
146+ const Dart2WebTarget (this .webRenderer);
146147
147148 final WebRendererMode webRenderer;
148-
149- @override
150- String get name => 'dart2js' ;
149+ Source get compilerSnapshot;
151150
152151 @override
153152 List <Target > get dependencies => const < Target > [
@@ -156,22 +155,17 @@ class Dart2JSTarget extends Target {
156155 ];
157156
158157 @override
159- List <Source > get inputs => const < Source > [
160- Source .hostArtifact (HostArtifact .flutterWebSdk),
161- Source . artifact ( Artifact .dart2jsSnapshot) ,
162- Source .artifact (Artifact .engineDartBinary),
163- Source .pattern ('{BUILD_DIR}/main.dart' ),
164- Source .pattern ('{PROJECT_DIR}/.dart_tool/package_config_subset' ),
158+ List <Source > get inputs => < Source > [
159+ const Source .hostArtifact (HostArtifact .flutterWebSdk),
160+ compilerSnapshot ,
161+ const Source .artifact (Artifact .engineDartBinary),
162+ const Source .pattern ('{BUILD_DIR}/main.dart' ),
163+ const Source .pattern ('{PROJECT_DIR}/.dart_tool/package_config_subset' ),
165164 ];
166165
167166 @override
168167 List <Source > get outputs => const < Source > [];
169168
170- @override
171- List <String > get depfiles => const < String > [
172- 'dart2js.d' ,
173- ];
174-
175169 String _collectOutput (ProcessResult result) {
176170 final String stdout = result.stdout is List <int >
177171 ? utf8.decode (result.stdout as List <int >)
@@ -181,6 +175,21 @@ class Dart2JSTarget extends Target {
181175 : result.stderr as String ;
182176 return stdout + stderr;
183177 }
178+ }
179+
180+ class Dart2JSTarget extends Dart2WebTarget {
181+ Dart2JSTarget (super .webRenderer);
182+
183+ @override
184+ String get name => 'dart2js' ;
185+
186+ @override
187+ Source get compilerSnapshot => const Source .artifact (Artifact .dart2jsSnapshot);
188+
189+ @override
190+ List <String > get depfiles => const < String > [
191+ 'dart2js.d' ,
192+ ];
184193
185194 @override
186195 Future <void > build (Environment environment) async {
@@ -270,29 +279,94 @@ class Dart2JSTarget extends Target {
270279 }
271280}
272281
273- /// Unpacks the dart2js compilation and resources to a given output directory.
282+ class Dart2WasmTarget extends Dart2WebTarget {
283+ Dart2WasmTarget (super .webRenderer);
284+
285+ @override
286+ Future <void > build (Environment environment) async {
287+ final String ? buildModeEnvironment = environment.defines[kBuildMode];
288+ if (buildModeEnvironment == null ) {
289+ throw MissingDefineException (kBuildMode, name);
290+ }
291+ final BuildMode buildMode = getBuildModeForName (buildModeEnvironment);
292+ final Artifacts artifacts = globals.artifacts! ;
293+ final File outputWasmFile = environment.buildDir.childFile ('main.dart.wasm' );
294+ final String dartSdkPath = artifacts.getArtifactPath (Artifact .engineDartSdkPath, platform: TargetPlatform .web_javascript);
295+ final String dartSdkRoot = environment.fileSystem.directory (dartSdkPath).parent.path;
296+
297+ final List <String > compilationArgs = < String > [
298+ artifacts.getArtifactPath (Artifact .engineDartAotRuntime, platform: TargetPlatform .web_javascript),
299+ '--disable-dart-dev' ,
300+ artifacts.getArtifactPath (Artifact .dart2wasmSnapshot, platform: TargetPlatform .web_javascript),
301+ if (buildMode == BuildMode .profile)
302+ '-Ddart.vm.profile=true'
303+ else
304+ '-Ddart.vm.product=true' ,
305+ ...decodeCommaSeparated (environment.defines, kExtraFrontEndOptions),
306+ for (final String dartDefine in decodeDartDefines (environment.defines, kDartDefines))
307+ '-D$dartDefine ' ,
308+ '--packages=.dart_tool/package_config.json' ,
309+ '--dart-sdk=$dartSdkPath ' ,
310+ '--multi-root-scheme' ,
311+ 'org-dartlang-sdk' ,
312+ '--multi-root' ,
313+ artifacts.getHostArtifact (HostArtifact .flutterWebSdk).path,
314+ '--multi-root' ,
315+ dartSdkRoot,
316+ '--libraries-spec' ,
317+ artifacts.getHostArtifact (HostArtifact .flutterWebLibrariesJson).path,
318+
319+ environment.buildDir.childFile ('main.dart' ).path, // dartfile
320+ outputWasmFile.path,
321+ ];
322+ final ProcessResult compileResult = await globals.processManager.run (compilationArgs);
323+ if (compileResult.exitCode != 0 ) {
324+ throw Exception (_collectOutput (compileResult));
325+ }
326+ }
327+
328+ @override
329+ Source get compilerSnapshot => const Source .artifact (Artifact .dart2wasmSnapshot);
330+
331+ @override
332+ String get name => 'dart2wasm' ;
333+
334+ @override
335+ List <Source > get outputs => const < Source > [
336+ Source .pattern ('{OUTPUT_DIR}/main.dart.wasm' ),
337+ ];
338+
339+ // TODO(jacksongardner): override `depfiles` once dart2wasm begins producing
340+ // them: https://github.com/dart-lang/sdk/issues/50747
341+ }
342+
343+ /// Unpacks the dart2js or dart2wasm compilation and resources to a given
344+ /// output directory.
274345class WebReleaseBundle extends Target {
275- const WebReleaseBundle (this .webRenderer);
346+ const WebReleaseBundle (this .webRenderer, this .isWasm );
276347
277348 final WebRendererMode webRenderer;
349+ final bool isWasm;
350+
351+ String get outputFileName => isWasm ? 'main.dart.wasm' : 'main.dart.js' ;
278352
279353 @override
280354 String get name => 'web_release_bundle' ;
281355
282356 @override
283357 List <Target > get dependencies => < Target > [
284- Dart2JSTarget (webRenderer),
358+ if (isWasm) Dart2WasmTarget (webRenderer) else Dart2JSTarget (webRenderer),
285359 ];
286360
287361 @override
288- List <Source > get inputs => const < Source > [
289- Source .pattern ('{BUILD_DIR}/main.dart.js ' ),
290- Source .pattern ('{PROJECT_DIR}/pubspec.yaml' ),
362+ List <Source > get inputs => < Source > [
363+ Source .pattern ('{BUILD_DIR}/$ outputFileName ' ),
364+ const Source .pattern ('{PROJECT_DIR}/pubspec.yaml' ),
291365 ];
292366
293367 @override
294- List <Source > get outputs => const < Source > [
295- Source .pattern ('{OUTPUT_DIR}/main.dart.js ' ),
368+ List <Source > get outputs => < Source > [
369+ Source .pattern ('{OUTPUT_DIR}/$ outputFileName ' ),
296370 ];
297371
298372 @override
@@ -306,7 +380,7 @@ class WebReleaseBundle extends Target {
306380 Future <void > build (Environment environment) async {
307381 for (final File outputFile in environment.buildDir.listSync (recursive: true ).whereType <File >()) {
308382 final String basename = globals.fs.path.basename (outputFile.path);
309- if (! basename.contains ('main.dart.js' )) {
383+ if (! basename.contains (outputFileName )) {
310384 continue ;
311385 }
312386 // Do not copy the deps file.
@@ -318,6 +392,12 @@ class WebReleaseBundle extends Target {
318392 );
319393 }
320394
395+ if (isWasm) {
396+ // TODO(jacksongardner): Enable icon tree shaking once dart2wasm can do a two-phase compile.
397+ // https://github.com/flutter/flutter/issues/117248
398+ environment.defines[kIconTreeShakerFlag] = 'false' ;
399+ }
400+
321401 createVersionFile (environment, environment.defines);
322402 final Directory outputDirectory = environment.outputDir.childDirectory ('assets' );
323403 outputDirectory.createSync (recursive: true );
@@ -413,10 +493,11 @@ class WebReleaseBundle extends Target {
413493/// These assets can be cached forever and are only invalidated when the
414494/// Flutter SDK is upgraded to a new version.
415495class WebBuiltInAssets extends Target {
416- const WebBuiltInAssets (this .fileSystem, this .cache);
496+ const WebBuiltInAssets (this .fileSystem, this .cache, this .isWasm );
417497
418498 final FileSystem fileSystem;
419499 final Cache cache;
500+ final bool isWasm;
420501
421502 @override
422503 String get name => 'web_static_assets' ;
@@ -451,6 +532,21 @@ class WebBuiltInAssets extends Target {
451532 file.copySync (targetPath);
452533 }
453534
535+ if (isWasm) {
536+ final String dartSdkPath =
537+ globals.artifacts! .getArtifactPath (Artifact .engineDartSdkPath);
538+ final File dart2wasmRuntime = fileSystem.directory (dartSdkPath)
539+ .childDirectory ('bin' )
540+ .childFile ('dart2wasm_runtime.mjs' );
541+ final String targetPath = fileSystem.path.join (
542+ environment.outputDir.path,
543+ 'dart2wasm_runtime.mjs' );
544+ dart2wasmRuntime.copySync (targetPath);
545+
546+ final File bootstrapFile = environment.outputDir.childFile ('main.dart.js' );
547+ bootstrapFile.writeAsStringSync (wasm_bootstrap.generateWasmBootstrapFile ());
548+ }
549+
454550 // Write the flutter.js file
455551 final File flutterJsFile = environment.outputDir.childFile ('flutter.js' );
456552 flutterJsFile.writeAsStringSync (flutter_js.generateFlutterJsFile ());
@@ -459,20 +555,21 @@ class WebBuiltInAssets extends Target {
459555
460556/// Generate a service worker for a web target.
461557class WebServiceWorker extends Target {
462- const WebServiceWorker (this .fileSystem, this .cache, this .webRenderer);
558+ const WebServiceWorker (this .fileSystem, this .cache, this .webRenderer, this .isWasm );
463559
464560 final FileSystem fileSystem;
465561 final Cache cache;
466562 final WebRendererMode webRenderer;
563+ final bool isWasm;
467564
468565 @override
469566 String get name => 'web_service_worker' ;
470567
471568 @override
472569 List <Target > get dependencies => < Target > [
473- Dart2JSTarget (webRenderer),
474- WebReleaseBundle (webRenderer),
475- WebBuiltInAssets (fileSystem, cache),
570+ if (isWasm) Dart2WasmTarget (webRenderer) else Dart2JSTarget (webRenderer),
571+ WebReleaseBundle (webRenderer, isWasm ),
572+ WebBuiltInAssets (fileSystem, cache, isWasm ),
476573 ];
477574
478575 @override
0 commit comments