Use recompile-restart instruction when hot restarting on the web (#162616)
recompile has been split into recompile and recompile-restart in the frontend server so that DDC can distinguish between hot reload recompiles and hot restart recompiles, and therefore emit rejection errors only on hot reload. https://github.com/dart-lang/webdev/issues/2516 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing.
This commit is contained in:
parent
f5e8fc35f0
commit
5157d2314b
@ -410,6 +410,7 @@ class _RecompileRequest extends _CompilationRequest {
|
|||||||
this.suppressErrors, {
|
this.suppressErrors, {
|
||||||
this.additionalSourceUri,
|
this.additionalSourceUri,
|
||||||
this.nativeAssetsYamlUri,
|
this.nativeAssetsYamlUri,
|
||||||
|
required this.recompileRestart,
|
||||||
});
|
});
|
||||||
|
|
||||||
Uri mainUri;
|
Uri mainUri;
|
||||||
@ -419,6 +420,7 @@ class _RecompileRequest extends _CompilationRequest {
|
|||||||
bool suppressErrors;
|
bool suppressErrors;
|
||||||
final Uri? additionalSourceUri;
|
final Uri? additionalSourceUri;
|
||||||
final Uri? nativeAssetsYamlUri;
|
final Uri? nativeAssetsYamlUri;
|
||||||
|
final bool recompileRestart;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CompilerOutput?> _run(DefaultResidentCompiler compiler) async => compiler._recompile(this);
|
Future<CompilerOutput?> _run(DefaultResidentCompiler compiler) async => compiler._recompile(this);
|
||||||
@ -533,6 +535,9 @@ abstract class ResidentCompiler {
|
|||||||
/// If [checkDartPluginRegistry] is true, it is the caller's responsibility
|
/// If [checkDartPluginRegistry] is true, it is the caller's responsibility
|
||||||
/// to ensure that the generated registrant file has been updated such that
|
/// to ensure that the generated registrant file has been updated such that
|
||||||
/// it is wrapping [mainUri].
|
/// it is wrapping [mainUri].
|
||||||
|
///
|
||||||
|
/// If [recompileRestart] is true, uses the `recompile-restart` instruction
|
||||||
|
/// intended for a hot restart instead.
|
||||||
Future<CompilerOutput?> recompile(
|
Future<CompilerOutput?> recompile(
|
||||||
Uri mainUri,
|
Uri mainUri,
|
||||||
List<Uri>? invalidatedFiles, {
|
List<Uri>? invalidatedFiles, {
|
||||||
@ -544,6 +549,7 @@ abstract class ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<CompilerOutput?> compileExpression(
|
Future<CompilerOutput?> compileExpression(
|
||||||
@ -695,6 +701,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
|||||||
String? projectRootPath,
|
String? projectRootPath,
|
||||||
FileSystem? fs,
|
FileSystem? fs,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (!_controller.hasListener) {
|
if (!_controller.hasListener) {
|
||||||
_controller.stream.listen(_handleCompilationRequest);
|
_controller.stream.listen(_handleCompilationRequest);
|
||||||
@ -717,6 +724,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
|||||||
suppressErrors,
|
suppressErrors,
|
||||||
additionalSourceUri: additionalSourceUri,
|
additionalSourceUri: additionalSourceUri,
|
||||||
nativeAssetsYamlUri: nativeAssetsYaml,
|
nativeAssetsYamlUri: nativeAssetsYaml,
|
||||||
|
recompileRestart: recompileRestart,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return completer.future;
|
return completer.future;
|
||||||
@ -759,7 +767,11 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
|||||||
server.stdin.writeln('native-assets $nativeAssets');
|
server.stdin.writeln('native-assets $nativeAssets');
|
||||||
_logger.printTrace('<- native-assets $nativeAssets');
|
_logger.printTrace('<- native-assets $nativeAssets');
|
||||||
}
|
}
|
||||||
server.stdin.writeln('recompile $mainUri $inputKey');
|
if (request.recompileRestart) {
|
||||||
|
server.stdin.writeln('recompile-restart $mainUri $inputKey');
|
||||||
|
} else {
|
||||||
|
server.stdin.writeln('recompile $mainUri $inputKey');
|
||||||
|
}
|
||||||
_logger.printTrace('<- recompile $mainUri $inputKey');
|
_logger.printTrace('<- recompile $mainUri $inputKey');
|
||||||
final List<Uri>? invalidatedFiles = request.invalidatedFiles;
|
final List<Uri>? invalidatedFiles = request.invalidatedFiles;
|
||||||
if (invalidatedFiles != null) {
|
if (invalidatedFiles != null) {
|
||||||
|
@ -1138,6 +1138,7 @@ class WebDevFS implements DevFS {
|
|||||||
projectRootPath: projectRootPath,
|
projectRootPath: projectRootPath,
|
||||||
fs: globals.fs,
|
fs: globals.fs,
|
||||||
dartPluginRegistrant: dartPluginRegistrant,
|
dartPluginRegistrant: dartPluginRegistrant,
|
||||||
|
recompileRestart: fullRestart,
|
||||||
);
|
);
|
||||||
if (compilerOutput == null || compilerOutput.errorCount > 0) {
|
if (compilerOutput == null || compilerOutput.errorCount > 0) {
|
||||||
return UpdateFSReport();
|
return UpdateFSReport();
|
||||||
|
@ -966,6 +966,7 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) {
|
}) {
|
||||||
return onRecompile?.call(mainUri, invalidatedFiles) ??
|
return onRecompile?.call(mainUri, invalidatedFiles) ??
|
||||||
Future<CompilerOutput>.value(const CompilerOutput('', 1, <Uri>[]));
|
Future<CompilerOutput>.value(const CompilerOutput('', 1, <Uri>[]));
|
||||||
|
@ -321,6 +321,7 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) async {
|
}) async {
|
||||||
recompileCalled = true;
|
recompileCalled = true;
|
||||||
receivedNativeAssetsYaml = nativeAssetsYaml;
|
receivedNativeAssetsYaml = nativeAssetsYaml;
|
||||||
|
@ -1604,6 +1604,7 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) async {
|
}) async {
|
||||||
return const CompilerOutput('foo.dill', 0, <Uri>[]);
|
return const CompilerOutput('foo.dill', 0, <Uri>[]);
|
||||||
}
|
}
|
||||||
|
@ -311,6 +311,7 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) async {
|
}) async {
|
||||||
if (compilerOutput != null) {
|
if (compilerOutput != null) {
|
||||||
fileSystem!.file(compilerOutput!.outputFilename).createSync(recursive: true);
|
fileSystem!.file(compilerOutput!.outputFilename).createSync(recursive: true);
|
||||||
|
@ -1386,6 +1386,7 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) async {
|
}) async {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -1703,6 +1703,7 @@ class FakeResidentCompiler extends Fake implements ResidentCompiler {
|
|||||||
bool checkDartPluginRegistry = false,
|
bool checkDartPluginRegistry = false,
|
||||||
File? dartPluginRegistrant,
|
File? dartPluginRegistrant,
|
||||||
Uri? nativeAssetsYaml,
|
Uri? nativeAssetsYaml,
|
||||||
|
bool recompileRestart = false,
|
||||||
}) async {
|
}) async {
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -5,45 +5,9 @@
|
|||||||
@Tags(<String>['flutter-test-driver'])
|
@Tags(<String>['flutter-test-driver'])
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'package:file/file.dart';
|
|
||||||
|
|
||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
import 'test_data/hot_reload_const_project.dart';
|
import 'test_data/hot_reload_errors_common.dart';
|
||||||
import 'test_driver.dart';
|
|
||||||
import 'test_utils.dart';
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
late Directory tempDir;
|
testAll();
|
||||||
final HotReloadConstProject project = HotReloadConstProject();
|
|
||||||
late FlutterRunTestDriver flutter;
|
|
||||||
|
|
||||||
setUp(() async {
|
|
||||||
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
|
|
||||||
await project.setUpIn(tempDir);
|
|
||||||
flutter = FlutterRunTestDriver(tempDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() async {
|
|
||||||
await flutter.stop();
|
|
||||||
tryToDelete(tempDir);
|
|
||||||
});
|
|
||||||
|
|
||||||
testWithoutContext(
|
|
||||||
'hot reload displays a formatted error message when removing a field from a const class',
|
|
||||||
() async {
|
|
||||||
await flutter.run();
|
|
||||||
project.removeFieldFromConstClass();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
flutter.hotReload(),
|
|
||||||
throwsA(
|
|
||||||
isA<Exception>().having(
|
|
||||||
(Exception e) => e.toString(),
|
|
||||||
'message',
|
|
||||||
contains('Try performing a hot restart instead.'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2014 The Flutter 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:file/file.dart';
|
||||||
|
|
||||||
|
import '../../src/common.dart';
|
||||||
|
import '../test_driver.dart';
|
||||||
|
import '../test_utils.dart';
|
||||||
|
import 'hot_reload_const_project.dart';
|
||||||
|
|
||||||
|
void testAll({
|
||||||
|
bool chrome = false,
|
||||||
|
List<String> additionalCommandArgs = const <String>[],
|
||||||
|
String constClassFieldRemovalErrorMessage = 'Try performing a hot restart instead.',
|
||||||
|
Object? skip = false,
|
||||||
|
}) {
|
||||||
|
group('chrome: $chrome'
|
||||||
|
'${additionalCommandArgs.isEmpty ? '' : ' with args: $additionalCommandArgs'}', () {
|
||||||
|
late Directory tempDir;
|
||||||
|
final HotReloadConstProject project = HotReloadConstProject();
|
||||||
|
late FlutterRunTestDriver flutter;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
tempDir = createResolvedTempDirectorySync('hot_reload_test.');
|
||||||
|
await project.setUpIn(tempDir);
|
||||||
|
flutter = FlutterRunTestDriver(tempDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
await flutter.stop();
|
||||||
|
tryToDelete(tempDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext(
|
||||||
|
'hot reload displays a formatted error message when removing a field from a const class',
|
||||||
|
() async {
|
||||||
|
await flutter.run();
|
||||||
|
project.removeFieldFromConstClass();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
flutter.hotReload(),
|
||||||
|
throwsA(
|
||||||
|
isA<Exception>().having(
|
||||||
|
(Exception e) => e.toString(),
|
||||||
|
'message',
|
||||||
|
contains(constClassFieldRemovalErrorMessage),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWithoutContext('hot restart succeeds when removing a field from a const class', () async {
|
||||||
|
await flutter.run(chrome: true, additionalCommandArgs: additionalCommandArgs);
|
||||||
|
project.removeFieldFromConstClass();
|
||||||
|
await flutter.hotRestart();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
@Tags(<String>['flutter-test-driver'])
|
||||||
|
library;
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import '../integration.shard/test_data/hot_reload_errors_common.dart';
|
||||||
|
import '../src/common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testAll(
|
||||||
|
chrome: true,
|
||||||
|
additionalCommandArgs: <String>[
|
||||||
|
'--extra-front-end-options=--dartdevc-canary,--dartdevc-module-format=ddc',
|
||||||
|
],
|
||||||
|
// TODO(srujzs): Remove this custom message once we have the delta inspector emitting the same
|
||||||
|
// string as the VM.
|
||||||
|
constClassFieldRemovalErrorMessage: 'Const class cannot remove fields',
|
||||||
|
// https://github.com/flutter/flutter/issues/162567
|
||||||
|
skip: Platform.isWindows,
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user