Wire dart2js through flutter tool, add compilation test (#27668)
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart
index fe8f968..989ca6f 100644
--- a/packages/flutter_tools/lib/src/application_package.dart
+++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -49,6 +49,7 @@
case TargetPlatform.linux_x64:
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia:
+ case TargetPlatform.web:
return null;
}
assert(platform != null);
@@ -352,6 +353,7 @@
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia:
case TargetPlatform.tester:
+ case TargetPlatform.web:
return null;
}
return null;
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 6cfb8bc..9bf9280 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -26,6 +26,8 @@
frontendServerSnapshotForEngineDartSdk,
engineDartSdkPath,
engineDartBinary,
+ dart2jsSnapshot,
+ kernelWorkerSnapshot,
}
String _artifactToFileName(Artifact artifact, [TargetPlatform platform, BuildMode mode]) {
@@ -70,6 +72,10 @@
return 'frontend_server.dart.snapshot';
case Artifact.engineDartBinary:
return 'dart';
+ case Artifact.dart2jsSnapshot:
+ return 'flutter_dart2js.dart.snapshot';
+ case Artifact.kernelWorkerSnapshot:
+ return 'flutter_kernel_worker.dart.snapshot';
}
assert(false, 'Invalid artifact $artifact.');
return null;
@@ -121,6 +127,7 @@
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia:
case TargetPlatform.tester:
+ case TargetPlatform.web:
return _getHostArtifactPath(artifact, platform, mode);
}
assert(false, 'Invalid platform $platform.');
@@ -183,13 +190,17 @@
case Artifact.engineDartSdkPath:
return dartSdkPath;
case Artifact.engineDartBinary:
- return fs.path.join(dartSdkPath,'bin', _artifactToFileName(artifact));
+ return fs.path.join(dartSdkPath, 'bin', _artifactToFileName(artifact));
case Artifact.platformKernelDill:
return fs.path.join(_getFlutterPatchedSdkPath(), _artifactToFileName(artifact));
case Artifact.platformLibrariesJson:
return fs.path.join(_getFlutterPatchedSdkPath(), 'lib', _artifactToFileName(artifact));
case Artifact.flutterPatchedSdkPath:
return _getFlutterPatchedSdkPath();
+ case Artifact.dart2jsSnapshot:
+ return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
+ case Artifact.kernelWorkerSnapshot:
+ return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
default:
assert(false, 'Artifact $artifact not available for platform $platform.');
return null;
@@ -205,6 +216,7 @@
case TargetPlatform.windows_x64:
case TargetPlatform.fuchsia:
case TargetPlatform.tester:
+ case TargetPlatform.web:
assert(mode == null, 'Platform $platform does not support different build modes.');
return fs.path.join(engineDir, platformName);
case TargetPlatform.ios:
@@ -265,6 +277,10 @@
return fs.path.join(_hostEngineOutPath, 'dart-sdk');
case Artifact.engineDartBinary:
return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', _artifactToFileName(artifact));
+ case Artifact.dart2jsSnapshot:
+ return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _artifactToFileName(artifact));
+ case Artifact.kernelWorkerSnapshot:
+ return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _artifactToFileName(artifact));
}
assert(false, 'Invalid artifact $artifact.');
return null;
diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart
index cd8ce05..2e3322b 100644
--- a/packages/flutter_tools/lib/src/build_info.dart
+++ b/packages/flutter_tools/lib/src/build_info.dart
@@ -266,6 +266,7 @@
windows_x64,
fuchsia,
tester,
+ web,
}
/// iOS target device architecture.
@@ -325,6 +326,8 @@
return 'fuchsia';
case TargetPlatform.tester:
return 'flutter-tester';
+ case TargetPlatform.web:
+ return 'web';
}
assert(false);
return null;
@@ -346,6 +349,8 @@
return TargetPlatform.darwin_x64;
case 'linux-x64':
return TargetPlatform.linux_x64;
+ case 'web':
+ return TargetPlatform.web;
}
assert(platform != null);
return null;
@@ -400,6 +405,11 @@
return fs.path.join(getBuildDirectory(), 'ios');
}
+/// Returns the web build output directory.
+String getWebBuildDirectory() {
+ return fs.path.join(getBuildDirectory(), 'web');
+}
+
/// Returns directory used by incremental compiler (IKG - incremental kernel
/// generator) to store cached intermediate state.
String getIncrementalCompilerByteStoreDirectory() {
diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart
index c31f741..64100e2 100644
--- a/packages/flutter_tools/lib/src/commands/build.dart
+++ b/packages/flutter_tools/lib/src/commands/build.dart
@@ -11,6 +11,7 @@
import 'build_bundle.dart';
import 'build_flx.dart';
import 'build_ios.dart';
+import 'build_web.dart';
class BuildCommand extends FlutterCommand {
BuildCommand({bool verboseHelp = false}) {
@@ -20,6 +21,7 @@
addSubcommand(BuildIOSCommand());
addSubcommand(BuildFlxCommand());
addSubcommand(BuildBundleCommand(verboseHelp: verboseHelp));
+ addSubcommand(BuildWebCommand());
}
@override
diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart
new file mode 100644
index 0000000..a1a4645
--- /dev/null
+++ b/packages/flutter_tools/lib/src/commands/build_web.dart
@@ -0,0 +1,38 @@
+// 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.
+
+import 'dart:async';
+
+import '../base/logger.dart';
+import '../build_info.dart';
+import '../globals.dart';
+import '../runner/flutter_command.dart' show ExitStatus, FlutterCommandResult;
+import '../web/compile.dart';
+import 'build.dart';
+
+class BuildWebCommand extends BuildSubCommand {
+ BuildWebCommand() {
+ usesTargetOption();
+ usesPubOption();
+ defaultBuildMode = BuildMode.release;
+ }
+
+ @override
+ final String name = 'web';
+
+ @override
+ bool get hidden => true;
+
+ @override
+ final String description = '(EXPERIMENTAL) build a web application bundle.';
+
+ @override
+ Future<FlutterCommandResult> runCommand() async {
+ final String target = argResults['target'];
+ final Status status = logger.startProgress('Compiling $target to JavaScript...', timeout: null);
+ final int result = await webCompiler.compile(target: target);
+ status.stop();
+ return FlutterCommandResult(result == 0 ? ExitStatus.success : ExitStatus.fail);
+ }
+}
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index ba6a2b1..7a4490b 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -39,6 +39,7 @@
import 'run_hot.dart';
import 'usage.dart';
import 'version.dart';
+import 'web/compile.dart';
import 'windows/windows_workflow.dart';
Future<T> runInContext<T>(
@@ -91,6 +92,7 @@
Usage: () => Usage(),
UserMessages: () => UserMessages(),
WindowsWorkflow: () => const WindowsWorkflow(),
+ WebCompiler: () => const WebCompiler(),
Xcode: () => Xcode(),
XcodeProjectInterpreter: () => XcodeProjectInterpreter(),
},
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
new file mode 100644
index 0000000..4dab748
--- /dev/null
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -0,0 +1,70 @@
+// 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.
+
+import 'package:meta/meta.dart';
+
+import '../artifacts.dart';
+import '../base/common.dart';
+import '../base/context.dart';
+import '../base/file_system.dart';
+import '../base/io.dart';
+import '../base/process_manager.dart';
+import '../build_info.dart';
+import '../convert.dart';
+import '../globals.dart';
+
+/// The [WebCompiler] instance.
+WebCompiler get webCompiler => context[WebCompiler];
+
+/// A wrapper around dart2js for web compilation.
+class WebCompiler {
+ const WebCompiler();
+
+ /// Compile `target` using dart2js.
+ ///
+ /// `minify` controls whether minifaction of the source is enabled. Defaults to `true`.
+ /// `enabledAssertions` controls whether assertions are enabled. Defaults to `false`.
+ Future<int> compile({@required String target, bool minify = true, bool enabledAssertions = false}) async {
+ final String engineDartPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
+ final String dart2jsPath = artifacts.getArtifactPath(Artifact.dart2jsSnapshot);
+ final String flutterPatchedSdkPath = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
+ final String librariesPath = fs.path.join(flutterPatchedSdkPath, 'libraries.json');
+ final Directory outputDir = fs.directory(getWebBuildDirectory());
+ if (!outputDir.existsSync()) {
+ outputDir.createSync(recursive: true);
+ }
+ final String outputPath = fs.path.join(outputDir.path, 'main.dart.js');
+ if (!processManager.canRun(engineDartPath)) {
+ throwToolExit('Unable to find Dart binary at $engineDartPath');
+ }
+ final List<String> command = <String>[
+ engineDartPath,
+ dart2jsPath,
+ target,
+ '-o',
+ '$outputPath',
+ '--libraries-spec=$librariesPath',
+ '--platform-binaries=$flutterPatchedSdkPath',
+ ];
+ if (minify) {
+ command.add('-m');
+ }
+ if (enabledAssertions) {
+ command.add('--enable-asserts');
+ }
+ printTrace(command.join(' '));
+ final Process result = await processManager.start(command);
+ result
+ .stdout
+ .transform(utf8.decoder)
+ .transform(const LineSplitter())
+ .listen(printStatus);
+ result
+ .stderr
+ .transform(utf8.decoder)
+ .transform(const LineSplitter())
+ .listen(printError);
+ return result.exitCode;
+ }
+}
diff --git a/packages/flutter_tools/test/web/compile_test.dart b/packages/flutter_tools/test/web/compile_test.dart
new file mode 100644
index 0000000..0316a33
--- /dev/null
+++ b/packages/flutter_tools/test/web/compile_test.dart
@@ -0,0 +1,54 @@
+// 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.
+
+import 'package:flutter_tools/src/artifacts.dart';
+import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/globals.dart';
+import 'package:flutter_tools/src/web/compile.dart';
+import 'package:mockito/mockito.dart';
+import 'package:process/process.dart';
+
+import '../src/context.dart';
+
+void main() {
+ final MockProcessManager mockProcessManager = MockProcessManager();
+ final MockProcess mockProcess = MockProcess();
+ final BufferLogger mockLogger = BufferLogger();
+
+ testUsingContext('invokes dart2js with correct arguments', () async {
+ const WebCompiler webCompiler = WebCompiler();
+ final String engineDartPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
+ final String dart2jsPath = artifacts.getArtifactPath(Artifact.dart2jsSnapshot);
+ final String flutterPatchedSdkPath = artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath);
+ final String librariesPath = fs.path.join(flutterPatchedSdkPath, 'libraries.json');
+
+ when(mockProcess.stdout).thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
+ when(mockProcess.stderr).thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
+ when(mockProcess.exitCode).thenAnswer((Invocation invocation) async => 0);
+ when(mockProcessManager.start(any)).thenAnswer((Invocation invocation) async => mockProcess);
+ when(mockProcessManager.canRun(engineDartPath)).thenReturn(true);
+
+ await webCompiler.compile(target: 'lib/main.dart');
+
+ final String outputPath = fs.path.join('build', 'web', 'main.dart.js');
+ verify(mockProcessManager.start(<String>[
+ engineDartPath,
+ dart2jsPath,
+ 'lib/main.dart',
+ '-o',
+ outputPath,
+ '--libraries-spec=$librariesPath',
+ '--platform-binaries=$flutterPatchedSdkPath',
+ '-m',
+ ])).called(1);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ Logger: () => mockLogger,
+ });
+}
+
+class MockProcessManager extends Mock implements ProcessManager {}
+class MockProcess extends Mock implements Process {}
\ No newline at end of file