Wasm/JS Dual Compile with the flutter tool (#141396)
This implements dual compile via the newly available flutter.js bootstrapping APIs for intelligent build fallback.
* Users can now use the `FlutterLoader.load` API from flutter.js
* Flutter tool injects build info into the `index.html` of the user so that the bootstrapper knows which build variants are available to bootstrap
* The semantics of the `--wasm` flag for `flutter build web` have changed:
- Instead of producing a separate `build/web_wasm` directory, the output goes to the `build/web` directory like a normal web build
- Produces a dual build that contains two build variants: dart2wasm+skwasm and dart2js+CanvasKit. The dart2wasm+skwasm will only work on Chrome in a cross-origin isolated context, all other environments will fall back to dart2js+CanvasKit.
- `--wasm` and `--web-renderer` are now mutually exclusive. Since there are multiple build variants with `--wasm`, the web renderer cannot be expressed via a single command-line flag. For now, we are hard coding what build variants are produced with the `--wasm` flag, but I plan on making this more customizable in the future.
* Build targets now can optionally provide a "build key" which can uniquely identify any specific parameterization of that build target. This way, the build target can invalidate itself by changing its build key. This works a bit better than just stuffing everything into the environment defines because (a) it doesn't invalidate the entire build, just the targets which are affected and (b) settings for multiple build variants don't translate well to the flat map of environment defines.
diff --git a/dev/benchmarks/macrobenchmarks/web/index.html b/dev/benchmarks/macrobenchmarks/web/index.html
index dec5a51..f73aee4 100644
--- a/dev/benchmarks/macrobenchmarks/web/index.html
+++ b/dev/benchmarks/macrobenchmarks/web/index.html
@@ -6,8 +6,12 @@
<head>
<meta charset="UTF-8">
<title>Web Benchmarks</title>
+ <script src="flutter.js"></script>
</head>
<body>
- <script src="main.dart.js" type="application/javascript"></script>
+ <script>
+ {{flutter_build_config}}
+ _flutter.loader.load();
+ </script>
</body>
</html>
diff --git a/dev/devicelab/lib/tasks/web_benchmarks.dart b/dev/devicelab/lib/tasks/web_benchmarks.dart
index a4748b8..031d478 100644
--- a/dev/devicelab/lib/tasks/web_benchmarks.dart
+++ b/dev/devicelab/lib/tasks/web_benchmarks.dart
@@ -39,7 +39,7 @@
'--omit-type-checks',
],
'--dart-define=FLUTTER_WEB_ENABLE_PROFILING=true',
- '--web-renderer=${benchmarkOptions.webRenderer}',
+ if (!benchmarkOptions.useWasm) '--web-renderer=${benchmarkOptions.webRenderer}',
'--profile',
'--no-web-resources-cdn',
'-t',
@@ -125,7 +125,7 @@
return Response.internalServerError(body: '$error');
}
}).add(createBuildDirectoryHandler(
- path.join(macrobenchmarksDirectory, 'build', benchmarkOptions.useWasm ? 'web_wasm' : 'web'),
+ path.join(macrobenchmarksDirectory, 'build', 'web'),
));
server = await io.HttpServer.bind('localhost', benchmarkServerPort);
diff --git a/dev/integration_tests/web_e2e_tests/web/index.html b/dev/integration_tests/web_e2e_tests/web/index.html
index 9dcd43f..70d8bac 100644
--- a/dev/integration_tests/web_e2e_tests/web/index.html
+++ b/dev/integration_tests/web_e2e_tests/web/index.html
@@ -5,14 +5,17 @@
<html>
<head>
<title>Web Integration Tests</title>
- <script>
- // Use the local CanvasKit bundle instead of the CDN to reduce test flakiness.
- window.flutterConfiguration = {
- canvasKitBaseUrl: "/canvaskit/"
- };
- </script>
+ <script src="flutter.js"></script>
</head>
<body>
- <script src="main.dart.js"></script>
+ <script>
+ {{flutter_build_config}}
+ _flutter.loader.load({
+ config: {
+ // Use the local CanvasKit bundle instead of the CDN to reduce test flakiness.
+ canvasKitBaseUrl: "/canvaskit/",
+ },
+ });
+ </script>
</body>
</html>
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index 6b107e1..59e2459 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -12,7 +12,6 @@
import 'base/utils.dart';
import 'convert.dart';
import 'globals.dart' as globals;
-import 'web/compile.dart';
/// Whether icon font subsetting is enabled by default.
const bool kIconTreeShakerEnabledDefault = true;
@@ -36,7 +35,6 @@
List<String>? dartDefines,
this.bundleSkSLPath,
List<String>? dartExperiments,
- this.webRenderer = WebRendererMode.auto,
required this.treeShakeIcons,
this.performanceMeasurementFile,
this.packagesPath = '.dart_tool/package_config.json', // TODO(zanderso): make this required and remove the default.
@@ -130,9 +128,6 @@
/// A list of Dart experiments.
final List<String> dartExperiments;
- /// When compiling to web, which web renderer mode we are using (html, canvaskit, auto)
- final WebRendererMode webRenderer;
-
/// The name of a file where flutter assemble will output performance
/// information in a JSON format.
///
@@ -798,10 +793,6 @@
return HostPlatform.linux_x64;
}
-FileSystemEntity getWebPlatformBinariesDirectory(Artifacts artifacts, WebRendererMode webRenderer) {
- return artifacts.getHostArtifact(HostArtifact.webPlatformKernelFolder);
-}
-
/// Returns the top-level build output directory.
String getBuildDirectory([Config? config, FileSystem? fileSystem]) {
// TODO(johnmccutchan): Stop calling this function as part of setting
@@ -844,8 +835,8 @@
}
/// Returns the web build output directory.
-String getWebBuildDirectory([bool isWasm = false]) {
- return globals.fs.path.join(getBuildDirectory(), isWasm ? 'web_wasm' : 'web');
+String getWebBuildDirectory() {
+ return globals.fs.path.join(getBuildDirectory(), 'web');
}
/// Returns the Linux build output directory.
diff --git a/packages/flutter_tools/lib/src/build_system/build_system.dart b/packages/flutter_tools/lib/src/build_system/build_system.dart
index 2438202..849f4f3 100644
--- a/packages/flutter_tools/lib/src/build_system/build_system.dart
+++ b/packages/flutter_tools/lib/src/build_system/build_system.dart
@@ -136,6 +136,15 @@
/// A list of zero or more depfiles, located directly under {BUILD_DIR}.
List<String> get depfiles => const <String>[];
+ /// A string that differentiates different build variants from each other
+ /// with regards to build flags or settings on the target. This string should
+ /// represent each build variant as a different unique value. If this value
+ /// changes between builds, the target will be invalidated and rebuilt.
+ ///
+ /// By default, this returns null, which indicates there is only one build
+ /// variant, and the target won't invalidate or rebuild due to this property.
+ String? get buildKey => null;
+
/// Whether this target can be executed with the given [environment].
///
/// Returning `true` will cause [build] to be skipped. This is equivalent
@@ -156,6 +165,7 @@
<Node>[
for (final Target target in dependencies) target._toNode(environment),
],
+ buildKey,
environment,
inputsFiles.containsNewDepfile,
);
@@ -181,9 +191,11 @@
for (final File output in outputs) {
outputPaths.add(output.path);
}
+ final String? key = buildKey;
final Map<String, Object> result = <String, Object>{
'inputs': inputPaths,
'outputs': outputPaths,
+ if (key != null) 'buildKey': key,
};
if (!stamp.existsSync()) {
stamp.createSync();
@@ -218,6 +230,7 @@
/// This requires constants from the [Environment] to resolve the paths of
/// inputs and the output stamp.
Map<String, Object> toJson(Environment environment) {
+ final String? key = buildKey;
return <String, Object>{
'name': name,
'dependencies': <String>[
@@ -229,6 +242,7 @@
'outputs': <String>[
for (final File file in resolveOutputs(environment).sources) file.path,
],
+ if (key != null) 'buildKey': key,
'stamp': _findStampFile(environment).absolute.path,
};
}
@@ -980,49 +994,85 @@
/// A node in the build graph.
class Node {
- Node(
+ factory Node(
+ Target target,
+ List<File> inputs,
+ List<File> outputs,
+ List<Node> dependencies,
+ String? buildKey,
+ Environment environment,
+ bool missingDepfile,
+ ) {
+ final File stamp = target._findStampFile(environment);
+ Map<String, Object?>? stampValues;
+
+ // If the stamp file doesn't exist, we haven't run this step before and
+ // all inputs were added.
+ if (stamp.existsSync()) {
+ final String content = stamp.readAsStringSync();
+ if (content.isEmpty) {
+ stamp.deleteSync();
+ } else {
+ try {
+ stampValues = castStringKeyedMap(json.decode(content));
+ } on FormatException {
+ // The json is malformed in some way.
+ }
+ }
+ }
+ if (stampValues != null) {
+ final String? previousBuildKey = stampValues['buildKey'] as String?;
+ final Object? stampInputs = stampValues['inputs'];
+ final Object? stampOutputs = stampValues['outputs'];
+ if (stampInputs is List<Object?> && stampOutputs is List<Object?>) {
+ final Set<String> previousInputs = stampInputs.whereType<String>().toSet();
+ final Set<String> previousOutputs = stampOutputs.whereType<String>().toSet();
+ return Node.withStamp(
+ target,
+ inputs,
+ previousInputs,
+ outputs,
+ previousOutputs,
+ dependencies,
+ buildKey,
+ previousBuildKey,
+ missingDepfile,
+ );
+ }
+ }
+ return Node.withNoStamp(
+ target,
+ inputs,
+ outputs,
+ dependencies,
+ buildKey,
+ missingDepfile,
+ );
+ }
+
+ Node.withNoStamp(
this.target,
this.inputs,
this.outputs,
this.dependencies,
- Environment environment,
+ this.buildKey,
this.missingDepfile,
- ) {
- final File stamp = target._findStampFile(environment);
+ ) : previousInputs = <String>{},
+ previousOutputs = <String>{},
+ previousBuildKey = null,
+ _dirty = true;
- // If the stamp file doesn't exist, we haven't run this step before and
- // all inputs were added.
- if (!stamp.existsSync()) {
- // No stamp file, not safe to skip.
- _dirty = true;
- return;
- }
- final String content = stamp.readAsStringSync();
- // Something went wrong writing the stamp file.
- if (content.isEmpty) {
- stamp.deleteSync();
- // Malformed stamp file, not safe to skip.
- _dirty = true;
- return;
- }
- Map<String, Object?>? values;
- try {
- values = castStringKeyedMap(json.decode(content));
- } on FormatException {
- // The json is malformed in some way.
- _dirty = true;
- return;
- }
- final Object? inputs = values?['inputs'];
- final Object? outputs = values?['outputs'];
- if (inputs is List<Object?> && outputs is List<Object?>) {
- inputs.cast<String?>().whereType<String>().forEach(previousInputs.add);
- outputs.cast<String?>().whereType<String>().forEach(previousOutputs.add);
- } else {
- // The json is malformed in some way.
- _dirty = true;
- }
- }
+ Node.withStamp(
+ this.target,
+ this.inputs,
+ this.previousInputs,
+ this.outputs,
+ this.previousOutputs,
+ this.dependencies,
+ this.buildKey,
+ this.previousBuildKey,
+ this.missingDepfile,
+ ) : _dirty = false;
/// The resolved input files.
///
@@ -1034,6 +1084,11 @@
/// These files may not yet exist if the target hasn't run yet.
final List<File> outputs;
+ /// The current build key of the target
+ ///
+ /// See `buildKey` in the `Target` class for more information.
+ final String? buildKey;
+
/// Whether this node is missing a depfile.
///
/// This requires an additional pass of source resolution after the target
@@ -1047,10 +1102,15 @@
final List<Node> dependencies;
/// Output file paths from the previous invocation of this build node.
- final Set<String> previousOutputs = <String>{};
+ final Set<String> previousOutputs;
/// Input file paths from the previous invocation of this build node.
- final Set<String> previousInputs = <String>{};
+ final Set<String> previousInputs;
+
+ /// The buildKey from the previous invocation of this build node.
+ ///
+ /// See `buildKey` in the `Target` class for more information.
+ final String? previousBuildKey;
/// One or more reasons why a task was invalidated.
///
@@ -1074,6 +1134,10 @@
FileSystem fileSystem,
Logger logger,
) {
+ if (buildKey != previousBuildKey) {
+ _invalidate(InvalidatedReasonKind.buildKeyChanged);
+ _dirty = true;
+ }
final Set<String> currentOutputPaths = <String>{
for (final File file in outputs) file.path,
};
@@ -1173,7 +1237,8 @@
InvalidatedReasonKind.inputChanged => 'The following inputs have updated contents: ${data.join(',')}',
InvalidatedReasonKind.outputChanged => 'The following outputs have updated contents: ${data.join(',')}',
InvalidatedReasonKind.outputMissing => 'The following outputs were missing: ${data.join(',')}',
- InvalidatedReasonKind.outputSetChanged => 'The following outputs were removed from the output set: ${data.join(',')}'
+ InvalidatedReasonKind.outputSetChanged => 'The following outputs were removed from the output set: ${data.join(',')}',
+ InvalidatedReasonKind.buildKeyChanged => 'The target build key changed.',
};
}
}
@@ -1195,4 +1260,7 @@
/// The set of expected output files changed.
outputSetChanged,
+
+ /// The build key changed
+ buildKeyChanged,
}
diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart
index 28413c4..7b5035c 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/web.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart
@@ -5,6 +5,7 @@
import 'dart:math';
import 'package:crypto/crypto.dart';
+import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart';
import '../../artifacts.dart';
@@ -22,7 +23,6 @@
import '../../web/compile.dart';
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';
@@ -38,6 +38,17 @@
/// The caching strategy to use for service worker generation.
const String kServiceWorkerStrategy = 'ServiceWorkerStrategy';
+@visibleForTesting
+List<String> updateDartDefines(List<String> dartDefines, WebRendererMode webRenderer) {
+ final Set<String> dartDefinesSet = dartDefines.toSet();
+ if (!dartDefines.any((String d) => d.startsWith('FLUTTER_WEB_AUTO_DETECT='))
+ && dartDefines.any((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='))) {
+ dartDefinesSet.removeWhere((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='));
+ }
+ dartDefinesSet.addAll(webRenderer.dartDefines);
+ return dartDefinesSet.toList();
+}
+
/// Generates an entry point for a web target.
// Keep this in sync with build_runner/resident_web_runner.dart
class WebEntrypointTarget extends Target {
@@ -104,11 +115,15 @@
/// Compiles a web entry point with dart2js.
abstract class Dart2WebTarget extends Target {
- const Dart2WebTarget(this.webRenderer);
+ const Dart2WebTarget();
- final WebRendererMode webRenderer;
Source get compilerSnapshot;
+ WebCompilerConfig get compilerConfig;
+
+ Map<String, Object?> get buildConfig;
+ List<String> get buildFiles;
+
@override
List<Target> get dependencies => const <Target>[
WebEntrypointTarget(),
@@ -125,11 +140,19 @@
];
@override
- List<Source> get outputs => const <Source>[];
+ List<Source> get outputs => buildFiles.map(
+ (String file) => Source.pattern('{OUTPUT_DIR}/$file')
+ ).toList();
+
+ @override
+ String get buildKey => compilerConfig.buildKey;
}
class Dart2JSTarget extends Dart2WebTarget {
- Dart2JSTarget(super.webRenderer);
+ Dart2JSTarget(this.compilerConfig);
+
+ @override
+ final JsCompilerConfig compilerConfig;
@override
String get name => 'dart2js';
@@ -149,9 +172,12 @@
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
- final JsCompilerConfig compilerConfig = JsCompilerConfig.fromBuildSystemEnvironment(environment.defines);
final Artifacts artifacts = environment.artifacts;
- final String platformBinariesPath = getWebPlatformBinariesDirectory(artifacts, webRenderer).path;
+ final String platformBinariesPath = artifacts.getHostArtifact(HostArtifact.webPlatformKernelFolder).path;
+ final List<String> dartDefines = updateDartDefines(
+ decodeDartDefines(environment.defines, kDartDefines),
+ compilerConfig.renderer,
+ );
final List<String> sharedCommandOptions = <String>[
artifacts.getArtifactPath(Artifact.engineDartBinary, platform: TargetPlatform.web_javascript),
'--disable-dart-dev',
@@ -163,7 +189,7 @@
'-Ddart.vm.profile=true'
else
'-Ddart.vm.product=true',
- for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines))
+ for (final String dartDefine in dartDefines)
'-D$dartDefine',
];
@@ -217,10 +243,25 @@
environment.buildDir.childFile('dart2js.d'),
);
}
+
+ @override
+ Map<String, Object?> get buildConfig => <String, Object?>{
+ 'compileTarget': 'dart2js',
+ 'renderer': compilerConfig.renderer.name,
+ 'mainJsPath': 'main.dart.js',
+ };
+
+ @override
+ List<String> get buildFiles => <String>[
+ 'main.dart.js',
+ ];
}
class Dart2WasmTarget extends Dart2WebTarget {
- Dart2WasmTarget(super.webRenderer);
+ Dart2WasmTarget(this.compilerConfig);
+
+ @override
+ final WasmCompilerConfig compilerConfig;
@override
Future<void> build(Environment environment) async {
@@ -228,7 +269,6 @@
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
- final WasmCompilerConfig compilerConfig = WasmCompilerConfig.fromBuildSystemEnvironment(environment.defines);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Artifacts artifacts = environment.artifacts;
final File outputWasmFile = environment.buildDir.childFile(
@@ -236,8 +276,12 @@
);
final File depFile = environment.buildDir.childFile('dart2wasm.d');
final String dartSdkPath = artifacts.getArtifactPath(Artifact.engineDartSdkPath, platform: TargetPlatform.web_javascript);
- final String platformBinariesPath = getWebPlatformBinariesDirectory(artifacts, webRenderer).path;
+ final String platformBinariesPath = artifacts.getHostArtifact(HostArtifact.webPlatformKernelFolder).path;
final String platformFilePath = environment.fileSystem.path.join(platformBinariesPath, 'dart2wasm_platform.dill');
+ final List<String> dartDefines = updateDartDefines(
+ decodeDartDefines(environment.defines, kDartDefines),
+ compilerConfig.renderer,
+ );
final List<String> compilationArgs = <String>[
artifacts.getArtifactPath(Artifact.engineDartAotRuntime, platform: TargetPlatform.web_javascript),
@@ -251,10 +295,10 @@
else
'-Ddart.vm.product=true',
...decodeCommaSeparated(environment.defines, kExtraFrontEndOptions),
- for (final String dartDefine in decodeDartDefines(environment.defines, kDartDefines))
+ for (final String dartDefine in dartDefines)
'-D$dartDefine',
...compilerConfig.toCommandOptions(),
- if (webRenderer == WebRendererMode.skwasm)
+ if (compilerConfig.renderer == WebRendererMode.skwasm)
...<String>[
'--import-shared-memory',
'--shared-memory-max-pages=32768',
@@ -319,74 +363,75 @@
];
@override
- List<Source> get outputs => const <Source>[
- Source.pattern('{OUTPUT_DIR}/main.dart.wasm'),
- Source.pattern('{OUTPUT_DIR}/main.dart.mjs'),
+ List<Source> get outputs => const <Source>[];
+
+ @override
+ Map<String, Object?> get buildConfig => <String, Object?>{
+ 'compileTarget': 'dart2wasm',
+ 'renderer': compilerConfig.renderer.name,
+ 'mainWasmPath': 'main.dart.wasm',
+ 'jsSupportRuntimePath': 'main.dart.mjs',
+ };
+
+ @override
+ List<String> get buildFiles => <String>[
+ 'main.dart.wasm',
+ 'main.dart.mjs',
];
}
/// Unpacks the dart2js or dart2wasm compilation and resources to a given
/// output directory.
class WebReleaseBundle extends Target {
- const WebReleaseBundle(this.webRenderer, {required this.isWasm});
+ WebReleaseBundle(List<WebCompilerConfig> configs) : this._withTargets(
+ configs.map((WebCompilerConfig config) =>
+ switch (config) {
+ WasmCompilerConfig() => Dart2WasmTarget(config),
+ JsCompilerConfig() => Dart2JSTarget(config),
+ }
+ ).toList()
+ );
- final WebRendererMode webRenderer;
- final bool isWasm;
+ const WebReleaseBundle._withTargets(this.compileTargets);
- String get outputFileNameNoSuffix => 'main.dart';
- String get outputFileName => '$outputFileNameNoSuffix${isWasm ? '.wasm' : '.js'}';
- String get wasmJSRuntimeFileName => '$outputFileNameNoSuffix.mjs';
+ final List<Dart2WebTarget> compileTargets;
+
+ List<String> get buildFiles => compileTargets.fold(
+ const Iterable<String>.empty(),
+ (Iterable<String> current, Dart2WebTarget target) => current.followedBy(target.buildFiles)
+ ).toList();
@override
String get name => 'web_release_bundle';
@override
- List<Target> get dependencies => <Target>[
- if (isWasm) Dart2WasmTarget(webRenderer) else Dart2JSTarget(webRenderer),
- ];
+ List<Target> get dependencies => compileTargets;
@override
List<Source> get inputs => <Source>[
- Source.pattern('{BUILD_DIR}/$outputFileName'),
const Source.pattern('{PROJECT_DIR}/pubspec.yaml'),
- if (isWasm) Source.pattern('{BUILD_DIR}/$wasmJSRuntimeFileName'),
];
@override
- List<Source> get outputs => <Source>[
- Source.pattern('{OUTPUT_DIR}/$outputFileName'),
- if (isWasm) Source.pattern('{OUTPUT_DIR}/$wasmJSRuntimeFileName'),
- ];
+ List<Source> get outputs => <Source>[];
@override
List<String> get depfiles => const <String>[
- 'dart2js.d',
'flutter_assets.d',
'web_resources.d',
];
- bool shouldCopy(String name) =>
- // Do not copy the deps file.
- (name.contains(outputFileName) && !name.endsWith('.deps')) ||
- (isWasm && name == wasmJSRuntimeFileName);
-
@override
Future<void> build(Environment environment) async {
for (final File outputFile in environment.buildDir.listSync(recursive: true).whereType<File>()) {
final String basename = environment.fileSystem.path.basename(outputFile.path);
- if (shouldCopy(basename)) {
+ if (buildFiles.contains(basename)) {
outputFile.copySync(
environment.outputDir.childFile(environment.fileSystem.path.basename(outputFile.path)).path
);
}
}
- 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);
@@ -422,10 +467,19 @@
// because it would need to be the hash for the entire bundle and not just the resource
// in question.
if (environment.fileSystem.path.basename(inputFile.path) == 'index.html') {
+ final List<Map<String, Object?>> buildDescriptions = compileTargets.map(
+ (Dart2WebTarget target) => target.buildConfig
+ ).toList();
+ final Map<String, Object?> buildConfig = <String, Object?>{
+ 'engineRevision': globals.flutterVersion.engineRevision,
+ 'builds': buildDescriptions,
+ };
+ final String buildConfigString = '_flutter.buildConfig = ${jsonEncode(buildConfig)};';
final IndexHtml indexHtml = IndexHtml(inputFile.readAsStringSync());
indexHtml.applySubstitutions(
baseHref: environment.defines[kBaseHref] ?? '/',
serviceWorkerVersion: Random().nextInt(4294967296).toString(),
+ buildConfig: buildConfigString,
);
outputFile.writeAsStringSync(indexHtml.content);
continue;
@@ -465,11 +519,9 @@
/// These assets can be cached until a new version of the flutter web sdk is
/// downloaded.
class WebBuiltInAssets extends Target {
- const WebBuiltInAssets(this.fileSystem, this.webRenderer, {required this.isWasm});
+ const WebBuiltInAssets(this.fileSystem);
final FileSystem fileSystem;
- final WebRendererMode webRenderer;
- final bool isWasm;
@override
String get name => 'web_static_assets';
@@ -500,7 +552,6 @@
@override
List<Source> get outputs => <Source>[
- if (isWasm) const Source.pattern('{BUILD_DIR}/main.dart.js'),
const Source.pattern('{BUILD_DIR}/flutter.js'),
for (final File file in _canvasKitFiles)
Source.pattern('{BUILD_DIR}/canvaskit/${_filePathRelativeToCanvasKitDirectory(file)}'),
@@ -514,13 +565,6 @@
file.copySync(targetPath);
}
- if (isWasm) {
- final File bootstrapFile = environment.outputDir.childFile('main.dart.js');
- bootstrapFile.writeAsStringSync(
- wasm_bootstrap.generateWasmBootstrapFile(webRenderer == WebRendererMode.skwasm)
- );
- }
-
// Write the flutter.js file
final String flutterJsOut = fileSystem.path.join(environment.outputDir.path, 'flutter.js');
final File flutterJsFile = fileSystem.file(fileSystem.path.join(
@@ -533,20 +577,18 @@
/// Generate a service worker for a web target.
class WebServiceWorker extends Target {
- const WebServiceWorker(this.fileSystem, this.webRenderer, {required this.isWasm});
+ const WebServiceWorker(this.fileSystem, this.compileConfigs);
final FileSystem fileSystem;
- final WebRendererMode webRenderer;
- final bool isWasm;
+ final List<WebCompilerConfig> compileConfigs;
@override
String get name => 'web_service_worker';
@override
List<Target> get dependencies => <Target>[
- if (isWasm) Dart2WasmTarget(webRenderer) else Dart2JSTarget(webRenderer),
- WebReleaseBundle(webRenderer, isWasm: isWasm),
- WebBuiltInAssets(fileSystem, webRenderer, isWasm: isWasm),
+ WebReleaseBundle(compileConfigs),
+ WebBuiltInAssets(fileSystem),
];
@override
@@ -601,6 +643,10 @@
urlToHash,
<String>[
'main.dart.js',
+ if (compileConfigs.any((WebCompilerConfig config) => config is WasmCompilerConfig)) ...<String>[
+ 'main.dart.wasm',
+ 'main.dart.mjs',
+ ],
'index.html',
if (urlToHash.containsKey('assets/AssetManifest.bin.json'))
'assets/AssetManifest.bin.json',
diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart
index 52e2410..7be2209 100644
--- a/packages/flutter_tools/lib/src/commands/build_web.dart
+++ b/packages/flutter_tools/lib/src/commands/build_web.dart
@@ -138,24 +138,50 @@
throwToolExit('"build web" is not currently supported. To enable, run "flutter config --enable-web".');
}
- final WebCompilerConfig compilerConfig;
+ final List<WebCompilerConfig> compilerConfigs;
if (boolArg('wasm')) {
if (!featureFlags.isFlutterWebWasmEnabled) {
throwToolExit('Compiling to WebAssembly (wasm) is only available on the master channel.');
}
- compilerConfig = WasmCompilerConfig(
- omitTypeChecks: boolArg('omit-type-checks'),
- wasmOpt: WasmOptLevel.values.byName(stringArg('wasm-opt')!),
+ if (stringArg(FlutterOptions.kWebRendererFlag) != argParser.defaultFor(FlutterOptions.kWebRendererFlag)) {
+ throwToolExit('"--${FlutterOptions.kWebRendererFlag}" cannot be combined with "--${FlutterOptions.kWebWasmFlag}"');
+ }
+ globals.logger.printBox(
+ title: 'Experimental feature',
+ '''
+ WebAssembly compilation is experimental.
+ $kWasmMoreInfo''',
);
+
+ compilerConfigs = <WebCompilerConfig>[
+ WasmCompilerConfig(
+ omitTypeChecks: boolArg('omit-type-checks'),
+ wasmOpt: WasmOptLevel.values.byName(stringArg('wasm-opt')!),
+ renderer: WebRendererMode.skwasm,
+ ),
+ JsCompilerConfig(
+ csp: boolArg('csp'),
+ optimizationLevel: stringArg('dart2js-optimization') ?? JsCompilerConfig.kDart2jsDefaultOptimizationLevel,
+ dumpInfo: boolArg('dump-info'),
+ nativeNullAssertions: boolArg('native-null-assertions'),
+ noFrequencyBasedMinification: boolArg('no-frequency-based-minification'),
+ sourceMaps: boolArg('source-maps'),
+ renderer: WebRendererMode.canvaskit,
+ )];
} else {
- compilerConfig = JsCompilerConfig(
+ WebRendererMode webRenderer = WebRendererMode.auto;
+ if (argParser.options.containsKey(FlutterOptions.kWebRendererFlag)) {
+ webRenderer = WebRendererMode.values.byName(stringArg(FlutterOptions.kWebRendererFlag)!);
+ }
+ compilerConfigs = <WebCompilerConfig>[JsCompilerConfig(
csp: boolArg('csp'),
optimizationLevel: stringArg('dart2js-optimization') ?? JsCompilerConfig.kDart2jsDefaultOptimizationLevel,
dumpInfo: boolArg('dump-info'),
nativeNullAssertions: boolArg('native-null-assertions'),
noFrequencyBasedMinification: boolArg('no-frequency-based-minification'),
sourceMaps: boolArg('source-maps'),
- );
+ renderer: webRenderer,
+ )];
}
final FlutterProject flutterProject = FlutterProject.current();
@@ -205,7 +231,7 @@
target,
buildInfo,
ServiceWorkerStrategy.fromCliName(stringArg('pwa-strategy')),
- compilerConfig: compilerConfig,
+ compilerConfigs: compilerConfigs,
baseHref: baseHref,
outputDirectoryPath: outputDirectoryPath,
);
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 0328453..16fbf80 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -29,6 +29,7 @@
import '../runner/flutter_command_runner.dart';
import '../tracing.dart';
import '../vmservice.dart';
+import '../web/compile.dart';
import '../web/web_runner.dart';
import 'daemon.dart';
@@ -241,6 +242,10 @@
final Map<String, String> webHeaders = featureFlags.isWebEnabled
? extractWebHeaders()
: const <String, String>{};
+ final String? webRendererString = stringArg('web-renderer');
+ final WebRendererMode webRenderer = (webRendererString != null)
+ ? WebRendererMode.values.byName(webRendererString)
+ : WebRendererMode.auto;
if (buildInfo.mode.isRelease) {
return DebuggingOptions.disabled(
@@ -258,6 +263,7 @@
webBrowserDebugPort: webBrowserDebugPort,
webBrowserFlags: webBrowserFlags,
webHeaders: webHeaders,
+ webRenderer: webRenderer,
enableImpeller: enableImpeller,
enableVulkanValidation: enableVulkanValidation,
uninstallFirst: uninstallFirst,
@@ -307,6 +313,7 @@
webEnableExpressionEvaluation: featureFlags.isWebEnabled && boolArg('web-enable-expression-evaluation'),
webLaunchUrl: featureFlags.isWebEnabled ? stringArg('web-launch-url') : null,
webHeaders: webHeaders,
+ webRenderer: webRenderer,
vmserviceOutFile: stringArg('vmservice-out-file'),
fastStart: argParser.options.containsKey('fast-start')
&& boolArg('fast-start')
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 69938a9..1f0cc0b 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -18,6 +18,7 @@
import 'device_port_forwarder.dart';
import 'project.dart';
import 'vmservice.dart';
+import 'web/compile.dart';
DeviceManager? get deviceManager => context.get<DeviceManager>();
@@ -952,6 +953,7 @@
this.webEnableExpressionEvaluation = false,
this.webHeaders = const <String, String>{},
this.webLaunchUrl,
+ this.webRenderer = WebRendererMode.auto,
this.vmserviceOutFile,
this.fastStart = false,
this.nullAssertions = false,
@@ -981,6 +983,7 @@
this.webBrowserFlags = const <String>[],
this.webLaunchUrl,
this.webHeaders = const <String, String>{},
+ this.webRenderer = WebRendererMode.auto,
this.cacheSkSL = false,
this.traceAllowlist,
this.enableImpeller = ImpellerStatus.platformDefault,
@@ -1060,6 +1063,7 @@
required this.webEnableExpressionEvaluation,
required this.webHeaders,
required this.webLaunchUrl,
+ required this.webRenderer,
required this.vmserviceOutFile,
required this.fastStart,
required this.nullAssertions,
@@ -1144,6 +1148,9 @@
/// Allow developers to add custom headers to web server
final Map<String, String> webHeaders;
+ /// Which web renderer to use for the debugging session
+ final WebRendererMode webRenderer;
+
/// A file where the VM Service URL should be written after the application is started.
final String? vmserviceOutFile;
final bool fastStart;
@@ -1252,6 +1259,7 @@
'webEnableExpressionEvaluation': webEnableExpressionEvaluation,
'webLaunchUrl': webLaunchUrl,
'webHeaders': webHeaders,
+ 'webRenderer': webRenderer.name,
'vmserviceOutFile': vmserviceOutFile,
'fastStart': fastStart,
'nullAssertions': nullAssertions,
@@ -1307,6 +1315,7 @@
webEnableExpressionEvaluation: json['webEnableExpressionEvaluation']! as bool,
webHeaders: (json['webHeaders']! as Map<dynamic, dynamic>).cast<String, String>(),
webLaunchUrl: json['webLaunchUrl'] as String?,
+ webRenderer: WebRendererMode.values.byName(json['webRenderer']! as String),
vmserviceOutFile: json['vmserviceOutFile'] as String?,
fastStart: json['fastStart']! as bool,
nullAssertions: json['nullAssertions']! as bool,
diff --git a/packages/flutter_tools/lib/src/drive/web_driver_service.dart b/packages/flutter_tools/lib/src/drive/web_driver_service.dart
index 8f6241a..fa10cfb 100644
--- a/packages/flutter_tools/lib/src/drive/web_driver_service.dart
+++ b/packages/flutter_tools/lib/src/drive/web_driver_service.dart
@@ -78,12 +78,14 @@
buildInfo,
port: debuggingOptions.port,
hostname: debuggingOptions.hostname,
+ webRenderer: debuggingOptions.webRenderer,
)
: DebuggingOptions.enabled(
buildInfo,
port: debuggingOptions.port,
hostname: debuggingOptions.hostname,
disablePortPublication: debuggingOptions.disablePortPublication,
+ webRenderer: debuggingOptions.webRenderer,
),
stayResident: true,
flutterProject: FlutterProject.current(),
diff --git a/packages/flutter_tools/lib/src/html_utils.dart b/packages/flutter_tools/lib/src/html_utils.dart
index ad394aa..2477a90 100644
--- a/packages/flutter_tools/lib/src/html_utils.dart
+++ b/packages/flutter_tools/lib/src/html_utils.dart
@@ -62,6 +62,7 @@
void applySubstitutions({
required String baseHref,
required String? serviceWorkerVersion,
+ String? buildConfig,
}) {
if (_content.contains(kBaseHrefPlaceholder)) {
_content = _content.replaceAll(kBaseHrefPlaceholder, baseHref);
@@ -81,6 +82,12 @@
"navigator.serviceWorker.register('flutter_service_worker.js?v=$serviceWorkerVersion')",
);
}
+ if (buildConfig != null) {
+ _content = _content.replaceFirst(
+ '{{flutter_build_config}}',
+ buildConfig,
+ );
+ }
}
}
diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart
index fcc1de0..ac05676 100644
--- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart
+++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart
@@ -117,8 +117,9 @@
this.internetAddress,
this._modules,
this._digests,
- this._nullSafetyMode,
- ) : basePath = _getIndexHtml().getBaseHref();
+ this._nullSafetyMode, {
+ required this.webRenderer,
+ }) : basePath = _getIndexHtml().getBaseHref();
// Fallback to "application/octet-stream" on null which
// makes no claims as to the structure of the data.
@@ -177,6 +178,7 @@
ExpressionCompiler? expressionCompiler,
Map<String, String> extraHeaders,
NullSafetyMode nullSafetyMode, {
+ required WebRendererMode webRenderer,
bool testMode = false,
DwdsLauncher dwdsLauncher = Dwds.start,
}) async {
@@ -225,6 +227,7 @@
modules,
digests,
nullSafetyMode,
+ webRenderer: webRenderer,
);
if (testMode) {
return server;
@@ -504,16 +507,29 @@
}
/// Determines what rendering backed to use.
- WebRendererMode webRenderer = WebRendererMode.html;
+ final WebRendererMode webRenderer;
shelf.Response _serveIndex() {
final IndexHtml indexHtml = _getIndexHtml();
+ final Map<String, dynamic> buildConfig = <String, dynamic>{
+ 'engineRevision': globals.flutterVersion.engineRevision,
+ 'builds': <dynamic>[
+ <String, dynamic>{
+ 'compileTarget': 'dartdevc',
+ 'renderer': webRenderer.name,
+ 'mainJsPath': 'main.dart.js',
+ },
+ ],
+ };
+ final String buildConfigString = '_flutter.buildConfig = ${jsonEncode(buildConfig)};';
+
indexHtml.applySubstitutions(
// Currently, we don't support --base-href for the "run" command.
baseHref: '/',
serviceWorkerVersion: null,
+ buildConfig: buildConfigString,
);
final Map<String, String> headers = <String, String>{
@@ -663,6 +679,7 @@
required this.nullAssertions,
required this.nativeNullAssertions,
required this.nullSafetyMode,
+ required this.webRenderer,
this.testMode = false,
}) : _port = port;
@@ -686,6 +703,7 @@
final NullSafetyMode nullSafetyMode;
final String? tlsCertPath;
final String? tlsCertKeyPath;
+ final WebRendererMode webRenderer;
late WebAssetServer webAssetServer;
@@ -785,15 +803,11 @@
expressionCompiler,
extraHeaders,
nullSafetyMode,
+ webRenderer: webRenderer,
testMode: testMode,
);
final int selectedPort = webAssetServer.selectedPort;
- if (buildInfo.dartDefines.contains('FLUTTER_WEB_AUTO_DETECT=true')) {
- webAssetServer.webRenderer = WebRendererMode.auto;
- } else if (buildInfo.dartDefines.contains('FLUTTER_WEB_USE_SKIA=true')) {
- webAssetServer.webRenderer = WebRendererMode.canvaskit;
- }
String url = '$hostname:$selectedPort';
if (hostname == 'any') {
url ='localhost:$selectedPort';
diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
index 545df73..94e1a88 100644
--- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
@@ -309,6 +309,7 @@
nullAssertions: debuggingOptions.nullAssertions,
nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode,
nativeNullAssertions: debuggingOptions.nativeNullAssertions,
+ webRenderer: debuggingOptions.webRenderer,
);
Uri url = await device!.devFS!.create();
if (debuggingOptions.tlsCertKeyPath != null && debuggingOptions.tlsCertPath != null) {
@@ -339,7 +340,12 @@
target,
debuggingOptions.buildInfo,
ServiceWorkerStrategy.none,
- compilerConfig: JsCompilerConfig.run(nativeNullAssertions: debuggingOptions.nativeNullAssertions)
+ compilerConfigs: <WebCompilerConfig>[
+ JsCompilerConfig.run(
+ nativeNullAssertions: debuggingOptions.nativeNullAssertions,
+ renderer: debuggingOptions.webRenderer,
+ )
+ ]
);
}
await device!.device!.startApp(
@@ -418,7 +424,12 @@
target,
debuggingOptions.buildInfo,
ServiceWorkerStrategy.none,
- compilerConfig: JsCompilerConfig.run(nativeNullAssertions: debuggingOptions.nativeNullAssertions),
+ compilerConfigs: <WebCompilerConfig>[
+ JsCompilerConfig.run(
+ nativeNullAssertions: debuggingOptions.nativeNullAssertions,
+ renderer: debuggingOptions.webRenderer,
+ )
+ ],
);
} on ToolExit {
return OperationResult(1, 'Failed to recompile application.');
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index c969484..0b4d405 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -137,7 +137,7 @@
}
final String platformDillPath = globals.fs.path.join(
- getWebPlatformBinariesDirectory(globals.artifacts!, buildInfo.webRenderer).path,
+ globals.artifacts!.getHostArtifact(HostArtifact.webPlatformKernelFolder).path,
platformDillName,
);
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 905b0fa..6859b74 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -1263,13 +1263,7 @@
: null;
final Map<String, Object?> defineConfigJsonMap = extractDartDefineConfigJsonMap();
- List<String> dartDefines = extractDartDefines(defineConfigJsonMap: defineConfigJsonMap);
-
- WebRendererMode webRenderer = WebRendererMode.auto;
- if (argParser.options.containsKey(FlutterOptions.kWebRendererFlag)) {
- webRenderer = WebRendererMode.values.byName(stringArg(FlutterOptions.kWebRendererFlag)!);
- dartDefines = updateDartDefines(dartDefines, webRenderer);
- }
+ final List<String> dartDefines = extractDartDefines(defineConfigJsonMap: defineConfigJsonMap);
if (argParser.options.containsKey(FlutterOptions.kWebResourcesCdnFlag)) {
final bool hasLocalWebSdk = argParser.options.containsKey('local-web-sdk') && stringArg('local-web-sdk') != null;
@@ -1317,7 +1311,6 @@
dartDefines: dartDefines,
bundleSkSLPath: bundleSkSLPath,
dartExperiments: experiments,
- webRenderer: webRenderer,
performanceMeasurementFile: performanceMeasurementFile,
packagesPath: packagesPath ?? globals.fs.path.absolute('.dart_tool', 'package_config.json'),
nullSafetyMode: nullSafetyMode,
@@ -1555,19 +1548,6 @@
return jsonEncode(propertyMap);
}
- /// Updates dart-defines based on [webRenderer].
- @visibleForTesting
- static List<String> updateDartDefines(List<String> dartDefines, WebRendererMode webRenderer) {
- final Set<String> dartDefinesSet = dartDefines.toSet();
- if (!dartDefines.any((String d) => d.startsWith('FLUTTER_WEB_AUTO_DETECT='))
- && dartDefines.any((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='))) {
- dartDefinesSet.removeWhere((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='));
- }
- dartDefinesSet.addAll(webRenderer.dartDefines);
- return dartDefinesSet.toList();
- }
-
-
Map<String, String> extractWebHeaders() {
final Map<String, String> webHeaders = <String, String>{};
diff --git a/packages/flutter_tools/lib/src/test/web_test_compiler.dart b/packages/flutter_tools/lib/src/test/web_test_compiler.dart
index ecf28ae..7067590 100644
--- a/packages/flutter_tools/lib/src/test/web_test_compiler.dart
+++ b/packages/flutter_tools/lib/src/test/web_test_compiler.dart
@@ -69,7 +69,7 @@
}
final String platformDillPath = _fileSystem.path.join(
- getWebPlatformBinariesDirectory(_artifacts, buildInfo.webRenderer).path,
+ _artifacts.getHostArtifact(HostArtifact.webPlatformKernelFolder).path,
platformDillName
);
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
index 9fb7073..c16b041 100644
--- a/packages/flutter_tools/lib/src/web/compile.dart
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -25,7 +25,6 @@
import 'compiler_config.dart';
import 'file_generators/flutter_service_worker_js.dart';
import 'migrations/scrub_generated_plugin_registrant.dart';
-import 'web_constants.dart';
export 'compiler_config.dart';
@@ -59,23 +58,14 @@
String target,
BuildInfo buildInfo,
ServiceWorkerStrategy serviceWorkerStrategy, {
- required WebCompilerConfig compilerConfig,
+ required List<WebCompilerConfig> compilerConfigs,
String? baseHref,
String? outputDirectoryPath,
}) async {
- if (compilerConfig.isWasm) {
- globals.logger.printBox(
- title: 'Experimental feature',
- '''
- WebAssembly compilation is experimental.
- $kWasmMoreInfo''',
- );
- }
-
final bool hasWebPlugins =
(await findPlugins(flutterProject)).any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
final Directory outputDirectory = outputDirectoryPath == null
- ? _fileSystem.directory(getWebBuildDirectory(compilerConfig.isWasm))
+ ? _fileSystem.directory(getWebBuildDirectory())
: _fileSystem.directory(outputDirectoryPath);
outputDirectory.createSync(recursive: true);
@@ -91,7 +81,7 @@
final Stopwatch sw = Stopwatch()..start();
try {
final BuildResult result = await _buildSystem.build(
- WebServiceWorker(_fileSystem, buildInfo.webRenderer, isWasm: compilerConfig.isWasm),
+ WebServiceWorker(_fileSystem, compilerConfigs),
Environment(
projectDir: _fileSystem.currentDirectory,
outputDir: outputDirectory,
@@ -101,7 +91,6 @@
kHasWebPlugins: hasWebPlugins.toString(),
if (baseHref != null) kBaseHref: baseHref,
kServiceWorkerStrategy: serviceWorkerStrategy.cliName,
- ...compilerConfig.toBuildSystemEnvironment(),
...buildInfo.toBuildSystemEnvironment(),
},
artifacts: globals.artifacts!,
@@ -134,8 +123,7 @@
}
final String buildSettingsString = _buildEventAnalyticsSettings(
- config: compilerConfig,
- buildInfo: buildInfo,
+ configs: compilerConfigs,
);
BuildEvent(
@@ -151,14 +139,15 @@
));
final Duration elapsedDuration = sw.elapsed;
+ final String variableName = compilerConfigs.length > 1 ? 'dual-compile' : 'dart2js';
_flutterUsage.sendTiming(
'build',
- compilerConfig.isWasm ? 'dart2wasm' : 'dart2js',
+ variableName,
elapsedDuration,
);
_analytics.send(Event.timing(
workflow: 'build',
- variableName: compilerConfig.isWasm ? 'dart2wasm' : 'dart2js',
+ variableName: variableName,
elapsedMilliseconds: elapsedDuration.inMilliseconds,
));
}
@@ -245,13 +234,18 @@
};
String _buildEventAnalyticsSettings({
- required WebCompilerConfig config,
- required BuildInfo buildInfo,
+ required List<WebCompilerConfig> configs,
}) {
- final Map<String, Object> values = <String, Object>{
- ...config.buildEventAnalyticsValues,
- 'web-renderer': buildInfo.webRenderer.cliName,
- };
+ final Map<String, Object> values = <String, Object>{};
+ final List<String> renderers = <String>[];
+ final List<String> targets = <String>[];
+ for (final WebCompilerConfig config in configs) {
+ values.addAll(config.buildEventAnalyticsValues);
+ renderers.add(config.renderer.name);
+ targets.add(config.compileTarget.name);
+ }
+ values['web-renderer'] = renderers.join(',');
+ values['web-target'] = targets.join(',');
final List<String> sortedList = values.entries
.map((MapEntry<String, Object> e) => '${e.key}: ${e.value};')
diff --git a/packages/flutter_tools/lib/src/web/compiler_config.dart b/packages/flutter_tools/lib/src/web/compiler_config.dart
index 286725b..676c8dc 100644
--- a/packages/flutter_tools/lib/src/web/compiler_config.dart
+++ b/packages/flutter_tools/lib/src/web/compiler_config.dart
@@ -3,58 +3,48 @@
// found in the LICENSE file.
import '../base/utils.dart';
+import '../convert.dart';
+import 'compile.dart';
-abstract class WebCompilerConfig {
- const WebCompilerConfig();
+enum CompileTarget {
+ js,
+ wasm,
+}
- /// Returns `true` if `this` represents configuration for the Wasm compiler.
- ///
- /// Otherwise, `false`–represents the JavaScript compiler.
- bool get isWasm;
+sealed class WebCompilerConfig {
+ const WebCompilerConfig({required this.renderer});
- Map<String, String> toBuildSystemEnvironment();
+ /// Returns which target this compiler outputs (js or wasm)
+ CompileTarget get compileTarget;
+ final WebRendererMode renderer;
- Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
- 'wasm-compile': isWasm,
- };
+ String get buildKey;
+
+ Map<String, Object> get buildEventAnalyticsValues => <String, Object>{};
}
/// Configuration for the Dart-to-Javascript compiler (dart2js).
class JsCompilerConfig extends WebCompilerConfig {
const JsCompilerConfig({
- required this.csp,
- required this.dumpInfo,
- required this.nativeNullAssertions,
- required this.optimizationLevel,
- required this.noFrequencyBasedMinification,
- required this.sourceMaps,
+ this.csp = false,
+ this.dumpInfo = false,
+ this.nativeNullAssertions = false,
+ this.optimizationLevel = kDart2jsDefaultOptimizationLevel,
+ this.noFrequencyBasedMinification = false,
+ this.sourceMaps = true,
+ super.renderer = WebRendererMode.auto,
});
/// Instantiates [JsCompilerConfig] suitable for the `flutter run` command.
- const JsCompilerConfig.run({required bool nativeNullAssertions})
- : this(
- csp: false,
- dumpInfo: false,
+ const JsCompilerConfig.run({
+ required bool nativeNullAssertions,
+ required WebRendererMode renderer,
+ }) : this(
nativeNullAssertions: nativeNullAssertions,
- noFrequencyBasedMinification: false,
optimizationLevel: kDart2jsDefaultOptimizationLevel,
- sourceMaps: true,
+ renderer: renderer,
);
- /// Creates a new [JsCompilerConfig] from build system environment values.
- ///
- /// Should correspond exactly with [toBuildSystemEnvironment].
- factory JsCompilerConfig.fromBuildSystemEnvironment(
- Map<String, String> defines) =>
- JsCompilerConfig(
- csp: defines[kCspMode] == 'true',
- dumpInfo: defines[kDart2jsDumpInfo] == 'true',
- nativeNullAssertions: defines[kNativeNullAssertions] == 'true',
- optimizationLevel: defines[kDart2jsOptimization] ?? kDart2jsDefaultOptimizationLevel,
- noFrequencyBasedMinification: defines[kDart2jsNoFrequencyBasedMinification] == 'true',
- sourceMaps: defines[kSourceMapsEnabled] == 'true',
- );
-
/// The default optimization level for dart2js.
///
/// Maps to [kDart2jsOptimization].
@@ -102,17 +92,7 @@
final bool sourceMaps;
@override
- bool get isWasm => false;
-
- @override
- Map<String, String> toBuildSystemEnvironment() => <String, String>{
- kCspMode: csp.toString(),
- kDart2jsDumpInfo: dumpInfo.toString(),
- kNativeNullAssertions: nativeNullAssertions.toString(),
- kDart2jsNoFrequencyBasedMinification: noFrequencyBasedMinification.toString(),
- kDart2jsOptimization: optimizationLevel,
- kSourceMapsEnabled: sourceMaps.toString(),
- };
+ CompileTarget get compileTarget => CompileTarget.js;
/// Arguments to use in both phases: full JS compile and CFE-only.
List<String> toSharedCommandOptions() => <String>[
@@ -130,25 +110,29 @@
if (noFrequencyBasedMinification) '--no-frequency-based-minification',
if (csp) '--csp',
];
+
+ @override
+ String get buildKey {
+ final Map<String, dynamic> settings = <String, dynamic>{
+ 'csp': csp,
+ 'dumpInfo': dumpInfo,
+ 'nativeNullAssertions': nativeNullAssertions,
+ 'noFrequencyBasedMinification': noFrequencyBasedMinification,
+ 'optimizationLevel': optimizationLevel,
+ 'sourceMaps': sourceMaps,
+ };
+ return jsonEncode(settings);
+ }
}
/// Configuration for the Wasm compiler.
class WasmCompilerConfig extends WebCompilerConfig {
const WasmCompilerConfig({
- required this.omitTypeChecks,
- required this.wasmOpt,
+ this.omitTypeChecks = false,
+ this.wasmOpt = WasmOptLevel.defaultValue,
+ super.renderer = WebRendererMode.auto,
});
- /// Creates a new [WasmCompilerConfig] from build system environment values.
- ///
- /// Should correspond exactly with [toBuildSystemEnvironment].
- factory WasmCompilerConfig.fromBuildSystemEnvironment(
- Map<String, String> defines) =>
- WasmCompilerConfig(
- omitTypeChecks: defines[kOmitTypeChecks] == 'true',
- wasmOpt: WasmOptLevel.values.byName(defines[kRunWasmOpt]!),
- );
-
/// Build environment for [omitTypeChecks].
static const String kOmitTypeChecks = 'WasmOmitTypeChecks';
@@ -162,25 +146,31 @@
final WasmOptLevel wasmOpt;
@override
- bool get isWasm => true;
+ CompileTarget get compileTarget => CompileTarget.wasm;
- bool get runWasmOpt => wasmOpt == WasmOptLevel.full || wasmOpt == WasmOptLevel.debug;
-
- @override
- Map<String, String> toBuildSystemEnvironment() => <String, String>{
- kOmitTypeChecks: omitTypeChecks.toString(),
- kRunWasmOpt: wasmOpt.name,
- };
+ bool get runWasmOpt =>
+ wasmOpt == WasmOptLevel.full || wasmOpt == WasmOptLevel.debug;
List<String> toCommandOptions() => <String>[
- if (omitTypeChecks) '--omit-type-checks',
- ];
+ if (omitTypeChecks) '--omit-type-checks',
+ ];
@override
Map<String, Object> get buildEventAnalyticsValues => <String, Object>{
- ...super.buildEventAnalyticsValues,
- ...toBuildSystemEnvironment(),
- };
+ ...super.buildEventAnalyticsValues,
+ kOmitTypeChecks: omitTypeChecks.toString(),
+ kRunWasmOpt: wasmOpt.name,
+ };
+
+ @override
+ String get buildKey {
+ final Map<String, dynamic> settings = <String, dynamic>{
+ 'omitTypeChecks': omitTypeChecks,
+ 'wasmOpt': wasmOpt.name,
+ };
+ return jsonEncode(settings);
+ }
+
}
enum WasmOptLevel implements CliEnum {
@@ -195,8 +185,11 @@
@override
String get helpText => switch (this) {
- WasmOptLevel.none => 'wasm-opt is not run. Fastest build; bigger, slower output.',
- WasmOptLevel.debug => 'Similar to `${WasmOptLevel.full.name}`, but member names are preserved. Debugging is easier, but size is a bit bigger.',
- WasmOptLevel.full => 'wasm-opt is run. Build time is slower, but output is smaller and faster.',
- };
+ WasmOptLevel.none =>
+ 'wasm-opt is not run. Fastest build; bigger, slower output.',
+ WasmOptLevel.debug =>
+ 'Similar to `${WasmOptLevel.full.name}`, but member names are preserved. Debugging is easier, but size is a bit bigger.',
+ WasmOptLevel.full =>
+ 'wasm-opt is run. Build time is slower, but output is smaller and faster.',
+ };
}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
index 073bc90..15a8ed0 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
@@ -11,11 +11,13 @@
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
+import 'package:flutter_tools/src/build_system/targets/web.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/build_web.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
+import 'package:flutter_tools/src/web/compile.dart';
import '../../src/common.dart';
import '../../src/context.dart';
@@ -147,15 +149,9 @@
expect(environment.defines, <String, String>{
'TargetFile': 'lib/main.dart',
'HasWebPlugins': 'true',
- 'cspMode': 'false',
- 'SourceMaps': 'false',
- 'NativeNullAssertions': 'true',
'ServiceWorkerStrategy': 'offline-first',
- 'Dart2jsDumpInfo': 'false',
- 'Dart2jsNoFrequencyBasedMinification': 'false',
- 'Dart2jsOptimization': 'O3',
'BuildMode': 'release',
- 'DartDefines': 'Zm9vPWE=,RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==',
+ 'DartDefines': 'Zm9vPWE=',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'false',
'TreeShakeIcons': 'true',
@@ -249,15 +245,8 @@
expect(environment.defines, <String, String>{
'TargetFile': 'lib/main.dart',
'HasWebPlugins': 'true',
- 'cspMode': 'false',
- 'SourceMaps': 'false',
- 'NativeNullAssertions': 'true',
'ServiceWorkerStrategy': 'offline-first',
- 'Dart2jsDumpInfo': 'false',
- 'Dart2jsNoFrequencyBasedMinification': 'false',
- 'Dart2jsOptimization': 'O4',
'BuildMode': 'release',
- 'DartDefines': 'RkxVVFRFUl9XRUJfQVVUT19ERVRFQ1Q9dHJ1ZQ==',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'false',
'TreeShakeIcons': 'true',
@@ -288,15 +277,17 @@
final CommandRunner<void> runner = createTestCommandRunner(buildCommand);
setupFileSystemForEndToEndTest(fileSystem);
await runner.run(<String>['build', 'web', '--no-pub']);
- final BuildInfo buildInfo =
- await buildCommand.webCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
- expect(buildInfo.dartDefines, contains('FLUTTER_WEB_AUTO_DETECT=true'));
}, overrides: <Type, Generator>{
Platform: () => fakePlatform,
FileSystem: () => fileSystem,
FeatureFlags: () => TestFeatureFlags(isWebEnabled: true),
ProcessManager: () => processManager,
- BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
+ BuildSystem: () => TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) {
+ expect(target, isA<WebServiceWorker>());
+ final List<WebCompilerConfig> configs = (target as WebServiceWorker).compileConfigs;
+ expect(configs.length, 1);
+ expect(configs.first.renderer, WebRendererMode.auto);
+ }),
});
testUsingContext('Web build supports build-name and build-number', () async {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index 8c097e6..96d9449 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -31,7 +31,6 @@
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/vmservice.dart';
-import 'package:flutter_tools/src/web/compile.dart';
import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart' as analytics;
import 'package:vm_service/vm_service.dart';
@@ -1086,47 +1085,6 @@
});
});
- group('dart-defines and web-renderer options', () {
- late List<String> dartDefines;
-
- setUp(() {
- dartDefines = <String>[];
- });
-
- test('auto web-renderer with no dart-defines', () {
- dartDefines = FlutterCommand.updateDartDefines(dartDefines, WebRendererMode.auto);
- expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=true']);
- });
-
- test('canvaskit web-renderer with no dart-defines', () {
- dartDefines = FlutterCommand.updateDartDefines(dartDefines, WebRendererMode.canvaskit);
- expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=true']);
- });
-
- test('html web-renderer with no dart-defines', () {
- dartDefines = FlutterCommand.updateDartDefines(dartDefines, WebRendererMode.html);
- expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=false']);
- });
-
- test('auto web-renderer with existing dart-defines', () {
- dartDefines = <String>['FLUTTER_WEB_USE_SKIA=false'];
- dartDefines = FlutterCommand.updateDartDefines(dartDefines, WebRendererMode.auto);
- expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=true']);
- });
-
- test('canvaskit web-renderer with no dart-defines', () {
- dartDefines = <String>['FLUTTER_WEB_USE_SKIA=false'];
- dartDefines = FlutterCommand.updateDartDefines(dartDefines, WebRendererMode.canvaskit);
- expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=true']);
- });
-
- test('html web-renderer with no dart-defines', () {
- dartDefines = <String>['FLUTTER_WEB_USE_SKIA=true'];
- dartDefines = FlutterCommand.updateDartDefines(dartDefines, WebRendererMode.html);
- expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=false']);
- });
- });
-
group('terminal', () {
late FakeAnsiTerminal fakeTerminal;
diff --git a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
index 22bfa2a..a5c67ab 100644
--- a/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/build_system_test.dart
@@ -21,10 +21,10 @@
void main() {
late FileSystem fileSystem;
late Environment environment;
- late Target fooTarget;
- late Target barTarget;
- late Target fizzTarget;
- late Target sharedTarget;
+ late TestTarget fooTarget;
+ late TestTarget barTarget;
+ late TestTarget fizzTarget;
+ late TestTarget sharedTarget;
late int fooInvocations;
late int barInvocations;
late int shared;
@@ -138,6 +138,23 @@
json.decode(stampFile.readAsStringSync()));
expect(stampContents, containsPair('inputs', <Object>['/foo.dart']));
+ expect(stampContents!.containsKey('buildKey'), false);
+ });
+
+ testWithoutContext('Saves a stamp file with inputs, outputs and build key', () async {
+ fooTarget.buildKey = 'fooBuildKey';
+ final BuildSystem buildSystem = setUpBuildSystem(fileSystem);
+ await buildSystem.build(fooTarget, environment);
+ final File stampFile = fileSystem.file(
+ '${environment.buildDir.path}/foo.stamp');
+
+ expect(stampFile, exists);
+
+ final Map<String, Object?>? stampContents = castStringKeyedMap(
+ json.decode(stampFile.readAsStringSync()));
+
+ expect(stampContents, containsPair('inputs', <Object>['/foo.dart']));
+ expect(stampContents, containsPair('buildKey', 'fooBuildKey'));
});
testWithoutContext('Creates a BuildResult with inputs and outputs', () async {
@@ -168,6 +185,19 @@
expect(fooInvocations, 2);
});
+ testWithoutContext('Re-invoke build if build key is modified', () async {
+ final BuildSystem buildSystem = setUpBuildSystem(fileSystem);
+ fooTarget.buildKey = 'old';
+
+ await buildSystem.build(fooTarget, environment);
+
+ fooTarget.buildKey = 'new';
+
+ await buildSystem.build(fooTarget, environment);
+
+ expect(fooInvocations, 2);
+ });
+
testWithoutContext('does not re-invoke build if input timestamp changes', () async {
final BuildSystem buildSystem = setUpBuildSystem(fileSystem);
await buildSystem.build(fooTarget, environment);
@@ -723,4 +753,7 @@
@override
List<Source> outputs = <Source>[];
+
+ @override
+ String? buildKey;
}
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_defines_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_defines_test.dart
new file mode 100644
index 0000000..71e0423
--- /dev/null
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_defines_test.dart
@@ -0,0 +1,51 @@
+// 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 'package:flutter_tools/src/build_system/targets/web.dart';
+import 'package:flutter_tools/src/web/compile.dart';
+import 'package:test/test.dart';
+
+void main() {
+
+ group('dart-defines and web-renderer options', () {
+ late List<String> dartDefines;
+
+ setUp(() {
+ dartDefines = <String>[];
+ });
+
+ test('auto web-renderer with no dart-defines', () {
+ dartDefines = updateDartDefines(dartDefines, WebRendererMode.auto);
+ expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=true']);
+ });
+
+ test('canvaskit web-renderer with no dart-defines', () {
+ dartDefines = updateDartDefines(dartDefines, WebRendererMode.canvaskit);
+ expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=true']);
+ });
+
+ test('html web-renderer with no dart-defines', () {
+ dartDefines = updateDartDefines(dartDefines, WebRendererMode.html);
+ expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=false']);
+ });
+
+ test('auto web-renderer with existing dart-defines', () {
+ dartDefines = <String>['FLUTTER_WEB_USE_SKIA=false'];
+ dartDefines = updateDartDefines(dartDefines, WebRendererMode.auto);
+ expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=true']);
+ });
+
+ test('canvaskit web-renderer with no dart-defines', () {
+ dartDefines = <String>['FLUTTER_WEB_USE_SKIA=false'];
+ dartDefines = updateDartDefines(dartDefines, WebRendererMode.canvaskit);
+ expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=true']);
+ });
+
+ test('html web-renderer with no dart-defines', () {
+ dartDefines = <String>['FLUTTER_WEB_USE_SKIA=true'];
+ dartDefines = updateDartDefines(dartDefines, WebRendererMode.html);
+ expect(dartDefines, <String>['FLUTTER_WEB_AUTO_DETECT=false','FLUTTER_WEB_USE_SKIA=false']);
+ });
+ });
+}
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
index b4c0cbd..7a5dc7b 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
@@ -120,7 +120,9 @@
webResources.childFile('index.html')
.createSync(recursive: true);
environment.buildDir.childFile('main.dart.js').createSync();
- await const WebReleaseBundle(WebRendererMode.auto, isWasm: false).build(environment);
+ await WebReleaseBundle(<WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('version.json'), exists);
}));
@@ -132,7 +134,9 @@
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.auto, isWasm: false).build(environment);
+ await WebReleaseBundle(<WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
final String versionFile = environment.outputDir
.childFile('version.json')
@@ -150,7 +154,9 @@
<!DOCTYPE html><html><base href="$kBaseHrefPlaceholder"><head></head></html>
''');
environment.buildDir.childFile('main.dart.js').createSync();
- await const WebReleaseBundle(WebRendererMode.auto, isWasm: false).build(environment);
+ await WebReleaseBundle(<WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('index.html').readAsStringSync(), contains('/basehreftest/'));
}));
@@ -163,7 +169,9 @@
<!DOCTYPE html><html><head><base href='/basehreftest/'></head></html>
''');
environment.buildDir.childFile('main.dart.js').createSync();
- await const WebReleaseBundle(WebRendererMode.auto, isWasm: false).build(environment);
+ await WebReleaseBundle(<WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('index.html').readAsStringSync(), contains('/basehreftest/'));
}));
@@ -185,7 +193,9 @@
.writeAsStringSync('A');
environment.buildDir.childFile('main.dart.js').createSync();
- await const WebReleaseBundle(WebRendererMode.auto, isWasm: false).build(environment);
+ await WebReleaseBundle(<WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'A');
@@ -197,7 +207,9 @@
// Update to arbitrary resource file triggers rebuild.
webResources.childFile('foo.txt').writeAsStringSync('B');
- await const WebReleaseBundle(WebRendererMode.auto, isWasm: false).build(environment);
+ await WebReleaseBundle(<WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('foo.txt')
.readAsStringSync(), 'B');
@@ -354,6 +366,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -366,6 +379,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -376,7 +390,12 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ csp: true,
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -388,6 +407,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -400,6 +420,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -409,7 +430,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -422,6 +447,7 @@
..._kDart2jsLinuxArgs,
'--enable-experiment=non-nullable',
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -435,6 +461,7 @@
..._kDart2jsLinuxArgs,
'--enable-experiment=non-nullable',
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -444,7 +471,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -455,6 +486,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -467,6 +499,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -476,7 +509,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -487,6 +524,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -499,6 +537,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-O4',
'-o',
@@ -507,7 +546,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -519,6 +562,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--native-null-assertions',
'--no-source-maps',
'-o',
@@ -532,6 +576,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--native-null-assertions',
'--no-source-maps',
'-O4',
@@ -541,7 +586,12 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ nativeNullAssertions: true,
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -553,6 +603,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -565,6 +616,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-O3',
'-o',
@@ -573,7 +625,12 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ optimizationLevel: 'O3',
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -584,6 +641,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -599,6 +657,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-O4',
'-o',
@@ -607,7 +666,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
expect(environment.buildDir.childFile('dart2js.d'), exists);
final Depfile depfile = environment.depFileService.parse(environment.buildDir.childFile('dart2js.d'));
@@ -628,6 +691,7 @@
'-Ddart.vm.product=true',
'-DFOO=bar',
'-DBAZ=qux',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -642,6 +706,7 @@
'-Ddart.vm.product=true',
'-DFOO=bar',
'-DBAZ=qux',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-O4',
'-o',
@@ -650,7 +715,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -662,6 +731,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
'--packages=.dart_tool/package_config.json',
@@ -673,6 +743,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'-O4',
'-o',
environment.buildDir.childFile('main.dart.js').absolute.path,
@@ -680,7 +751,9 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig()
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -695,6 +768,7 @@
'-Ddart.vm.profile=true',
'-DFOO=bar',
'-DBAZ=qux',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -709,6 +783,7 @@
'-Ddart.vm.profile=true',
'-DFOO=bar',
'-DBAZ=qux',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -718,7 +793,11 @@
]
));
- await Dart2JSTarget(WebRendererMode.auto).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -730,6 +809,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -742,6 +822,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -752,7 +833,12 @@
]
));
- await Dart2JSTarget(WebRendererMode.canvaskit).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ dumpInfo: true,
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -764,6 +850,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-source-maps',
'-o',
environment.buildDir.childFile('app.dill').absolute.path,
@@ -776,6 +863,7 @@
command: <String>[
..._kDart2jsLinuxArgs,
'-Ddart.vm.profile=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=true',
'--no-minify',
'--no-source-maps',
'-O4',
@@ -786,7 +874,12 @@
]
));
- await Dart2JSTarget(WebRendererMode.canvaskit).build(environment);
+ await Dart2JSTarget(
+ const JsCompilerConfig(
+ noFrequencyBasedMinification: true,
+ sourceMaps: false,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -805,6 +898,8 @@
'-Ddart.vm.profile=true',
'-DFOO=bar',
'-DBAZ=qux',
+ '-DFLUTTER_WEB_AUTO_DETECT=false',
+ '-DFLUTTER_WEB_USE_SKIA=true',
'--depfile=${depFile.absolute.path}',
environment.buildDir.childFile('main.dart').absolute.path,
environment.buildDir.childFile('main.dart.unopt.wasm').absolute.path,
@@ -821,7 +916,11 @@
])
);
- await Dart2WasmTarget(WebRendererMode.canvaskit).build(environment);
+ await Dart2WasmTarget(
+ const WasmCompilerConfig(
+ renderer: WebRendererMode.canvaskit
+ )
+ ).build(environment);
expect(outputJsFile.existsSync(), isFalse);
final File movedJsFile = environment.buildDir.childFile('main.dart.mjs');
@@ -843,6 +942,8 @@
command: <String>[
..._kDart2WasmLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=false',
+ '-DFLUTTER_WEB_USE_SKIA=true',
'--omit-type-checks',
'--depfile=${depFile.absolute.path}',
environment.buildDir.childFile('main.dart').absolute.path,
@@ -860,7 +961,12 @@
])
);
- await Dart2WasmTarget(WebRendererMode.canvaskit).build(environment);
+ await Dart2WasmTarget(
+ const WasmCompilerConfig(
+ omitTypeChecks: true,
+ renderer: WebRendererMode.canvaskit
+ )
+ ).build(environment);
expect(outputJsFile.existsSync(), isFalse);
final File movedJsFile = environment.buildDir.childFile('main.dart.mjs');
@@ -881,6 +987,8 @@
command: <String>[
..._kDart2WasmLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=false',
+ '-DFLUTTER_WEB_USE_SKIA=true',
'--depfile=${depFile.absolute.path}',
environment.buildDir.childFile('main.dart').absolute.path,
environment.buildDir.childFile('main.dart.unopt.wasm').absolute.path,
@@ -895,7 +1003,12 @@
environment.buildDir.childFile('main.dart.wasm').absolute.path,
]));
- await Dart2WasmTarget(WebRendererMode.canvaskit).build(environment);
+ await Dart2WasmTarget(
+ const WasmCompilerConfig(
+ wasmOpt: WasmOptLevel.debug,
+ renderer: WebRendererMode.canvaskit
+ )
+ ).build(environment);
expect(outputJsFile.existsSync(), isFalse);
final File movedJsFile = environment.buildDir.childFile('main.dart.mjs');
@@ -916,12 +1029,19 @@
command: <String>[
..._kDart2WasmLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=false',
+ '-DFLUTTER_WEB_USE_SKIA=true',
'--depfile=${depFile.absolute.path}',
environment.buildDir.childFile('main.dart').absolute.path,
environment.buildDir.childFile('main.dart.wasm').absolute.path,
], onRun: (_) => outputJsFile..createSync()..writeAsStringSync('foo')));
- await Dart2WasmTarget(WebRendererMode.canvaskit).build(environment);
+ await Dart2WasmTarget(
+ const WasmCompilerConfig(
+ wasmOpt: WasmOptLevel.none,
+ renderer: WebRendererMode.canvaskit
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -936,6 +1056,9 @@
command: <String>[
..._kDart2WasmLinuxArgs,
'-Ddart.vm.product=true',
+ '-DFLUTTER_WEB_AUTO_DETECT=false',
+ '-DFLUTTER_WEB_USE_SKIA=false',
+ '-DFLUTTER_WEB_USE_SKWASM=true',
'--import-shared-memory',
'--shared-memory-max-pages=32768',
'--depfile=${depFile.absolute.path}',
@@ -954,7 +1077,11 @@
])
);
- await Dart2WasmTarget(WebRendererMode.skwasm).build(environment);
+ await Dart2WasmTarget(
+ const WasmCompilerConfig(
+ renderer: WebRendererMode.skwasm,
+ )
+ ).build(environment);
}, overrides: <Type, Generator>{
ProcessManager: () => processManager,
}));
@@ -1002,7 +1129,9 @@
environment.outputDir.childDirectory('a').childFile('a.txt')
..createSync(recursive: true)
..writeAsStringSync('A');
- await WebServiceWorker(globals.fs, WebRendererMode.auto, isWasm: false).build(environment);
+ await WebServiceWorker(globals.fs, <WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('flutter_service_worker.js'), exists);
// Contains file hash.
@@ -1025,7 +1154,9 @@
.childFile('assets/index.html')
..createSync(recursive: true)
..writeAsStringSync('A');
- await WebServiceWorker(globals.fs, WebRendererMode.auto, isWasm: false).build(environment);
+ await WebServiceWorker(globals.fs, <WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
expect(environment.outputDir.childFile('flutter_service_worker.js'), exists);
// Contains the same file hash for both `/` and the root index.html file.
@@ -1047,7 +1178,9 @@
environment.outputDir
.childFile('main.dart.js.map')
.createSync(recursive: true);
- await WebServiceWorker(globals.fs, WebRendererMode.auto, isWasm: false).build(environment);
+ await WebServiceWorker(globals.fs, <WebCompilerConfig>[
+ const JsCompilerConfig()
+ ]).build(environment);
// No caching of source maps.
expect(environment.outputDir.childFile('flutter_service_worker.js').readAsStringSync(),
@@ -1057,24 +1190,12 @@
contains('"main.dart.js"'));
}));
- test('wasm build copies and generates specific files', () => testbed.run(() async {
- globals.fs.file('bin/cache/flutter_web_sdk/canvaskit/canvaskit.wasm')
- .createSync(recursive: true);
-
- await WebBuiltInAssets(globals.fs, WebRendererMode.auto, isWasm: true).build(environment);
-
- expect(environment.outputDir.childFile('main.dart.js').existsSync(), true);
- expect(environment.outputDir.childDirectory('canvaskit')
- .childFile('canvaskit.wasm')
- .existsSync(), true);
- }));
-
- test('wasm copies over canvaskit again if the web sdk changes', () => testbed.run(() async {
+ test('WebBuiltInAssets copies over canvaskit again if the web sdk changes', () => testbed.run(() async {
final File canvasKitInput = globals.fs.file('bin/cache/flutter_web_sdk/canvaskit/canvaskit.wasm')
..createSync(recursive: true);
canvasKitInput.writeAsStringSync('foo', flush: true);
- await WebBuiltInAssets(globals.fs, WebRendererMode.auto, isWasm: true).build(environment);
+ await WebBuiltInAssets(globals.fs).build(environment);
final File canvasKitOutputBefore = environment.outputDir.childDirectory('canvaskit')
.childFile('canvaskit.wasm');
@@ -1083,7 +1204,7 @@
canvasKitInput.writeAsStringSync('bar', flush: true);
- await WebBuiltInAssets(globals.fs, WebRendererMode.auto, isWasm: true).build(environment);
+ await WebBuiltInAssets(globals.fs).build(environment);
final File canvasKitOutputAfter = environment.outputDir.childDirectory('canvaskit')
.childFile('canvaskit.wasm');
diff --git a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart
index b355741..39a8b12 100644
--- a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart
@@ -43,16 +43,11 @@
testUsingContext('WebBuilder sets environment on success', () async {
final TestBuildSystem buildSystem =
TestBuildSystem.all(BuildResult(success: true), (Target target, Environment environment) {
- final WebServiceWorker webServiceWorker = target as WebServiceWorker;
- expect(webServiceWorker.isWasm, isTrue, reason: 'should be wasm');
- expect(webServiceWorker.webRenderer, WebRendererMode.auto);
-
+ expect(target, isA<WebServiceWorker>());
expect(environment.defines, <String, String>{
'TargetFile': 'target',
'HasWebPlugins': 'false',
'ServiceWorkerStrategy': ServiceWorkerStrategy.offlineFirst.cliName,
- 'WasmOmitTypeChecks': 'false',
- 'RunWasmOpt': 'none',
'BuildMode': 'debug',
'DartObfuscation': 'false',
'TrackWidgetCreation': 'true',
@@ -77,10 +72,16 @@
'target',
BuildInfo.debug,
ServiceWorkerStrategy.offlineFirst,
- compilerConfig: const WasmCompilerConfig(
- omitTypeChecks: false,
- wasmOpt: WasmOptLevel.none,
- ),
+ compilerConfigs: <WebCompilerConfig>[
+ const WasmCompilerConfig(
+ wasmOpt: WasmOptLevel.none,
+ renderer: WebRendererMode.skwasm,
+ ),
+ const JsCompilerConfig.run(
+ nativeNullAssertions: true,
+ renderer: WebRendererMode.canvaskit,
+ ),
+ ],
);
expect(logger.statusText, contains('Compiling target for the Web...'));
@@ -102,7 +103,7 @@
label: 'web-compile',
parameters: CustomDimensions(
buildEventSettings:
- 'RunWasmOpt: none; WasmOmitTypeChecks: false; wasm-compile: true; web-renderer: auto;',
+ 'RunWasmOpt: none; WasmOmitTypeChecks: false; web-renderer: skwasm,canvaskit; web-target: wasm,js;',
),
),
],
@@ -115,7 +116,7 @@
Event.flutterBuildInfo(
label: 'web-compile',
buildType: 'web',
- settings: 'RunWasmOpt: none; WasmOmitTypeChecks: false; wasm-compile: true; web-renderer: auto;',
+ settings: 'RunWasmOpt: none; WasmOmitTypeChecks: false; web-renderer: skwasm,canvaskit; web-target: wasm,js;',
),
]),
);
@@ -123,12 +124,12 @@
// Sends timing event.
final TestTimingEvent timingEvent = testUsage.timings.single;
expect(timingEvent.category, 'build');
- expect(timingEvent.variableName, 'dart2wasm');
+ expect(timingEvent.variableName, 'dual-compile');
expect(
analyticsTimingEventExists(
sentEvents: fakeAnalytics.sentEvents,
workflow: 'build',
- variableName: 'dart2wasm',
+ variableName: 'dual-compile',
),
true,
);
@@ -161,7 +162,9 @@
'target',
BuildInfo.debug,
ServiceWorkerStrategy.offlineFirst,
- compilerConfig: const JsCompilerConfig.run(nativeNullAssertions: true),
+ compilerConfigs: <WebCompilerConfig>[
+ const JsCompilerConfig.run(nativeNullAssertions: true, renderer: WebRendererMode.auto),
+ ]
),
throwsToolExit(message: 'Failed to compile application for the Web.'));
diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
index 5034959..0a52ef0 100644
--- a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart
@@ -66,6 +66,7 @@
<String, String>{},
<String, String>{},
NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
);
releaseAssetServer = ReleaseAssetServer(
globals.fs.file('main.dart').uri,
@@ -291,6 +292,7 @@
<String, String>{},
<String, String>{},
NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
);
expect(webAssetServer.basePath, 'foo/bar');
@@ -310,6 +312,7 @@
<String, String>{},
<String, String>{},
NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
);
// Defaults to "/" when there's no base element.
@@ -331,6 +334,7 @@
<String, String>{},
<String, String>{},
NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
),
throwsToolExit(),
);
@@ -351,6 +355,7 @@
<String, String>{},
<String, String>{},
NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
),
throwsToolExit(),
);
@@ -684,6 +689,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.html,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.flutterJs.createSync(recursive: true);
@@ -745,13 +751,6 @@
// New SDK should be visible..
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
- // Toggle CanvasKit
- expect(webDevFS.webAssetServer.webRenderer, WebRendererMode.html);
- webDevFS.webAssetServer.webRenderer = WebRendererMode.canvaskit;
-
- expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'OL');
- expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'CHUM');
-
// Generated entrypoint.
expect(await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'),
contains('GENERATED'));
@@ -800,6 +799,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound,
+ webRenderer: WebRendererMode.html,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.flutterJs.createSync(recursive: true);
@@ -861,11 +861,6 @@
// New SDK should be visible..
expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW');
- // Toggle CanvasKit
- webDevFS.webAssetServer.webRenderer = WebRendererMode.canvaskit;
- expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'OL');
- expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'CHUM');
-
// Generated entrypoint.
expect(await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'),
contains('GENERATED'));
@@ -913,6 +908,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound,
+ webRenderer: WebRendererMode.canvaskit,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -974,6 +970,7 @@
nullAssertions: true,
nativeNullAssertions: true,
nullSafetyMode: NullSafetyMode.sound,
+ webRenderer: WebRendererMode.canvaskit,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -1019,6 +1016,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound,
+ webRenderer: WebRendererMode.canvaskit,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -1065,6 +1063,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.sound,
+ webRenderer: WebRendererMode.auto,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -1112,6 +1111,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
@@ -1148,7 +1148,9 @@
null,
const <String, String>{},
NullSafetyMode.unsound,
- testMode: true);
+ webRenderer: WebRendererMode.canvaskit,
+ testMode: true
+ );
expect(webAssetServer.defaultResponseHeaders['x-frame-options'], null);
await webAssetServer.dispose();
@@ -1180,7 +1182,9 @@
extraHeaderKey: extraHeaderValue,
},
NullSafetyMode.unsound,
- testMode: true);
+ webRenderer: WebRendererMode.canvaskit,
+ testMode: true
+ );
expect(webAssetServer.defaultResponseHeaders[extraHeaderKey], <String>[extraHeaderValue]);
@@ -1215,6 +1219,7 @@
<String, String>{},
<String, String>{},
NullSafetyMode.sound,
+ webRenderer: WebRendererMode.canvaskit,
);
expect(await webAssetServer.metadataContents('foo/main_module.ddc_merged_metadata'), null);
@@ -1257,6 +1262,7 @@
extraHeaders: const <String, String>{},
chromiumLauncher: null,
nullSafetyMode: NullSafetyMode.unsound,
+ webRenderer: WebRendererMode.canvaskit,
);
webDevFS.requireJS.createSync(recursive: true);
webDevFS.stackTraceMapper.createSync(recursive: true);
diff --git a/packages/flutter_tools/test/integration.shard/flutter_build_wasm_test.dart b/packages/flutter_tools/test/integration.shard/flutter_build_wasm_test.dart
index 4290db3..40716ae 100644
--- a/packages/flutter_tools/test/integration.shard/flutter_build_wasm_test.dart
+++ b/packages/flutter_tools/test/integration.shard/flutter_build_wasm_test.dart
@@ -51,7 +51,7 @@
final Directory appBuildDir = fileSystem.directory(fileSystem.path.join(
exampleAppDir.path,
'build',
- 'web_wasm',
+ 'web',
));
for (final String filename in const <String>[
'flutter.js',
diff --git a/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart b/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart
index e01e0b9..6220347 100644
--- a/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart
+++ b/packages/flutter_tools/test/web.shard/hot_reload_web_test.dart
@@ -19,6 +19,7 @@
await _testProject(HotReloadProject(indexHtml: indexHtmlFlutterJsCallback), name: 'flutter.js (callback)');
await _testProject(HotReloadProject(indexHtml: indexHtmlFlutterJsPromisesFull), name: 'flutter.js (promises)');
await _testProject(HotReloadProject(indexHtml: indexHtmlFlutterJsPromisesShort), name: 'flutter.js (promises, short)');
+ await _testProject(HotReloadProject(indexHtml: indexHtmlFlutterJsLoad), name: 'flutter.js (load)');
await _testProject(HotReloadProject(indexHtml: indexHtmlNoFlutterJs), name: 'No flutter.js');
}
diff --git a/packages/flutter_tools/test/web.shard/test_data/hot_reload_index_html_samples.dart b/packages/flutter_tools/test/web.shard/test_data/hot_reload_index_html_samples.dart
index b1d5d2a..873604b 100644
--- a/packages/flutter_tools/test/web.shard/test_data/hot_reload_index_html_samples.dart
+++ b/packages/flutter_tools/test/web.shard/test_data/hot_reload_index_html_samples.dart
@@ -56,6 +56,27 @@
});
''');
+/// index_with_flutterjs.html
+String indexHtmlFlutterJsLoad = _generateFlutterJsIndexHtml('''
+ window.addEventListener('load', function(ev) {
+ _flutter.buildConfig = {
+ builds: [
+ {
+ "compileTarget": "dartdevc",
+ "renderer": "html",
+ "mainJsPath": "main.dart.js",
+ }
+ ]
+ };
+ // Download main.dart.js
+ _flutter.loader.load({
+ serviceWorkerSettings: {
+ serviceWorkerVersion: serviceWorkerVersion,
+ },
+ });
+ });
+''');
+
/// index_without_flutterjs.html
String indexHtmlNoFlutterJs = '''
<!DOCTYPE HTML>