Unverified Commit cc51ad5d authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Reland: enable usage of experimental web compiler (#44400)

parent 8f2ea9d0
...@@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart'; ...@@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart';
Future<void> main() async { Future<void> main() async {
await task(createWebDevModeTest()); await task(createWebDevModeTest(WebDevice.webServer, false));
} }
...@@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart'; ...@@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart';
Future<void> main() async { Future<void> main() async {
await task(createWebDevModeTest()); await task(createWebDevModeTest(WebDevice.webServer, false));
} }
// 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_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart';
Future<void> main() async {
await task(createWebDevModeTest(WebDevice.chrome, true));
}
...@@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart'; ...@@ -6,5 +6,5 @@ import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart'; import 'package:flutter_devicelab/tasks/web_dev_mode_tests.dart';
Future<void> main() async { Future<void> main() async {
await task(createWebDevModeTest()); await task(createWebDevModeTest(WebDevice.webServer, false));
} }
...@@ -20,12 +20,21 @@ const String kFirstRecompileTime = 'FirstRecompileTime'; ...@@ -20,12 +20,21 @@ const String kFirstRecompileTime = 'FirstRecompileTime';
const String kSecondStartupTime = 'SecondStartupTime'; const String kSecondStartupTime = 'SecondStartupTime';
const String kSecondRestartTime = 'SecondRestartTime'; const String kSecondRestartTime = 'SecondRestartTime';
TaskFunction createWebDevModeTest() {
abstract class WebDevice {
static const String chrome = 'chrome';
static const String webServer = 'web-server';
}
TaskFunction createWebDevModeTest(String webDevice, bool enableIncrementalCompiler) {
return () async { return () async {
final List<String> options = <String>[ final List<String> options = <String>[
'--hot', '-d', 'web-server', '--verbose', '--resident', '--target=lib/main.dart', '--hot', '-d', webDevice, '--verbose', '--resident', '--target=lib/main.dart',
]; ];
int hotRestartCount = 0; int hotRestartCount = 0;
final String expectedMessage = webDevice == WebDevice.webServer
? 'Recompile complete'
: 'Reloaded application';
final Map<String, int> measurements = <String, int>{}; final Map<String, int> measurements = <String, int>{};
await inDirectory<void>(flutterDirectory, () async { await inDirectory<void>(flutterDirectory, () async {
rmTree(_editedFlutterGalleryDir); rmTree(_editedFlutterGalleryDir);
...@@ -38,6 +47,8 @@ TaskFunction createWebDevModeTest() { ...@@ -38,6 +47,8 @@ TaskFunction createWebDevModeTest() {
<String>['packages', 'get'], <String>['packages', 'get'],
environment: <String, String>{ environment: <String, String>{
'FLUTTER_WEB': 'true', 'FLUTTER_WEB': 'true',
if (enableIncrementalCompiler)
'WEB_INCREMENTAL_COMPILER': 'true',
}, },
); );
await packagesGet.exitCode; await packagesGet.exitCode;
...@@ -46,16 +57,26 @@ TaskFunction createWebDevModeTest() { ...@@ -46,16 +57,26 @@ TaskFunction createWebDevModeTest() {
flutterCommandArgs('run', options), flutterCommandArgs('run', options),
environment: <String, String>{ environment: <String, String>{
'FLUTTER_WEB': 'true', 'FLUTTER_WEB': 'true',
if (enableIncrementalCompiler)
'WEB_INCREMENTAL_COMPILER': 'true',
}, },
); );
final Completer<void> stdoutDone = Completer<void>(); final Completer<void> stdoutDone = Completer<void>();
final Completer<void> stderrDone = Completer<void>(); final Completer<void> stderrDone = Completer<void>();
final Stopwatch sw = Stopwatch()..start(); final Stopwatch sw = Stopwatch()..start();
bool restarted = false;
process.stdout process.stdout
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
.transform<String>(const LineSplitter()) .transform<String>(const LineSplitter())
.listen((String line) { .listen((String line) {
// TODO(jonahwilliams): non-dwds builds do not know when the browser is loaded.
if (line.contains('Ignoring terminal input')) {
Future<void>.delayed(const Duration(seconds: 1)).then((void _) {
process.stdin.write(restarted ? 'q' : 'r');
});
return;
}
if (line.contains('To hot restart')) { if (line.contains('To hot restart')) {
// measure clean start-up time. // measure clean start-up time.
sw.stop(); sw.stop();
...@@ -63,9 +84,10 @@ TaskFunction createWebDevModeTest() { ...@@ -63,9 +84,10 @@ TaskFunction createWebDevModeTest() {
sw sw
..reset() ..reset()
..start(); ..start();
process.stdin.write('R'); process.stdin.write('r');
return;
} }
if (line.contains('Recompile complete')) { if (line.contains(expectedMessage)) {
if (hotRestartCount == 0) { if (hotRestartCount == 0) {
measurements[kFirstRestartTime] = sw.elapsedMilliseconds; measurements[kFirstRestartTime] = sw.elapsedMilliseconds;
// Update the file and reload again. // Update the file and reload again.
...@@ -80,9 +102,10 @@ TaskFunction createWebDevModeTest() { ...@@ -80,9 +102,10 @@ TaskFunction createWebDevModeTest() {
sw sw
..reset() ..reset()
..start(); ..start();
process.stdin.writeln('R'); process.stdin.writeln('r');
++hotRestartCount; ++hotRestartCount;
} else { } else {
restarted = true;
measurements[kFirstRecompileTime] = sw.elapsedMilliseconds; measurements[kFirstRecompileTime] = sw.elapsedMilliseconds;
// Quit after second hot restart. // Quit after second hot restart.
process.stdin.writeln('q'); process.stdin.writeln('q');
...@@ -119,24 +142,35 @@ TaskFunction createWebDevModeTest() { ...@@ -119,24 +142,35 @@ TaskFunction createWebDevModeTest() {
flutterCommandArgs('run', options), flutterCommandArgs('run', options),
environment: <String, String>{ environment: <String, String>{
'FLUTTER_WEB': 'true', 'FLUTTER_WEB': 'true',
if (enableIncrementalCompiler)
'WEB_INCREMENTAL_COMPILER': 'true',
}, },
); );
final Completer<void> stdoutDone = Completer<void>(); final Completer<void> stdoutDone = Completer<void>();
final Completer<void> stderrDone = Completer<void>(); final Completer<void> stderrDone = Completer<void>();
bool restarted = false;
process.stdout process.stdout
.transform<String>(utf8.decoder) .transform<String>(utf8.decoder)
.transform<String>(const LineSplitter()) .transform<String>(const LineSplitter())
.listen((String line) { .listen((String line) {
// TODO(jonahwilliams): non-dwds builds do not know when the browser is loaded.
if (line.contains('Ignoring terminal input')) {
Future<void>.delayed(const Duration(seconds: 1)).then((void _) {
process.stdin.write(restarted ? 'q' : 'r');
});
return;
}
if (line.contains('To hot restart')) { if (line.contains('To hot restart')) {
measurements[kSecondStartupTime] = sw.elapsedMilliseconds; measurements[kSecondStartupTime] = sw.elapsedMilliseconds;
sw sw
..reset() ..reset()
..start(); ..start();
process.stdin.write('R'); process.stdin.write('r');
return;
} }
if (line.contains('Recompile complete')) { if (line.contains(expectedMessage)) {
measurements[kSecondRestartTime] = sw.elapsedMilliseconds; restarted = true;
measurements[kSecondRestartTime] = sw.elapsedMilliseconds;
process.stdin.writeln('q'); process.stdin.writeln('q');
} }
print('stdout: $line'); print('stdout: $line');
......
...@@ -112,6 +112,13 @@ tasks: ...@@ -112,6 +112,13 @@ tasks:
stage: devicelab stage: devicelab
required_agent_capabilities: ["linux/android"] required_agent_capabilities: ["linux/android"]
web_incremental_test:
description: >
Verify that the experimental frontend server support is functional.
stage: devicelab
required_agent_capabilities: ["linux/android"]
flaky: true
flutter_gallery_ios__compile: flutter_gallery_ios__compile:
description: > description: >
Collects various performance metrics of compiling the Flutter Collects various performance metrics of compiling the Flutter
......
...@@ -28,7 +28,7 @@ enum Artifact { ...@@ -28,7 +28,7 @@ enum Artifact {
platformLibrariesJson, platformLibrariesJson,
flutterPatchedSdkPath, flutterPatchedSdkPath,
frontendServerSnapshotForEngineDartSdk, frontendServerSnapshotForEngineDartSdk,
/// The root directory of the dartk SDK. /// The root directory of the dart SDK.
engineDartSdkPath, engineDartSdkPath,
/// The dart binary used to execute any of the required snapshots. /// The dart binary used to execute any of the required snapshots.
engineDartBinary, engineDartBinary,
......
...@@ -430,7 +430,7 @@ class AppDomain extends Domain { ...@@ -430,7 +430,7 @@ class AppDomain extends Domain {
if (await device.targetPlatform == TargetPlatform.web_javascript) { if (await device.targetPlatform == TargetPlatform.web_javascript) {
runner = webRunnerFactory.createWebRunner( runner = webRunnerFactory.createWebRunner(
device, flutterDevice,
flutterProject: flutterProject, flutterProject: flutterProject,
target: target, target: target,
debuggingOptions: options, debuggingOptions: options,
......
...@@ -453,7 +453,7 @@ class RunCommand extends RunCommandBase { ...@@ -453,7 +453,7 @@ class RunCommand extends RunCommandBase {
); );
} else if (webMode) { } else if (webMode) {
runner = webRunnerFactory.createWebRunner( runner = webRunnerFactory.createWebRunner(
devices.single, flutterDevices.single,
target: targetFile, target: targetFile,
flutterProject: flutterProject, flutterProject: flutterProject,
ipv6: ipv6, ipv6: ipv6,
......
...@@ -201,7 +201,6 @@ class PackageUriMapper { ...@@ -201,7 +201,6 @@ class PackageUriMapper {
PackageUriMapper(String scriptPath, String packagesPath, String fileSystemScheme, List<String> fileSystemRoots) { PackageUriMapper(String scriptPath, String packagesPath, String fileSystemScheme, List<String> fileSystemRoots) {
final Map<String, Uri> packageMap = PackageMap(fs.path.absolute(packagesPath)).map; final Map<String, Uri> packageMap = PackageMap(fs.path.absolute(packagesPath)).map;
final String scriptUri = Uri.file(scriptPath, windows: platform.isWindows).toString(); final String scriptUri = Uri.file(scriptPath, windows: platform.isWindows).toString();
for (String packageName in packageMap.keys) { for (String packageName in packageMap.keys) {
final String prefix = packageMap[packageName].toString(); final String prefix = packageMap[packageName].toString();
// Only perform a multi-root mapping if there are multiple roots. // Only perform a multi-root mapping if there are multiple roots.
......
...@@ -349,12 +349,19 @@ class UpdateFSReport { ...@@ -349,12 +349,19 @@ class UpdateFSReport {
int get invalidatedSourcesCount => _invalidatedSourcesCount; int get invalidatedSourcesCount => _invalidatedSourcesCount;
int get syncedBytes => _syncedBytes; int get syncedBytes => _syncedBytes;
/// JavaScript modules produced by the incremental compiler in `dartdevc`
/// mode.
///
/// Only used for JavaScript compilation.
List<String> invalidatedModules;
void incorporateResults(UpdateFSReport report) { void incorporateResults(UpdateFSReport report) {
if (!report._success) { if (!report._success) {
_success = false; _success = false;
} }
_invalidatedSourcesCount += report._invalidatedSourcesCount; _invalidatedSourcesCount += report._invalidatedSourcesCount;
_syncedBytes += report._syncedBytes; _syncedBytes += report._syncedBytes;
invalidatedModules ??= report.invalidatedModules;
} }
bool _success; bool _success;
......
...@@ -151,10 +151,15 @@ const Feature flutterAndroidEmbeddingV2Feature = Feature( ...@@ -151,10 +151,15 @@ const Feature flutterAndroidEmbeddingV2Feature = Feature(
const Feature flutterWebIncrementalCompiler = Feature( const Feature flutterWebIncrementalCompiler = Feature(
name: 'Enable the incremental compiler for web builds', name: 'Enable the incremental compiler for web builds',
configSetting: 'enable-web-incremental-compiler', configSetting: 'enable-web-incremental-compiler',
environmentOverride: 'WEB_INCREMENTAL_COMPILER',
master: FeatureChannelSetting( master: FeatureChannelSetting(
available: true, available: true,
enabledByDefault: false, enabledByDefault: false,
), ),
dev: FeatureChannelSetting(
available: true,
enabledByDefault: false,
),
); );
/// A [Feature] is a process for conditionally enabling tool features. /// A [Feature] is a process for conditionally enabling tool features.
......
...@@ -75,21 +75,25 @@ define("main_module", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) { ...@@ -75,21 +75,25 @@ define("main_module", ["$entrypoint", "dart_sdk"], function(app, dart_sdk) {
dart_sdk.dart.setStartAsyncSynchronously(true); dart_sdk.dart.setStartAsyncSynchronously(true);
dart_sdk._isolate_helper.startRootIsolate(() => {}, []); dart_sdk._isolate_helper.startRootIsolate(() => {}, []);
dart_sdk._debugger.registerDevtoolsFormatter(); dart_sdk._debugger.registerDevtoolsFormatter();
dart_sdk.ui.webOnlyInitializePlatform(); let voidToNull = () => (voidToNull = dart_sdk.dart.constFn(dart_sdk.dart.fnType(dart_sdk.core.Null, [dart_sdk.dart.void])))();
// Attach the main entrypoint and hot reload functionality to the window. // Attach the main entrypoint and hot reload functionality to the window.
window.\$mainEntrypoint = app.main.main; window.\$mainEntrypoint = app.main.main;
if (window.\$hotReload == null) { if (window.\$hotReload == null) {
window.\$hotReload = function(cb) { window.\$hotReload = function(cb) {
dart_sdk.developer.invokeExtension("ext.flutter.disassemble", "{}"); dart_sdk.developer.invokeExtension("ext.flutter.disassemble", "{}").then((_) => {
dart_sdk.dart.hotRestart(); dart_sdk.dart.hotRestart();
window.\$mainEntrypoint(); dart_sdk.ui.webOnlyInitializePlatform().then(dart_sdk.core.Null, dart_sdk.dart.fn(_ => {
if (cb != null) { window.\$mainEntrypoint();
cb(); window.requestAnimationFrame(cb);
} }, voidToNull()));
});
} }
} }
app.main.main();
dart_sdk.ui.webOnlyInitializePlatform().then(dart_sdk.core.Null, dart_sdk.dart.fn(_ => {
app.main.main();
}, voidToNull()));
}); });
// Require JS configuration. // Require JS configuration.
......
...@@ -71,6 +71,11 @@ void resetChromeForTesting() { ...@@ -71,6 +71,11 @@ void resetChromeForTesting() {
ChromeLauncher._currentCompleter = Completer<Chrome>(); ChromeLauncher._currentCompleter = Completer<Chrome>();
} }
@visibleForTesting
void launchChromeInstance(Chrome chrome) {
ChromeLauncher._currentCompleter.complete(chrome);
}
/// Responsible for launching chrome with devtools configured. /// Responsible for launching chrome with devtools configured.
class ChromeLauncher { class ChromeLauncher {
const ChromeLauncher(); const ChromeLauncher();
......
...@@ -7,12 +7,18 @@ import 'dart:typed_data'; ...@@ -7,12 +7,18 @@ import 'dart:typed_data';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:mime/mime.dart' as mime; import 'package:mime/mime.dart' as mime;
import '../artifacts.dart';
import '../asset.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart';
import '../compile.dart';
import '../convert.dart'; import '../convert.dart';
import '../devfs.dart';
import '../globals.dart'; import '../globals.dart';
import 'bootstrap.dart';
/// A web server which handles serving JavaScript and assets. /// A web server which handles serving JavaScript and assets.
/// ///
...@@ -49,7 +55,10 @@ class WebAssetServer { ...@@ -49,7 +55,10 @@ class WebAssetServer {
} }
final HttpServer _httpServer; final HttpServer _httpServer;
// If holding these in memory is too much overhead, this can be switched to a
// RandomAccessFile and read on demand.
final Map<String, Uint8List> _files = <String, Uint8List>{}; final Map<String, Uint8List> _files = <String, Uint8List>{};
final Map<String, Uint8List> _sourcemaps = <String, Uint8List>{};
// handle requests for JavaScript source, dart sources maps, or asset files. // handle requests for JavaScript source, dart sources maps, or asset files.
Future<void> _handleRequest(HttpRequest request) async { Future<void> _handleRequest(HttpRequest request) async {
...@@ -71,8 +80,7 @@ class WebAssetServer { ...@@ -71,8 +80,7 @@ class WebAssetServer {
} }
// If this is a JavaScript file, it must be in the in-memory cache. // If this is a JavaScript file, it must be in the in-memory cache.
// Attempt to look up the file by URI, returning a 404 if it is not // Attempt to look up the file by URI.
// found.
if (_files.containsKey(request.uri.path)) { if (_files.containsKey(request.uri.path)) {
final List<int> bytes = _files[request.uri.path]; final List<int> bytes = _files[request.uri.path];
response.headers response.headers
...@@ -82,6 +90,18 @@ class WebAssetServer { ...@@ -82,6 +90,18 @@ class WebAssetServer {
await response.close(); await response.close();
return; return;
} }
// If this is a sourcemap file, then it might be in the in-memory cache.
// Attempt to lookup the file by URI.
if (_sourcemaps.containsKey(request.uri.path)) {
final List<int> bytes = _sourcemaps[request.uri.path];
response.headers
..add('Content-Length', bytes.length)
..add('Content-Type', 'application/json');
response.add(bytes);
await response.close();
return;
}
// If this is a dart file, it must be on the local file system and is // If this is a dart file, it must be on the local file system and is
// likely coming from a source map request. Attempt to look in the // likely coming from a source map request. Attempt to look in the
// local filesystem for it, and return a 404 if it is not found. The tool // local filesystem for it, and return a 404 if it is not found. The tool
...@@ -95,6 +115,18 @@ class WebAssetServer { ...@@ -95,6 +115,18 @@ class WebAssetServer {
file = fs.file(fs.path.join(getAssetBuildDirectory(), fs.path.relative(assetPath))); file = fs.file(fs.path.join(getAssetBuildDirectory(), fs.path.relative(assetPath)));
} }
// If it isn't a project source or an asset, it must be a dart SDK source.
// or a flutter web SDK source.
if (!file.existsSync()) {
final Directory dartSdkParent = fs.directory(artifacts.getArtifactPath(Artifact.engineDartSdkPath)).parent;
file = fs.file(fs.path.joinAll(<String>[dartSdkParent.path, ...request.uri.pathSegments]));
}
if (!file.existsSync()) {
final String flutterWebSdk = artifacts.getArtifactPath(Artifact.flutterWebSdk);
file = fs.file(fs.path.joinAll(<String>[flutterWebSdk, ...request.uri.pathSegments]));
}
if (!file.existsSync()) { if (!file.existsSync()) {
response.statusCode = HttpStatus.notFound; response.statusCode = HttpStatus.notFound;
await response.close(); await response.close();
...@@ -131,30 +163,186 @@ class WebAssetServer { ...@@ -131,30 +163,186 @@ class WebAssetServer {
/// Update the in-memory asset server with the provided source and manifest files. /// Update the in-memory asset server with the provided source and manifest files.
/// ///
/// Returns a list of updated modules. /// Returns a list of updated modules.
List<String> write(File sourceFile, File manifestFile) { List<String> write(File codeFile, File manifestFile, File sourcemapFile) {
final List<String> modules = <String>[]; final List<String> modules = <String>[];
final Uint8List bytes = sourceFile.readAsBytesSync(); final Uint8List codeBytes = codeFile.readAsBytesSync();
final Uint8List sourcemapBytes = sourcemapFile.readAsBytesSync();
final Map<String, Object> manifest = json.decode(manifestFile.readAsStringSync()); final Map<String, Object> manifest = json.decode(manifestFile.readAsStringSync());
for (String filePath in manifest.keys) { for (String filePath in manifest.keys) {
if (filePath == null) { if (filePath == null) {
printTrace('Invalid manfiest file: $filePath'); printTrace('Invalid manfiest file: $filePath');
continue; continue;
} }
final List<Object> offsets = manifest[filePath]; final Map<String, Object> offsets = manifest[filePath];
if (offsets.length != 2) { final List<Object> codeOffsets = offsets['code'];
final List<Object> sourcemapOffsets = offsets['sourcemap'];
if (codeOffsets.length != 2 || sourcemapOffsets.length != 2) {
printTrace('Invalid manifest byte offsets: $offsets'); printTrace('Invalid manifest byte offsets: $offsets');
continue; continue;
} }
final int start = offsets[0];
final int end = offsets[1]; final int codeStart = codeOffsets[0];
if (start < 0 || end > bytes.lengthInBytes) { final int codeEnd = codeOffsets[1];
printTrace('Invalid byte index: [$start, $end]'); if (codeStart < 0 || codeEnd > codeBytes.lengthInBytes) {
printTrace('Invalid byte index: [$codeStart, $codeEnd]');
continue; continue;
} }
final Uint8List byteView = Uint8List.view(bytes.buffer, start, end - start); final Uint8List byteView = Uint8List.view(
codeBytes.buffer,
codeStart,
codeEnd - codeStart,
);
_files[filePath] = byteView; _files[filePath] = byteView;
final int sourcemapStart = sourcemapOffsets[0];
final int sourcemapEnd = sourcemapOffsets[1];
if (sourcemapStart < 0 || sourcemapEnd > sourcemapBytes.lengthInBytes) {
printTrace('Invalid byte index: [$sourcemapStart, $sourcemapEnd]');
continue;
}
final Uint8List sourcemapView = Uint8List.view(
sourcemapBytes.buffer,
sourcemapStart,
sourcemapEnd - sourcemapStart ,
);
_sourcemaps['$filePath.map'] = sourcemapView;
modules.add(filePath); modules.add(filePath);
} }
return modules; return modules;
} }
} }
class WebDevFS implements DevFS {
WebDevFS(this.hostname, this.port, this._packagesFilePath);
final String hostname;
final int port;
final String _packagesFilePath;
WebAssetServer _webAssetServer;
@override
List<Uri> sources = <Uri>[];
@override
DateTime lastCompiled;
// We do not evict assets on the web.
@override
Set<String> get assetPathsToEvict => const <String>{};
@override
Uri get baseUri => null;
@override
Future<Uri> create() async {
_webAssetServer = await WebAssetServer.start(hostname, port);
return Uri.base;
}
@override
Future<void> destroy() async {
await _webAssetServer.dispose();
}
@override
Uri deviceUriToHostUri(Uri deviceUri) {
return deviceUri;
}
@override
String get fsName => 'web_asset';
@override
Directory get rootDirectory => null;
@override
Future<UpdateFSReport> update({
String mainPath,
String target,
AssetBundle bundle,
DateTime firstBuildTime,
bool bundleFirstUpload = false,
@required ResidentCompiler generator,
String dillOutputPath,
@required bool trackWidgetCreation,
bool fullRestart = false,
String projectRootPath,
String pathToReload,
List<Uri> invalidatedFiles,
}) async {
assert(trackWidgetCreation != null);
assert(generator != null);
if (bundleFirstUpload) {
final File requireJS = fs.file(fs.path.join(
artifacts.getArtifactPath(Artifact.engineDartSdkPath),
'lib',
'dev_compiler',
'kernel',
'amd',
'require.js',
));
final File dartSdk = fs.file(fs.path.join(
artifacts.getArtifactPath(Artifact.flutterWebSdk),
'kernel',
'amd',
'dart_sdk.js',
));
final File dartSdkSourcemap = fs.file(fs.path.join(
artifacts.getArtifactPath(Artifact.flutterWebSdk),
'kernel',
'amd',
'dart_sdk.js.map',
));
final File stackTraceMapper = fs.file(fs.path.join(
artifacts.getArtifactPath(Artifact.engineDartSdkPath),
'lib',
'dev_compiler',
'web',
'dart_stack_trace_mapper.js',
));
_webAssetServer.writeFile('/main.dart.js', generateBootstrapScript(
requireUrl: requireJS.path,
mapperUrl: stackTraceMapper.path,
entrypoint: '$mainPath.js',
));
_webAssetServer.writeFile('/main_module.js', generateMainModule(
entrypoint: '$mainPath.js',
));
_webAssetServer.writeFile('/dart_sdk.js', dartSdk.readAsStringSync());
_webAssetServer.writeFile('/dart_sdk.js.map', dartSdkSourcemap.readAsStringSync());
}
final DateTime candidateCompileTime = DateTime.now();
if (fullRestart) {
generator.reset();
}
final CompilerOutput compilerOutput = await generator.recompile(
mainPath,
invalidatedFiles,
outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation),
packagesFilePath : _packagesFilePath,
);
if (compilerOutput == null || compilerOutput.errorCount > 0) {
return UpdateFSReport(success: false);
}
// Only update the last compiled time if we successfully compiled.
lastCompiled = candidateCompileTime;
// list of sources that needs to be monitored are in [compilerOutput.sources]
sources = compilerOutput.sources;
File codeFile;
File manifestFile;
File sourcemapFile;
List<String> modules;
try {
codeFile = fs.file('${compilerOutput.outputFilename}.sources');
manifestFile = fs.file('${compilerOutput.outputFilename}.json');
sourcemapFile = fs.file('${compilerOutput.outputFilename}.map');
modules = _webAssetServer.write(codeFile, manifestFile, sourcemapFile);
} on FileSystemException catch (err) {
throwToolExit('Failed to load recompiled sources:\n$err');
}
return UpdateFSReport(success: true, syncedBytes: codeFile.lengthSync(),
invalidatedSourcesCount: invalidatedFiles.length)
..invalidatedModules = modules;
}
}
...@@ -17,7 +17,7 @@ abstract class WebRunnerFactory { ...@@ -17,7 +17,7 @@ abstract class WebRunnerFactory {
/// Create a [ResidentRunner] for the web. /// Create a [ResidentRunner] for the web.
ResidentRunner createWebRunner( ResidentRunner createWebRunner(
Device device, { FlutterDevice device, {
String target, String target,
@required bool stayResident, @required bool stayResident,
@required FlutterProject flutterProject, @required FlutterProject flutterProject,
......
...@@ -63,11 +63,12 @@ void main() { ...@@ -63,11 +63,12 @@ void main() {
test('Refuses to build using runner when missing index.html', () => testbed.run(() async { test('Refuses to build using runner when missing index.html', () => testbed.run(() async {
fs.file(fs.path.join('web', 'index.html')).deleteSync(); fs.file(fs.path.join('web', 'index.html')).deleteSync();
final ResidentWebRunner runner = ResidentWebRunner( final ResidentWebRunner runner = DwdsWebRunnerFactory().createWebRunner(
null, null,
flutterProject: FlutterProject.current(), flutterProject: FlutterProject.current(),
ipv6: false, ipv6: false,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug), debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
stayResident: true,
); );
expect(await runner.run(), 1); expect(await runner.run(), 1);
})); }));
......
...@@ -26,17 +26,21 @@ void main() { ...@@ -26,17 +26,21 @@ void main() {
Testbed testbed; Testbed testbed;
MockFlutterWebFs mockWebFs; MockFlutterWebFs mockWebFs;
ResidentWebRunner residentWebRunner; ResidentWebRunner residentWebRunner;
MockFlutterDevice mockFlutterDevice;
setUp(() { setUp(() {
mockWebFs = MockFlutterWebFs(); mockWebFs = MockFlutterWebFs();
final MockWebDevice mockWebDevice = MockWebDevice(); final MockWebDevice mockWebDevice = MockWebDevice();
mockFlutterDevice = MockFlutterDevice();
when(mockFlutterDevice.device).thenReturn(mockWebDevice);
testbed = Testbed( testbed = Testbed(
setup: () { setup: () {
residentWebRunner = ResidentWebRunner( residentWebRunner = residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockWebDevice, mockFlutterDevice,
flutterProject: FlutterProject.current(), flutterProject: FlutterProject.current(),
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release), debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true, ipv6: true,
stayResident: true,
); );
}, },
overrides: <Type, Generator>{ overrides: <Type, Generator>{
...@@ -118,4 +122,4 @@ class MockFlutterWebFs extends Mock implements WebFs {} ...@@ -118,4 +122,4 @@ class MockFlutterWebFs extends Mock implements WebFs {}
class MockDebugConnection extends Mock implements DebugConnection {} class MockDebugConnection extends Mock implements DebugConnection {}
class MockVmService extends Mock implements VmService {} class MockVmService extends Mock implements VmService {}
class MockStatus extends Mock implements Status {} class MockStatus extends Mock implements Status {}
class MockFlutterDevice extends Mock implements FlutterDevice {}
...@@ -89,28 +89,36 @@ void main() { ...@@ -89,28 +89,36 @@ void main() {
test('Handles against malformed manifest', () => testbed.run(() async { test('Handles against malformed manifest', () => testbed.run(() async {
final File source = fs.file('source') final File source = fs.file('source')
..writeAsStringSync('main() {}'); ..writeAsStringSync('main() {}');
final File sourcemap = fs.file('sourcemap')
..writeAsStringSync('{}');
// Missing ending offset. // Missing ending offset.
final File manifestMissingOffset = fs.file('manifestA') final File manifestMissingOffset = fs.file('manifestA')
..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <int>[0]})); ..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <String, Object>{
// Non-file URI. 'code': <int>[0],
final File manifestNonFileScheme = fs.file('manifestA') 'sourcemap': <int>[0],
..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <int>[0, 10]})); }}));
final File manifestOutOfBounds = fs.file('manifest') final File manifestOutOfBounds = fs.file('manifest')
..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <int>[0, 100]})); ..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <String, Object>{
'code': <int>[0, 100],
'sourcemap': <int>[0],
}}));
expect(webAssetServer.write(source, manifestMissingOffset), isEmpty); expect(webAssetServer.write(source, manifestMissingOffset, sourcemap), isEmpty);
expect(webAssetServer.write(source, manifestNonFileScheme), isEmpty); expect(webAssetServer.write(source, manifestOutOfBounds, sourcemap), isEmpty);
expect(webAssetServer.write(source, manifestOutOfBounds), isEmpty);
})); }));
test('serves JavaScript files from in memory cache', () => testbed.run(() async { test('serves JavaScript files from in memory cache', () => testbed.run(() async {
final File source = fs.file('source') final File source = fs.file('source')
..writeAsStringSync('main() {}'); ..writeAsStringSync('main() {}');
final File sourcemap = fs.file('sourcemap')
..writeAsStringSync('{}');
final File manifest = fs.file('manifest') final File manifest = fs.file('manifest')
..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <int>[0, source.lengthSync()]})); ..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <String, Object>{
webAssetServer.write(source, manifest); 'code': <int>[0, source.lengthSync()],
'sourcemap': <int>[0, 2],
}}));
webAssetServer.write(source, manifest, sourcemap);
when(request.uri).thenReturn(Uri.parse('http://foobar/foo.js')); when(request.uri).thenReturn(Uri.parse('http://foobar/foo.js'));
requestController.add(request); requestController.add(request);
...@@ -136,9 +144,14 @@ void main() { ...@@ -136,9 +144,14 @@ void main() {
test('handles missing JavaScript files from in memory cache', () => testbed.run(() async { test('handles missing JavaScript files from in memory cache', () => testbed.run(() async {
final File source = fs.file('source') final File source = fs.file('source')
..writeAsStringSync('main() {}'); ..writeAsStringSync('main() {}');
final File sourcemap = fs.file('sourcemap')
..writeAsStringSync('{}');
final File manifest = fs.file('manifest') final File manifest = fs.file('manifest')
..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <int>[0, source.lengthSync()]})); ..writeAsStringSync(json.encode(<String, Object>{'/foo.js': <String, Object>{
webAssetServer.write(source, manifest); 'code': <int>[0, source.lengthSync()],
'sourcemap': <int>[0, 2],
}}));
webAssetServer.write(source, manifest, sourcemap);
when(request.uri).thenReturn(Uri.parse('http://foobar/bar.js')); when(request.uri).thenReturn(Uri.parse('http://foobar/bar.js'));
requestController.add(request); requestController.add(request);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment