diff --git a/build_web_compilers/CHANGELOG.md b/build_web_compilers/CHANGELOG.md index dc0def701..91f27cb8e 100644 --- a/build_web_compilers/CHANGELOG.md +++ b/build_web_compilers/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.6 + +- Add build options to customize the SDK used for compiling to js and wasm. + ## 4.4.5 - Updating DDC's bootstrapper to be consistent with the rest of the ecosystem. diff --git a/build_web_compilers/README.md b/build_web_compilers/README.md index ab5a7ec3a..26d11fd17 100644 --- a/build_web_compilers/README.md +++ b/build_web_compilers/README.md @@ -164,6 +164,33 @@ targets: In this case, you need to use another builder or a predefined loader instead. +### Advanced SDK Customization + +The following options allow you to customize the Dart SDK used for compiling to +js and wasm. They are intended for advanced use cases and should be used with +caution. + +
+Expand to see configuration options. + +```yaml +global_options: + build_web_compilers:ddc: + options: + use-ui-libraries: false + platform-sdk: /path/to/platform_sdk/ + ddc-kernel-path: relative/path/to/kernel_summary + libraries-path: /path/to/libraries.json + build_web_compilers:entrypoint: + options: + use-ui-libraries: false + libraries-path: /path/to/libraries.json + build_web_compilers:sdk_js: + options: + use-prebuilt-sdk-from-path: /path/to/prebuilt/sdk_js/ +``` +
+ ### Configuring -D environment variables dartdevc is a modular compiler, so in order to ensure consistent builds diff --git a/build_web_compilers/lib/build_web_compilers.dart b/build_web_compilers/lib/build_web_compilers.dart index 804429233..661452874 100644 --- a/build_web_compilers/lib/build_web_compilers.dart +++ b/build_web_compilers/lib/build_web_compilers.dart @@ -13,6 +13,6 @@ export 'src/common.dart' symbolsExtension; export 'src/dev_compiler_builder.dart' show DevCompilerBuilder; export 'src/platforms.dart' - show dart2jsPlatform, dart2wasmPlatform, ddcPlatform; + show dart2jsPlatform, dart2wasmPlatform, ddcPlatform, initializePlatforms; export 'src/web_entrypoint_builder.dart' show WebCompiler, WebEntrypointBuilder, ddcBootstrapExtension; diff --git a/build_web_compilers/lib/builders.dart b/build_web_compilers/lib/builders.dart index f11ddac3e..0a369fbc6 100644 --- a/build_web_compilers/lib/builders.dart +++ b/build_web_compilers/lib/builders.dart @@ -15,11 +15,13 @@ import 'src/web_entrypoint_marker_builder.dart'; // Shared entrypoint builder Builder webEntrypointBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); return WebEntrypointBuilder.fromOptions(options); } Builder webEntrypointMarkerBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); return WebEntrypointMarkerBuilder( usesWebHotReload: _readWebHotReloadOption(options), @@ -28,16 +30,19 @@ Builder webEntrypointMarkerBuilder(BuilderOptions options) { // DDC related builders Builder ddcMetaModuleBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); return MetaModuleBuilder.forOptions(ddcPlatform, options); } Builder ddcMetaModuleCleanBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); return MetaModuleCleanBuilder(ddcPlatform); } Builder ddcModuleBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); return ModuleBuilder( ddcPlatform, @@ -47,6 +52,7 @@ Builder ddcModuleBuilder(BuilderOptions options) { Builder ddcBuilder(BuilderOptions options) { validateOptions(options.config, _supportedOptions, 'build_web_compilers:ddc'); + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); _ensureSameDdcOptions(options); @@ -60,9 +66,11 @@ Builder ddcBuilder(BuilderOptions options) { emitDebugSymbols: _readEmitDebugSymbolsOption(options), canaryFeatures: _readCanaryOption(options), ddcModules: _readWebHotReloadOption(options), - sdkKernelPath: sdkDdcKernelPath, trackUnusedInputs: _readTrackInputsCompilerOption(options), platform: ddcPlatform, + sdkKernelPath: _readDdcKernelPathOption(options), + librariesPath: _readLibrariesPathOption(options), + platformSdk: _readPlatformSdkOption(options), environment: _readEnvironmentOption(options), ); } @@ -71,16 +79,19 @@ final ddcKernelExtension = '.ddc.dill'; Builder ddcKernelBuilder(BuilderOptions options) { validateOptions(options.config, _supportedOptions, 'build_web_compilers:ddc'); + _ensureSamePlatformOptions(options); _ensureSameDdcHotReloadOptions(options); _ensureSameDdcOptions(options); return KernelBuilder( summaryOnly: true, - sdkKernelPath: sdkDdcKernelPath, + sdkKernelPath: _readDdcKernelPathOption(options), outputExtension: ddcKernelExtension, platform: ddcPlatform, + librariesPath: _readLibrariesPathOption(options), useIncrementalCompiler: _readUseIncrementalCompilerOption(options), trackUnusedInputs: _readTrackInputsCompilerOption(options), + platformSdk: _readPlatformSdkOption(options), ); } @@ -93,26 +104,44 @@ Builder sdkJsCompile(BuilderOptions options) { canaryFeatures: _readWebHotReloadOption(options) || _readCanaryOption(options), usesWebHotReload: _readWebHotReloadOption(options), + usePrebuiltSdkFromPath: _readUsePrebuiltSdkFromPathOption(options), ); } // Dart2js related builders -Builder dart2jsMetaModuleBuilder(BuilderOptions options) => - MetaModuleBuilder.forOptions(dart2jsPlatform, options); -Builder dart2jsMetaModuleCleanBuilder(BuilderOptions _) => - MetaModuleCleanBuilder(dart2jsPlatform); -Builder dart2jsModuleBuilder(BuilderOptions _) => - ModuleBuilder(dart2jsPlatform); +Builder dart2jsMetaModuleBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); + return MetaModuleBuilder.forOptions(dart2jsPlatform, options); +} + +Builder dart2jsMetaModuleCleanBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); + return MetaModuleCleanBuilder(dart2jsPlatform); +} + +Builder dart2jsModuleBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); + return ModuleBuilder(dart2jsPlatform); +} + PostProcessBuilder dart2jsArchiveExtractor(BuilderOptions options) => Dart2JsArchiveExtractor.fromOptions(options); // Dart2wasm related builders -Builder dart2wasmMetaModuleBuilder(BuilderOptions options) => - MetaModuleBuilder.forOptions(dart2wasmPlatform, options); -Builder dart2wasmMetaModuleCleanBuilder(BuilderOptions _) => - MetaModuleCleanBuilder(dart2wasmPlatform); -Builder dart2wasmModuleBuilder(BuilderOptions _) => - ModuleBuilder(dart2wasmPlatform); +Builder dart2wasmMetaModuleBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); + return MetaModuleBuilder.forOptions(dart2wasmPlatform, options); +} + +Builder dart2wasmMetaModuleCleanBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); + return MetaModuleCleanBuilder(dart2wasmPlatform); +} + +Builder dart2wasmModuleBuilder(BuilderOptions options) { + _ensureSamePlatformOptions(options); + return ModuleBuilder(dart2wasmPlatform); +} // General purpose builders PostProcessBuilder dartSourceCleanup(BuilderOptions options) => @@ -170,6 +199,27 @@ void _ensureSameDdcHotReloadOptions(BuilderOptions options) { } } +void _ensureSamePlatformOptions(BuilderOptions options) { + final useUiLibraries = _readUseUiLibrariesOption(options); + if (_lastUseUiLibrariesValue == null) { + initializePlatforms(useUiLibraries); + _lastUseUiLibrariesValue = useUiLibraries; + } else if (_lastUseUiLibrariesValue != useUiLibraries) { + throw ArgumentError( + '`use-ui-libraries` must be configured the same across the ' + 'following builders: build_web_compilers:ddc, ' + 'build_web_compilers|entrypoint, ' + 'build_web_compilers|entrypoint_marker, ' + 'build_web_compilers|ddc, ' + 'build_web_compilers|ddc_modules, ' + 'build_web_compilers|dart2js_modules, ' + 'build_web_compilers|dart2wasm_modules.' + '\n\nPlease use the `global_options` section in ' + '`build.yaml` or the `--define` flag to set global options.', + ); + } +} + bool _readUseIncrementalCompilerOption(BuilderOptions options) { return options.config[_useIncrementalCompilerOption] as bool? ?? true; } @@ -194,6 +244,26 @@ bool _readWebHotReloadOption(BuilderOptions options) { return options.config[_webHotReloadOption] as bool? ?? false; } +bool _readUseUiLibrariesOption(BuilderOptions options) { + return options.config[_useUiLibrariesOption] as bool? ?? false; +} + +String _readDdcKernelPathOption(BuilderOptions options) { + return options.config[_ddcKernelPathOption] as String? ?? sdkDdcKernelPath; +} + +String? _readLibrariesPathOption(BuilderOptions options) { + return options.config[_librariesPathOption] as String?; +} + +String? _readPlatformSdkOption(BuilderOptions options) { + return options.config[_platformSdkOption] as String?; +} + +String? _readUsePrebuiltSdkFromPathOption(BuilderOptions options) { + return options.config[_usePrebuiltSdkFromPathOption] as String?; +} + Map _readEnvironmentOption(BuilderOptions options) { final environment = options.config[_environmentOption] as Map? ?? const {}; return environment.map((key, value) => MapEntry('$key', '$value')); @@ -201,6 +271,7 @@ Map _readEnvironmentOption(BuilderOptions options) { Map? _previousDdcConfig; bool? _lastWebHotReloadValue; +bool? _lastUseUiLibrariesValue; const _useIncrementalCompilerOption = 'use-incremental-compiler'; const _generateFullDillOption = 'generate-full-dill'; const _emitDebugSymbolsOption = 'emit-debug-symbols'; @@ -208,6 +279,11 @@ const _canaryOption = 'canary'; const _trackUnusedInputsCompilerOption = 'track-unused-inputs'; const _environmentOption = 'environment'; const _webHotReloadOption = 'web-hot-reload'; +const _useUiLibrariesOption = 'use-ui-libraries'; +const _ddcKernelPathOption = 'ddc-kernel-path'; +const _librariesPathOption = 'libraries-path'; +const _platformSdkOption = 'platform-sdk'; +const _usePrebuiltSdkFromPathOption = 'use-prebuilt-sdk-from-path'; const _supportedOptions = [ _environmentOption, @@ -217,4 +293,8 @@ const _supportedOptions = [ _canaryOption, _trackUnusedInputsCompilerOption, _webHotReloadOption, + _useUiLibrariesOption, + _ddcKernelPathOption, + _librariesPathOption, + _platformSdkOption, ]; diff --git a/build_web_compilers/lib/src/dart2js_bootstrap.dart b/build_web_compilers/lib/src/dart2js_bootstrap.dart index f3c431578..d37ec2f99 100644 --- a/build_web_compilers/lib/src/dart2js_bootstrap.dart +++ b/build_web_compilers/lib/src/dart2js_bootstrap.dart @@ -28,6 +28,8 @@ Future bootstrapDart2Js( required bool? nativeNullAssertions, required bool onlyCompiler, String entrypointExtension = jsEntrypointExtension, + String? librariesPath, + bool unsafeAllowUnsupportedModules = false, }) => _resourcePool.withResource( () => _bootstrapDart2Js( buildStep, @@ -35,6 +37,8 @@ Future bootstrapDart2Js( nativeNullAssertions: nativeNullAssertions, onlyCompiler: onlyCompiler, entrypointExtension: entrypointExtension, + librariesPath: librariesPath, + unsafeAllowUnsupportedModules: unsafeAllowUnsupportedModules, ), ); @@ -44,6 +48,8 @@ Future _bootstrapDart2Js( required bool? nativeNullAssertions, required bool onlyCompiler, required String entrypointExtension, + String? librariesPath, + bool unsafeAllowUnsupportedModules = false, }) async { final dartEntrypointId = buildStep.inputId; final moduleId = dartEntrypointId.changeExtension( @@ -59,7 +65,7 @@ Future _bootstrapDart2Js( try { allDeps = (await module.computeTransitiveDependencies( buildStep, - throwIfUnsupported: true, + throwIfUnsupported: !unsafeAllowUnsupportedModules, ))..add(module); } on UnsupportedModules catch (e) { final librariesString = (await e.exactLibraries(buildStep).toList()) @@ -97,7 +103,8 @@ $librariesString : dartUri.path.substring(1), ) + entrypointExtension; - final librariesSpec = p.joinAll([sdkDir, 'lib', 'libraries.json']); + final librariesSpec = + librariesPath ?? p.joinAll([sdkDir, 'lib', 'libraries.json']); _validateUserArgs(dart2JsArgs); args = dart2JsArgs.toList()..addAll([ diff --git a/build_web_compilers/lib/src/dart2wasm_bootstrap.dart b/build_web_compilers/lib/src/dart2wasm_bootstrap.dart index 690e0f769..ffdd20a85 100644 --- a/build_web_compilers/lib/src/dart2wasm_bootstrap.dart +++ b/build_web_compilers/lib/src/dart2wasm_bootstrap.dart @@ -45,13 +45,15 @@ final class Dart2WasmBootstrapResult { Future bootstrapDart2Wasm( BuildStep buildStep, List additionalArguments, - String javaScriptModuleExtension, -) async { + String javaScriptModuleExtension, { + bool unsafeAllowUnsupportedModules = false, +}) async { return await _resourcePool.withResource( () => _bootstrapDart2Wasm( buildStep, additionalArguments, javaScriptModuleExtension, + unsafeAllowUnsupportedModules: unsafeAllowUnsupportedModules, ), ); } @@ -59,8 +61,9 @@ Future bootstrapDart2Wasm( Future _bootstrapDart2Wasm( BuildStep buildStep, List additionalArguments, - String javaScriptModuleExtension, -) async { + String javaScriptModuleExtension, { + bool unsafeAllowUnsupportedModules = false, +}) async { final dartEntrypointId = buildStep.inputId; final moduleId = dartEntrypointId.changeExtension( moduleExtension(dart2wasmPlatform), @@ -75,7 +78,7 @@ Future _bootstrapDart2Wasm( try { allDeps = (await module.computeTransitiveDependencies( buildStep, - throwIfUnsupported: true, + throwIfUnsupported: !unsafeAllowUnsupportedModules, ))..add(module); } on UnsupportedModules catch (e) { final librariesString = (await e.exactLibraries(buildStep).toList()) diff --git a/build_web_compilers/lib/src/dev_compiler_bootstrap.dart b/build_web_compilers/lib/src/dev_compiler_bootstrap.dart index bb71c44f9..6d5af8a8b 100644 --- a/build_web_compilers/lib/src/dev_compiler_bootstrap.dart +++ b/build_web_compilers/lib/src/dev_compiler_bootstrap.dart @@ -33,18 +33,19 @@ final stackTraceMapperPath = /// available to the app by making them inputs of this build action. Future bootstrapDdc( BuildStep buildStep, { - DartPlatform? platform, Iterable requiredAssets = const [], String entrypointExtension = jsEntrypointExtension, required bool? nativeNullAssertions, bool usesWebHotReload = false, + bool unsafeAllowUnsupportedModules = false, }) async { - platform = ddcPlatform; // Ensures that the sdk resources are built and available. await _ensureResources(buildStep, requiredAssets); final dartEntrypointId = buildStep.inputId; - final moduleId = buildStep.inputId.changeExtension(moduleExtension(platform)); + final moduleId = buildStep.inputId.changeExtension( + moduleExtension(ddcPlatform), + ); final module = Module.fromJson( json.decode(await buildStep.readAsString(moduleId)) as Map, ); @@ -56,6 +57,7 @@ Future bootstrapDdc( module, buildStep, computeStronglyConnectedComponents: !usesWebHotReload, + throwIfUnsupported: !unsafeAllowUnsupportedModules, ); } on UnsupportedModules catch (e) { final librariesString = (await e.exactLibraries(buildStep).toList()) @@ -251,11 +253,12 @@ Future> _ensureTransitiveJsModules( Module module, BuildStep buildStep, { bool computeStronglyConnectedComponents = true, + bool throwIfUnsupported = true, }) async { // Collect all the modules this module depends on, plus this module. final transitiveDeps = await module.computeTransitiveDependencies( buildStep, - throwIfUnsupported: true, + throwIfUnsupported: throwIfUnsupported, computeStronglyConnectedComponents: computeStronglyConnectedComponents, ); diff --git a/build_web_compilers/lib/src/platforms.dart b/build_web_compilers/lib/src/platforms.dart index e99446e52..0fc3eb922 100644 --- a/build_web_compilers/lib/src/platforms.dart +++ b/build_web_compilers/lib/src/platforms.dart @@ -24,7 +24,6 @@ const _coreLibraries = [ /// Additional libraries supported by both ddc and dart2js. const _additionalWebLibraries = [ - 'html', 'html', 'html_common', 'indexed_db', @@ -37,13 +36,44 @@ const _additionalWebLibraries = [ /// Additional libraries supported by dart2wasm. const _additionalWasmLibraries = ['ffi']; -const _jsCompilerLibraries = [..._coreLibraries, ..._additionalWebLibraries]; +/// Additional libraries supported by the Flutter SDK. +const _additionalUiLibraries = ['ui', 'ui_web']; -final ddcPlatform = DartPlatform.register('ddc', _jsCompilerLibraries); +// These intentionally throw if [initializePlatforms] wasn't called first. +final ddcPlatform = DartPlatform.byName('ddc'); +final dart2jsPlatform = DartPlatform.byName('dart2js'); +final dart2wasmPlatform = DartPlatform.byName('dart2wasm'); -final dart2jsPlatform = DartPlatform.register('dart2js', _jsCompilerLibraries); +bool? _useAdditionalUiLibraries; -final dart2wasmPlatform = DartPlatform.register('dart2wasm', [ - ..._coreLibraries, - ..._additionalWasmLibraries, -]); +/// Registers the platforms with [DartPlatform]. +/// +/// Must be called before [ddcPlatform], [dart2jsPlatform], or +/// [dart2wasmPlatform] is used. +void initializePlatforms([bool useAdditionalUiLibraries = false]) { + if (_useAdditionalUiLibraries != null) { + if (_useAdditionalUiLibraries != useAdditionalUiLibraries) { + throw ArgumentError( + 'Function initializePlatforms() called multiple times with different ' + 'values. Make sure to call it always with the same value.', + ); + } + return; + } + _useAdditionalUiLibraries = useAdditionalUiLibraries; + DartPlatform.register('ddc', [ + ..._coreLibraries, + ..._additionalWebLibraries, + if (useAdditionalUiLibraries) ..._additionalUiLibraries, + ]); + DartPlatform.register('dart2js', [ + ..._coreLibraries, + ..._additionalWebLibraries, + if (useAdditionalUiLibraries) ..._additionalUiLibraries, + ]); + DartPlatform.register('dart2wasm', [ + ..._coreLibraries, + ..._additionalWasmLibraries, + if (useAdditionalUiLibraries) ..._additionalUiLibraries, + ]); +} diff --git a/build_web_compilers/lib/src/sdk_js_compile_builder.dart b/build_web_compilers/lib/src/sdk_js_compile_builder.dart index 824fc8983..fe2b9e037 100644 --- a/build_web_compilers/lib/src/sdk_js_compile_builder.dart +++ b/build_web_compilers/lib/src/sdk_js_compile_builder.dart @@ -46,6 +46,12 @@ class SdkJsCompileBuilder implements Builder { /// reload. final bool usesWebHotReload; + /// An optional directory path that contains prebuilt sdk files. + /// + /// If provided, skips compilation and copies the sdk and + /// source map files to the output. + final String? usePrebuiltSdkFromPath; + SdkJsCompileBuilder({ required this.sdkKernelPath, required String outputPath, @@ -53,6 +59,7 @@ class SdkJsCompileBuilder implements Builder { String? platformSdk, required this.canaryFeatures, required this.usesWebHotReload, + this.usePrebuiltSdkFromPath, }) : platformSdk = platformSdk ?? sdkDir, librariesPath = librariesPath ?? @@ -70,15 +77,19 @@ class SdkJsCompileBuilder implements Builder { @override Future build(BuildStep buildStep) async { - await _createDevCompilerModule( - buildStep, - platformSdk, - sdkKernelPath, - librariesPath, - jsOutputId, - canaryFeatures, - usesWebHotReload, - ); + if (usePrebuiltSdkFromPath case final String sdkPath) { + await _copyPrebuiltSdk(buildStep, sdkPath, jsOutputId); + } else { + await _createDevCompilerModule( + buildStep, + platformSdk, + sdkKernelPath, + librariesPath, + jsOutputId, + canaryFeatures, + usesWebHotReload, + ); + } } } @@ -152,3 +163,34 @@ Future _createDevCompilerModule( buildStep, ); } + +/// Copy the prebuilt SDK to the output. +Future _copyPrebuiltSdk( + BuildStep buildStep, + String sdkPath, + AssetId jsOutputId, +) async { + final sdkFile = File(p.join(sdkPath, 'dart_sdk.js')); + if (!sdkFile.existsSync()) { + throw ArgumentError( + 'Prebuilt SDK file "dart_sdk.js" ' + 'does not exist at $sdkPath', + ); + } + + await buildStep.writeAsBytes(jsOutputId, await sdkFile.readAsBytes()); + + final sourceMapFile = File(p.join(sdkPath, 'dart_sdk.js.map')); + if (!sourceMapFile.existsSync()) { + log.warning( + 'Prebuilt SDK source map file "dart_sdk.js.map" ' + 'does not exist at $sdkPath', + ); + return; + } + + await buildStep.writeAsBytes( + jsOutputId.changeExtension(_jsSourceMapExtension), + await sourceMapFile.readAsBytes(), + ); +} diff --git a/build_web_compilers/lib/src/web_entrypoint_builder.dart b/build_web_compilers/lib/src/web_entrypoint_builder.dart index 8452e7590..3fd13cf8a 100644 --- a/build_web_compilers/lib/src/web_entrypoint_builder.dart +++ b/build_web_compilers/lib/src/web_entrypoint_builder.dart @@ -129,11 +129,24 @@ final class EntrypointBuilderOptions { /// Web hot reload is only supported for DDC's Library Bundle module system. final bool usesWebHotReload; + /// The absolute path to the libraries file for the current platform. + /// + /// If not provided, defaults to "lib/libraries.json" in the sdk directory. + final String? librariesPath; + + /// Whether or not to allow unsupported modules. + /// + /// If `true` then native core library imports that are not officially + /// supported by the current platform will be silently allowed. + final bool unsafeAllowUnsupportedModules; + EntrypointBuilderOptions({ required this.compilers, this.nativeNullAssertions, this.loaderExtension, this.usesWebHotReload = false, + this.librariesPath, + this.unsafeAllowUnsupportedModules = false, }); factory EntrypointBuilderOptions.fromOptions(BuilderOptions options) { @@ -146,6 +159,9 @@ final class EntrypointBuilderOptions { const nativeNullAssertionsOption = 'native_null_assertions'; const loaderOption = 'loader'; const webHotReloadOption = 'web-hot-reload'; + const librariesPathOption = 'libraries-path'; + const unsafeAllowUnsupportedModulesOption = + 'unsafe-allow-unsupported-modules'; String? defaultLoaderOption; const supportedOptions = [ @@ -156,12 +172,18 @@ final class EntrypointBuilderOptions { dart2wasmArgsOption, loaderOption, webHotReloadOption, + librariesPathOption, + unsafeAllowUnsupportedModulesOption, + 'use-ui-libraries', ]; final config = options.config; final nativeNullAssertions = options.config[nativeNullAssertionsOption] as bool?; final usesWebHotReload = options.config[webHotReloadOption] as bool?; + final librariesPath = options.config[librariesPathOption] as String?; + final unsafeAllowUnsupportedModules = + options.config[unsafeAllowUnsupportedModulesOption] as bool?; final compilers = []; validateOptions( @@ -249,6 +271,8 @@ final class EntrypointBuilderOptions { ? config[loaderOption] as String? : defaultLoaderOption, usesWebHotReload: usesWebHotReload ?? false, + librariesPath: librariesPath, + unsafeAllowUnsupportedModules: unsafeAllowUnsupportedModules ?? false, ); } @@ -338,6 +362,8 @@ class WebEntrypointBuilder implements Builder { ? _ddcLibraryBundleSdkResources : _ddcSdkResources, usesWebHotReload: usesWebHotReload, + unsafeAllowUnsupportedModules: + options.unsafeAllowUnsupportedModules, ); } on MissingModulesException catch (e) { log.severe('$e'); @@ -352,6 +378,9 @@ class WebEntrypointBuilder implements Builder { nativeNullAssertions: options.nativeNullAssertions, onlyCompiler: options.compilers.length == 1, entrypointExtension: compiler.extension, + librariesPath: options.librariesPath, + unsafeAllowUnsupportedModules: + options.unsafeAllowUnsupportedModules, ), ); case WebCompiler.Dart2Wasm: @@ -361,6 +390,8 @@ class WebEntrypointBuilder implements Builder { buildStep, compiler.compilerArguments, compiler.extension, + unsafeAllowUnsupportedModules: + options.unsafeAllowUnsupportedModules, ); }), ); diff --git a/build_web_compilers/pubspec.yaml b/build_web_compilers/pubspec.yaml index cc952ef33..1a03e75b6 100644 --- a/build_web_compilers/pubspec.yaml +++ b/build_web_compilers/pubspec.yaml @@ -1,5 +1,5 @@ name: build_web_compilers -version: 4.4.5 +version: 4.4.6 description: Builder implementations wrapping the dart2js and DDC compilers. repository: https://github.com/dart-lang/build/tree/master/build_web_compilers resolution: workspace @@ -35,6 +35,7 @@ dev_dependencies: d: path: test/fixtures/d dart_flutter_team_lints: ^3.1.0 + file: ^7.0.1 test: ^1.16.0 yaml: ^3.1.0 diff --git a/build_web_compilers/test/dart2js_bootstrap_test.dart b/build_web_compilers/test/dart2js_bootstrap_test.dart index 990cc1a9c..adc028a1d 100644 --- a/build_web_compilers/test/dart2js_bootstrap_test.dart +++ b/build_web_compilers/test/dart2js_bootstrap_test.dart @@ -12,6 +12,7 @@ import 'package:logging/logging.dart'; import 'package:test/test.dart'; void main() { + initializePlatforms(); final platform = dart2jsPlatform; late StreamSubscription logSubscription; @@ -142,4 +143,93 @@ void main() { outputs: expectedOutputs, ); }); + + test('throws on unsupported platform library imports', () async { + final assets = { + 'a|lib/index.dart': ''' + import 'dart:io'; + main() { + print('hello world'); + } + ''', + }; + final expectedOutputs = { + 'a|lib/.dart2js.meta_module.clean': isNotNull, + 'a|lib/.dart2js.meta_module.raw': isNotNull, + 'a|lib/index.dart2js.module': isNotNull, + 'a|lib/index.module.library': isNotNull, + }; + final logs = []; + await testBuilders( + [ + const ModuleLibraryBuilder(), + MetaModuleBuilder(platform), + MetaModuleCleanBuilder(platform), + ModuleBuilder(platform), + WebEntrypointBuilder.fromOptions( + const BuilderOptions({ + 'compiler': 'dart2js', + 'native_null_assertions': false, + }), + ), + ], + assets, + outputs: expectedOutputs, + onLog: logs.add, + ); + expect( + logs, + contains( + isA().having( + (r) => r.message, + 'message', + contains( + 'Skipping compiling a|lib/index.dart with dart2js because some of ' + 'its\ntransitive libraries have sdk dependencies that are not ' + 'supported on this platform:\n\na|lib/index.dart', + ), + ), + ), + ); + }); + + test( + 'ignores unsupported platform library imports when allow flag is set', + () async { + final assets = { + 'a|lib/index.dart': ''' + import 'dart:io'; + main() { + print('hello world'); + } + ''', + }; + final expectedOutputs = { + 'a|lib/.dart2js.meta_module.clean': isNotNull, + 'a|lib/.dart2js.meta_module.raw': isNotNull, + 'a|lib/index.dart.js.map': isNotNull, + 'a|lib/index.dart.js.tar.gz': isNotNull, + 'a|lib/index.dart.js': decodedMatches(contains('world')), + 'a|lib/index.dart2js.module': isNotNull, + 'a|lib/index.module.library': isNotNull, + }; + await testBuilders( + [ + const ModuleLibraryBuilder(), + MetaModuleBuilder(platform), + MetaModuleCleanBuilder(platform), + ModuleBuilder(platform), + WebEntrypointBuilder.fromOptions( + const BuilderOptions({ + 'compiler': 'dart2js', + 'native_null_assertions': false, + 'unsafe-allow-unsupported-modules': true, + }), + ), + ], + assets, + outputs: expectedOutputs, + ); + }, + ); } diff --git a/build_web_compilers/test/dart2wasm_bootstrap_test.dart b/build_web_compilers/test/dart2wasm_bootstrap_test.dart index c6adbb519..1c1877d7a 100644 --- a/build_web_compilers/test/dart2wasm_bootstrap_test.dart +++ b/build_web_compilers/test/dart2wasm_bootstrap_test.dart @@ -9,6 +9,7 @@ import 'package:build_web_compilers/build_web_compilers.dart'; import 'package:test/test.dart'; void main() { + initializePlatforms(); final startingAssets = { 'a|web/index.dart': ''' void main() { diff --git a/build_web_compilers/test/dev_compiler_bootstrap_test.dart b/build_web_compilers/test/dev_compiler_bootstrap_test.dart index 0bda980b0..7891275d1 100644 --- a/build_web_compilers/test/dev_compiler_bootstrap_test.dart +++ b/build_web_compilers/test/dev_compiler_bootstrap_test.dart @@ -2,14 +2,18 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:io'; + import 'package:build/build.dart'; import 'package:build_modules/build_modules.dart'; import 'package:build_test/build_test.dart'; import 'package:build_web_compilers/build_web_compilers.dart'; import 'package:build_web_compilers/builders.dart'; +import 'package:file/memory.dart'; import 'package:test/test.dart'; void main() { + initializePlatforms(); final startingBuilders = { // Uses the real sdk copy builder to copy required files from the SDK. sdkJsCopyRequirejs(const BuilderOptions({})), @@ -253,5 +257,27 @@ void main() { }; await testBuilder(builder, sdkAssets, outputs: expectedOutputs); }); + + test('can use prebuilt sdk from path', () async { + final builder = sdkJsCompile( + const BuilderOptions({'use-prebuilt-sdk-from-path': 'path/to/sdk'}), + ); + final sdkAssets = {'build_web_compilers|fake.txt': ''}; + final expectedOutputs = { + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js': decodedMatches( + 'prebuilt-sdk', + ), + 'build_web_compilers|lib/src/dev_compiler/dart_sdk.js.map': + decodedMatches('prebuilt-sdk-map'), + }; + final fs = MemoryFileSystem(); + fs.directory('path/to/sdk') + ..createSync(recursive: true) + ..childFile('dart_sdk.js').writeAsStringSync('prebuilt-sdk') + ..childFile('dart_sdk.js.map').writeAsStringSync('prebuilt-sdk-map'); + await IOOverrides.runZoned(createFile: fs.file, () async { + await testBuilder(builder, sdkAssets, outputs: expectedOutputs); + }); + }); }); } diff --git a/build_web_compilers/test/dev_compiler_builder_test.dart b/build_web_compilers/test/dev_compiler_builder_test.dart index e3e7f6fe1..235bdd3b7 100644 --- a/build_web_compilers/test/dev_compiler_builder_test.dart +++ b/build_web_compilers/test/dev_compiler_builder_test.dart @@ -11,6 +11,8 @@ import 'package:logging/logging.dart'; import 'package:test/test.dart'; void main() { + initializePlatforms(); + group('error free project', () { final startingAssets = { 'a|lib/a.dart': r'''