From 75ae44df77aef3d109add07abfd88438732bbee8 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 5 Apr 2024 12:43:15 -0700 Subject: [PATCH] Copy part files and sourcemaps when building with dart2js. (#146356) This fixes https://github.com/flutter/flutter/issues/145653 When dart2js emits deferred part files, they need to be copied from the build folder to the output folder. --- .../lib/src/build_system/targets/web.dart | 78 ++++++++++++++----- .../build_system/targets/web_test.dart | 6 ++ 2 files changed, 65 insertions(+), 19 deletions(-) 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 fad26c594d0..d7c7f27257a 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/web.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart @@ -93,7 +93,6 @@ class WebEntrypointTarget extends Target { } } -/// Compiles a web entry point with dart2js. abstract class Dart2WebTarget extends Target { const Dart2WebTarget(); @@ -102,7 +101,8 @@ abstract class Dart2WebTarget extends Target { WebCompilerConfig get compilerConfig; Map get buildConfig; - List get buildFiles; + Iterable buildFiles(Environment environment); + Iterable get buildPatternStems; @override List get dependencies => const [ @@ -120,14 +120,15 @@ abstract class Dart2WebTarget extends Target { ]; @override - List get outputs => buildFiles.map( - (String file) => Source.pattern('{BUILD_DIR}/$file') - ).toList(); + List get outputs => [ + for (final String stem in buildPatternStems) Source.pattern('{BUILD_DIR}/$stem'), + ]; @override String get buildKey => compilerConfig.buildKey; } +/// Compiles a web entry point with dart2js. class Dart2JSTarget extends Dart2WebTarget { Dart2JSTarget(this.compilerConfig); @@ -230,12 +231,43 @@ class Dart2JSTarget extends Dart2WebTarget { }; @override - List get buildFiles => [ + Iterable buildFiles(Environment environment) + => environment.buildDir + .listSync(recursive: true) + .whereType() + .where((File file) { + if (file.basename == 'main.dart.js') { + return true; + } + if (file.basename == 'main.dart.js.map') { + return compilerConfig.sourceMaps; + } + final RegExp partFileRegex = RegExp(r'main\.dart\.js_[0-9].*\.part\.js'); + if (partFileRegex.hasMatch(file.basename)) { + return true; + } + + if (compilerConfig.sourceMaps) { + final RegExp partFileSourceMapRegex = RegExp(r'main\.dart\.js_[0-9].*.part\.js\.map'); + if (partFileSourceMapRegex.hasMatch(file.basename)) { + return true; + } + } + return false; + }); + + @override + Iterable get buildPatternStems => [ 'main.dart.js', - if (compilerConfig.sourceMaps) 'main.dart.js.map', + 'main.dart.js_*.part.js', + if (compilerConfig.sourceMaps) ...[ + 'main.dart.js.map', + 'main.dart.js_*.part.js.map', + ], ]; } +/// Compiles a web entry point with dart2wasm. class Dart2WasmTarget extends Dart2WebTarget { Dart2WasmTarget(this.compilerConfig); @@ -273,7 +305,7 @@ class Dart2WasmTarget extends Dart2WebTarget { if (compilerConfig.renderer == WebRendererMode.skwasm) ...[ '--extra-compiler-option=--import-shared-memory', '--extra-compiler-option=--shared-memory-max-pages=32768', - ], + ], if (buildMode == BuildMode.profile) '-Ddart.vm.profile=true' else @@ -320,7 +352,17 @@ class Dart2WasmTarget extends Dart2WebTarget { }; @override - List get buildFiles => [ + Iterable buildFiles(Environment environment) + => environment.buildDir + .listSync(recursive: true) + .whereType() + .where((File file) => switch (file.basename) { + 'main.dart.wasm' || 'main.dart.mjs' => true, + _ => false, + }); + + @override + Iterable get buildPatternStems => const [ 'main.dart.wasm', 'main.dart.mjs', ]; @@ -361,11 +403,6 @@ _flutter.buildConfig = ${jsonEncode(buildConfig)}; final List compileTargets; final WebTemplatedFiles templatedFilesTarget; - List get buildFiles => compileTargets.fold( - const Iterable.empty(), - (Iterable current, Dart2WebTarget target) => current.followedBy(target.buildFiles) - ).toList(); - @override String get name => 'web_release_bundle'; @@ -375,15 +412,19 @@ _flutter.buildConfig = ${jsonEncode(buildConfig)}; templatedFilesTarget, ]; + Iterable get buildPatternStems => compileTargets.expand( + (Dart2WebTarget target) => target.buildPatternStems, + ); + @override List get inputs => [ const Source.pattern('{PROJECT_DIR}/pubspec.yaml'), - ...buildFiles.map((String file) => Source.pattern('{BUILD_DIR}/$file')) + ...buildPatternStems.map((String file) => Source.pattern('{BUILD_DIR}/$file')) ]; @override List get outputs => [ - ...buildFiles.map((String file) => Source.pattern('{OUTPUT_DIR}/$file')) + ...buildPatternStems.map((String file) => Source.pattern('{OUTPUT_DIR}/$file')) ]; @override @@ -395,9 +436,8 @@ _flutter.buildConfig = ${jsonEncode(buildConfig)}; @override Future build(Environment environment) async { final FileSystem fileSystem = environment.fileSystem; - for (final File outputFile in environment.buildDir.listSync(recursive: true).whereType()) { - final String basename = fileSystem.path.basename(outputFile.path); - if (buildFiles.contains(basename)) { + for (final Dart2WebTarget target in compileTargets) { + for (final File outputFile in target.buildFiles(environment)) { outputFile.copySync( environment.outputDir.childFile(fileSystem.path.basename(outputFile.path)).path ); 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 d54228ef073..6afa2979926 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 @@ -170,6 +170,8 @@ void main() { ..writeAsStringSync('A'); environment.buildDir.childFile('main.dart.js').createSync(); environment.buildDir.childFile('main.dart.js.map').createSync(); + environment.buildDir.childFile('main.dart.js_1.part.js').createSync(); + environment.buildDir.childFile('main.dart.js_1.part.js.map').createSync(); await WebReleaseBundle([ const JsCompilerConfig() @@ -181,6 +183,10 @@ void main() { .existsSync(), true); expect(environment.outputDir.childFile('main.dart.js.map') .existsSync(), true); + expect(environment.outputDir.childFile('main.dart.js_1.part.js') + .existsSync(), true); + expect(environment.outputDir.childFile('main.dart.js_1.part.js.map') + .existsSync(), true); expect(environment.outputDir.childDirectory('assets') .childFile('AssetManifest.bin.json').existsSync(), true);