Support flutter build web --wasm (#117075)

* Work in progress.

* Some fixes to the command line.

* Bootstrapping works.

* Change kickoff order to maximize concurrency.

* Fix analyzer errors and formatting issues.

* Fix doc comment.

* Added unit tests for some of the web targets.

* Format issue.

* Add an integration test that builds an app to wasm.

* Add a todo for depfiles.

* Formatting.

* Apparently the license header needs to say 2014.

* `file://` URIs confuse dart2wasm on Windows. Just use absolute paths.

* Update unit tests to match new path passing.

* Have a distinct build directory for wasm, and fixes for some upstream changes.
This commit is contained in:
Jackson Gardner 2022-12-19 12:09:02 -08:00 committed by GitHub
parent 70f391db77
commit 9f2c5d8e21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 322 additions and 61 deletions

View file

@ -34,10 +34,14 @@ enum Artifact {
engineDartSdkPath,
/// The dart binary used to execute any of the required snapshots.
engineDartBinary,
/// The dart binary for running aot snapshots
engineDartAotRuntime,
/// The snapshot of frontend_server compiler.
frontendServerSnapshotForEngineDartSdk,
/// The dart snapshot of the dart2js compiler.
dart2jsSnapshot,
/// The dart snapshot of the dart2wasm compiler.
dart2wasmSnapshot,
/// The root of the Linux desktop sources.
linuxDesktopPath,
@ -168,8 +172,12 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return 'dart-sdk';
case Artifact.engineDartBinary:
return 'dart$exe';
case Artifact.engineDartAotRuntime:
return 'dartaotruntime$exe';
case Artifact.dart2jsSnapshot:
return 'dart2js.dart.snapshot';
case Artifact.dart2wasmSnapshot:
return 'dart2wasm_product.snapshot';
case Artifact.frontendServerSnapshotForEngineDartSdk:
return 'frontend_server.dart.snapshot';
case Artifact.linuxDesktopPath:
@ -488,7 +496,9 @@ class CachedArtifacts implements Artifacts {
return _fileSystem.path.join(engineDir, hostPlatform, _artifactToFileName(artifact, _platform));
case Artifact.engineDartSdkPath:
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder:
case Artifact.flutterFramework:
@ -525,7 +535,9 @@ class CachedArtifacts implements Artifacts {
return _getIosEngineArtifactPath(engineDir, environmentType, _fileSystem, _platform);
case Artifact.engineDartSdkPath:
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.constFinder:
case Artifact.flutterMacOSFramework:
@ -580,7 +592,9 @@ class CachedArtifacts implements Artifacts {
case Artifact.fontSubset:
case Artifact.engineDartSdkPath:
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
case Artifact.icuData:
case Artifact.isolateSnapshotData:
@ -613,6 +627,7 @@ class CachedArtifacts implements Artifacts {
// android_arm in profile mode because it is available on all supported host platforms.
return _getAndroidArtifactPath(artifact, TargetPlatform.android_arm, BuildMode.profile);
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
return _fileSystem.path.join(
_dartSdkPath(_cache), 'bin', 'snapshots',
@ -634,6 +649,7 @@ class CachedArtifacts implements Artifacts {
case Artifact.engineDartSdkPath:
return _dartSdkPath(_cache);
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
return _fileSystem.path.join(_dartSdkPath(_cache), 'bin', _artifactToFileName(artifact, _platform));
case Artifact.flutterMacOSFramework:
case Artifact.linuxDesktopPath:
@ -925,13 +941,12 @@ class CachedLocalEngineArtifacts implements Artifacts {
case Artifact.engineDartSdkPath:
return _getDartSdkPath();
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', artifactFileName);
case Artifact.dart2jsSnapshot:
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', artifactFileName);
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
return _fileSystem.path.join(
_getDartSdkPath(), 'bin', 'snapshots', artifactFileName,
);
return _fileSystem.path.join(_getDartSdkPath(), 'bin', 'snapshots', artifactFileName);
}
}
@ -1048,10 +1063,12 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case Artifact.engineDartSdkPath:
return _getDartSdkPath();
case Artifact.engineDartBinary:
case Artifact.engineDartAotRuntime:
return _fileSystem.path.join(
_getDartSdkPath(), 'bin',
_artifactToFileName(artifact, _platform, mode));
case Artifact.dart2jsSnapshot:
case Artifact.dart2wasmSnapshot:
case Artifact.frontendServerSnapshotForEngineDartSdk:
return _fileSystem.path.join(
_getDartSdkPath(), 'bin', 'snapshots',

View file

@ -914,8 +914,8 @@ String getMacOSBuildDirectory() {
}
/// Returns the web build output directory.
String getWebBuildDirectory() {
return globals.fs.path.join(getBuildDirectory(), 'web');
String getWebBuildDirectory([bool isWasm = false]) {
return globals.fs.path.join(getBuildDirectory(), isWasm ? 'web_wasm' : 'web');
}
/// Returns the Linux build output directory.

View file

@ -22,6 +22,7 @@ import '../../web/compile.dart';
import '../../web/file_generators/flutter_js.dart' as flutter_js;
import '../../web/file_generators/flutter_service_worker_js.dart';
import '../../web/file_generators/main_dart.dart' as main_dart;
import '../../web/file_generators/wasm_bootstrap.dart' as wasm_bootstrap;
import '../build_system.dart';
import '../depfile.dart';
import '../exceptions.dart';
@ -141,13 +142,11 @@ class WebEntrypointTarget extends Target {
}
/// Compiles a web entry point with dart2js.
class Dart2JSTarget extends Target {
const Dart2JSTarget(this.webRenderer);
abstract class Dart2WebTarget extends Target {
const Dart2WebTarget(this.webRenderer);
final WebRendererMode webRenderer;
@override
String get name => 'dart2js';
Source get compilerSnapshot;
@override
List<Target> get dependencies => const <Target>[
@ -156,22 +155,17 @@ class Dart2JSTarget extends Target {
];
@override
List<Source> get inputs => const <Source>[
Source.hostArtifact(HostArtifact.flutterWebSdk),
Source.artifact(Artifact.dart2jsSnapshot),
Source.artifact(Artifact.engineDartBinary),
Source.pattern('{BUILD_DIR}/main.dart'),
Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'),
List<Source> get inputs => <Source>[
const Source.hostArtifact(HostArtifact.flutterWebSdk),
compilerSnapshot,
const Source.artifact(Artifact.engineDartBinary),
const Source.pattern('{BUILD_DIR}/main.dart'),
const Source.pattern('{PROJECT_DIR}/.dart_tool/package_config_subset'),
];
@override
List<Source> get outputs => const <Source>[];
@override
List<String> get depfiles => const <String>[
'dart2js.d',
];
String _collectOutput(ProcessResult result) {
final String stdout = result.stdout is List<int>
? utf8.decode(result.stdout as List<int>)
@ -181,6 +175,21 @@ class Dart2JSTarget extends Target {
: result.stderr as String;
return stdout + stderr;
}
}
class Dart2JSTarget extends Dart2WebTarget {
Dart2JSTarget(super.webRenderer);
@override
String get name => 'dart2js';
@override
Source get compilerSnapshot => const Source.artifact(Artifact.dart2jsSnapshot);
@override
List<String> get depfiles => const <String>[
'dart2js.d',
];
@override
Future<void> build(Environment environment) async {
@ -270,29 +279,94 @@ class Dart2JSTarget extends Target {
}
}
/// Unpacks the dart2js compilation and resources to a given output directory.
class Dart2WasmTarget extends Dart2WebTarget {
Dart2WasmTarget(super.webRenderer);
@override
Future<void> build(Environment environment) async {
final String? buildModeEnvironment = environment.defines[kBuildMode];
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final Artifacts artifacts = globals.artifacts!;
final File outputWasmFile = environment.buildDir.childFile('main.dart.wasm');
final String dartSdkPath = artifacts.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript);
final String dartSdkRoot = environment.fileSystem.directory(dartSdkPath).parent.path;
final List<String> compilationArgs = <String>[
artifacts.getArtifactPath(Artifact.engineDartAotRuntime, platform: TargetPlatform.web_javascript),
'--disable-dart-dev',
artifacts.getArtifactPath(Artifact.dart2wasmSnapshot, platform: TargetPlatform.web_javascript),
if (buildMode == BuildMode.profile)
'-Ddart.vm.profile=true'
else
'-Ddart.vm.product=true',
...decodeCommaSeparated(environment.defines, kExtraFrontEndOptions),
for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines))
'-D$dartDefine',
'--packages=.dart_tool/package_config.json',
'--dart-sdk=$dartSdkPath',
'--multi-root-scheme',
'org-dartlang-sdk',
'--multi-root',
artifacts.getHostArtifact(HostArtifact.flutterWebSdk).path,
'--multi-root',
dartSdkRoot,
'--libraries-spec',
artifacts.getHostArtifact(HostArtifact.flutterWebLibrariesJson).path,
environment.buildDir.childFile('main.dart').path, // dartfile
outputWasmFile.path,
];
final ProcessResult compileResult = await globals.processManager.run(compilationArgs);
if (compileResult.exitCode != 0) {
throw Exception(_collectOutput(compileResult));
}
}
@override
Source get compilerSnapshot => const Source.artifact(Artifact.dart2wasmSnapshot);
@override
String get name => 'dart2wasm';
@override
List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/main.dart.wasm'),
];
// TODO(jacksongardner): override `depfiles` once dart2wasm begins producing
// them: https://github.com/dart-lang/sdk/issues/50747
}
/// Unpacks the dart2js or dart2wasm compilation and resources to a given
/// output directory.
class WebReleaseBundle extends Target {
const WebReleaseBundle(this.webRenderer);
const WebReleaseBundle(this.webRenderer, this.isWasm);
final WebRendererMode webRenderer;
final bool isWasm;
String get outputFileName => isWasm ? 'main.dart.wasm' : 'main.dart.js';
@override
String get name => 'web_release_bundle';
@override
List<Target> get dependencies => <Target>[
Dart2JSTarget(webRenderer),
if (isWasm) Dart2WasmTarget(webRenderer) else Dart2JSTarget(webRenderer),
];
@override
List<Source> get inputs => const <Source>[
Source.pattern('{BUILD_DIR}/main.dart.js'),
Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
List<Source> get inputs => <Source>[
Source.pattern('{BUILD_DIR}/$outputFileName'),
const Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
];
@override
List<Source> get outputs => const <Source>[
Source.pattern('{OUTPUT_DIR}/main.dart.js'),
List<Source> get outputs => <Source>[
Source.pattern('{OUTPUT_DIR}/$outputFileName'),
];
@override
@ -306,7 +380,7 @@ class WebReleaseBundle extends Target {
Future<void> build(Environment environment) async {
for (final File outputFile in environment.buildDir.listSync(recursive: true).whereType<File>()) {
final String basename = globals.fs.path.basename(outputFile.path);
if (!basename.contains('main.dart.js')) {
if (!basename.contains(outputFileName)) {
continue;
}
// Do not copy the deps file.
@ -318,6 +392,12 @@ class WebReleaseBundle extends Target {
);
}
if (isWasm) {
// TODO(jacksongardner): Enable icon tree shaking once dart2wasm can do a two-phase compile.
// https://github.com/flutter/flutter/issues/117248
environment.defines[kIconTreeShakerFlag] = 'false';
}
createVersionFile(environment, environment.defines);
final Directory outputDirectory = environment.outputDir.childDirectory('assets');
outputDirectory.createSync(recursive: true);
@ -413,10 +493,11 @@ class WebReleaseBundle extends Target {
/// These assets can be cached forever and are only invalidated when the
/// Flutter SDK is upgraded to a new version.
class WebBuiltInAssets extends Target {
const WebBuiltInAssets(this.fileSystem, this.cache);
const WebBuiltInAssets(this.fileSystem, this.cache, this.isWasm);
final FileSystem fileSystem;
final Cache cache;
final bool isWasm;
@override
String get name => 'web_static_assets';
@ -451,6 +532,21 @@ class WebBuiltInAssets extends Target {
file.copySync(targetPath);
}
if (isWasm) {
final String dartSdkPath =
globals.artifacts!.getArtifactPath(Artifact.engineDartSdkPath);
final File dart2wasmRuntime = fileSystem.directory(dartSdkPath)
.childDirectory('bin')
.childFile('dart2wasm_runtime.mjs');
final String targetPath = fileSystem.path.join(
environment.outputDir.path,
'dart2wasm_runtime.mjs');
dart2wasmRuntime.copySync(targetPath);
final File bootstrapFile = environment.outputDir.childFile('main.dart.js');
bootstrapFile.writeAsStringSync(wasm_bootstrap.generateWasmBootstrapFile());
}
// Write the flutter.js file
final File flutterJsFile = environment.outputDir.childFile('flutter.js');
flutterJsFile.writeAsStringSync(flutter_js.generateFlutterJsFile());
@ -459,20 +555,21 @@ class WebBuiltInAssets extends Target {
/// Generate a service worker for a web target.
class WebServiceWorker extends Target {
const WebServiceWorker(this.fileSystem, this.cache, this.webRenderer);
const WebServiceWorker(this.fileSystem, this.cache, this.webRenderer, this.isWasm);
final FileSystem fileSystem;
final Cache cache;
final WebRendererMode webRenderer;
final bool isWasm;
@override
String get name => 'web_service_worker';
@override
List<Target> get dependencies => <Target>[
Dart2JSTarget(webRenderer),
WebReleaseBundle(webRenderer),
WebBuiltInAssets(fileSystem, cache),
if (isWasm) Dart2WasmTarget(webRenderer) else Dart2JSTarget(webRenderer),
WebReleaseBundle(webRenderer, isWasm),
WebBuiltInAssets(fileSystem, cache, isWasm),
];
@override

View file

@ -42,6 +42,10 @@ class BuildWebCommand extends BuildSubCommand {
'to view and debug the original source code of a compiled and minified Dart '
'application.'
);
argParser.addFlag(
'wasm',
help: 'Compile to WebAssembly rather than Javascript (experimental).'
);
argParser.addOption('pwa-strategy',
defaultsTo: kOfflineFirst,
@ -140,6 +144,7 @@ class BuildWebCommand extends BuildSubCommand {
stringArgDeprecated('pwa-strategy')!,
boolArgDeprecated('source-maps'),
boolArgDeprecated('native-null-assertions'),
boolArgDeprecated('wasm'),
baseHref: baseHref,
dart2jsOptimization: stringArgDeprecated('dart2js-optimization'),
outputDirectoryPath: outputDirectoryPath,

View file

@ -298,6 +298,7 @@ class ResidentWebRunner extends ResidentRunner {
kNoneWorker,
true,
debuggingOptions.nativeNullAssertions,
false,
);
}
await device!.device!.startApp(
@ -370,6 +371,7 @@ class ResidentWebRunner extends ResidentRunner {
kNoneWorker,
true,
debuggingOptions.nativeNullAssertions,
false,
baseHref: kBaseHref,
);
} on ToolExit {

View file

@ -25,7 +25,8 @@ Future<void> buildWeb(
bool csp,
String serviceWorkerStrategy,
bool sourceMaps,
bool nativeNullAssertions, {
bool nativeNullAssertions,
bool isWasm, {
String? dart2jsOptimization,
String? baseHref,
bool dumpInfo = false,
@ -35,7 +36,7 @@ Future<void> buildWeb(
final bool hasWebPlugins = (await findPlugins(flutterProject))
.any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
final Directory outputDirectory = outputDirectoryPath == null
? globals.fs.directory(getWebBuildDirectory())
? globals.fs.directory(getWebBuildDirectory(isWasm))
: globals.fs.directory(outputDirectoryPath);
outputDirectory.createSync(recursive: true);
@ -51,7 +52,7 @@ Future<void> buildWeb(
final Stopwatch sw = Stopwatch()..start();
try {
final BuildResult result = await globals.buildSystem.build(
WebServiceWorker(globals.fs, globals.cache, buildInfo.webRenderer),
WebServiceWorker(globals.fs, globals.cache, buildInfo.webRenderer, isWasm),
Environment(
projectDir: globals.fs.currentDirectory,
outputDir: outputDirectory,

View file

@ -0,0 +1,31 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
String generateWasmBootstrapFile() {
return r'''
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
(async function () {
let dart2wasm_runtime;
let moduleInstance;
try {
const dartModulePromise = WebAssembly.compileStreaming(fetch("main.dart.wasm"));
dart2wasm_runtime = await import('./dart2wasm_runtime.mjs');
moduleInstance = await dart2wasm_runtime.instantiate(dartModulePromise, {});
} catch (exception) {
console.error(`Failed to fetch and instantiate wasm module: ${exception}`);
}
if (moduleInstance) {
try {
await dart2wasm_runtime.invoke(moduleInstance);
} catch (exception) {
console.error(`Exception while invoking test: ${exception}`);
}
}
})();
''';
}

View file

@ -100,7 +100,7 @@ void main() {
webResources.childFile('index.html')
.createSync(recursive: true);
environment.buildDir.childFile('main.dart.js').createSync();
await const WebReleaseBundle(WebRendererMode.autoDetect).build(environment);
await const WebReleaseBundle(WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('version.json'), exists);
}));
@ -112,7 +112,7 @@ void main() {
final Directory webResources = environment.projectDir.childDirectory('web');
webResources.childFile('index.html').createSync(recursive: true);
environment.buildDir.childFile('main.dart.js').createSync();
await const WebReleaseBundle(WebRendererMode.autoDetect).build(environment);
await const WebReleaseBundle(WebRendererMode.autoDetect, false).build(environment);
final String versionFile = environment.outputDir
.childFile('version.json')
@ -130,7 +130,7 @@ void main() {
<!DOCTYPE html><html><base href="$kBaseHrefPlaceholder"><head></head></html>
''');
environment.buildDir.childFile('main.dart.js').createSync();
await const WebReleaseBundle(WebRendererMode.autoDetect).build(environment);
await const WebReleaseBundle(WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('index.html').readAsStringSync(), contains('/basehreftest/'));
}));
@ -143,7 +143,7 @@ void main() {
<!DOCTYPE html><html><head><base href='/basehreftest/'></head></html>
''');
environment.buildDir.childFile('main.dart.js').createSync();
await const WebReleaseBundle(WebRendererMode.autoDetect).build(environment);
await const WebReleaseBundle(WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('index.html').readAsStringSync(), contains('/basehreftest/'));
}));
@ -165,7 +165,7 @@ void main() {
.writeAsStringSync('A');
environment.buildDir.childFile('main.dart.js').createSync();
await const WebReleaseBundle(WebRendererMode.autoDetect).build(environment);
await const WebReleaseBundle(WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'A');
@ -177,7 +177,7 @@ void main() {
// Update to arbitrary resource file triggers rebuild.
webResources.childFile('foo.txt').writeAsStringSync('B');
await const WebReleaseBundle(WebRendererMode.autoDetect).build(environment);
await const WebReleaseBundle(WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'B');
@ -358,7 +358,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -396,7 +396,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -430,7 +430,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -463,7 +463,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -499,7 +499,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -533,7 +533,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -556,7 +556,7 @@ void main() {
.writeAsStringSync('file:///a.dart');
},
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
expect(environment.buildDir.childFile('dart2js.d'), exists);
final Depfile depfile = depfileService.parse(environment.buildDir.childFile('dart2js.d'));
@ -601,7 +601,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -633,7 +633,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -673,7 +673,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
await Dart2JSTarget(WebRendererMode.autoDetect).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -709,7 +709,7 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.canvaskit).build(environment);
await Dart2JSTarget(WebRendererMode.canvaskit).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -745,7 +745,40 @@ void main() {
]
));
await const Dart2JSTarget(WebRendererMode.canvaskit).build(environment);
await Dart2JSTarget(WebRendererMode.canvaskit).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
test('Dart2WasmTarget invokes dart2wasm with dart defines', () => testbed.run(() async {
environment.defines[kBuildMode] = 'profile';
environment.defines[kDartDefines] = encodeDartDefines(<String>['FOO=bar', 'BAZ=qux']);
processManager.addCommand(FakeCommand(
command: <String>[
'bin/cache/dart-sdk/bin/dartaotruntime',
'--disable-dart-dev',
'bin/cache/dart-sdk/bin/snapshots/dart2wasm_product.snapshot',
'-Ddart.vm.profile=true',
'-DFOO=bar',
'-DBAZ=qux',
'--packages=.dart_tool/package_config.json',
'--dart-sdk=bin/cache/dart-sdk',
'--multi-root-scheme',
'org-dartlang-sdk',
'--multi-root',
'bin/cache/flutter_web_sdk',
'--multi-root',
'bin/cache',
'--libraries-spec',
'bin/cache/flutter_web_sdk/libraries.json',
environment.buildDir.childFile('main.dart').absolute.path,
environment.buildDir.childFile('main.dart.wasm').absolute.path,
])
);
await Dart2WasmTarget(WebRendererMode.canvaskit).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@ -772,7 +805,7 @@ void main() {
environment.outputDir.childDirectory('a').childFile('a.txt')
..createSync(recursive: true)
..writeAsStringSync('A');
await WebServiceWorker(globals.fs, globals.cache, WebRendererMode.autoDetect).build(environment);
await WebServiceWorker(globals.fs, globals.cache, WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('flutter_service_worker.js'), exists);
// Contains file hash.
@ -791,7 +824,7 @@ void main() {
environment.outputDir
.childFile('index.html')
.createSync(recursive: true);
await WebServiceWorker(globals.fs, globals.cache, WebRendererMode.autoDetect).build(environment);
await WebServiceWorker(globals.fs, globals.cache, WebRendererMode.autoDetect, false).build(environment);
expect(environment.outputDir.childFile('flutter_service_worker.js'), exists);
// Contains file hash for both `/` and index.html.
@ -809,7 +842,7 @@ void main() {
environment.outputDir
.childFile('main.dart.js.map')
.createSync(recursive: true);
await WebServiceWorker(globals.fs, globals.cache, WebRendererMode.autoDetect).build(environment);
await WebServiceWorker(globals.fs, globals.cache, WebRendererMode.autoDetect, false).build(environment);
// No caching of source maps.
expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
@ -824,10 +857,25 @@ void main() {
..createSync(recursive: true)
..writeAsStringSync('OL');
await WebBuiltInAssets(globals.fs, globals.cache).build(environment);
await WebBuiltInAssets(globals.fs, globals.cache, false).build(environment);
// No caching of source maps.
expect(environment.outputDir.childFile('flutter.js').readAsStringSync(),
equals(flutter_js.generateFlutterJsFile()));
}));
test('wasm build copies and generates specific files', () => testbed.run(() async {
globals.fs.file('bin/cache/dart-sdk/bin/dart2wasm_runtime.mjs')
.createSync(recursive: true);
globals.fs.file('bin/cache/flutter_web_sdk/canvaskit/canvaskit.wasm')
.createSync(recursive: true);
await WebBuiltInAssets(globals.fs, globals.cache, true).build(environment);
expect(environment.outputDir.childFile('dart2wasm_runtime.mjs').existsSync(), true);
expect(environment.outputDir.childFile('main.dart.js').existsSync(), true);
expect(environment.outputDir.childDirectory('canvaskit')
.childFile('canvaskit.wasm')
.existsSync(), true);
}));
}

View file

@ -0,0 +1,60 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import '../src/common.dart';
import 'test_utils.dart';
void main() {
late Directory tempDir;
late String flutterBin;
late Directory exampleAppDir;
setUp(() async {
tempDir = createResolvedTempDirectorySync('flutter_web_wasm_test.');
flutterBin = fileSystem.path.join(
getFlutterRoot(),
'bin',
'flutter',
);
exampleAppDir = tempDir.childDirectory('test_app');
processManager.runSync(<String>[
flutterBin,
'create',
'--platforms=web',
'test_app',
], workingDirectory: tempDir.path);
});
test('building web with --wasm produces expected files', () async {
final ProcessResult result = processManager.runSync(<String>[
flutterBin,
'build',
'web',
'--wasm',
], workingDirectory: exampleAppDir.path);
expect(result.exitCode, 0);
final Directory appBuildDir = fileSystem.directory(fileSystem.path.join(
exampleAppDir.path,
'build',
'web_wasm'
));
for (final String filename in const <String>[
'dart2wasm_runtime.mjs',
'flutter.js',
'flutter_service_worker.js',
'index.html',
'main.dart.wasm',
'main.dart.js',
]) {
expect(appBuildDir.childFile(filename), exists);
}
});
}