mirror of
https://github.com/flutter/flutter.git
synced 2026-01-09 07:51:35 +08:00
[ Widget Preview ] Add support for dart:ffi imports (#180586)
Adds logic to pass `--include-unsupported-platform-library-stubs` to the CFE when launched via `flutter widget-preview start`, as well as logic to ensure the flag can't be passed as an extra frontend option to bypass compile time errors due to imports of unsupported libraries. Fixes https://github.com/flutter/flutter/issues/166431
This commit is contained in:
parent
afdf7e2029
commit
15c48f2922
@ -220,6 +220,8 @@ template("_compile_platform") {
|
||||
skwasm_library = "dart:_skwasm_stub"
|
||||
if (invoker.kernel_target == "dart2wasm") {
|
||||
skwasm_library = "dart:_skwasm_impl"
|
||||
} else if (invoker.kernel_target == "ddc") {
|
||||
args += [ "--include-unsupported-platform-library-stubs" ]
|
||||
}
|
||||
|
||||
args += [
|
||||
|
||||
@ -53,6 +53,7 @@ class BuildInfo {
|
||||
this.assumeInitializeFromDillUpToDate = false,
|
||||
this.buildNativeAssets = true,
|
||||
this.useLocalCanvasKit = false,
|
||||
this.includeUnsupportedPlatformLibraryStubs = false,
|
||||
this.webEnableHotReload = false,
|
||||
}) : extraFrontEndOptions = extraFrontEndOptions ?? const <String>[],
|
||||
extraGenSnapshotOptions = extraGenSnapshotOptions ?? const <String>[],
|
||||
@ -181,6 +182,11 @@ class BuildInfo {
|
||||
/// If set, builds native assets with `build.dart` from all packages.
|
||||
final bool buildNativeAssets;
|
||||
|
||||
/// If set, unsupported core libraries can be imported without causing a compile time error.
|
||||
///
|
||||
/// This should only be used by developer tooling as unsupported APIs will throw at runtime.
|
||||
final bool includeUnsupportedPlatformLibraryStubs;
|
||||
|
||||
/// If set, web builds will use the locally built CanvasKit instead of using the CDN
|
||||
final bool useLocalCanvasKit;
|
||||
|
||||
|
||||
@ -481,6 +481,7 @@ final class WidgetPreviewStartCommand extends WidgetPreviewSubCommandBase with C
|
||||
// Don't try and download canvaskit from the CDN.
|
||||
useLocalCanvasKit: true,
|
||||
webEnableHotReload: true,
|
||||
includeUnsupportedPlatformLibraryStubs: true,
|
||||
),
|
||||
webEnableExposeUrl: false,
|
||||
webEnableExpressionEvaluation: true,
|
||||
|
||||
@ -207,6 +207,16 @@ List<String> buildModeOptions(BuildMode mode, List<String> dartDefines) => switc
|
||||
_ => throw Exception('Unknown BuildMode: $mode'),
|
||||
};
|
||||
|
||||
final _extraFrontEndOptionsFilterSet = <String>{
|
||||
// Don't allow for users to pass --include-unsupported-platform-library-stubs to bypass
|
||||
// unsupported library compile time errors as this flag is only safe for use by developer
|
||||
// tooling.
|
||||
'--include-unsupported-platform-library-stubs',
|
||||
};
|
||||
|
||||
Iterable<String>? _filterExtraFrontEndOptions(List<String>? options) =>
|
||||
options?.where((e) => !_extraFrontEndOptionsFilterSet.contains(e));
|
||||
|
||||
/// A compiler interface for producing single (non-incremental) kernel files.
|
||||
class KernelCompiler {
|
||||
KernelCompiler({
|
||||
@ -371,7 +381,7 @@ class KernelCompiler {
|
||||
if (nativeAssets != null) ...<String>['--native-assets', nativeAssets],
|
||||
// See: https://github.com/flutter/flutter/issues/103994
|
||||
'--verbosity=error',
|
||||
...?extraFrontEndOptions,
|
||||
...?_filterExtraFrontEndOptions(extraFrontEndOptions),
|
||||
mainUri ?? '--native-assets-only',
|
||||
];
|
||||
|
||||
@ -509,6 +519,7 @@ abstract class ResidentCompiler {
|
||||
required ShutdownHooks shutdownHooks,
|
||||
bool testCompilation,
|
||||
bool trackWidgetCreation,
|
||||
bool includeUnsupportedPlatformLibraryStubs,
|
||||
String packagesPath,
|
||||
List<String> fileSystemRoots,
|
||||
String? fileSystemScheme,
|
||||
@ -631,6 +642,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
required ShutdownHooks shutdownHooks,
|
||||
this.testCompilation = false,
|
||||
this.trackWidgetCreation = true,
|
||||
this.includeUnsupportedPlatformLibraryStubs = false,
|
||||
this.packagesPath,
|
||||
List<String> fileSystemRoots = const <String>[],
|
||||
this.fileSystemScheme,
|
||||
@ -653,7 +665,17 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
// This is a URI, not a file path, so the forward slash is correct even on Windows.
|
||||
sdkRoot = sdkRoot.endsWith('/') ? sdkRoot : '$sdkRoot/',
|
||||
// Make a copy, we might need to modify it later.
|
||||
fileSystemRoots = List<String>.from(fileSystemRoots);
|
||||
fileSystemRoots = List<String>.from(fileSystemRoots) {
|
||||
// Currently, the only developer tooling that requires support for importing unsupported
|
||||
// platform libraries at compile time is the widget previewer. Only developer tooling use cases
|
||||
// should support this, so this is restricted to the widget previewer's runtime target for now.
|
||||
if (includeUnsupportedPlatformLibraryStubs && targetModel != TargetModel.dartdevc) {
|
||||
throw StateError(
|
||||
'includeUnsupportedPlatformLibraryStubs should only be used by the widget-preview '
|
||||
'command.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
final Logger _logger;
|
||||
final ProcessManager _processManager;
|
||||
@ -664,6 +686,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
final bool testCompilation;
|
||||
final BuildMode buildMode;
|
||||
final bool trackWidgetCreation;
|
||||
final bool includeUnsupportedPlatformLibraryStubs;
|
||||
final String? packagesPath;
|
||||
final TargetModel targetModel;
|
||||
final List<String> fileSystemRoots;
|
||||
@ -867,6 +890,8 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
if (packagesPath != null) ...<String>['--packages', packagesPath!],
|
||||
...buildModeOptions(buildMode, dartDefines),
|
||||
if (trackWidgetCreation) '--track-widget-creation',
|
||||
if (includeUnsupportedPlatformLibraryStubs)
|
||||
'--include-unsupported-platform-library-stubs',
|
||||
for (final String root in fileSystemRoots) ...<String>['--filesystem-root', root],
|
||||
if (fileSystemScheme != null) ...<String>['--filesystem-scheme', fileSystemScheme!],
|
||||
if (initializeFromDill != null) ...<String>[
|
||||
@ -886,7 +911,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
||||
if (unsafePackageSerialization) '--unsafe-package-serialization',
|
||||
// See: https://github.com/flutter/flutter/issues/103994
|
||||
'--verbosity=error',
|
||||
...?extraFrontEndOptions,
|
||||
...?_filterExtraFrontEndOptions(extraFrontEndOptions),
|
||||
];
|
||||
_logger.printTrace(command.join(' '));
|
||||
_server = await _processManager.start(command);
|
||||
|
||||
@ -125,6 +125,7 @@ class FlutterDevice {
|
||||
globals.artifacts!.getHostArtifact(HostArtifact.flutterWebSdk).path,
|
||||
buildMode: buildInfo.mode,
|
||||
trackWidgetCreation: buildInfo.trackWidgetCreation,
|
||||
includeUnsupportedPlatformLibraryStubs: buildInfo.includeUnsupportedPlatformLibraryStubs,
|
||||
fileSystemRoots: buildInfo.fileSystemRoots,
|
||||
// Override the filesystem scheme so that the frontend_server can find
|
||||
// the generated entrypoint code.
|
||||
|
||||
@ -2,13 +2,21 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/compile.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/fake_process_manager.dart';
|
||||
import '../src/fakes.dart' hide FakeProcess;
|
||||
|
||||
void main() {
|
||||
testWithoutContext('StdoutHandler can produce output message', () async {
|
||||
@ -147,4 +155,195 @@ void main() {
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWithoutContext(
|
||||
'includeUnsupportedPlatformLibraryStubs is only valid for Target.dartdevc',
|
||||
() {
|
||||
final unsupportedTargetModels = <TargetModel>{
|
||||
TargetModel.flutter,
|
||||
TargetModel.flutterRunner,
|
||||
TargetModel.vm,
|
||||
};
|
||||
|
||||
// Initializing the compiler with includeUnsupportedPlatformLibraryStubs for targets other
|
||||
// than DDC is not currently supported as it's limited for use with the widget previewer.
|
||||
for (final target in unsupportedTargetModels) {
|
||||
try {
|
||||
ResidentCompiler(
|
||||
'sdkroot',
|
||||
buildMode: BuildMode.debug,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
artifacts: Artifacts.test(),
|
||||
platform: FakePlatform(),
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
shutdownHooks: FakeShutdownHooks(),
|
||||
targetModel: target,
|
||||
includeUnsupportedPlatformLibraryStubs: true,
|
||||
);
|
||||
fail('Unsupported target did not throw.');
|
||||
} on StateError catch (e) {
|
||||
expect(
|
||||
e.message,
|
||||
'includeUnsupportedPlatformLibraryStubs should only be used by the widget-preview '
|
||||
'command.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Initializing the compiler with includeUnsupportedPlatformLibraryStubs for DDC is
|
||||
// supported.
|
||||
ResidentCompiler(
|
||||
'sdkroot',
|
||||
buildMode: BuildMode.debug,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: FakeProcessManager.any(),
|
||||
artifacts: Artifacts.test(),
|
||||
platform: FakePlatform(),
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
shutdownHooks: FakeShutdownHooks(),
|
||||
targetModel: TargetModel.dartdevc,
|
||||
includeUnsupportedPlatformLibraryStubs: true,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
testWithoutContext(
|
||||
'Strips --include-unsupported-platform-library-stubs from extraFrontEndOptions',
|
||||
() async {
|
||||
final completer = Completer<void>();
|
||||
final processManager = FakeProcessManager.list([
|
||||
FakeCommand(
|
||||
command: const <String>[
|
||||
'Artifact.engineDartAotRuntime.TargetPlatform.web_javascript',
|
||||
'Artifact.frontendServerSnapshotForEngineDartSdk.TargetPlatform.web_javascript',
|
||||
'--sdk-root',
|
||||
'sdkroot/',
|
||||
'--incremental',
|
||||
'--target=dartdevc',
|
||||
'--experimental-emit-debug-metadata',
|
||||
'--output-dill',
|
||||
'foo.dill',
|
||||
'-Ddart.vm.profile=false',
|
||||
'-Ddart.vm.product=false',
|
||||
'--enable-asserts',
|
||||
'--track-widget-creation',
|
||||
'--verbosity=error',
|
||||
'--extra-flag',
|
||||
],
|
||||
onRun: (_) => completer.complete(),
|
||||
),
|
||||
]);
|
||||
final compiler = DefaultResidentCompiler(
|
||||
'sdkroot',
|
||||
buildMode: BuildMode.debug,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: processManager,
|
||||
artifacts: Artifacts.test(),
|
||||
platform: FakePlatform(),
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
shutdownHooks: FakeShutdownHooks(),
|
||||
targetModel: TargetModel.dartdevc,
|
||||
// Don't explicitly enable includeUnsupportedPlatformLibraryStubs to ensure it's not
|
||||
// included in the argument list.
|
||||
// ignore: avoid_redundant_argument_values
|
||||
includeUnsupportedPlatformLibraryStubs: false,
|
||||
extraFrontEndOptions: [
|
||||
'--include-unsupported-platform-library-stubs',
|
||||
// Include a random extra flag to ensure not all extra options are stripped.
|
||||
'--extra-flag',
|
||||
],
|
||||
);
|
||||
|
||||
await runZonedGuarded(
|
||||
() {
|
||||
// This throws ToolExit as the FakeProcess immediately closes stdout and stderr.
|
||||
compiler.recompile(
|
||||
Uri.file('foo.dart'),
|
||||
[],
|
||||
outputPath: 'foo.dill',
|
||||
packageConfig: PackageConfig.empty,
|
||||
);
|
||||
},
|
||||
(e, st) {
|
||||
if (e is! ToolExit) {
|
||||
completer.completeError(e, st);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Fail if the command isn't run. This can happen when the commands actual arguments don't
|
||||
// match.
|
||||
await completer.future.timeout(const Duration(seconds: 5));
|
||||
},
|
||||
);
|
||||
|
||||
testWithoutContext(
|
||||
'--include-unsupported-platform-library-stubs when includeUnsupportedPlatformLibraryStubs is set',
|
||||
() async {
|
||||
final completer = Completer<void>();
|
||||
final processManager = FakeProcessManager.list([
|
||||
FakeCommand(
|
||||
command: const <String>[
|
||||
'Artifact.engineDartAotRuntime.TargetPlatform.web_javascript',
|
||||
'Artifact.frontendServerSnapshotForEngineDartSdk.TargetPlatform.web_javascript',
|
||||
'--sdk-root',
|
||||
'sdkroot/',
|
||||
'--incremental',
|
||||
'--target=dartdevc',
|
||||
'--experimental-emit-debug-metadata',
|
||||
'--output-dill',
|
||||
'foo.dill',
|
||||
'-Ddart.vm.profile=false',
|
||||
'-Ddart.vm.product=false',
|
||||
'--enable-asserts',
|
||||
'--track-widget-creation',
|
||||
'--include-unsupported-platform-library-stubs',
|
||||
'--verbosity=error',
|
||||
'--extra-flag',
|
||||
],
|
||||
onRun: (_) => completer.complete(),
|
||||
),
|
||||
]);
|
||||
final compiler = DefaultResidentCompiler(
|
||||
'sdkroot',
|
||||
buildMode: BuildMode.debug,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: processManager,
|
||||
artifacts: Artifacts.test(),
|
||||
platform: FakePlatform(),
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
shutdownHooks: FakeShutdownHooks(),
|
||||
targetModel: TargetModel.dartdevc,
|
||||
// Explicitly enable includeUnsupportedPlatformLibraryStubs to ensure it's included in the
|
||||
// argument list.
|
||||
includeUnsupportedPlatformLibraryStubs: true,
|
||||
extraFrontEndOptions: [
|
||||
// Include a random extra flag to ensure not all extra options are stripped.
|
||||
'--extra-flag',
|
||||
],
|
||||
);
|
||||
|
||||
await runZonedGuarded(
|
||||
() {
|
||||
// This throws ToolExit as the FakeProcess immediately closes stdout and stderr.
|
||||
compiler.recompile(
|
||||
Uri.file('foo.dart'),
|
||||
[],
|
||||
outputPath: 'foo.dill',
|
||||
packageConfig: PackageConfig.empty,
|
||||
);
|
||||
},
|
||||
(e, st) {
|
||||
if (e is! ToolExit) {
|
||||
completer.completeError(e, st);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Fail if the command isn't run. This can happen when the commands actual arguments don't
|
||||
// match.
|
||||
await completer.future.timeout(const Duration(seconds: 5));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user