From a38abc864ce67fad39cad7808235b06e10137f7c Mon Sep 17 00:00:00 2001 From: Srujan Gaddam <58529443+srujzs@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:36:43 -0800 Subject: [PATCH] Support DDC library bundle format and remove support for DDC module format (#161276) This gets us closer to enabling [hot reload](https://github.com/dart-lang/sdk/issues/54934) on the web as this format is a prerequisite. Historically, we added support for the DDC module format only to enable hot reload, but that format is not feasible for the goal, so we added the DDC library bundle format. The DDC library bundle format is currently represented as the combination of the `ddc` module format and `canary`. We no longer need to support the old DDC module format. - Adds build artifacts to build the SDKs for this format (but only in sound mode as unsound is unsupported), and removes said artifacts for the DDC module format. - Update artifact maps and constants to add the new format and remove the old format. - Adds handling of the `canaryFeatures` flag. - Update dwds to 24.3.0 and use the new `FrontendServerDdcLibraryBundleStrategyProvider`. - Add bootstrap code for the new format. Kept DDC module format bootstrap code as it's used internally. - Updates tests. I ran `spinning_square` with the new module format to verify that it can run. ## Pre-launch Checklist - [X] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [X] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [X] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [X] I signed the [CLA]. - [X] I listed at least one issue that this PR fixes in the description above. - [X] I updated/added relevant documentation (doc comments with `///`). - [X] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [X] All existing and new tests are passing. --- engine/src/flutter/web_sdk/BUILD.gn | 91 ++++++--- packages/flutter_tools/lib/src/artifacts.dart | 175 +++++------------- .../flutter_tools/lib/src/build_info.dart | 37 ++-- .../lib/src/isolated/devfs_web.dart | 41 +++- .../lib/src/isolated/resident_web_runner.dart | 1 + .../lib/src/test/flutter_web_platform.dart | 14 +- .../flutter_tools/lib/src/web/bootstrap.dart | 157 ++++++++++++++-- .../flutter_tools/lib/src/web/compile.dart | 63 ++++--- packages/flutter_tools/pubspec.yaml | 4 +- .../hermetic/flutter_web_platform_test.dart | 15 +- .../test/general.shard/artifacts_test.dart | 63 +++---- .../general.shard/web/bootstrap_test.dart | 60 +++--- .../web/devfs_web_ddc_modules_test.dart | 150 +++------------ .../general.shard/web/devfs_web_test.dart | 16 ++ 14 files changed, 449 insertions(+), 438 deletions(-) diff --git a/engine/src/flutter/web_sdk/BUILD.gn b/engine/src/flutter/web_sdk/BUILD.gn index 704c1aa7e0..925d6fac45 100644 --- a/engine/src/flutter/web_sdk/BUILD.gn +++ b/engine/src/flutter/web_sdk/BUILD.gn @@ -336,13 +336,17 @@ group("flutter_platform_dills") { ] } -template("_compile_ddc_modules") { +template("_compile_ddc_module") { assert(defined(invoker.sound_null_safety), "sound_null_safety must be defined for $target_name") - assert(defined(invoker.use_skia), - "use_skia must be defined for $target_name") + assert(defined(invoker.use_skia), "use_skia must be defined for $target_name") assert(defined(invoker.auto_detect), "auto_detect must be defined for $target_name") + assert(defined(invoker.sdk_path_prefix), + "sdk_path_prefix must be defined for $target_name") + assert(defined(invoker.module_format), + "module_name must be defined for $target_name") + assert(defined(invoker.canary), "canary must be defined for $target_name") _dartdevc(target_name) { inputs = [ "sdk_rewriter.dart" ] + web_ui_sources @@ -360,17 +364,11 @@ template("_compile_ddc_modules") { name_suffix += "-sound" } - amd_js_path = - "$root_out_dir/flutter_web_sdk/kernel/amd${name_suffix}/dart_sdk.js" - - ddc_js_path = - "$root_out_dir/flutter_web_sdk/kernel/ddc${name_suffix}/dart_sdk.js" + dart_sdk_js_path = "$root_out_dir/flutter_web_sdk/kernel/${invoker.sdk_path_prefix}${name_suffix}/dart_sdk.js" outputs = [ - amd_js_path, - amd_js_path + ".map", - ddc_js_path, - ddc_js_path + ".map", + dart_sdk_js_path, + dart_sdk_js_path + ".map", ] if (invoker.sound_null_safety) { @@ -405,14 +403,13 @@ template("_compile_ddc_modules") { "-DFLUTTER_WEB_USE_SKIA=${invoker.use_skia}", "-DFLUTTER_WEB_AUTO_DETECT=${invoker.auto_detect}", "--modules", - "amd", + invoker.module_format, "-o", - rebase_path(amd_js_path), - "--modules", - "ddc", - "-o", - rebase_path(ddc_js_path), + rebase_path(dart_sdk_js_path), ] + if (invoker.canary) { + args += [ "--canary" ] + } if (flutter_prebuilt_dart_sdk) { args += [ "--multi-root", @@ -427,6 +424,30 @@ template("_compile_ddc_modules") { } } +template("_compile_ddc_modules") { + assert(defined(invoker.sound_null_safety), + "sound_null_safety must be defined for $target_name") + forward_variables_from(invoker, "*") + + # We compile multiple times instead of passing multiple modules to a single + # compile as the DDC library bundle format cannot be used when passing + # multiple module formats as arguments. + _compile_ddc_module("${target_name}_amd") { + sdk_path_prefix = "amd" + module_format = "amd" + canary = false + } + + # No support for unsound null safety with the DDC library bundle format. + if (sound_null_safety) { + _compile_ddc_module("${target_name}_ddcLibraryBundle") { + sdk_path_prefix = "ddcLibraryBundle" + module_format = "ddc" + canary = true + } + } +} + # Compiles the unsound html only renderer. _compile_ddc_modules("flutter_dartdevc_kernel_sdk") { sound_null_safety = false @@ -471,12 +492,15 @@ _compile_ddc_modules("flutter_dartdevc_canvaskit_html_kernel_sdk_sound") { group("flutter_ddc_modules") { public_deps = [ - ":flutter_dartdevc_canvaskit_html_kernel_sdk", - ":flutter_dartdevc_canvaskit_html_kernel_sdk_sound", - ":flutter_dartdevc_canvaskit_kernel_sdk", - ":flutter_dartdevc_canvaskit_kernel_sdk_sound", - ":flutter_dartdevc_kernel_sdk", - ":flutter_dartdevc_kernel_sdk_sound", + ":flutter_dartdevc_canvaskit_html_kernel_sdk_amd", + ":flutter_dartdevc_canvaskit_html_kernel_sdk_sound_amd", + ":flutter_dartdevc_canvaskit_html_kernel_sdk_sound_ddcLibraryBundle", + ":flutter_dartdevc_canvaskit_kernel_sdk_amd", + ":flutter_dartdevc_canvaskit_kernel_sdk_sound_amd", + ":flutter_dartdevc_canvaskit_kernel_sdk_sound_ddcLibraryBundle", + ":flutter_dartdevc_kernel_sdk_amd", + ":flutter_dartdevc_kernel_sdk_sound_amd", + ":flutter_dartdevc_kernel_sdk_sound_ddcLibraryBundle", ] } @@ -500,14 +524,21 @@ if (!is_fuchsia) { deps += [ "//flutter/lib/web_ui/flutter_js" ] # flutter_ddc_modules - sources = get_target_outputs(":flutter_dartdevc_kernel_sdk") - sources += get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk") - sources += get_target_outputs(":flutter_dartdevc_canvaskit_html_kernel_sdk") - sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_sound") + sources = get_target_outputs(":flutter_dartdevc_kernel_sdk_amd") + sources += get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk_amd") sources += - get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk_sound") + get_target_outputs(":flutter_dartdevc_canvaskit_html_kernel_sdk_amd") + sources += get_target_outputs(":flutter_dartdevc_kernel_sdk_sound_amd") sources += - get_target_outputs(":flutter_dartdevc_canvaskit_html_kernel_sdk_sound") + get_target_outputs(":flutter_dartdevc_canvaskit_kernel_sdk_sound_amd") + sources += get_target_outputs( + ":flutter_dartdevc_canvaskit_html_kernel_sdk_sound_amd") + sources += get_target_outputs( + ":flutter_dartdevc_kernel_sdk_sound_ddcLibraryBundle") + sources += get_target_outputs( + ":flutter_dartdevc_canvaskit_kernel_sdk_sound_ddcLibraryBundle") + sources += get_target_outputs( + ":flutter_dartdevc_canvaskit_html_kernel_sdk_sound_ddcLibraryBundle") # flutter_platform_dills sources += diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index 0c9239e5d4..d665e5af86 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -119,19 +119,15 @@ enum HostArtifact { webPrecompiledAmdCanvaskitAndHtmlSoundSdk, webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps, - /// The precompiled SDKs and sourcemaps for web debug builds with the DDC module system. - webPrecompiledDdcSdk, - webPrecompiledDdcSdkSourcemaps, - webPrecompiledDdcCanvaskitSdk, - webPrecompiledDdcCanvaskitSdkSourcemaps, - webPrecompiledDdcCanvaskitAndHtmlSdk, - webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps, - webPrecompiledDdcSoundSdk, - webPrecompiledDdcSoundSdkSourcemaps, - webPrecompiledDdcCanvaskitSoundSdk, - webPrecompiledDdcCanvaskitSoundSdkSourcemaps, - webPrecompiledDdcCanvaskitAndHtmlSoundSdk, - webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps, + /// The precompiled SDKs and sourcemaps for web debug builds with the DDC + /// library bundle module system. Only SDKs built with sound null-safety are + /// provided here. + webPrecompiledDdcLibraryBundleSoundSdk, + webPrecompiledDdcLibraryBundleSoundSdkSourcemaps, + webPrecompiledDdcLibraryBundleCanvaskitSoundSdk, + webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps, + webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk, + webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdkSourcemaps, iosDeploy, idevicesyslog, @@ -289,12 +285,9 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) { case HostArtifact.webPrecompiledAmdSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdk: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdk: - case HostArtifact.webPrecompiledDdcSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk: - case HostArtifact.webPrecompiledDdcSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk: return 'dart_sdk.js'; case HostArtifact.webPrecompiledAmdSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSdkSourcemaps: @@ -302,12 +295,9 @@ String _hostArtifactToFileName(HostArtifact artifact, Platform platform) { case HostArtifact.webPrecompiledAmdSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitSoundSdkSourcemaps: case HostArtifact.webPrecompiledAmdCanvaskitAndHtmlSoundSdkSourcemaps: - case HostArtifact.webPrecompiledDdcSdkSourcemaps: - case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps: - case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps: - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdkSourcemaps: return 'dart_sdk.js.map'; case HostArtifact.impellerc: return 'impellerc$exe'; @@ -547,57 +537,30 @@ class CachedArtifacts implements Artifacts { _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcSdk: - case HostArtifact.webPrecompiledDdcSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc', + 'ddcLibraryBundle-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc-canvaskit', + 'ddcLibraryBundle-canvaskit-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc-canvaskit-html', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcSoundSdk: - case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-sound', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-canvaskit-sound', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-canvaskit-html-sound', + 'ddcLibraryBundle-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); @@ -1313,57 +1276,30 @@ class CachedLocalEngineArtifacts implements Artifacts { _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcSdk: - case HostArtifact.webPrecompiledDdcSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc', + 'ddcLibraryBundle-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc-canvaskit', + 'ddcLibraryBundle-canvaskit-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc-canvaskit-html', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcSoundSdk: - case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-sound', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-canvaskit-sound', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-canvaskit-html-sound', + 'ddcLibraryBundle-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); @@ -1791,57 +1727,30 @@ class CachedLocalWebSdkArtifacts implements Artifacts { _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcSdk: - case HostArtifact.webPrecompiledDdcSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc', + 'ddcLibraryBundle-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc-canvaskit', + 'ddcLibraryBundle-canvaskit-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk: + case HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdkSourcemaps: final String path = _fileSystem.path.join( _getFlutterWebSdkPath(), 'kernel', - 'ddc-canvaskit-html', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcSoundSdk: - case HostArtifact.webPrecompiledDdcSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-sound', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-canvaskit-sound', - _hostArtifactToFileName(artifact, _platform), - ); - return _fileSystem.file(path); - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk: - case HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps: - final String path = _fileSystem.path.join( - _getFlutterWebSdkPath(), - 'kernel', - 'ddc-canvaskit-html-sound', + 'ddcLibraryBundle-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform), ); return _fileSystem.file(path); diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index fde25bf117..88f46b73e2 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -265,9 +265,16 @@ class BuildInfo { /// so the uncapitalized flavor name is used to compute the output file name String? get uncapitalizedFlavor => _uncapitalize(flavor); - /// The module system DDC is targeting, or null if not using DDC. + /// The module system DDC is targeting, or null if not using DDC or the + /// associated flag isn't present. // TODO(markzipan): delete this when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060. - DdcModuleFormat? get ddcModuleFormat => _ddcModuleFormatFromFrontEndArgs(extraFrontEndOptions); + DdcModuleFormat? get ddcModuleFormat => + _ddcModuleFormatAndCanaryFeaturesFromFrontEndArgs(extraFrontEndOptions).ddcModuleFormat; + + /// Whether to enable canary features when using DDC, or null if not using + /// DDC or the associated flag isn't present. + bool? get canaryFeatures => + _ddcModuleFormatAndCanaryFeaturesFromFrontEndArgs(extraFrontEndOptions).canaryFeatures; /// Convert to a structured string encoded structure appropriate for usage /// in build system [Environment.defines]. @@ -1041,18 +1048,24 @@ enum NullSafetyMode { enum DdcModuleFormat { amd, ddc } // TODO(markzipan): delete this when DDC's AMD module system is deprecated, https://github.com/flutter/flutter/issues/142060. -DdcModuleFormat? _ddcModuleFormatFromFrontEndArgs(List? extraFrontEndArgs) { - if (extraFrontEndArgs == null) { - return null; - } - const String ddcModuleFormatString = '--dartdevc-module-format='; - for (final String flag in extraFrontEndArgs) { - if (flag.startsWith(ddcModuleFormatString)) { - final String moduleFormatString = flag.substring(ddcModuleFormatString.length, flag.length); - return DdcModuleFormat.values.byName(moduleFormatString); +({DdcModuleFormat? ddcModuleFormat, bool? canaryFeatures}) +_ddcModuleFormatAndCanaryFeaturesFromFrontEndArgs(List? extraFrontEndArgs) { + DdcModuleFormat? ddcModuleFormat; + bool? canaryFeatures; + if (extraFrontEndArgs != null) { + const String ddcModuleFormatArg = '--dartdevc-module-format='; + const String canaryFeaturesArg = '--dartdevc-canary'; + for (final String flag in extraFrontEndArgs) { + if (flag.startsWith(ddcModuleFormatArg)) { + final String moduleFormatString = flag.substring(ddcModuleFormatArg.length, flag.length); + assert(ddcModuleFormat == null); + ddcModuleFormat = DdcModuleFormat.values.byName(moduleFormatString); + } else if (flag == canaryFeaturesArg) { + canaryFeatures = true; + } } } - return null; + return (ddcModuleFormat: ddcModuleFormat, canaryFeatures: canaryFeatures); } String _getCurrentHostPlatformArchName() { diff --git a/packages/flutter_tools/lib/src/isolated/devfs_web.dart b/packages/flutter_tools/lib/src/isolated/devfs_web.dart index 801e01eb89..d41853a01f 100644 --- a/packages/flutter_tools/lib/src/isolated/devfs_web.dart +++ b/packages/flutter_tools/lib/src/isolated/devfs_web.dart @@ -125,10 +125,17 @@ class WebAssetServer implements AssetReader { this._modules, this._digests, this._nullSafetyMode, - this._ddcModuleSystem, { + this._ddcModuleSystem, + this._canaryFeatures, { required this.webRenderer, required this.useLocalCanvasKit, - }) : basePath = _getWebTemplate('index.html', _kDefaultIndex).getBaseHref(); + }) : basePath = _getWebTemplate('index.html', _kDefaultIndex).getBaseHref() { + // TODO(srujzs): Remove this assertion when the library bundle format is + // supported without canary mode. + if (_ddcModuleSystem) { + assert(_canaryFeatures); + } + } // Fallback to "application/octet-stream" on null which // makes no claims as to the structure of the data. @@ -188,7 +195,13 @@ class WebAssetServer implements AssetReader { DwdsLauncher dwdsLauncher = Dwds.start, // TODO(markzipan): Make sure this default value aligns with that in the debugger options. bool ddcModuleSystem = false, + bool canaryFeatures = false, }) async { + // TODO(srujzs): Remove this assertion when the library bundle format is + // supported without canary mode. + if (ddcModuleSystem) { + assert(canaryFeatures); + } InternetAddress address; if (hostname == 'any') { address = InternetAddress.anyIPv4; @@ -236,6 +249,7 @@ class WebAssetServer implements AssetReader { digests, nullSafetyMode, ddcModuleSystem, + canaryFeatures, webRenderer: webRenderer, useLocalCanvasKit: useLocalCanvasKit, ); @@ -300,7 +314,7 @@ class WebAssetServer implements AssetReader { toolConfiguration: ToolConfiguration( loadStrategy: ddcModuleSystem - ? FrontendServerDdcStrategyProvider( + ? FrontendServerDdcLibraryBundleStrategyProvider( ReloadConfiguration.none, server, PackageUriMapper(packageConfig), @@ -356,6 +370,7 @@ class WebAssetServer implements AssetReader { final NullSafetyMode _nullSafetyMode; final bool _ddcModuleSystem; + final bool _canaryFeatures; final HttpServer _httpServer; final WebMemoryFS _webMemoryFS = WebMemoryFS(); final PackageConfig _packages; @@ -674,7 +689,7 @@ _flutter.buildConfig = ${jsonEncode(buildConfig)}; File get _resolveDartSdkJsFile { final Map> dartSdkArtifactMap = - _ddcModuleSystem ? kDdcDartSdkJsArtifactMap : kAmdDartSdkJsArtifactMap; + _ddcModuleSystem ? kDdcLibraryBundleDartSdkJsArtifactMap : kAmdDartSdkJsArtifactMap; return globals.fs.file( globals.artifacts!.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!), ); @@ -682,7 +697,7 @@ _flutter.buildConfig = ${jsonEncode(buildConfig)}; File get _resolveDartSdkJsMapFile { final Map> dartSdkArtifactMap = - _ddcModuleSystem ? kDdcDartSdkJsMapArtifactMap : kAmdDartSdkJsMapArtifactMap; + _ddcModuleSystem ? kDdcLibraryBundleDartSdkJsMapArtifactMap : kAmdDartSdkJsMapArtifactMap; return globals.fs.file( globals.artifacts!.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!), ); @@ -762,12 +777,19 @@ class WebDevFS implements DevFS { required this.nativeNullAssertions, required this.nullSafetyMode, required this.ddcModuleSystem, + required this.canaryFeatures, required this.webRenderer, required this.isWasm, required this.useLocalCanvasKit, required this.rootDirectory, this.testMode = false, - }) : _port = port; + }) : _port = port { + // TODO(srujzs): Remove this assertion when the library bundle format is + // supported without canary mode. + if (ddcModuleSystem) { + assert(canaryFeatures); + } + } final Uri entrypoint; final String hostname; @@ -782,6 +804,7 @@ class WebDevFS implements DevFS { final Map extraHeaders; final bool testMode; final bool ddcModuleSystem; + final bool canaryFeatures; final ExpressionCompiler? expressionCompiler; final ChromiumLauncher? chromiumLauncher; final bool nullAssertions; @@ -894,6 +917,7 @@ class WebDevFS implements DevFS { useLocalCanvasKit: useLocalCanvasKit, testMode: testMode, ddcModuleSystem: ddcModuleSystem, + canaryFeatures: canaryFeatures, ); final int selectedPort = webAssetServer.selectedPort; @@ -983,7 +1007,7 @@ class WebDevFS implements DevFS { webAssetServer.writeFile( 'main.dart.js', ddcModuleSystem - ? generateDDCBootstrapScript( + ? generateDDCLibraryBundleBootstrapScript( entrypoint: entrypoint, ddcModuleLoaderUrl: 'ddc_module_loader.js', mapperUrl: 'stack_trace_mapper.js', @@ -998,11 +1022,10 @@ class WebDevFS implements DevFS { webAssetServer.writeFile( 'main_module.bootstrap.js', ddcModuleSystem - ? generateDDCMainModule( + ? generateDDCLibraryBundleMainModule( entrypoint: entrypoint, nullAssertions: nullAssertions, nativeNullAssertions: nativeNullAssertions, - exportedMain: pathToJSIdentifier(entrypoint.split('.')[0]), ) : generateMainModule( entrypoint: entrypoint, 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 0ee93d6d05..f7d1288499 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -314,6 +314,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive). nullSafetyMode: debuggingOptions.buildInfo.nullSafetyMode, nativeNullAssertions: debuggingOptions.nativeNullAssertions, ddcModuleSystem: debuggingOptions.buildInfo.ddcModuleFormat == DdcModuleFormat.ddc, + canaryFeatures: debuggingOptions.buildInfo.canaryFeatures ?? false, webRenderer: debuggingOptions.webRenderer, isWasm: debuggingOptions.webUseWasm, useLocalCanvasKit: debuggingOptions.buildInfo.useLocalCanvasKit, diff --git a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart index 6e711fb2aa..57c50b6cc7 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -293,9 +293,14 @@ class FlutterWebPlatform extends PlatformPlugin { ); File get _dartSdk { + // TODO(srujzs): Remove this assertion when the library bundle format is + // supported without canary mode. + if (buildInfo.ddcModuleFormat == DdcModuleFormat.ddc) { + assert(buildInfo.canaryFeatures ?? true); + } final Map> dartSdkArtifactMap = buildInfo.ddcModuleFormat == DdcModuleFormat.ddc - ? kDdcDartSdkJsArtifactMap + ? kDdcLibraryBundleDartSdkJsArtifactMap : kAmdDartSdkJsArtifactMap; return _fileSystem.file( _artifacts!.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!), @@ -303,9 +308,14 @@ class FlutterWebPlatform extends PlatformPlugin { } File get _dartSdkSourcemaps { + // TODO(srujzs): Remove this assertion when the library bundle format is + // supported without canary mode. + if (buildInfo.ddcModuleFormat == DdcModuleFormat.ddc) { + assert(buildInfo.canaryFeatures ?? true); + } final Map> dartSdkArtifactMap = buildInfo.ddcModuleFormat == DdcModuleFormat.ddc - ? kDdcDartSdkJsMapArtifactMap + ? kDdcLibraryBundleDartSdkJsMapArtifactMap : kAmdDartSdkJsMapArtifactMap; return _fileSystem.file( _artifacts!.getHostArtifact(dartSdkArtifactMap[webRenderer]![_nullSafetyMode]!), diff --git a/packages/flutter_tools/lib/src/web/bootstrap.dart b/packages/flutter_tools/lib/src/web/bootstrap.dart index 4351b01227..633b7ed06d 100644 --- a/packages/flutter_tools/lib/src/web/bootstrap.dart +++ b/packages/flutter_tools/lib/src/web/bootstrap.dart @@ -4,20 +4,17 @@ import 'package:package_config/package_config.dart'; -String generateDDCBootstrapScript({ - required String entrypoint, - required String ddcModuleLoaderUrl, - required String mapperUrl, - required bool generateLoadingIndicator, - String appRootDirectory = '/', -}) { - return ''' -${generateLoadingIndicator ? _generateLoadingIndicator() : ""} -// TODO(markzipan): This is safe if Flutter app roots are always equal to the -// host root '/'. Validate if this is true. -var _currentDirectory = "$appRootDirectory"; +/// Used to load prerequisite scripts such as ddc_module_loader.js +const String _simpleLoaderScript = r''' +// Declare the character set of the document to align with require.js. Using a +// meta element is preferable to changing the individual script elements' +// `charset` as the scripts should inherit the document's character set, and +// modifying a script element's character set is deprecated. +var meta = document.createElement('meta'); +meta.charset = 'utf-8'; +document.head.insertBefore(meta, document.head.firstChild); -window.\$dartCreateScript = (function() { +window.$dartCreateScript = (function() { // Find the nonce value. (Note, this is only computed once.) var scripts = Array.from(document.getElementsByTagName("script")); var nonce; @@ -43,7 +40,7 @@ window.\$dartCreateScript = (function() { var forceLoadModule = function (relativeUrl, root) { var actualRoot = root ?? _currentDirectory; return new Promise(function(resolve, reject) { - var script = self.\$dartCreateScript(); + var script = self.$dartCreateScript(); let policy = { createScriptURL: function(src) {return src;} }; @@ -56,6 +53,23 @@ var forceLoadModule = function (relativeUrl, root) { document.head.appendChild(script); }); }; +'''; + +// TODO(srujzs): Delete this once it's no longer used internally. +String generateDDCBootstrapScript({ + required String entrypoint, + required String ddcModuleLoaderUrl, + required String mapperUrl, + required bool generateLoadingIndicator, + String appRootDirectory = '/', +}) { + return ''' +${generateLoadingIndicator ? _generateLoadingIndicator() : ""} +// TODO(markzipan): This is safe if Flutter app roots are always equal to the +// host root '/'. Validate if this is true. +var _currentDirectory = "$appRootDirectory"; + +$_simpleLoaderScript // A map containing the URLs for the bootstrap scripts in debug. let _scriptUrls = { @@ -134,6 +148,91 @@ let _scriptUrls = { '''; } +String generateDDCLibraryBundleBootstrapScript({ + required String entrypoint, + required String ddcModuleLoaderUrl, + required String mapperUrl, + required bool generateLoadingIndicator, + String appRootDirectory = '/', +}) { + return ''' +${generateLoadingIndicator ? _generateLoadingIndicator() : ""} +// TODO(markzipan): This is safe if Flutter app roots are always equal to the +// host root '/'. Validate if this is true. +var _currentDirectory = "$appRootDirectory"; + +$_simpleLoaderScript + +// A map containing the URLs for the bootstrap scripts in debug. +let _scriptUrls = { + "mapper": "$mapperUrl", + "moduleLoader": "$ddcModuleLoaderUrl" +}; + +(function() { + // Load pre-requisite DDC scripts. We intentionally use invalid names to avoid + // namespace clashes. + let prerequisiteScripts = [ + { + "src": "$ddcModuleLoaderUrl", + "id": "ddc_module_loader \x00" + }, + { + "src": "$mapperUrl", + "id": "dart_stack_trace_mapper \x00" + } + ]; + + // Load ddc_module_loader.js to access DDC's module loader API. + let prerequisiteLoads = []; + for (let i = 0; i < prerequisiteScripts.length; i++) { + prerequisiteLoads.push(forceLoadModule(prerequisiteScripts[i].src)); + } + Promise.all(prerequisiteLoads).then((_) => afterPrerequisiteLogic()); + + var afterPrerequisiteLogic = function() { + window.\$dartLoader.rootDirectories.push(_currentDirectory); + let scripts = [ + { + "src": "dart_sdk.js", + "id": "dart_sdk" + }, + { + "src": "main_module.bootstrap.js", + "id": "data-main" + } + ]; + + let loadConfig = new window.\$dartLoader.LoadConfiguration(); + loadConfig.bootstrapScript = scripts[scripts.length - 1]; + + loadConfig.loadScriptFn = function(loader) { + loader.addScriptsToQueue(scripts, null); + loader.loadEnqueuedModules(); + } + loadConfig.ddcEventForLoadStart = /* LOAD_ALL_MODULES_START */ 1; + loadConfig.ddcEventForLoadedOk = /* LOAD_ALL_MODULES_END_OK */ 2; + loadConfig.ddcEventForLoadedError = /* LOAD_ALL_MODULES_END_ERROR */ 3; + + let loader = new window.\$dartLoader.DDCLoader(loadConfig); + + // Record prerequisite scripts' fully resolved URLs. + prerequisiteScripts.forEach(script => loader.registerScript(script)); + + // Note: these variables should only be used in non-multi-app scenarios + // since they can be arbitrarily overridden based on multi-app load order. + window.\$dartLoader.loadConfig = loadConfig; + window.\$dartLoader.loader = loader; + + // TODO(srujzs): Support hot restart. + + // Begin loading libraries + loader.nextAttempt(); + } +})(); +'''; +} + /// The JavaScript bootstrap script to support in-browser hot restart. /// /// The [requireUrl] loads our cached RequireJS script file. The [mapperUrl] @@ -287,6 +386,7 @@ document.addEventListener('dart-app-ready', function (e) { '''; } +// TODO(srujzs): Delete this once it's no longer used internally. String generateDDCMainModule({ required String entrypoint, required bool nullAssertions, @@ -320,6 +420,35 @@ String generateDDCMainModule({ '''; } +String generateDDCLibraryBundleMainModule({ + required String entrypoint, + required bool nullAssertions, + required bool nativeNullAssertions, +}) { + // The typo below in "EXTENTION" is load-bearing, package:build depends on it. + return ''' +/* ENTRYPOINT_EXTENTION_MARKER */ + +(function() { + let appName = "org-dartlang-app:/$entrypoint"; + + dartDevEmbedder.debugger.registerDevtoolsFormatter(); + + let child = {}; + child.main = function() { + let sdkOptions = { + nonNullAsserts: $nullAssertions, + nativeNonNullAsserts: $nativeNullAssertions, + }; + dartDevEmbedder.runMain(appName, sdkOptions); + } + + /* MAIN_EXTENSION_MARKER */ + child.main(); +})(); +'''; +} + /// Generate a synthetic main module which captures the application's main /// method. /// diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 68fb4a3f13..c7c7aef321 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -296,39 +296,38 @@ const Map> kAmdDartSdkJsMapAr }, }; -/// The correct precompiled artifact to use for each build and render mode for DDC with DDC modules. -const Map> kDdcDartSdkJsArtifactMap = - >{ - WebRendererMode.auto: { - NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk, - NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk, - }, - WebRendererMode.canvaskit: { - NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitSoundSdk, - NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitSdk, - }, - WebRendererMode.html: { - NullSafetyMode.sound: HostArtifact.webPrecompiledDdcSoundSdk, - NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcSdk, - }, - }; +/// The correct precompiled artifact to use for each build and render mode for +/// DDC with DDC library bundle module format. Only artifacts with sound +/// null-safety are provided. +const Map> +kDdcLibraryBundleDartSdkJsArtifactMap = >{ + WebRendererMode.auto: { + NullSafetyMode.sound: HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk, + }, + WebRendererMode.canvaskit: { + NullSafetyMode.sound: HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk, + }, + WebRendererMode.html: { + NullSafetyMode.sound: HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk, + }, +}; -/// The correct source map artifact to use for each build and render mode for DDC with DDC modules. -const Map> kDdcDartSdkJsMapArtifactMap = - >{ - WebRendererMode.auto: { - NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdkSourcemaps, - NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdkSourcemaps, - }, - WebRendererMode.canvaskit: { - NullSafetyMode.sound: HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps, - NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps, - }, - WebRendererMode.html: { - NullSafetyMode.sound: HostArtifact.webPrecompiledDdcSoundSdkSourcemaps, - NullSafetyMode.unsound: HostArtifact.webPrecompiledDdcSdkSourcemaps, - }, - }; +/// The correct source map artifact to use for each build and render mode for +/// DDC with DDC library bundle module format. Only artifacts with sound +/// null-safety are provided. +const Map> +kDdcLibraryBundleDartSdkJsMapArtifactMap = >{ + WebRendererMode.auto: { + NullSafetyMode.sound: + HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdkSourcemaps, + }, + WebRendererMode.canvaskit: { + NullSafetyMode.sound: HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps, + }, + WebRendererMode.html: { + NullSafetyMode.sound: HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps, + }, +}; String _buildEventAnalyticsSettings({required List configs}) { final Map values = {}; diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index ce33adee7a..bb460b6a7e 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: args: 2.6.0 browser_launcher: 1.1.3 dds: 4.2.7 - dwds: 24.2.0 + dwds: 24.3.0 completion: 1.0.1 coverage: 1.11.1 crypto: 3.0.6 @@ -121,4 +121,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 5640 +# PUBSPEC CHECKSUM: 7741 diff --git a/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart index cb0533c25e..101d3380b8 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/flutter_web_platform_test.dart @@ -59,12 +59,9 @@ void main() { HostArtifact.webPrecompiledAmdCanvaskitSdk, HostArtifact.webPrecompiledAmdSoundSdk, HostArtifact.webPrecompiledAmdSdk, - HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSoundSdk, - HostArtifact.webPrecompiledDdcCanvaskitAndHtmlSdk, - HostArtifact.webPrecompiledDdcCanvaskitSoundSdk, - HostArtifact.webPrecompiledDdcCanvaskitSdk, - HostArtifact.webPrecompiledDdcSoundSdk, - HostArtifact.webPrecompiledDdcSdk, + HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitAndHtmlSoundSdk, + HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk, + HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk, ]) { final File artifactFile = artifacts.getHostArtifact(artifact) as File; artifactFile.createSync(); @@ -123,7 +120,7 @@ void main() { ); testUsingContext( - 'FlutterWebPlatform serves the correct dart_sdk.js (ddc module system) for the passed web renderer', + 'FlutterWebPlatform serves the correct dart_sdk.js (ddc library bundle module system) for the passed web renderer', () async { final ChromiumLauncher chromiumLauncher = ChromiumLauncher( fileSystem: fileSystem, @@ -142,7 +139,7 @@ void main() { '', packageConfigPath: '.dart_tool/package_config.json', treeShakeIcons: false, - extraFrontEndOptions: ['--dartdevc-module-format=ddc'], + extraFrontEndOptions: ['--dartdevc-module-format=ddc', '--canary'], ), webMemoryFS: WebMemoryFS(), fileSystem: fileSystem, @@ -164,7 +161,7 @@ void main() { shelf.Request('GET', Uri.parse('http://localhost/dart_sdk.js')), ); final String contents = await response.readAsString(); - expect(contents, HostArtifact.webPrecompiledDdcCanvaskitSoundSdk.name); + expect(contents, HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk.name); await webPlatform.close(); }, overrides: { diff --git a/packages/flutter_tools/test/general.shard/artifacts_test.dart b/packages/flutter_tools/test/general.shard/artifacts_test.dart index d3b02aacef..6addc3942a 100644 --- a/packages/flutter_tools/test/general.shard/artifacts_test.dart +++ b/packages/flutter_tools/test/general.shard/artifacts_test.dart @@ -270,40 +270,35 @@ void main() { ); }); - testWithoutContext('Precompiled web DDC module system artifact paths are correct', () { - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSdk).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc/dart_sdk.js', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSdkSourcemaps).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc/dart_sdk.js.map', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSdk).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit/dart_sdk.js', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit/dart_sdk.js.map', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSoundSdk).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc-sound/dart_sdk.js', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcSoundSdkSourcemaps).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc-sound/dart_sdk.js.map', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSoundSdk).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit-sound/dart_sdk.js', - ); - expect( - artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps).path, - 'root/bin/cache/flutter_web_sdk/kernel/ddc-canvaskit-sound/dart_sdk.js.map', - ); - }); + testWithoutContext( + 'Precompiled web DDC library bundle module system artifact paths are correct', + () { + expect( + artifacts.getHostArtifact(HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk).path, + 'root/bin/cache/flutter_web_sdk/kernel/ddcLibraryBundle-sound/dart_sdk.js', + ); + expect( + artifacts + .getHostArtifact(HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps) + .path, + 'root/bin/cache/flutter_web_sdk/kernel/ddcLibraryBundle-sound/dart_sdk.js.map', + ); + expect( + artifacts + .getHostArtifact(HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk) + .path, + 'root/bin/cache/flutter_web_sdk/kernel/ddcLibraryBundle-canvaskit-sound/dart_sdk.js', + ); + expect( + artifacts + .getHostArtifact( + HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps, + ) + .path, + 'root/bin/cache/flutter_web_sdk/kernel/ddcLibraryBundle-canvaskit-sound/dart_sdk.js.map', + ); + }, + ); testWithoutContext('getEngineType', () { expect(artifacts.getEngineType(TargetPlatform.android_arm, BuildMode.debug), 'android-arm'); diff --git a/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart b/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart index aa9f3e8fb9..6b31f0b720 100644 --- a/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart +++ b/packages/flutter_tools/test/general.shard/web/bootstrap_test.dart @@ -177,9 +177,9 @@ void main() { expect(result, contains("import 'org-dartlang-app:///bar_config.dart'")); }); - group('Using the DDC module system', () { - test('generateDDCBootstrapScript embeds urls correctly', () { - final String result = generateDDCBootstrapScript( + group('Using the DDC library bundle module system', () { + test('bootstrap script embeds urls correctly', () { + final String result = generateDDCLibraryBundleBootstrapScript( entrypoint: 'foo/bar/main.js', ddcModuleLoaderUrl: 'ddc_module_loader.js', mapperUrl: 'mapper.js', @@ -196,8 +196,8 @@ void main() { expect(result, contains('"id": "data-main"')); }); - test('generateDDCBootstrapScript initializes configuration objects', () { - final String result = generateDDCBootstrapScript( + test('bootstrap script initializes configuration objects', () { + final String result = generateDDCLibraryBundleBootstrapScript( entrypoint: 'foo/bar/main.js', ddcModuleLoaderUrl: 'ddc_module_loader.js', mapperUrl: 'mapper.js', @@ -216,8 +216,8 @@ void main() { expect(result, contains(r'window.$dartLoader.loader =')); }); - test('generateDDCBootstrapScript includes loading indicator', () { - final String result = generateDDCBootstrapScript( + test('bootstrap script includes loading indicator', () { + final String result = generateDDCLibraryBundleBootstrapScript( entrypoint: 'foo/bar/main.js', ddcModuleLoaderUrl: 'ddc_module_loader.js', mapperUrl: 'mapper.js', @@ -227,8 +227,8 @@ void main() { expect(result, contains('"indeterminate"')); }); - test('generateDDCBootstrapScript does not include loading indicator', () { - final String result = generateDDCBootstrapScript( + test('bootstrap script does not include loading indicator', () { + final String result = generateDDCLibraryBundleBootstrapScript( entrypoint: 'foo/bar/main.js', ddcModuleLoaderUrl: 'ddc_module_loader.js', mapperUrl: 'mapper.js', @@ -239,8 +239,8 @@ void main() { }); // https://github.com/flutter/flutter/issues/107742 - test('generateDDCBootstrapScript loading indicator does not trigger scrollbars', () { - final String result = generateDDCBootstrapScript( + test('bootstrap script loading indicator does not trigger scrollbars', () { + final String result = generateDDCLibraryBundleBootstrapScript( entrypoint: 'foo/bar/main.js', ddcModuleLoaderUrl: 'ddc_module_loader.js', mapperUrl: 'mapper.js', @@ -253,51 +253,37 @@ void main() { expect(result, matches(regex), reason: '.flutter-loader must have overflow: hidden'); }); - test('generateDDCMainModule embeds the entrypoint correctly', () { - final String result = generateDDCMainModule( + test('generateDDCLibraryBundleMainModule embeds the entrypoint correctly', () { + final String result = generateDDCLibraryBundleMainModule( entrypoint: 'main.js', nullAssertions: false, nativeNullAssertions: false, ); // bootstrap main module has correct defined module. - expect(result, contains('let appName = "main.js"')); - expect(result, contains('let moduleName = "main.js"')); - expect(result, contains('dart_library.start(appName, uuid, moduleName, "main");')); + expect(result, contains('let appName = "org-dartlang-app:/main.js";')); + expect(result, contains('dartDevEmbedder.runMain(appName, sdkOptions);')); }); - test('generateDDCMainModule embeds its exported main correctly', () { - final String result = generateDDCMainModule( - entrypoint: 'foo/bar/main.js', - nullAssertions: false, - nativeNullAssertions: false, - exportedMain: 'foo__bar__main', - ); - // bootstrap main module has correct defined module. - expect(result, contains('let appName = "foo/bar/main.js"')); - expect(result, contains('let moduleName = "foo/bar/main.js"')); - expect(result, contains('dart_library.start(appName, uuid, moduleName, "foo__bar__main");')); - }); - - test('generateDDCMainModule includes null safety switches', () { - final String result = generateDDCMainModule( + test('generateDDCLibraryBundleMainModule includes null safety switches', () { + final String result = generateDDCLibraryBundleMainModule( entrypoint: 'main.js', nullAssertions: true, nativeNullAssertions: true, ); - expect(result, contains('''dart.nonNullAsserts(true);''')); - expect(result, contains('''dart.nativeNonNullAsserts(true);''')); + expect(result, contains('nonNullAsserts: true')); + expect(result, contains('nativeNonNullAsserts: true')); }); - test('generateDDCMainModule can disable null safety switches', () { - final String result = generateDDCMainModule( + test('generateDDCLibraryBundleMainModule can disable null safety switches', () { + final String result = generateDDCLibraryBundleMainModule( entrypoint: 'main.js', nullAssertions: false, nativeNullAssertions: false, ); - expect(result, contains('''dart.nonNullAsserts(false);''')); - expect(result, contains('''dart.nativeNonNullAsserts(false);''')); + expect(result, contains('nonNullAsserts: false')); + expect(result, contains('nativeNonNullAsserts: false')); }); test('generateTestBootstrapFileContents embeds urls correctly', () { diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart index b583c7e670..cecd1ad062 100644 --- a/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devfs_web_ddc_modules_test.dart @@ -51,6 +51,7 @@ void main() { late FakeHttpServer httpServer; late BufferLogger logger; const bool usesDdcModuleSystem = true; + const bool canaryFeatures = true; setUpAll(() async { packages = PackageConfig([ @@ -73,6 +74,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -318,6 +320,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -338,6 +341,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -361,6 +365,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ), @@ -383,6 +388,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ), @@ -766,126 +772,6 @@ void main() { expect(httpServer.closed, true); }); - runInTestbed( - 'Can start web server with specified DDC module system assets', - () async { - final String path = globals.fs.path.join('lib', 'main.dart'); - final File outputFile = globals.fs.file(path)..createSync(recursive: true); - outputFile.parent.childFile('a.sources').writeAsStringSync(''); - outputFile.parent.childFile('a.json').writeAsStringSync('{}'); - outputFile.parent.childFile('a.map').writeAsStringSync('{}'); - outputFile.parent.childFile('a.metadata').writeAsStringSync('{}'); - - final ResidentCompiler residentCompiler = - FakeResidentCompiler()..output = const CompilerOutput('a', 0, []); - - final WebDevFS webDevFS = WebDevFS( - hostname: 'localhost', - port: 0, - tlsCertPath: null, - tlsCertKeyPath: null, - packagesFilePath: '.dart_tool/package_config.json', - urlTunneller: null, - useSseForDebugProxy: true, - useSseForDebugBackend: true, - useSseForInjectedClient: true, - nullAssertions: true, - nativeNullAssertions: true, - buildInfo: const BuildInfo( - BuildMode.debug, - '', - treeShakeIcons: false, - nullSafetyMode: NullSafetyMode.unsound, - packageConfigPath: '.dart_tool/package_config.json', - ), - enableDwds: false, - enableDds: false, - entrypoint: Uri.base, - testMode: true, - expressionCompiler: null, - extraHeaders: const {}, - chromiumLauncher: null, - nullSafetyMode: NullSafetyMode.unsound, - ddcModuleSystem: usesDdcModuleSystem, - webRenderer: WebRendererMode.html, - isWasm: false, - useLocalCanvasKit: false, - rootDirectory: globals.fs.currentDirectory, - ); - webDevFS.ddcModuleLoaderJS.createSync(recursive: true); - webDevFS.flutterJs.createSync(recursive: true); - webDevFS.stackTraceMapper.createSync(recursive: true); - - final Uri uri = await webDevFS.create(); - webDevFS.webAssetServer.entrypointCacheDirectory = globals.fs.currentDirectory; - final String webPrecompiledSdk = - globals.artifacts!.getHostArtifact(HostArtifact.webPrecompiledDdcSdk).path; - final String webPrecompiledSdkSourcemaps = - globals.artifacts!.getHostArtifact(HostArtifact.webPrecompiledDdcSdkSourcemaps).path; - final String webPrecompiledCanvaskitSdk = - globals.artifacts!.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSdk).path; - final String webPrecompiledCanvaskitSdkSourcemaps = - globals.artifacts! - .getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSdkSourcemaps) - .path; - globals.fs.currentDirectory.childDirectory('lib').childFile('web_entrypoint.dart') - ..createSync(recursive: true) - ..writeAsStringSync('GENERATED'); - globals.fs.file(webPrecompiledSdk) - ..createSync(recursive: true) - ..writeAsStringSync('HELLO'); - globals.fs.file(webPrecompiledSdkSourcemaps) - ..createSync(recursive: true) - ..writeAsStringSync('THERE'); - globals.fs.file(webPrecompiledCanvaskitSdk) - ..createSync(recursive: true) - ..writeAsStringSync('OL'); - globals.fs.file(webPrecompiledCanvaskitSdkSourcemaps) - ..createSync(recursive: true) - ..writeAsStringSync('CHUM'); - - await webDevFS.update( - mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri, - generator: residentCompiler, - trackWidgetCreation: true, - bundleFirstUpload: true, - invalidatedFiles: [], - packageConfig: PackageConfig.empty, - pathToReload: '', - dillOutputPath: 'out.dill', - shaderCompiler: const FakeShaderCompiler(), - ); - - expect(webDevFS.webAssetServer.getFile('ddc_module_loader.js'), isNotNull); - expect(webDevFS.webAssetServer.getFile('stack_trace_mapper.js'), isNotNull); - expect(webDevFS.webAssetServer.getFile('main.dart'), isNotNull); - expect(webDevFS.webAssetServer.getFile('manifest.json'), isNotNull); - expect(webDevFS.webAssetServer.getFile('flutter.js'), isNotNull); - expect(webDevFS.webAssetServer.getFile('flutter_service_worker.js'), isNotNull); - expect(webDevFS.webAssetServer.getFile('version.json'), isNotNull); - expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'HELLO'); - expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js.map'), 'THERE'); - - // Update to the SDK. - globals.fs.file(webPrecompiledSdk).writeAsStringSync('BELLOW'); - - // New SDK should be visible.. - expect(await webDevFS.webAssetServer.dartSourceContents('dart_sdk.js'), 'BELLOW'); - - // Generated entrypoint. - expect( - await webDevFS.webAssetServer.dartSourceContents('web_entrypoint.dart'), - contains('GENERATED'), - ); - - // served on localhost - expect(uri.host, 'localhost'); - - await webDevFS.destroy(); - }, - overrides: {Artifacts: Artifacts.test}, - ); - runInTestbed( 'Can start web server with specified assets in sound null safety mode', () async { @@ -926,6 +812,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.html, isWasm: false, useLocalCanvasKit: false, @@ -941,14 +828,22 @@ void main() { ..createSync(recursive: true) ..writeAsStringSync('GENERATED'); final String webPrecompiledSdk = - globals.artifacts!.getHostArtifact(HostArtifact.webPrecompiledDdcSoundSdk).path; + globals.artifacts! + .getHostArtifact(HostArtifact.webPrecompiledDdcLibraryBundleSoundSdk) + .path; final String webPrecompiledSdkSourcemaps = - globals.artifacts!.getHostArtifact(HostArtifact.webPrecompiledDdcSoundSdkSourcemaps).path; + globals.artifacts! + .getHostArtifact(HostArtifact.webPrecompiledDdcLibraryBundleSoundSdkSourcemaps) + .path; final String webPrecompiledCanvaskitSdk = - globals.artifacts!.getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSoundSdk).path; + globals.artifacts! + .getHostArtifact(HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdk) + .path; final String webPrecompiledCanvaskitSdkSourcemaps = globals.artifacts! - .getHostArtifact(HostArtifact.webPrecompiledDdcCanvaskitSoundSdkSourcemaps) + .getHostArtifact( + HostArtifact.webPrecompiledDdcLibraryBundleCanvaskitSoundSdkSourcemaps, + ) .path; globals.fs.file(webPrecompiledSdk) ..createSync(recursive: true) @@ -1044,6 +939,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1118,6 +1014,7 @@ void main() { nativeNullAssertions: true, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1167,6 +1064,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1217,6 +1115,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.auto, isWasm: false, useLocalCanvasKit: false, @@ -1267,6 +1166,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.unsound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1380,6 +1280,7 @@ void main() { {}, NullSafetyMode.sound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -1429,6 +1330,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.unsound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, 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 2e868d4bf3..cc6f6724bf 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 @@ -106,6 +106,7 @@ void main() { late FakeHttpServer httpServer; late BufferLogger logger; const bool usesDdcModuleSystem = false; + const bool canaryFeatures = false; setUpAll(() async { packages = PackageConfig([ @@ -128,6 +129,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -395,6 +397,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -418,6 +421,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -444,6 +448,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ), @@ -469,6 +474,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ), @@ -541,6 +547,7 @@ void main() { {}, NullSafetyMode.unsound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: true, ); @@ -983,6 +990,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.unsound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.html, isWasm: false, useLocalCanvasKit: false, @@ -1101,6 +1109,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.html, isWasm: false, useLocalCanvasKit: false, @@ -1225,6 +1234,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1302,6 +1312,7 @@ void main() { nativeNullAssertions: true, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1354,6 +1365,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1407,6 +1419,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.sound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.auto, isWasm: false, useLocalCanvasKit: false, @@ -1460,6 +1473,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.unsound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false, @@ -1582,6 +1596,7 @@ void main() { {}, NullSafetyMode.sound, usesDdcModuleSystem, + canaryFeatures, webRenderer: WebRendererMode.canvaskit, useLocalCanvasKit: false, ); @@ -1632,6 +1647,7 @@ void main() { chromiumLauncher: null, nullSafetyMode: NullSafetyMode.unsound, ddcModuleSystem: usesDdcModuleSystem, + canaryFeatures: canaryFeatures, webRenderer: WebRendererMode.canvaskit, isWasm: false, useLocalCanvasKit: false,