Add --frontend-server-starter-path
option to flutter run
and flutter test
(#135038)
This commit is contained in:
parent
da1f801fd5
commit
c627dbfbc6
@ -123,6 +123,7 @@ BuildApp() {
|
|||||||
"-dSplitDebugInfo=${SPLIT_DEBUG_INFO}"
|
"-dSplitDebugInfo=${SPLIT_DEBUG_INFO}"
|
||||||
"-dTrackWidgetCreation=${TRACK_WIDGET_CREATION}"
|
"-dTrackWidgetCreation=${TRACK_WIDGET_CREATION}"
|
||||||
"-dAction=${ACTION}"
|
"-dAction=${ACTION}"
|
||||||
|
"-dFrontendServerStarterPath=${FRONTEND_SERVER_STARTER_PATH}"
|
||||||
"--DartDefines=${DART_DEFINES}"
|
"--DartDefines=${DART_DEFINES}"
|
||||||
"--ExtraGenSnapshotOptions=${EXTRA_GEN_SNAPSHOT_OPTIONS}"
|
"--ExtraGenSnapshotOptions=${EXTRA_GEN_SNAPSHOT_OPTIONS}"
|
||||||
"--ExtraFrontEndOptions=${EXTRA_FRONT_END_OPTIONS}"
|
"--ExtraFrontEndOptions=${EXTRA_FRONT_END_OPTIONS}"
|
||||||
|
@ -13,6 +13,7 @@ Future<void> main(List<String> arguments) async {
|
|||||||
|
|
||||||
final String? dartDefines = Platform.environment['DART_DEFINES'];
|
final String? dartDefines = Platform.environment['DART_DEFINES'];
|
||||||
final bool dartObfuscation = Platform.environment['DART_OBFUSCATION'] == 'true';
|
final bool dartObfuscation = Platform.environment['DART_OBFUSCATION'] == 'true';
|
||||||
|
final String? frontendServerStarterPath = Platform.environment['FRONTEND_SERVER_STARTER_PATH'];
|
||||||
final String? extraFrontEndOptions = Platform.environment['EXTRA_FRONT_END_OPTIONS'];
|
final String? extraFrontEndOptions = Platform.environment['EXTRA_FRONT_END_OPTIONS'];
|
||||||
final String? extraGenSnapshotOptions = Platform.environment['EXTRA_GEN_SNAPSHOT_OPTIONS'];
|
final String? extraGenSnapshotOptions = Platform.environment['EXTRA_GEN_SNAPSHOT_OPTIONS'];
|
||||||
final String? flutterEngine = Platform.environment['FLUTTER_ENGINE'];
|
final String? flutterEngine = Platform.environment['FLUTTER_ENGINE'];
|
||||||
@ -106,6 +107,8 @@ or
|
|||||||
'--DartDefines=$dartDefines',
|
'--DartDefines=$dartDefines',
|
||||||
if (extraGenSnapshotOptions != null)
|
if (extraGenSnapshotOptions != null)
|
||||||
'--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
|
'--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
|
||||||
|
if (frontendServerStarterPath != null)
|
||||||
|
'-dFrontendServerStarterPath=$frontendServerStarterPath',
|
||||||
if (extraFrontEndOptions != null)
|
if (extraFrontEndOptions != null)
|
||||||
'--ExtraFrontEndOptions=$extraFrontEndOptions',
|
'--ExtraFrontEndOptions=$extraFrontEndOptions',
|
||||||
target,
|
target,
|
||||||
|
@ -402,6 +402,7 @@ class Context {
|
|||||||
'-dTrackWidgetCreation=${environment['TRACK_WIDGET_CREATION'] ?? ''}',
|
'-dTrackWidgetCreation=${environment['TRACK_WIDGET_CREATION'] ?? ''}',
|
||||||
'-dDartObfuscation=${environment['DART_OBFUSCATION'] ?? ''}',
|
'-dDartObfuscation=${environment['DART_OBFUSCATION'] ?? ''}',
|
||||||
'-dAction=${environment['ACTION'] ?? ''}',
|
'-dAction=${environment['ACTION'] ?? ''}',
|
||||||
|
'-dFrontendServerStarterPath=${environment['FRONTEND_SERVER_STARTER_PATH'] ?? ''}',
|
||||||
'--ExtraGenSnapshotOptions=${environment['EXTRA_GEN_SNAPSHOT_OPTIONS'] ?? ''}',
|
'--ExtraGenSnapshotOptions=${environment['EXTRA_GEN_SNAPSHOT_OPTIONS'] ?? ''}',
|
||||||
'--DartDefines=${environment['DART_DEFINES'] ?? ''}',
|
'--DartDefines=${environment['DART_DEFINES'] ?? ''}',
|
||||||
'--ExtraFrontEndOptions=${environment['EXTRA_FRONT_END_OPTIONS'] ?? ''}',
|
'--ExtraFrontEndOptions=${environment['EXTRA_FRONT_END_OPTIONS'] ?? ''}',
|
||||||
|
@ -964,6 +964,10 @@ class FlutterPlugin implements Plugin<Project> {
|
|||||||
if (project.hasProperty('track-widget-creation')) {
|
if (project.hasProperty('track-widget-creation')) {
|
||||||
trackWidgetCreationValue = project.property('track-widget-creation').toBoolean()
|
trackWidgetCreationValue = project.property('track-widget-creation').toBoolean()
|
||||||
}
|
}
|
||||||
|
String frontendServerStarterPathValue = null
|
||||||
|
if (project.hasProperty('frontend-server-starter-path')) {
|
||||||
|
frontendServerStarterPathValue = project.property('frontend-server-starter-path')
|
||||||
|
}
|
||||||
String extraFrontEndOptionsValue = null
|
String extraFrontEndOptionsValue = null
|
||||||
if (project.hasProperty('extra-front-end-options')) {
|
if (project.hasProperty('extra-front-end-options')) {
|
||||||
extraFrontEndOptionsValue = project.property('extra-front-end-options')
|
extraFrontEndOptionsValue = project.property('extra-front-end-options')
|
||||||
@ -1052,6 +1056,7 @@ class FlutterPlugin implements Plugin<Project> {
|
|||||||
targetPlatformValues = targetPlatforms
|
targetPlatformValues = targetPlatforms
|
||||||
sourceDir getFlutterSourceDirectory()
|
sourceDir getFlutterSourceDirectory()
|
||||||
intermediateDir project.file("${project.buildDir}/$INTERMEDIATES_DIR/flutter/${variant.name}/")
|
intermediateDir project.file("${project.buildDir}/$INTERMEDIATES_DIR/flutter/${variant.name}/")
|
||||||
|
frontendServerStarterPath frontendServerStarterPathValue
|
||||||
extraFrontEndOptions extraFrontEndOptionsValue
|
extraFrontEndOptions extraFrontEndOptionsValue
|
||||||
extraGenSnapshotOptions extraGenSnapshotOptionsValue
|
extraGenSnapshotOptions extraGenSnapshotOptionsValue
|
||||||
splitDebugInfo splitDebugInfoValue
|
splitDebugInfo splitDebugInfoValue
|
||||||
@ -1290,6 +1295,8 @@ abstract class BaseFlutterTask extends DefaultTask {
|
|||||||
@Internal
|
@Internal
|
||||||
File intermediateDir
|
File intermediateDir
|
||||||
@Optional @Input
|
@Optional @Input
|
||||||
|
String frontendServerStarterPath
|
||||||
|
@Optional @Input
|
||||||
String extraFrontEndOptions
|
String extraFrontEndOptions
|
||||||
@Optional @Input
|
@Optional @Input
|
||||||
String extraGenSnapshotOptions
|
String extraGenSnapshotOptions
|
||||||
@ -1394,6 +1401,9 @@ abstract class BaseFlutterTask extends DefaultTask {
|
|||||||
if (extraGenSnapshotOptions != null) {
|
if (extraGenSnapshotOptions != null) {
|
||||||
args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
|
args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
|
||||||
}
|
}
|
||||||
|
if (frontendServerStarterPath != null) {
|
||||||
|
args "-dFrontendServerStarterPath=${frontendServerStarterPath}"
|
||||||
|
}
|
||||||
if (extraFrontEndOptions != null) {
|
if (extraFrontEndOptions != null) {
|
||||||
args "--ExtraFrontEndOptions=${extraFrontEndOptions}"
|
args "--ExtraFrontEndOptions=${extraFrontEndOptions}"
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ class BuildInfo {
|
|||||||
this.mode,
|
this.mode,
|
||||||
this.flavor, {
|
this.flavor, {
|
||||||
this.trackWidgetCreation = false,
|
this.trackWidgetCreation = false,
|
||||||
|
this.frontendServerStarterPath,
|
||||||
List<String>? extraFrontEndOptions,
|
List<String>? extraFrontEndOptions,
|
||||||
List<String>? extraGenSnapshotOptions,
|
List<String>? extraGenSnapshotOptions,
|
||||||
List<String>? fileSystemRoots,
|
List<String>? fileSystemRoots,
|
||||||
@ -82,6 +83,10 @@ class BuildInfo {
|
|||||||
/// Whether the build should track widget creation locations.
|
/// Whether the build should track widget creation locations.
|
||||||
final bool trackWidgetCreation;
|
final bool trackWidgetCreation;
|
||||||
|
|
||||||
|
/// If provided, the frontend server will be started in JIT mode from this
|
||||||
|
/// file.
|
||||||
|
final String? frontendServerStarterPath;
|
||||||
|
|
||||||
/// Extra command-line options for front-end.
|
/// Extra command-line options for front-end.
|
||||||
final List<String> extraFrontEndOptions;
|
final List<String> extraFrontEndOptions;
|
||||||
|
|
||||||
@ -237,6 +242,8 @@ class BuildInfo {
|
|||||||
if (dartDefines.isNotEmpty)
|
if (dartDefines.isNotEmpty)
|
||||||
kDartDefines: encodeDartDefines(dartDefines),
|
kDartDefines: encodeDartDefines(dartDefines),
|
||||||
kDartObfuscation: dartObfuscation.toString(),
|
kDartObfuscation: dartObfuscation.toString(),
|
||||||
|
if (frontendServerStarterPath != null)
|
||||||
|
kFrontendServerStarterPath: frontendServerStarterPath!,
|
||||||
if (extraFrontEndOptions.isNotEmpty)
|
if (extraFrontEndOptions.isNotEmpty)
|
||||||
kExtraFrontEndOptions: extraFrontEndOptions.join(','),
|
kExtraFrontEndOptions: extraFrontEndOptions.join(','),
|
||||||
if (extraGenSnapshotOptions.isNotEmpty)
|
if (extraGenSnapshotOptions.isNotEmpty)
|
||||||
@ -274,6 +281,8 @@ class BuildInfo {
|
|||||||
if (dartDefines.isNotEmpty)
|
if (dartDefines.isNotEmpty)
|
||||||
'DART_DEFINES': encodeDartDefines(dartDefines),
|
'DART_DEFINES': encodeDartDefines(dartDefines),
|
||||||
'DART_OBFUSCATION': dartObfuscation.toString(),
|
'DART_OBFUSCATION': dartObfuscation.toString(),
|
||||||
|
if (frontendServerStarterPath != null)
|
||||||
|
'FRONTEND_SERVER_STARTER_PATH': frontendServerStarterPath!,
|
||||||
if (extraFrontEndOptions.isNotEmpty)
|
if (extraFrontEndOptions.isNotEmpty)
|
||||||
'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions.join(','),
|
'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions.join(','),
|
||||||
if (extraGenSnapshotOptions.isNotEmpty)
|
if (extraGenSnapshotOptions.isNotEmpty)
|
||||||
@ -310,6 +319,8 @@ class BuildInfo {
|
|||||||
if (dartDefines.isNotEmpty)
|
if (dartDefines.isNotEmpty)
|
||||||
'-Pdart-defines=${encodeDartDefines(dartDefines)}',
|
'-Pdart-defines=${encodeDartDefines(dartDefines)}',
|
||||||
'-Pdart-obfuscation=$dartObfuscation',
|
'-Pdart-obfuscation=$dartObfuscation',
|
||||||
|
if (frontendServerStarterPath != null)
|
||||||
|
'-Pfrontend-server-starter-path=$frontendServerStarterPath',
|
||||||
if (extraFrontEndOptions.isNotEmpty)
|
if (extraFrontEndOptions.isNotEmpty)
|
||||||
'-Pextra-front-end-options=${extraFrontEndOptions.join(',')}',
|
'-Pextra-front-end-options=${extraFrontEndOptions.join(',')}',
|
||||||
if (extraGenSnapshotOptions.isNotEmpty)
|
if (extraGenSnapshotOptions.isNotEmpty)
|
||||||
@ -901,6 +912,9 @@ const String kTargetFile = 'TargetFile';
|
|||||||
/// Whether to enable or disable track widget creation.
|
/// Whether to enable or disable track widget creation.
|
||||||
const String kTrackWidgetCreation = 'TrackWidgetCreation';
|
const String kTrackWidgetCreation = 'TrackWidgetCreation';
|
||||||
|
|
||||||
|
/// If provided, the frontend server will be started in JIT mode from this file.
|
||||||
|
const String kFrontendServerStarterPath = 'FrontendServerStarterPath';
|
||||||
|
|
||||||
/// Additional configuration passed to the dart front end.
|
/// Additional configuration passed to the dart front end.
|
||||||
///
|
///
|
||||||
/// This is expected to be a comma separated list of strings.
|
/// This is expected to be a comma separated list of strings.
|
||||||
|
@ -178,6 +178,7 @@ class KernelSnapshot extends Target {
|
|||||||
final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment);
|
final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment);
|
||||||
|
|
||||||
// This configuration is all optional.
|
// This configuration is all optional.
|
||||||
|
final String? frontendServerStarterPath = environment.defines[kFrontendServerStarterPath];
|
||||||
final List<String> extraFrontEndOptions = decodeCommaSeparated(environment.defines, kExtraFrontEndOptions);
|
final List<String> extraFrontEndOptions = decodeCommaSeparated(environment.defines, kExtraFrontEndOptions);
|
||||||
final List<String>? fileSystemRoots = environment.defines[kFileSystemRoots]?.split(',');
|
final List<String>? fileSystemRoots = environment.defines[kFileSystemRoots]?.split(',');
|
||||||
final String? fileSystemScheme = environment.defines[kFileSystemScheme];
|
final String? fileSystemScheme = environment.defines[kFileSystemScheme];
|
||||||
@ -254,6 +255,7 @@ class KernelSnapshot extends Target {
|
|||||||
linkPlatformKernelIn: forceLinkPlatform || buildMode.isPrecompiled,
|
linkPlatformKernelIn: forceLinkPlatform || buildMode.isPrecompiled,
|
||||||
mainPath: targetFileAbsolute,
|
mainPath: targetFileAbsolute,
|
||||||
depFilePath: environment.buildDir.childFile('kernel_snapshot.d').path,
|
depFilePath: environment.buildDir.childFile('kernel_snapshot.d').path,
|
||||||
|
frontendServerStarterPath: frontendServerStarterPath,
|
||||||
extraFrontEndOptions: extraFrontEndOptions,
|
extraFrontEndOptions: extraFrontEndOptions,
|
||||||
fileSystemRoots: fileSystemRoots,
|
fileSystemRoots: fileSystemRoots,
|
||||||
fileSystemScheme: fileSystemScheme,
|
fileSystemScheme: fileSystemScheme,
|
||||||
|
@ -315,6 +315,7 @@ class RunCommand extends RunCommandBase {
|
|||||||
requiresPubspecYaml();
|
requiresPubspecYaml();
|
||||||
usesFilesystemOptions(hide: !verboseHelp);
|
usesFilesystemOptions(hide: !verboseHelp);
|
||||||
usesExtraDartFlagOptions(verboseHelp: verboseHelp);
|
usesExtraDartFlagOptions(verboseHelp: verboseHelp);
|
||||||
|
usesFrontendServerStarterPathOption(verboseHelp: verboseHelp);
|
||||||
addEnableExperimentation(hide: !verboseHelp);
|
addEnableExperimentation(hide: !verboseHelp);
|
||||||
usesInitializeFromDillOption(hide: !verboseHelp);
|
usesInitializeFromDillOption(hide: !verboseHelp);
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
|
|||||||
requiresPubspecYaml();
|
requiresPubspecYaml();
|
||||||
usesPubOption();
|
usesPubOption();
|
||||||
addNullSafetyModeOptions(hide: !verboseHelp);
|
addNullSafetyModeOptions(hide: !verboseHelp);
|
||||||
|
usesFrontendServerStarterPathOption(verboseHelp: verboseHelp);
|
||||||
usesTrackWidgetCreation(verboseHelp: verboseHelp);
|
usesTrackWidgetCreation(verboseHelp: verboseHelp);
|
||||||
addEnableExperimentation(hide: !verboseHelp);
|
addEnableExperimentation(hide: !verboseHelp);
|
||||||
usesDartDefineOption();
|
usesDartDefineOption();
|
||||||
|
@ -227,6 +227,7 @@ class KernelCompiler {
|
|||||||
TargetModel targetModel = TargetModel.flutter,
|
TargetModel targetModel = TargetModel.flutter,
|
||||||
bool linkPlatformKernelIn = false,
|
bool linkPlatformKernelIn = false,
|
||||||
bool aot = false,
|
bool aot = false,
|
||||||
|
String? frontendServerStarterPath,
|
||||||
List<String>? extraFrontEndOptions,
|
List<String>? extraFrontEndOptions,
|
||||||
List<String>? fileSystemRoots,
|
List<String>? fileSystemRoots,
|
||||||
String? fileSystemScheme,
|
String? fileSystemScheme,
|
||||||
@ -243,10 +244,12 @@ class KernelCompiler {
|
|||||||
String? nativeAssets,
|
String? nativeAssets,
|
||||||
}) async {
|
}) async {
|
||||||
final TargetPlatform? platform = targetModel == TargetModel.dartdevc ? TargetPlatform.web_javascript : null;
|
final TargetPlatform? platform = targetModel == TargetModel.dartdevc ? TargetPlatform.web_javascript : null;
|
||||||
final String frontendServer = _artifacts.getArtifactPath(
|
final String frontendServer = (frontendServerStarterPath == null || frontendServerStarterPath.isEmpty)
|
||||||
|
? _artifacts.getArtifactPath(
|
||||||
Artifact.frontendServerSnapshotForEngineDartSdk,
|
Artifact.frontendServerSnapshotForEngineDartSdk,
|
||||||
platform: platform,
|
platform: platform,
|
||||||
);
|
)
|
||||||
|
: frontendServerStarterPath;
|
||||||
// This is a URI, not a file path, so the forward slash is correct even on Windows.
|
// This is a URI, not a file path, so the forward slash is correct even on Windows.
|
||||||
if (!sdkRoot.endsWith('/')) {
|
if (!sdkRoot.endsWith('/')) {
|
||||||
sdkRoot = '$sdkRoot/';
|
sdkRoot = '$sdkRoot/';
|
||||||
@ -490,6 +493,7 @@ abstract class ResidentCompiler {
|
|||||||
bool assumeInitializeFromDillUpToDate,
|
bool assumeInitializeFromDillUpToDate,
|
||||||
TargetModel targetModel,
|
TargetModel targetModel,
|
||||||
bool unsafePackageSerialization,
|
bool unsafePackageSerialization,
|
||||||
|
String? frontendServerStarterPath,
|
||||||
List<String> extraFrontEndOptions,
|
List<String> extraFrontEndOptions,
|
||||||
String platformDill,
|
String platformDill,
|
||||||
List<String>? dartDefines,
|
List<String>? dartDefines,
|
||||||
@ -605,6 +609,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
|||||||
this.assumeInitializeFromDillUpToDate = false,
|
this.assumeInitializeFromDillUpToDate = false,
|
||||||
this.targetModel = TargetModel.flutter,
|
this.targetModel = TargetModel.flutter,
|
||||||
this.unsafePackageSerialization = false,
|
this.unsafePackageSerialization = false,
|
||||||
|
this.frontendServerStarterPath,
|
||||||
this.extraFrontEndOptions,
|
this.extraFrontEndOptions,
|
||||||
this.platformDill,
|
this.platformDill,
|
||||||
List<String>? dartDefines,
|
List<String>? dartDefines,
|
||||||
@ -635,6 +640,7 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
|||||||
final String? initializeFromDill;
|
final String? initializeFromDill;
|
||||||
final bool assumeInitializeFromDillUpToDate;
|
final bool assumeInitializeFromDillUpToDate;
|
||||||
final bool unsafePackageSerialization;
|
final bool unsafePackageSerialization;
|
||||||
|
final String? frontendServerStarterPath;
|
||||||
final List<String>? extraFrontEndOptions;
|
final List<String>? extraFrontEndOptions;
|
||||||
final List<String> dartDefines;
|
final List<String> dartDefines;
|
||||||
final String? librariesSpec;
|
final String? librariesSpec;
|
||||||
@ -771,10 +777,12 @@ class DefaultResidentCompiler implements ResidentCompiler {
|
|||||||
String? nativeAssetsUri,
|
String? nativeAssetsUri,
|
||||||
}) async {
|
}) async {
|
||||||
final TargetPlatform? platform = (targetModel == TargetModel.dartdevc) ? TargetPlatform.web_javascript : null;
|
final TargetPlatform? platform = (targetModel == TargetModel.dartdevc) ? TargetPlatform.web_javascript : null;
|
||||||
final String frontendServer = artifacts.getArtifactPath(
|
final String frontendServer = (frontendServerStarterPath == null || frontendServerStarterPath!.isEmpty)
|
||||||
|
? artifacts.getArtifactPath(
|
||||||
Artifact.frontendServerSnapshotForEngineDartSdk,
|
Artifact.frontendServerSnapshotForEngineDartSdk,
|
||||||
platform: platform,
|
platform: platform,
|
||||||
);
|
)
|
||||||
|
: frontendServerStarterPath!;
|
||||||
final List<String> command = <String>[
|
final List<String> command = <String>[
|
||||||
artifacts.getArtifactPath(Artifact.engineDartBinary, platform: platform),
|
artifacts.getArtifactPath(Artifact.engineDartBinary, platform: platform),
|
||||||
'--disable-dart-dev',
|
'--disable-dart-dev',
|
||||||
|
@ -67,6 +67,7 @@ class FlutterDevice {
|
|||||||
targetModel: targetModel,
|
targetModel: targetModel,
|
||||||
dartDefines: buildInfo.dartDefines,
|
dartDefines: buildInfo.dartDefines,
|
||||||
packagesPath: buildInfo.packagesPath,
|
packagesPath: buildInfo.packagesPath,
|
||||||
|
frontendServerStarterPath: buildInfo.frontendServerStarterPath,
|
||||||
extraFrontEndOptions: buildInfo.extraFrontEndOptions,
|
extraFrontEndOptions: buildInfo.extraFrontEndOptions,
|
||||||
artifacts: globals.artifacts!,
|
artifacts: globals.artifacts!,
|
||||||
processManager: globals.processManager,
|
processManager: globals.processManager,
|
||||||
@ -155,6 +156,7 @@ class FlutterDevice {
|
|||||||
),
|
),
|
||||||
assumeInitializeFromDillUpToDate: buildInfo.assumeInitializeFromDillUpToDate,
|
assumeInitializeFromDillUpToDate: buildInfo.assumeInitializeFromDillUpToDate,
|
||||||
targetModel: TargetModel.dartdevc,
|
targetModel: TargetModel.dartdevc,
|
||||||
|
frontendServerStarterPath: buildInfo.frontendServerStarterPath,
|
||||||
extraFrontEndOptions: extraFrontEndOptions,
|
extraFrontEndOptions: extraFrontEndOptions,
|
||||||
platformDill: globals.fs.file(platformDillPath).absolute.uri.toString(),
|
platformDill: globals.fs.file(platformDillPath).absolute.uri.toString(),
|
||||||
dartDefines: buildInfo.dartDefines,
|
dartDefines: buildInfo.dartDefines,
|
||||||
@ -185,6 +187,7 @@ class FlutterDevice {
|
|||||||
fileSystemScheme: buildInfo.fileSystemScheme,
|
fileSystemScheme: buildInfo.fileSystemScheme,
|
||||||
targetModel: targetModel,
|
targetModel: targetModel,
|
||||||
dartDefines: buildInfo.dartDefines,
|
dartDefines: buildInfo.dartDefines,
|
||||||
|
frontendServerStarterPath: buildInfo.frontendServerStarterPath,
|
||||||
extraFrontEndOptions: extraFrontEndOptions,
|
extraFrontEndOptions: extraFrontEndOptions,
|
||||||
initializeFromDill: buildInfo.initializeFromDill ?? getDefaultCachedKernelPath(
|
initializeFromDill: buildInfo.initializeFromDill ?? getDefaultCachedKernelPath(
|
||||||
trackWidgetCreation: buildInfo.trackWidgetCreation,
|
trackWidgetCreation: buildInfo.trackWidgetCreation,
|
||||||
|
@ -112,6 +112,7 @@ class FlutterCommandResult {
|
|||||||
|
|
||||||
/// Common flutter command line options.
|
/// Common flutter command line options.
|
||||||
abstract final class FlutterOptions {
|
abstract final class FlutterOptions {
|
||||||
|
static const String kFrontendServerStarterPath = 'frontend-server-starter-path';
|
||||||
static const String kExtraFrontEndOptions = 'extra-front-end-options';
|
static const String kExtraFrontEndOptions = 'extra-front-end-options';
|
||||||
static const String kExtraGenSnapshotOptions = 'extra-gen-snapshot-options';
|
static const String kExtraGenSnapshotOptions = 'extra-gen-snapshot-options';
|
||||||
static const String kEnableExperiment = 'enable-experiment';
|
static const String kEnableExperiment = 'enable-experiment';
|
||||||
@ -849,6 +850,18 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void usesFrontendServerStarterPathOption({required bool verboseHelp}) {
|
||||||
|
argParser.addOption(
|
||||||
|
FlutterOptions.kFrontendServerStarterPath,
|
||||||
|
help: 'When this value is provided, the frontend server will be started '
|
||||||
|
'in JIT mode from the specified file, instead of from the AOT '
|
||||||
|
'snapshot shipped with the Dart SDK. The specified file can either '
|
||||||
|
'be a Dart source file, or an AppJIT snapshot. This option does '
|
||||||
|
'not affect web builds.',
|
||||||
|
hide: !verboseHelp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Enables support for the hidden options --extra-front-end-options and
|
/// Enables support for the hidden options --extra-front-end-options and
|
||||||
/// --extra-gen-snapshot-options.
|
/// --extra-gen-snapshot-options.
|
||||||
void usesExtraDartFlagOptions({ required bool verboseHelp }) {
|
void usesExtraDartFlagOptions({ required bool verboseHelp }) {
|
||||||
@ -1239,6 +1252,10 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
? stringArg('flavor')
|
? stringArg('flavor')
|
||||||
: null,
|
: null,
|
||||||
trackWidgetCreation: trackWidgetCreation,
|
trackWidgetCreation: trackWidgetCreation,
|
||||||
|
frontendServerStarterPath: argParser.options
|
||||||
|
.containsKey(FlutterOptions.kFrontendServerStarterPath)
|
||||||
|
? stringArg(FlutterOptions.kFrontendServerStarterPath)
|
||||||
|
: null,
|
||||||
extraFrontEndOptions: extraFrontEndOptions.isNotEmpty
|
extraFrontEndOptions: extraFrontEndOptions.isNotEmpty
|
||||||
? extraFrontEndOptions
|
? extraFrontEndOptions
|
||||||
: null,
|
: null,
|
||||||
|
@ -121,6 +121,7 @@ class TestCompiler {
|
|||||||
initializeFromDill: testFilePath,
|
initializeFromDill: testFilePath,
|
||||||
dartDefines: buildInfo.dartDefines,
|
dartDefines: buildInfo.dartDefines,
|
||||||
packagesPath: buildInfo.packagesPath,
|
packagesPath: buildInfo.packagesPath,
|
||||||
|
frontendServerStarterPath: buildInfo.frontendServerStarterPath,
|
||||||
extraFrontEndOptions: buildInfo.extraFrontEndOptions,
|
extraFrontEndOptions: buildInfo.extraFrontEndOptions,
|
||||||
platform: globals.platform,
|
platform: globals.platform,
|
||||||
testCompilation: true,
|
testCompilation: true,
|
||||||
|
@ -175,6 +175,7 @@ void main() {
|
|||||||
dartDefines: <String>['foo=2', 'bar=2'],
|
dartDefines: <String>['foo=2', 'bar=2'],
|
||||||
dartObfuscation: true,
|
dartObfuscation: true,
|
||||||
splitDebugInfoPath: 'foo/',
|
splitDebugInfoPath: 'foo/',
|
||||||
|
frontendServerStarterPath: 'foo/bar/frontend_server_starter.dart',
|
||||||
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
|
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
|
||||||
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
|
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
|
||||||
bundleSkSLPath: 'foo/bar/baz.sksl.json',
|
bundleSkSLPath: 'foo/bar/baz.sksl.json',
|
||||||
@ -190,6 +191,7 @@ void main() {
|
|||||||
'BuildMode': 'debug',
|
'BuildMode': 'debug',
|
||||||
'DartDefines': 'Zm9vPTI=,YmFyPTI=',
|
'DartDefines': 'Zm9vPTI=,YmFyPTI=',
|
||||||
'DartObfuscation': 'true',
|
'DartObfuscation': 'true',
|
||||||
|
'FrontendServerStarterPath': 'foo/bar/frontend_server_starter.dart',
|
||||||
'ExtraFrontEndOptions': '--enable-experiment=non-nullable,bar',
|
'ExtraFrontEndOptions': '--enable-experiment=non-nullable,bar',
|
||||||
'ExtraGenSnapshotOptions': '--enable-experiment=non-nullable,fizz',
|
'ExtraGenSnapshotOptions': '--enable-experiment=non-nullable,fizz',
|
||||||
'SplitDebugInfo': 'foo/',
|
'SplitDebugInfo': 'foo/',
|
||||||
@ -211,6 +213,7 @@ void main() {
|
|||||||
dartDefines: <String>['foo=2', 'bar=2'],
|
dartDefines: <String>['foo=2', 'bar=2'],
|
||||||
dartObfuscation: true,
|
dartObfuscation: true,
|
||||||
splitDebugInfoPath: 'foo/',
|
splitDebugInfoPath: 'foo/',
|
||||||
|
frontendServerStarterPath: 'foo/bar/frontend_server_starter.dart',
|
||||||
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
|
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
|
||||||
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
|
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
|
||||||
bundleSkSLPath: 'foo/bar/baz.sksl.json',
|
bundleSkSLPath: 'foo/bar/baz.sksl.json',
|
||||||
@ -226,6 +229,7 @@ void main() {
|
|||||||
'DART_DEFINES': 'Zm9vPTI=,YmFyPTI=',
|
'DART_DEFINES': 'Zm9vPTI=,YmFyPTI=',
|
||||||
'DART_OBFUSCATION': 'true',
|
'DART_OBFUSCATION': 'true',
|
||||||
'SPLIT_DEBUG_INFO': 'foo/',
|
'SPLIT_DEBUG_INFO': 'foo/',
|
||||||
|
'FRONTEND_SERVER_STARTER_PATH': 'foo/bar/frontend_server_starter.dart',
|
||||||
'EXTRA_FRONT_END_OPTIONS': '--enable-experiment=non-nullable,bar',
|
'EXTRA_FRONT_END_OPTIONS': '--enable-experiment=non-nullable,bar',
|
||||||
'EXTRA_GEN_SNAPSHOT_OPTIONS': '--enable-experiment=non-nullable,fizz',
|
'EXTRA_GEN_SNAPSHOT_OPTIONS': '--enable-experiment=non-nullable,fizz',
|
||||||
'BUNDLE_SKSL_PATH': 'foo/bar/baz.sksl.json',
|
'BUNDLE_SKSL_PATH': 'foo/bar/baz.sksl.json',
|
||||||
@ -242,6 +246,7 @@ void main() {
|
|||||||
dartDefineConfigJsonMap: <String, Object>{'baz': '2'},
|
dartDefineConfigJsonMap: <String, Object>{'baz': '2'},
|
||||||
dartObfuscation: true,
|
dartObfuscation: true,
|
||||||
splitDebugInfoPath: 'foo/',
|
splitDebugInfoPath: 'foo/',
|
||||||
|
frontendServerStarterPath: 'foo/bar/frontend_server_starter.dart',
|
||||||
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
|
extraFrontEndOptions: <String>['--enable-experiment=non-nullable', 'bar'],
|
||||||
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
|
extraGenSnapshotOptions: <String>['--enable-experiment=non-nullable', 'fizz'],
|
||||||
bundleSkSLPath: 'foo/bar/baz.sksl.json',
|
bundleSkSLPath: 'foo/bar/baz.sksl.json',
|
||||||
@ -253,6 +258,7 @@ void main() {
|
|||||||
expect(buildInfo.toGradleConfig(), <String>[
|
expect(buildInfo.toGradleConfig(), <String>[
|
||||||
'-Pdart-defines=Zm9vPTI=,YmFyPTI=',
|
'-Pdart-defines=Zm9vPTI=,YmFyPTI=',
|
||||||
'-Pdart-obfuscation=true',
|
'-Pdart-obfuscation=true',
|
||||||
|
'-Pfrontend-server-starter-path=foo/bar/frontend_server_starter.dart',
|
||||||
'-Pextra-front-end-options=--enable-experiment=non-nullable,bar',
|
'-Pextra-front-end-options=--enable-experiment=non-nullable,bar',
|
||||||
'-Pextra-gen-snapshot-options=--enable-experiment=non-nullable,fizz',
|
'-Pextra-gen-snapshot-options=--enable-experiment=non-nullable,fizz',
|
||||||
'-Psplit-debug-info=foo/',
|
'-Psplit-debug-info=foo/',
|
||||||
|
@ -217,6 +217,53 @@ native-assets: {}
|
|||||||
expect(processManager, hasNoRemainingExpectations);
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithoutContext('KernelSnapshot correctly forwards FrontendServerStarterPath', () async {
|
||||||
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
|
||||||
|
androidEnvironment.buildDir.childFile('native_assets.yaml')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync(emptyNativeAssets);
|
||||||
|
final String build = androidEnvironment.buildDir.path;
|
||||||
|
final String flutterPatchedSdkPath = artifacts.getArtifactPath(
|
||||||
|
Artifact.flutterPatchedSdkPath,
|
||||||
|
platform: TargetPlatform.android_arm,
|
||||||
|
mode: BuildMode.profile,
|
||||||
|
);
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
FakeCommand(command: <String>[
|
||||||
|
artifacts.getArtifactPath(Artifact.engineDartBinary),
|
||||||
|
'--disable-dart-dev',
|
||||||
|
'path/to/frontend_server_starter.dart',
|
||||||
|
'--sdk-root',
|
||||||
|
'$flutterPatchedSdkPath/',
|
||||||
|
'--target=flutter',
|
||||||
|
'--no-print-incremental-dependencies',
|
||||||
|
...buildModeOptions(BuildMode.profile, <String>[]),
|
||||||
|
'--track-widget-creation',
|
||||||
|
'--aot',
|
||||||
|
'--tfa',
|
||||||
|
'--target-os',
|
||||||
|
'android',
|
||||||
|
'--packages',
|
||||||
|
'/.dart_tool/package_config.json',
|
||||||
|
'--output-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--depfile',
|
||||||
|
'$build/kernel_snapshot.d',
|
||||||
|
'--native-assets',
|
||||||
|
'$build/native_assets.yaml',
|
||||||
|
'--verbosity=error',
|
||||||
|
'file:///lib/main.dart',
|
||||||
|
], stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n'),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await const KernelSnapshot()
|
||||||
|
.build(androidEnvironment..defines[kFrontendServerStarterPath] = 'path/to/frontend_server_starter.dart');
|
||||||
|
|
||||||
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
|
});
|
||||||
|
|
||||||
testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async {
|
testWithoutContext('KernelSnapshot correctly forwards ExtraFrontEndOptions', () async {
|
||||||
fileSystem.file('.dart_tool/package_config.json')
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
..createSync(recursive: true)
|
..createSync(recursive: true)
|
||||||
|
@ -391,6 +391,38 @@ void main() {
|
|||||||
ProcessManager: () => processManager,
|
ProcessManager: () => processManager,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
test('Dart2JSTarget ignores frontend server starter path option when calling dart2js', () => testbed.run(() async {
|
||||||
|
environment.defines[kBuildMode] = 'profile';
|
||||||
|
environment.defines[kFrontendServerStarterPath] = 'path/to/frontend_server_starter.dart';
|
||||||
|
processManager.addCommand(FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
..._kDart2jsLinuxArgs,
|
||||||
|
'-Ddart.vm.profile=true',
|
||||||
|
'--no-source-maps',
|
||||||
|
'-o',
|
||||||
|
environment.buildDir.childFile('app.dill').absolute.path,
|
||||||
|
'--packages=.dart_tool/package_config.json',
|
||||||
|
'--cfe-only',
|
||||||
|
environment.buildDir.childFile('main.dart').absolute.path,
|
||||||
|
]
|
||||||
|
));
|
||||||
|
processManager.addCommand(FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
..._kDart2jsLinuxArgs,
|
||||||
|
'-Ddart.vm.profile=true',
|
||||||
|
'--no-minify',
|
||||||
|
'--no-source-maps',
|
||||||
|
'-O4',
|
||||||
|
'-o',
|
||||||
|
environment.buildDir.childFile('main.dart.js').absolute.path,
|
||||||
|
environment.buildDir.childFile('app.dill').absolute.path,
|
||||||
|
]
|
||||||
|
));
|
||||||
|
|
||||||
|
await Dart2JSTarget(WebRendererMode.auto).build(environment);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
}));
|
||||||
|
|
||||||
test('Dart2JSTarget calls dart2js with expected args with enabled experiment', () => testbed.run(() async {
|
test('Dart2JSTarget calls dart2js with expected args with enabled experiment', () => testbed.run(() async {
|
||||||
environment.defines[kBuildMode] = 'profile';
|
environment.defines[kBuildMode] = 'profile';
|
||||||
|
@ -87,6 +87,7 @@ void main() {
|
|||||||
BuildMode.debug,
|
BuildMode.debug,
|
||||||
null,
|
null,
|
||||||
trackWidgetCreation: true,
|
trackWidgetCreation: true,
|
||||||
|
frontendServerStarterPath: 'path/to/frontend_server_starter.dart',
|
||||||
extraFrontEndOptions: <String>['test1', 'test2'],
|
extraFrontEndOptions: <String>['test1', 'test2'],
|
||||||
extraGenSnapshotOptions: <String>['test3', 'test4'],
|
extraGenSnapshotOptions: <String>['test3', 'test4'],
|
||||||
fileSystemRoots: <String>['test5', 'test6'],
|
fileSystemRoots: <String>['test5', 'test6'],
|
||||||
@ -106,6 +107,7 @@ void main() {
|
|||||||
expect(env!.defines[kTargetPlatform], 'ios');
|
expect(env!.defines[kTargetPlatform], 'ios');
|
||||||
expect(env!.defines[kTargetFile], mainPath);
|
expect(env!.defines[kTargetFile], mainPath);
|
||||||
expect(env!.defines[kTrackWidgetCreation], 'true');
|
expect(env!.defines[kTrackWidgetCreation], 'true');
|
||||||
|
expect(env!.defines[kFrontendServerStarterPath], 'path/to/frontend_server_starter.dart');
|
||||||
expect(env!.defines[kExtraFrontEndOptions], 'test1,test2');
|
expect(env!.defines[kExtraFrontEndOptions], 'test1,test2');
|
||||||
expect(env!.defines[kExtraGenSnapshotOptions], 'test3,test4');
|
expect(env!.defines[kExtraGenSnapshotOptions], 'test3,test4');
|
||||||
expect(env!.defines[kFileSystemRoots], 'test5,test6');
|
expect(env!.defines[kFileSystemRoots], 'test5,test6');
|
||||||
|
@ -1963,6 +1963,28 @@ flutter:
|
|||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testUsingContext('FlutterDevice passes frontendServerStarterPath parameter if specified', () async {
|
||||||
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||||
|
final FakeDevice device = FakeDevice();
|
||||||
|
|
||||||
|
final DefaultResidentCompiler? residentCompiler = (await FlutterDevice.create(
|
||||||
|
device,
|
||||||
|
buildInfo: const BuildInfo(
|
||||||
|
BuildMode.debug,
|
||||||
|
'',
|
||||||
|
treeShakeIcons: false,
|
||||||
|
frontendServerStarterPath: '/foo/bar/frontend_server_starter.dart',
|
||||||
|
),
|
||||||
|
target: null, platform: FakePlatform(),
|
||||||
|
)).generator as DefaultResidentCompiler?;
|
||||||
|
|
||||||
|
expect(residentCompiler!.frontendServerStarterPath, '/foo/bar/frontend_server_starter.dart');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Artifacts: () => Artifacts.test(),
|
||||||
|
FileSystem: () => MemoryFileSystem.test(),
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('Handle existing VM service clients DDS error', () => testbed.run(() async {
|
testUsingContext('Handle existing VM service clients DDS error', () => testbed.run(() async {
|
||||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||||
final FakeDevice device = FakeDevice()
|
final FakeDevice device = FakeDevice()
|
||||||
|
@ -51,6 +51,7 @@ void main() {
|
|||||||
'-dTrackWidgetCreation=',
|
'-dTrackWidgetCreation=',
|
||||||
'-dDartObfuscation=',
|
'-dDartObfuscation=',
|
||||||
'-dAction=build',
|
'-dAction=build',
|
||||||
|
'-dFrontendServerStarterPath=',
|
||||||
'--ExtraGenSnapshotOptions=',
|
'--ExtraGenSnapshotOptions=',
|
||||||
'--DartDefines=',
|
'--DartDefines=',
|
||||||
'--ExtraFrontEndOptions=',
|
'--ExtraFrontEndOptions=',
|
||||||
@ -103,6 +104,7 @@ void main() {
|
|||||||
'-dTrackWidgetCreation=',
|
'-dTrackWidgetCreation=',
|
||||||
'-dDartObfuscation=',
|
'-dDartObfuscation=',
|
||||||
'-dAction=',
|
'-dAction=',
|
||||||
|
'-dFrontendServerStarterPath=',
|
||||||
'--ExtraGenSnapshotOptions=',
|
'--ExtraGenSnapshotOptions=',
|
||||||
'--DartDefines=',
|
'--DartDefines=',
|
||||||
'--ExtraFrontEndOptions=',
|
'--ExtraFrontEndOptions=',
|
||||||
@ -136,6 +138,7 @@ void main() {
|
|||||||
const String expandedCodeSignIdentity = 'F1326572E0B71C3C8442805230CB4B33B708A2E2';
|
const String expandedCodeSignIdentity = 'F1326572E0B71C3C8442805230CB4B33B708A2E2';
|
||||||
const String extraFrontEndOptions = '--some-option';
|
const String extraFrontEndOptions = '--some-option';
|
||||||
const String extraGenSnapshotOptions = '--obfuscate';
|
const String extraGenSnapshotOptions = '--obfuscate';
|
||||||
|
const String frontendServerStarterPath = '/path/to/frontend_server_starter.dart';
|
||||||
const String sdkRoot = '/path/to/sdk';
|
const String sdkRoot = '/path/to/sdk';
|
||||||
const String splitDebugInfo = '/path/to/split/debug/info';
|
const String splitDebugInfo = '/path/to/split/debug/info';
|
||||||
const String trackWidgetCreation = 'true';
|
const String trackWidgetCreation = 'true';
|
||||||
@ -154,6 +157,7 @@ void main() {
|
|||||||
'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions,
|
'EXTRA_FRONT_END_OPTIONS': extraFrontEndOptions,
|
||||||
'EXTRA_GEN_SNAPSHOT_OPTIONS': extraGenSnapshotOptions,
|
'EXTRA_GEN_SNAPSHOT_OPTIONS': extraGenSnapshotOptions,
|
||||||
'FLUTTER_ROOT': flutterRoot.path,
|
'FLUTTER_ROOT': flutterRoot.path,
|
||||||
|
'FRONTEND_SERVER_STARTER_PATH': frontendServerStarterPath,
|
||||||
'INFOPLIST_PATH': 'Info.plist',
|
'INFOPLIST_PATH': 'Info.plist',
|
||||||
'SDKROOT': sdkRoot,
|
'SDKROOT': sdkRoot,
|
||||||
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
||||||
@ -177,6 +181,7 @@ void main() {
|
|||||||
'-dTrackWidgetCreation=$trackWidgetCreation',
|
'-dTrackWidgetCreation=$trackWidgetCreation',
|
||||||
'-dDartObfuscation=$dartObfuscation',
|
'-dDartObfuscation=$dartObfuscation',
|
||||||
'-dAction=install',
|
'-dAction=install',
|
||||||
|
'-dFrontendServerStarterPath=$frontendServerStarterPath',
|
||||||
'--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
|
'--ExtraGenSnapshotOptions=$extraGenSnapshotOptions',
|
||||||
'--DartDefines=$dartDefines',
|
'--DartDefines=$dartDefines',
|
||||||
'--ExtraFrontEndOptions=$extraFrontEndOptions',
|
'--ExtraFrontEndOptions=$extraFrontEndOptions',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user