Remove most of the target logic for build web, cleanup rules (#34589)
diff --git a/packages/flutter_tools/lib/src/build_runner/build_script.dart b/packages/flutter_tools/lib/src/build_runner/build_script.dart
new file mode 100644
index 0000000..f5da2d5
--- /dev/null
+++ b/packages/flutter_tools/lib/src/build_runner/build_script.dart
@@ -0,0 +1,459 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// ignore_for_file: implementation_imports
+import 'dart:async';
+import 'dart:convert'; // ignore: dart_convert_import
+import 'dart:io'; // ignore: dart_io_import
+import 'dart:isolate';
+
+import 'package:analyzer/analyzer.dart'; // ignore: deprecated_member_use
+import 'package:build_runner/build_runner.dart' as build_runner;
+import 'package:build/build.dart';
+import 'package:build_config/build_config.dart';
+import 'package:build_modules/build_modules.dart';
+import 'package:build_modules/builders.dart';
+import 'package:build_modules/src/module_builder.dart';
+import 'package:build_modules/src/platform.dart';
+import 'package:build_modules/src/workers.dart';
+import 'package:build_runner_core/build_runner_core.dart' as core;
+import 'package:build_test/builder.dart';
+import 'package:build_test/src/debug_test_builder.dart';
+import 'package:build_web_compilers/build_web_compilers.dart';
+import 'package:build_web_compilers/builders.dart';
+import 'package:build_web_compilers/src/dev_compiler_bootstrap.dart';
+import 'package:crypto/crypto.dart';
+
+import 'package:path/path.dart' as path; // ignore: package_path_import
+import 'package:scratch_space/scratch_space.dart';
+import 'package:test_core/backend.dart';
+
+const String ddcBootstrapExtension = '.dart.bootstrap.js';
+const String jsEntrypointExtension = '.dart.js';
+const String jsEntrypointSourceMapExtension = '.dart.js.map';
+const String jsEntrypointArchiveExtension = '.dart.js.tar.gz';
+const String digestsEntrypointExtension = '.digests';
+const String jsModuleErrorsExtension = '.ddc.js.errors';
+const String jsModuleExtension = '.ddc.js';
+const String jsSourceMapExtension = '.ddc.js.map';
+
+final DartPlatform flutterWebPlatform =
+ DartPlatform.register('flutter_web', <String>[
+ 'async',
+ 'collection',
+ 'convert',
+ 'core',
+ 'developer',
+ 'html',
+ 'html_common',
+ 'indexed_db',
+ 'js',
+ 'js_util',
+ 'math',
+ 'svg',
+ 'typed_data',
+ 'web_audio',
+ 'web_gl',
+ 'web_sql',
+ '_internal',
+ // Flutter web specific libraries.
+ 'ui',
+ '_engine',
+ 'io',
+ 'isolate',
+]);
+
+/// The builders required to compile a Flutter application to the web.
+final List<core.BuilderApplication> builders = <core.BuilderApplication>[
+ core.apply(
+ 'flutter_tools:test_bootstrap',
+ <BuilderFactory>[
+ (BuilderOptions options) => const DebugTestBuilder(),
+ (BuilderOptions options) => const FlutterWebTestBootstrapBuilder(),
+ ],
+ core.toRoot(),
+ hideOutput: true,
+ defaultGenerateFor: const InputSet(
+ include: <String>[
+ 'test/**',
+ ],
+ ),
+ ),
+ core.apply(
+ 'flutter_tools:shell',
+ <BuilderFactory>[
+ (BuilderOptions options) => const FlutterWebShellBuilder(),
+ ],
+ core.toRoot(),
+ hideOutput: true,
+ defaultGenerateFor: const InputSet(
+ include: <String>[
+ 'lib/**',
+ 'web/**',
+ ],
+ ),
+ ),
+ core.apply(
+ 'flutter_tools:module_library',
+ <Builder Function(BuilderOptions)>[moduleLibraryBuilder],
+ core.toAllPackages(),
+ isOptional: true,
+ hideOutput: true,
+ appliesBuilders: <String>['flutter_tools:module_cleanup']),
+ core.apply(
+ 'flutter_tools:ddc_modules',
+ <Builder Function(BuilderOptions)>[
+ (BuilderOptions options) => MetaModuleBuilder(flutterWebPlatform),
+ (BuilderOptions options) => MetaModuleCleanBuilder(flutterWebPlatform),
+ (BuilderOptions options) => ModuleBuilder(flutterWebPlatform),
+ ],
+ core.toNoneByDefault(),
+ isOptional: true,
+ hideOutput: true,
+ appliesBuilders: <String>['flutter_tools:module_cleanup']),
+ core.apply(
+ 'flutter_tools:ddc',
+ <Builder Function(BuilderOptions)>[
+ (BuilderOptions builderOptions) => KernelBuilder(
+ platformSdk: builderOptions.config['flutterWebSdk'],
+ summaryOnly: true,
+ sdkKernelPath: path.join('kernel', 'flutter_ddc_sdk.dill'),
+ outputExtension: ddcKernelExtension,
+ platform: flutterWebPlatform,
+ librariesPath: 'libraries.json',
+ ),
+ (BuilderOptions builderOptions) => DevCompilerBuilder(
+ useIncrementalCompiler: true,
+ platform: flutterWebPlatform,
+ platformSdk: builderOptions.config['flutterWebSdk'],
+ sdkKernelPath: path.join('kernel', 'flutter_ddc_sdk.dill'),
+ ),
+ ],
+ core.toAllPackages(),
+ isOptional: true,
+ hideOutput: true,
+ appliesBuilders: <String>['flutter_tools:ddc_modules']),
+ core.apply(
+ 'flutter_tools:entrypoint',
+ <BuilderFactory>[
+ (BuilderOptions options) => FlutterWebEntrypointBuilder(
+ options.config['release'] ?? false,
+ options.config['flutterWebSdk'],
+ ),
+ ],
+ core.toRoot(),
+ hideOutput: true,
+ defaultGenerateFor: const InputSet(
+ include: <String>[
+ 'lib/**_web_entrypoint.dart',
+ ],
+ ),
+ ),
+ core.apply(
+ 'flutter_tools:test_entrypoint',
+ <BuilderFactory>[
+ (BuilderOptions options) => const FlutterWebTestEntrypointBuilder(),
+ ],
+ core.toRoot(),
+ hideOutput: true,
+ defaultGenerateFor: const InputSet(
+ include: <String>[
+ 'test/**_test.dart.browser_test.dart',
+ ],
+ ),
+ ),
+ core.applyPostProcess('flutter_tools:module_cleanup', moduleCleanup,
+ defaultGenerateFor: const InputSet())
+];
+
+/// The entrypoint to this build script.
+Future<void> main(List<String> args, [SendPort sendPort]) async {
+ core.overrideGeneratedOutputDirectory('flutter_web');
+ final int result = await build_runner.run(args, builders);
+ sendPort?.send(result);
+}
+
+/// A ddc-only entrypoint builder that respects the Flutter target flag.
+class FlutterWebTestEntrypointBuilder implements Builder {
+ const FlutterWebTestEntrypointBuilder();
+
+ @override
+ Map<String, List<String>> get buildExtensions => const <String, List<String>>{
+ '.dart': <String>[
+ ddcBootstrapExtension,
+ jsEntrypointExtension,
+ jsEntrypointSourceMapExtension,
+ jsEntrypointArchiveExtension,
+ digestsEntrypointExtension,
+ ],
+ };
+
+ @override
+ Future<void> build(BuildStep buildStep) async {
+ log.info('building for target ${buildStep.inputId.path}');
+ await bootstrapDdc(buildStep, platform: flutterWebPlatform);
+ }
+}
+
+/// A ddc-only entrypoint builder that respects the Flutter target flag.
+class FlutterWebEntrypointBuilder implements Builder {
+ const FlutterWebEntrypointBuilder(this.release, this.flutterWebSdk);
+
+ final bool release;
+ final String flutterWebSdk;
+
+ @override
+ Map<String, List<String>> get buildExtensions => const <String, List<String>>{
+ '.dart': <String>[
+ ddcBootstrapExtension,
+ jsEntrypointExtension,
+ jsEntrypointSourceMapExtension,
+ jsEntrypointArchiveExtension,
+ digestsEntrypointExtension,
+ ],
+ };
+
+ @override
+ Future<void> build(BuildStep buildStep) async {
+ if (release) {
+ await bootstrapDart2Js(buildStep, flutterWebSdk);
+ } else {
+ await bootstrapDdc(buildStep, platform: flutterWebPlatform);
+ }
+ }
+}
+
+/// Bootstraps the test entrypoint.
+class FlutterWebTestBootstrapBuilder implements Builder {
+ const FlutterWebTestBootstrapBuilder();
+
+ @override
+ Map<String, List<String>> get buildExtensions => const <String, List<String>>{
+ '_test.dart': <String>[
+ '_test.dart.browser_test.dart',
+ ]
+ };
+
+ @override
+ Future<void> build(BuildStep buildStep) async {
+ final AssetId id = buildStep.inputId;
+ final String contents = await buildStep.readAsString(id);
+ final String assetPath = id.pathSegments.first == 'lib'
+ ? path.url.join('packages', id.package, id.path)
+ : id.path;
+ final Metadata metadata = parseMetadata(
+ assetPath, contents, Runtime.builtIn.map((Runtime runtime) => runtime.name).toSet());
+
+ if (metadata.testOn.evaluate(SuitePlatform(Runtime.chrome))) {
+ await buildStep.writeAsString(id.addExtension('.browser_test.dart'), '''
+import 'dart:ui' as ui;
+import 'dart:html';
+import 'dart:js';
+
+import 'package:stream_channel/stream_channel.dart';
+import 'package:test_api/src/backend/stack_trace_formatter.dart'; // ignore: implementation_imports
+import 'package:test_api/src/util/stack_trace_mapper.dart'; // ignore: implementation_imports
+import 'package:test_api/src/remote_listener.dart'; // ignore: implementation_imports
+import 'package:test_api/src/suite_channel_manager.dart'; // ignore: implementation_imports
+
+import "${path.url.basename(id.path)}" as test;
+
+Future<void> main() async {
+ // Extra initialization for flutter_web.
+ // The following parameters are hard-coded in Flutter's test embedder. Since
+ // we don't have an embedder yet this is the lowest-most layer we can put
+ // this stuff in.
+ await ui.webOnlyInitializeEngine();
+ // TODO(flutterweb): remove need for dynamic cast.
+ (ui.window as dynamic).debugOverrideDevicePixelRatio(3.0);
+ (ui.window as dynamic).webOnlyDebugPhysicalSizeOverride = const ui.Size(2400, 1800);
+ internalBootstrapBrowserTest(() => test.main);
+}
+
+void internalBootstrapBrowserTest(Function getMain()) {
+ var channel =
+ serializeSuite(getMain, hidePrints: false, beforeLoad: () async {
+ var serialized =
+ await suiteChannel("test.browser.mapper").stream.first as Map;
+ if (serialized == null) return;
+ });
+ postMessageChannel().pipe(channel);
+}
+StreamChannel serializeSuite(Function getMain(),
+ {bool hidePrints = true, Future beforeLoad()}) =>
+ RemoteListener.start(getMain,
+ hidePrints: hidePrints, beforeLoad: beforeLoad);
+
+StreamChannel suiteChannel(String name) {
+ var manager = SuiteChannelManager.current;
+ if (manager == null) {
+ throw StateError('suiteChannel() may only be called within a test worker.');
+ }
+
+ return manager.connectOut(name);
+}
+
+StreamChannel postMessageChannel() {
+ var controller = StreamChannelController(sync: true);
+ window.onMessage.firstWhere((message) {
+ return message.origin == window.location.origin && message.data == "port";
+ }).then((message) {
+ var port = message.ports.first;
+ var portSubscription = port.onMessage.listen((message) {
+ controller.local.sink.add(message.data);
+ });
+
+ controller.local.stream.listen((data) {
+ port.postMessage({"data": data});
+ }, onDone: () {
+ port.postMessage({"event": "done"});
+ portSubscription.cancel();
+ });
+ });
+
+ context['parent'].callMethod('postMessage', [
+ JsObject.jsify({"href": window.location.href, "ready": true}),
+ window.location.origin,
+ ]);
+ return controller.foreign;
+}
+
+void setStackTraceMapper(StackTraceMapper mapper) {
+ var formatter = StackTraceFormatter.current;
+ if (formatter == null) {
+ throw StateError(
+ 'setStackTraceMapper() may only be called within a test worker.');
+ }
+
+ formatter.configure(mapper: mapper);
+}
+''');
+ }
+ }
+}
+
+/// A shell builder which generates the web specific entrypoint.
+class FlutterWebShellBuilder implements Builder {
+ const FlutterWebShellBuilder();
+
+ @override
+ Future<void> build(BuildStep buildStep) async {
+ final AssetId dartEntrypointId = buildStep.inputId;
+ final bool isAppEntrypoint = await _isAppEntryPoint(dartEntrypointId, buildStep);
+ if (!isAppEntrypoint) {
+ return;
+ }
+ final AssetId outputId = buildStep.inputId.changeExtension('_web_entrypoint.dart');
+ await buildStep.writeAsString(outputId, '''
+import 'dart:ui' as ui;
+import "${path.url.basename(buildStep.inputId.path)}" as entrypoint;
+
+Future<void> main() async {
+ await ui.webOnlyInitializePlatform();
+ entrypoint.main();
+}
+
+''');
+ }
+
+ @override
+ Map<String, List<String>> get buildExtensions => const <String, List<String>>{
+ '.dart': <String>['_web_entrypoint.dart'],
+ };
+}
+
+Future<void> bootstrapDart2Js(BuildStep buildStep, String flutterWebSdk) async {
+ final AssetId dartEntrypointId = buildStep.inputId;
+ final AssetId moduleId = dartEntrypointId.changeExtension(moduleExtension(flutterWebPlatform));
+ final Module module = Module.fromJson(json.decode(await buildStep.readAsString(moduleId)));
+
+ final List<Module> allDeps = await module.computeTransitiveDependencies(buildStep, throwIfUnsupported: false)..add(module);
+ final ScratchSpace scratchSpace = await buildStep.fetchResource(scratchSpaceResource);
+ final Iterable<AssetId> allSrcs = allDeps.expand((Module module) => module.sources);
+ await scratchSpace.ensureAssets(allSrcs, buildStep);
+
+ final String packageFile = await _createPackageFile(allSrcs, buildStep, scratchSpace);
+ final String dartPath = dartEntrypointId.path.startsWith('lib/')
+ ? 'package:${dartEntrypointId.package}/'
+ '${dartEntrypointId.path.substring('lib/'.length)}'
+ : dartEntrypointId.path;
+ final String jsOutputPath =
+ '${path.withoutExtension(dartPath.replaceFirst('package:', 'packages/'))}'
+ '$jsEntrypointExtension';
+ final String flutterWebSdkPath = flutterWebSdk;
+ final String librariesPath = path.join(flutterWebSdkPath, 'libraries.json');
+ final List<String> args = <String>[
+ '--libraries-spec="$librariesPath"',
+ '-O4',
+ '-o',
+ '$jsOutputPath',
+ '--packages="$packageFile"',
+ '-Ddart.vm.product=true',
+ dartPath,
+ ];
+ final Dart2JsBatchWorkerPool dart2js = await buildStep.fetchResource(dart2JsWorkerResource);
+ final Dart2JsResult result = await dart2js.compile(args);
+ final AssetId jsOutputId = dartEntrypointId.changeExtension(jsEntrypointExtension);
+ final File jsOutputFile = scratchSpace.fileFor(jsOutputId);
+ if (result.succeeded && jsOutputFile.existsSync()) {
+ log.info(result.output);
+ // Explicitly write out the original js file and sourcemap.
+ await scratchSpace.copyOutput(jsOutputId, buildStep);
+ final AssetId jsSourceMapId =
+ dartEntrypointId.changeExtension(jsEntrypointSourceMapExtension);
+ await _copyIfExists(jsSourceMapId, scratchSpace, buildStep);
+ } else {
+ log.severe(result.output);
+ }
+}
+
+Future<void> _copyIfExists(
+ AssetId id, ScratchSpace scratchSpace, AssetWriter writer) async {
+ final File file = scratchSpace.fileFor(id);
+ if (file.existsSync()) {
+ await scratchSpace.copyOutput(id, writer);
+ }
+}
+
+/// Creates a `.packages` file unique to this entrypoint at the root of the
+/// scratch space and returns it's filename.
+///
+/// Since mulitple invocations of Dart2Js will share a scratch space and we only
+/// know the set of packages involved the current entrypoint we can't construct
+/// a `.packages` file that will work for all invocations of Dart2Js so a unique
+/// file is created for every entrypoint that is run.
+///
+/// The filename is based off the MD5 hash of the asset path so that files are
+/// unique regarless of situations like `web/foo/bar.dart` vs
+/// `web/foo-bar.dart`.
+Future<String> _createPackageFile(Iterable<AssetId> inputSources, BuildStep buildStep, ScratchSpace scratchSpace) async {
+ final Uri inputUri = buildStep.inputId.uri;
+ final String packageFileName =
+ '.package-${md5.convert(inputUri.toString().codeUnits)}';
+ final File packagesFile =
+ scratchSpace.fileFor(AssetId(buildStep.inputId.package, packageFileName));
+ final Set<String> packageNames = inputSources.map((AssetId s) => s.package).toSet();
+ final String packagesFileContent =
+ packageNames.map((String name) => '$name:packages/$name/').join('\n');
+ await packagesFile
+ .writeAsString('# Generated for $inputUri\n$packagesFileContent');
+ return packageFileName;
+}
+
+/// Returns whether or not [dartId] is an app entrypoint (basically, whether
+/// or not it has a `main` function).
+Future<bool> _isAppEntryPoint(AssetId dartId, AssetReader reader) async {
+ assert(dartId.extension == '.dart');
+ // Skip reporting errors here, dartdevc will report them later with nicer
+ // formatting.
+ final CompilationUnit parsed = parseCompilationUnit(await reader.readAsString(dartId),
+ suppressErrors: true);
+ // Allow two or fewer arguments so that entrypoints intended for use with
+ // [spawnUri] get counted.
+ return parsed.declarations.any((CompilationUnitMember node) {
+ return node is FunctionDeclaration &&
+ node.name.name == 'main' &&
+ node.functionExpression.parameters.parameters.length <= 2;
+ });
+}
diff --git a/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart b/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
index faeefb2..233497a 100644
--- a/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
+++ b/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
@@ -4,31 +4,16 @@
// ignore_for_file: implementation_imports
import 'dart:async';
-import 'dart:io' as io; // ignore: dart_io_import
import 'package:build/build.dart';
-import 'package:build_config/build_config.dart';
-import 'package:build_modules/build_modules.dart';
-import 'package:build_modules/builders.dart';
-import 'package:build_modules/src/module_builder.dart';
-import 'package:build_modules/src/platform.dart';
-import 'package:build_modules/src/workers.dart';
import 'package:build_runner_core/build_runner_core.dart' as core;
import 'package:build_runner_core/src/asset_graph/graph.dart';
import 'package:build_runner_core/src/asset_graph/node.dart';
import 'package:build_runner_core/src/generate/build_impl.dart';
import 'package:build_runner_core/src/generate/options.dart';
-import 'package:build_test/builder.dart';
-import 'package:build_test/src/debug_test_builder.dart';
-import 'package:build_web_compilers/build_web_compilers.dart';
-import 'package:build_web_compilers/builders.dart';
-import 'package:build_web_compilers/src/dev_compiler_bootstrap.dart';
-import 'package:crypto/crypto.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
-import 'package:scratch_space/scratch_space.dart';
-import 'package:test_core/backend.dart';
import 'package:watcher/watcher.dart';
import '../artifacts.dart';
@@ -36,152 +21,10 @@
import '../base/logger.dart';
import '../base/platform.dart';
import '../compile.dart';
-import '../convert.dart';
import '../dart/package_map.dart';
import '../globals.dart';
import '../web/compile.dart';
-
-const String ddcBootstrapExtension = '.dart.bootstrap.js';
-const String jsEntrypointExtension = '.dart.js';
-const String jsEntrypointSourceMapExtension = '.dart.js.map';
-const String jsEntrypointArchiveExtension = '.dart.js.tar.gz';
-const String digestsEntrypointExtension = '.digests';
-const String jsModuleErrorsExtension = '.ddc.js.errors';
-const String jsModuleExtension = '.ddc.js';
-const String jsSourceMapExtension = '.ddc.js.map';
-
-final DartPlatform flutterWebPlatform =
- DartPlatform.register('flutter_web', <String>[
- 'async',
- 'collection',
- 'convert',
- 'core',
- 'developer',
- 'html',
- 'html_common',
- 'indexed_db',
- 'js',
- 'js_util',
- 'math',
- 'svg',
- 'typed_data',
- 'web_audio',
- 'web_gl',
- 'web_sql',
- '_internal',
- // Flutter web specific libraries.
- 'ui',
- '_engine',
- 'io',
- 'isolate',
-]);
-
-/// The build application to compile a flutter application to the web.
-final List<core.BuilderApplication> builders = <core.BuilderApplication>[
- core.apply(
- 'flutter_tools|test_bootstrap',
- <BuilderFactory>[
- (BuilderOptions options) => const DebugTestBuilder(),
- (BuilderOptions options) => const FlutterWebTestBootstrapBuilder(),
- ],
- core.toRoot(),
- hideOutput: true,
- defaultGenerateFor: const InputSet(
- include: <String>[
- 'test/**',
- ],
- ),
- ),
- core.apply(
- 'flutter_tools|shell',
- <BuilderFactory>[
- (BuilderOptions options) => FlutterWebShellBuilder(
- options.config['targets'] ?? <String>['lib/main.dart']
- ),
- ],
- core.toRoot(),
- hideOutput: true,
- defaultGenerateFor: const InputSet(
- include: <String>[
- 'lib/**',
- 'web/**',
- ],
- ),
- ),
- core.apply(
- 'flutter_tools|module_library',
- <Builder Function(BuilderOptions)>[moduleLibraryBuilder],
- core.toAllPackages(),
- isOptional: true,
- hideOutput: true,
- appliesBuilders: <String>['flutter_tools|module_cleanup']),
- core.apply(
- 'flutter_tools|ddc_modules',
- <Builder Function(BuilderOptions)>[
- (BuilderOptions options) => MetaModuleBuilder(flutterWebPlatform),
- (BuilderOptions options) => MetaModuleCleanBuilder(flutterWebPlatform),
- (BuilderOptions options) => ModuleBuilder(flutterWebPlatform),
- ],
- core.toNoneByDefault(),
- isOptional: true,
- hideOutput: true,
- appliesBuilders: <String>['flutter_tools|module_cleanup']),
- core.apply(
- 'flutter_tools|ddc',
- <Builder Function(BuilderOptions)>[
- (BuilderOptions builderOptions) => KernelBuilder(
- platformSdk: artifacts.getArtifactPath(Artifact.flutterWebSdk),
- summaryOnly: true,
- sdkKernelPath: path.join('kernel', 'flutter_ddc_sdk.dill'),
- outputExtension: ddcKernelExtension,
- platform: flutterWebPlatform,
- librariesPath: 'libraries.json',
- ),
- (BuilderOptions builderOptions) => DevCompilerBuilder(
- useIncrementalCompiler: false,
- platform: flutterWebPlatform,
- platformSdk: artifacts.getArtifactPath(Artifact.flutterWebSdk),
- sdkKernelPath: path.join('kernel', 'flutter_ddc_sdk.dill'),
- ),
- ],
- core.toAllPackages(),
- isOptional: true,
- hideOutput: true,
- appliesBuilders: <String>['flutter_tools|ddc_modules']),
- core.apply(
- 'flutter_tools|entrypoint',
- <BuilderFactory>[
- (BuilderOptions options) => FlutterWebEntrypointBuilder(
- options.config['targets'] ?? <String>['lib/main.dart'],
- options.config['release'],
- ),
- ],
- core.toRoot(),
- hideOutput: true,
- defaultGenerateFor: const InputSet(
- include: <String>[
- 'lib/**',
- ],
- ),
- ),
- core.apply(
- 'flutter_tools|test_entrypoint',
- <BuilderFactory>[
- (BuilderOptions options) => FlutterWebTestEntrypointBuilder(
- options.config['targets'] ?? const <String>[]
- ),
- ],
- core.toRoot(),
- hideOutput: true,
- defaultGenerateFor: const InputSet(
- include: <String>[
- 'test/**_test.dart.browser_test.dart',
- ],
- ),
- ),
- core.applyPostProcess('flutter_tools|module_cleanup', moduleCleanup,
- defaultGenerateFor: const InputSet())
-];
+import 'build_script.dart';
/// A build_runner specific implementation of the [WebCompilationProxy].
class BuildRunnerWebCompilationProxy extends WebCompilationProxy {
@@ -193,8 +36,7 @@
@override
Future<bool> initialize({
- @required Directory projectDirectory,
- @required List<String> targets,
+ Directory projectDirectory,
String testOutputDir,
bool release = false,
}) async {
@@ -238,14 +80,11 @@
),
),
};
- final Status status =
- logger.startProgress('Compiling ${targets.first} for the Web...', timeout: null);
core.BuildResult result;
try {
result = await _runBuilder(
buildEnvironment,
buildOptions,
- targets,
release,
buildDirs,
);
@@ -255,7 +94,6 @@
result = await _runBuilder(
buildEnvironment,
buildOptions,
- targets,
release,
buildDirs,
);
@@ -265,13 +103,10 @@
result = await _runBuilder(
buildEnvironment,
buildOptions,
- targets,
release,
buildDirs,
);
return result.status == core.BuildStatus.success;
- } finally {
- status.stop();
}
}
@@ -293,24 +128,22 @@
return result.status == core.BuildStatus.success;
}
-
- Future<core.BuildResult> _runBuilder(core.BuildEnvironment buildEnvironment, BuildOptions buildOptions, List<String> targets, bool release, Set<core.BuildDirectory> buildDirs) async {
+ Future<core.BuildResult> _runBuilder(core.BuildEnvironment buildEnvironment, BuildOptions buildOptions, bool release, Set<core.BuildDirectory> buildDirs) async {
_builder = await BuildImpl.create(
buildOptions,
buildEnvironment,
builders,
<String, Map<String, dynamic>>{
- 'flutter_tools|entrypoint': <String, dynamic>{
- 'targets': targets,
+ 'flutter_tools:ddc': <String, dynamic>{
+ 'flutterWebSdk': artifacts.getArtifactPath(Artifact.flutterWebSdk),
+ },
+ 'flutter_tools:entrypoint': <String, dynamic>{
+ 'release': release,
+ 'flutterWebSdk': artifacts.getArtifactPath(Artifact.flutterWebSdk),
+ },
+ 'flutter_tools:test_entrypoint': <String, dynamic>{
'release': release,
},
- 'flutter_tools|test_entrypoint': <String, dynamic>{
- 'targets': targets,
- 'release': release,
- },
- 'flutter_tools|shell': <String, dynamic>{
- 'targets': targets,
- }
},
isReleaseBuild: false,
);
@@ -364,301 +197,3 @@
}
}
}
-
-/// A ddc-only entrypoint builder that respects the Flutter target flag.
-class FlutterWebTestEntrypointBuilder implements Builder {
- const FlutterWebTestEntrypointBuilder(this.targets);
-
- final List<String> targets;
-
- @override
- Map<String, List<String>> get buildExtensions => const <String, List<String>>{
- '.dart': <String>[
- ddcBootstrapExtension,
- jsEntrypointExtension,
- jsEntrypointSourceMapExtension,
- jsEntrypointArchiveExtension,
- digestsEntrypointExtension,
- ],
- };
-
- @override
- Future<void> build(BuildStep buildStep) async {
- bool matches = false;
- for (String target in targets) {
- if (buildStep.inputId.path.contains(target)) {
- matches = true;
- break;
- }
- }
- if (!matches) {
- return;
- }
- log.info('building for target ${buildStep.inputId.path}');
- await bootstrapDdc(buildStep, platform: flutterWebPlatform);
- }
-}
-
-/// A ddc-only entrypoint builder that respects the Flutter target flag.
-class FlutterWebEntrypointBuilder implements Builder {
- const FlutterWebEntrypointBuilder(this.targets, this.release);
-
- final List<String> targets;
- final bool release;
-
- @override
- Map<String, List<String>> get buildExtensions => const <String, List<String>>{
- '.dart': <String>[
- ddcBootstrapExtension,
- jsEntrypointExtension,
- jsEntrypointSourceMapExtension,
- jsEntrypointArchiveExtension,
- digestsEntrypointExtension,
- ],
- };
-
- @override
- Future<void> build(BuildStep buildStep) async {
- bool matches = false;
- for (String target in targets) {
- if (buildStep.inputId.path.contains(fs.path.setExtension(target, '_web_entrypoint.dart'))) {
- matches = true;
- break;
- }
- }
- if (!matches) {
- return;
- }
- log.info('building for target ${buildStep.inputId.path}');
- if (release) {
- await bootstrapDart2Js(buildStep);
- } else {
- await bootstrapDdc(buildStep, platform: flutterWebPlatform);
- }
- }
-}
-
-/// Bootstraps the test entrypoint.
-class FlutterWebTestBootstrapBuilder implements Builder {
- const FlutterWebTestBootstrapBuilder();
-
- @override
- Map<String, List<String>> get buildExtensions => const <String, List<String>>{
- '_test.dart': <String>[
- '_test.dart.browser_test.dart',
- ]
- };
-
- @override
- Future<void> build(BuildStep buildStep) async {
- final AssetId id = buildStep.inputId;
- final String contents = await buildStep.readAsString(id);
- final String assetPath = id.pathSegments.first == 'lib'
- ? path.url.join('packages', id.package, id.path)
- : id.path;
- final Metadata metadata = parseMetadata(
- assetPath, contents, Runtime.builtIn.map((Runtime runtime) => runtime.name).toSet());
-
- if (metadata.testOn.evaluate(SuitePlatform(Runtime.chrome))) {
- await buildStep.writeAsString(id.addExtension('.browser_test.dart'), '''
-import 'dart:ui' as ui;
-import 'dart:html';
-import 'dart:js';
-
-import 'package:stream_channel/stream_channel.dart';
-import 'package:test_api/src/backend/stack_trace_formatter.dart'; // ignore: implementation_imports
-import 'package:test_api/src/util/stack_trace_mapper.dart'; // ignore: implementation_imports
-import 'package:test_api/src/remote_listener.dart'; // ignore: implementation_imports
-import 'package:test_api/src/suite_channel_manager.dart'; // ignore: implementation_imports
-
-import "${path.url.basename(id.path)}" as test;
-
-Future<void> main() async {
- // Extra initialization for flutter_web.
- // The following parameters are hard-coded in Flutter's test embedder. Since
- // we don't have an embedder yet this is the lowest-most layer we can put
- // this stuff in.
- await ui.webOnlyInitializeEngine();
- // TODO(flutterweb): remove need for dynamic cast.
- (ui.window as dynamic).debugOverrideDevicePixelRatio(3.0);
- (ui.window as dynamic).webOnlyDebugPhysicalSizeOverride = const ui.Size(2400, 1800);
- internalBootstrapBrowserTest(() => test.main);
-}
-
-void internalBootstrapBrowserTest(Function getMain()) {
- var channel =
- serializeSuite(getMain, hidePrints: false, beforeLoad: () async {
- var serialized =
- await suiteChannel("test.browser.mapper").stream.first as Map;
- if (serialized == null) return;
- });
- postMessageChannel().pipe(channel);
-}
-StreamChannel serializeSuite(Function getMain(),
- {bool hidePrints = true, Future beforeLoad()}) =>
- RemoteListener.start(getMain,
- hidePrints: hidePrints, beforeLoad: beforeLoad);
-
-StreamChannel suiteChannel(String name) {
- var manager = SuiteChannelManager.current;
- if (manager == null) {
- throw StateError('suiteChannel() may only be called within a test worker.');
- }
-
- return manager.connectOut(name);
-}
-
-StreamChannel postMessageChannel() {
- var controller = StreamChannelController(sync: true);
- window.onMessage.firstWhere((message) {
- return message.origin == window.location.origin && message.data == "port";
- }).then((message) {
- var port = message.ports.first;
- var portSubscription = port.onMessage.listen((message) {
- controller.local.sink.add(message.data);
- });
-
- controller.local.stream.listen((data) {
- port.postMessage({"data": data});
- }, onDone: () {
- port.postMessage({"event": "done"});
- portSubscription.cancel();
- });
- });
-
- context['parent'].callMethod('postMessage', [
- JsObject.jsify({"href": window.location.href, "ready": true}),
- window.location.origin,
- ]);
- return controller.foreign;
-}
-
-void setStackTraceMapper(StackTraceMapper mapper) {
- var formatter = StackTraceFormatter.current;
- if (formatter == null) {
- throw StateError(
- 'setStackTraceMapper() may only be called within a test worker.');
- }
-
- formatter.configure(mapper: mapper);
-}
-''');
- }
- }
-}
-
-/// A shell builder which generates the web specific entrypoint.
-class FlutterWebShellBuilder implements Builder {
- const FlutterWebShellBuilder(this.targets);
-
- final List<String> targets;
-
- @override
- FutureOr<void> build(BuildStep buildStep) async {
- bool matches = false;
- for (String target in targets) {
- if (buildStep.inputId.path.contains(target)) {
- matches = true;
- break;
- }
- }
- if (!matches) {
- return;
- }
- final AssetId outputId = buildStep.inputId.changeExtension('_web_entrypoint.dart');
- await buildStep.writeAsString(outputId, '''
-import 'dart:ui' as ui;
-import "${path.url.basename(buildStep.inputId.path)}" as entrypoint;
-
-Future<void> main() async {
- await ui.webOnlyInitializePlatform();
- entrypoint.main();
-}
-
-''');
- }
-
- @override
- Map<String, List<String>> get buildExtensions => const <String, List<String>>{
- '.dart': <String>['_web_entrypoint.dart'],
- };
-}
-
-Future<void> bootstrapDart2Js(BuildStep buildStep) async {
- final AssetId dartEntrypointId = buildStep.inputId;
- final AssetId moduleId = dartEntrypointId.changeExtension(moduleExtension(flutterWebPlatform));
- final Module module = Module.fromJson(json.decode(await buildStep.readAsString(moduleId)));
-
- final List<Module> allDeps = await module.computeTransitiveDependencies(buildStep, throwIfUnsupported: false)..add(module);
- final ScratchSpace scratchSpace = await buildStep.fetchResource(scratchSpaceResource);
- final Iterable<AssetId> allSrcs = allDeps.expand((Module module) => module.sources);
- await scratchSpace.ensureAssets(allSrcs, buildStep);
-
- final String packageFile = await _createPackageFile(allSrcs, buildStep, scratchSpace);
- final String dartPath = dartEntrypointId.path.startsWith('lib/')
- ? 'package:${dartEntrypointId.package}/'
- '${dartEntrypointId.path.substring('lib/'.length)}'
- : dartEntrypointId.path;
- final String jsOutputPath =
- '${fs.path.withoutExtension(dartPath.replaceFirst('package:', 'packages/'))}'
- '$jsEntrypointExtension';
- final String flutterWebSdkPath = artifacts.getArtifactPath(Artifact.flutterWebSdk);
- final String librariesPath = fs.path.join(flutterWebSdkPath, 'libraries.json');
- final List<String> args = <String>[
- '--libraries-spec="$librariesPath"',
- '-m',
- '-o4',
- '-o',
- '$jsOutputPath',
- '--packages="$packageFile"',
- '-Ddart.vm.product=true',
- dartPath,
- ];
- final Dart2JsBatchWorkerPool dart2js = await buildStep.fetchResource(dart2JsWorkerResource);
- final Dart2JsResult result = await dart2js.compile(args);
- final AssetId jsOutputId = dartEntrypointId.changeExtension(jsEntrypointExtension);
- final io.File jsOutputFile = scratchSpace.fileFor(jsOutputId);
- if (result.succeeded && jsOutputFile.existsSync()) {
- log.info(result.output);
- // Explicitly write out the original js file and sourcemap.
- await scratchSpace.copyOutput(jsOutputId, buildStep);
- final AssetId jsSourceMapId =
- dartEntrypointId.changeExtension(jsEntrypointSourceMapExtension);
- await _copyIfExists(jsSourceMapId, scratchSpace, buildStep);
- } else {
- log.severe(result.output);
- }
-}
-
-Future<void> _copyIfExists(
- AssetId id, ScratchSpace scratchSpace, AssetWriter writer) async {
- final io.File file = scratchSpace.fileFor(id);
- if (file.existsSync()) {
- await scratchSpace.copyOutput(id, writer);
- }
-}
-
-/// Creates a `.packages` file unique to this entrypoint at the root of the
-/// scratch space and returns it's filename.
-///
-/// Since mulitple invocations of Dart2Js will share a scratch space and we only
-/// know the set of packages involved the current entrypoint we can't construct
-/// a `.packages` file that will work for all invocations of Dart2Js so a unique
-/// file is created for every entrypoint that is run.
-///
-/// The filename is based off the MD5 hash of the asset path so that files are
-/// unique regarless of situations like `web/foo/bar.dart` vs
-/// `web/foo-bar.dart`.
-Future<String> _createPackageFile(Iterable<AssetId> inputSources, BuildStep buildStep, ScratchSpace scratchSpace) async {
- final Uri inputUri = buildStep.inputId.uri;
- final String packageFileName =
- '.package-${md5.convert(inputUri.toString().codeUnits)}';
- final io.File packagesFile =
- scratchSpace.fileFor(AssetId(buildStep.inputId.package, packageFileName));
- final Set<String> packageNames = inputSources.map((AssetId s) => s.package).toSet();
- final String packagesFileContent =
- packageNames.map((String name) => '$name:packages/$name/').join('\n');
- await packagesFile
- .writeAsString('# Generated for $inputUri\n$packagesFileContent');
- return packageFileName;
-}
diff --git a/packages/flutter_tools/lib/src/resident_web_runner.dart b/packages/flutter_tools/lib/src/resident_web_runner.dart
index f774153..396409e 100644
--- a/packages/flutter_tools/lib/src/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_web_runner.dart
@@ -131,8 +131,7 @@
}
// Start the web compiler and build the assets.
await webCompilationProxy.initialize(
- projectDirectory: FlutterProject.current().directory,
- targets: <String>[target],
+ projectDirectory: flutterProject.directory,
);
_lastCompiled = DateTime.now();
final AssetBundle assetBundle = AssetBundleFactory.instance.createBundle();
diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart
index f9df159..925bc27 100644
--- a/packages/flutter_tools/lib/src/test/runner.dart
+++ b/packages/flutter_tools/lib/src/test/runner.dart
@@ -76,9 +76,6 @@
final bool result = await webCompilationProxy.initialize(
projectDirectory: flutterProject.directory,
testOutputDir: tempBuildDir,
- targets: testFiles.map((String testFile) {
- return fs.path.relative(testFile, from: flutterProject.directory.path);
- }).toList(),
);
if (!result) {
throwToolExit('Failed to compile tests');
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
index 42d6ff2..5f4b515 100644
--- a/packages/flutter_tools/lib/src/web/compile.dart
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -30,7 +30,6 @@
try {
result = await webCompilationProxy.initialize(
projectDirectory: FlutterProject.current().directory,
- targets: <String>[target],
release: buildInfo.isRelease,
);
if (result) {
@@ -79,11 +78,8 @@
///
/// `release` controls whether we build the bundle for dartdevc or only
/// the entrypoints for dart2js to later take over.
- ///
- /// `targets` controls the specific compiler targets.
Future<bool> initialize({
@required Directory projectDirectory,
- @required List<String> targets,
String testOutputDir,
bool release,
}) async {
diff --git a/packages/flutter_tools/test/commands/build_web_test.dart b/packages/flutter_tools/test/commands/build_web_test.dart
index f76c405..5f5df71 100644
--- a/packages/flutter_tools/test/commands/build_web_test.dart
+++ b/packages/flutter_tools/test/commands/build_web_test.dart
@@ -38,7 +38,6 @@
fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true);
when(mockWebCompilationProxy.initialize(
projectDirectory: anyNamed('projectDirectory'),
- targets: anyNamed('targets'),
release: anyNamed('release')
)).thenAnswer((Invocation invocation) {
final String path = fs.path.join('.dart_tool', 'build', 'flutter_web', 'foo', 'lib', 'main_web_entrypoint.dart.js');