add --dart-defines option (#44083)
diff --git a/packages/flutter_tools/lib/src/aot.dart b/packages/flutter_tools/lib/src/aot.dart
index 71e1558..50be745 100644
--- a/packages/flutter_tools/lib/src/aot.dart
+++ b/packages/flutter_tools/lib/src/aot.dart
@@ -34,6 +34,7 @@
Iterable<DarwinArch> iosBuildArchs = defaultIOSArchs,
List<String> extraFrontEndOptions,
List<String> extraGenSnapshotOptions,
+ @required List<String> dartDefines,
}) async {
if (platform == null) {
throwToolExit('No AOT build platform specified');
@@ -78,6 +79,7 @@
trackWidgetCreation: false,
outputPath: outputPath,
extraFrontEndOptions: extraFrontEndOptions,
+ dartDefines: dartDefines,
);
if (kernelOut == null) {
throwToolExit('Compiler terminated unexpectedly.');
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart
index d0f1642..9a70a94 100644
--- a/packages/flutter_tools/lib/src/base/build.dart
+++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -285,6 +285,7 @@
@required String packagesPath,
@required String outputPath,
@required bool trackWidgetCreation,
+ @required List<String> dartDefines,
List<String> extraFrontEndOptions = const <String>[],
}) async {
final FlutterProject flutterProject = FlutterProject.current();
@@ -315,6 +316,7 @@
aot: true,
buildMode: buildMode,
trackWidgetCreation: trackWidgetCreation,
+ dartDefines: dartDefines,
));
// Write path to frontend_server, since things need to be re-generated when that changes.
diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
index 36537c3..3613dad 100644
--- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
@@ -46,6 +46,7 @@
@required FlutterProject flutterProject,
@required bool ipv6,
@required DebuggingOptions debuggingOptions,
+ @required List<String> dartDefines,
}) {
if (featureFlags.isWebIncrementalCompilerEnabled) {
return _ExperimentalResidentWebRunner(
@@ -55,6 +56,7 @@
debuggingOptions: debuggingOptions,
ipv6: ipv6,
stayResident: stayResident,
+ dartDefines: dartDefines,
);
}
return _DwdsResidentWebRunner(
@@ -64,6 +66,7 @@
debuggingOptions: debuggingOptions,
ipv6: ipv6,
stayResident: stayResident,
+ dartDefines: dartDefines,
);
}
}
@@ -77,6 +80,7 @@
@required bool ipv6,
@required DebuggingOptions debuggingOptions,
bool stayResident = true,
+ @required this.dartDefines,
}) : super(
<FlutterDevice>[],
target: target ?? fs.path.join('lib', 'main.dart'),
@@ -87,6 +91,7 @@
final FlutterDevice device;
final FlutterProject flutterProject;
+ final List<String> dartDefines;
DateTime firstBuildTime;
// Only the debug builds of the web support the service protocol.
@@ -348,6 +353,7 @@
@required bool ipv6,
@required DebuggingOptions debuggingOptions,
bool stayResident = true,
+ @required List<String> dartDefines,
}) : super(
device,
flutterProject: flutterProject,
@@ -355,6 +361,7 @@
debuggingOptions: debuggingOptions,
ipv6: ipv6,
stayResident: stayResident,
+ dartDefines: dartDefines,
);
@override
@@ -540,6 +547,7 @@
@required bool ipv6,
@required DebuggingOptions debuggingOptions,
bool stayResident = true,
+ @required List<String> dartDefines,
}) : super(
device,
flutterProject: flutterProject,
@@ -547,6 +555,7 @@
debuggingOptions: debuggingOptions,
ipv6: ipv6,
stayResident: stayResident,
+ dartDefines: dartDefines,
);
@override
@@ -594,6 +603,7 @@
hostname: debuggingOptions.hostname,
port: debuggingOptions.port,
skipDwds: device is WebServerDevice || !debuggingOptions.buildInfo.isDebug,
+ dartDefines: dartDefines,
);
// When connecting to a browser, update the message with a seemsSlow notification
// to handle the case where we fail to connect.
diff --git a/packages/flutter_tools/lib/src/build_runner/web_fs.dart b/packages/flutter_tools/lib/src/build_runner/web_fs.dart
index da25e08..688d2d3 100644
--- a/packages/flutter_tools/lib/src/build_runner/web_fs.dart
+++ b/packages/flutter_tools/lib/src/build_runner/web_fs.dart
@@ -81,6 +81,7 @@
@required bool initializePlatform,
@required String hostname,
@required String port,
+ @required List<String> dartDefines,
});
/// The dev filesystem responsible for building and serving web applications.
@@ -97,6 +98,7 @@
this._target,
this._buildInfo,
this._initializePlatform,
+ this._dartDefines,
);
/// The server uri.
@@ -111,6 +113,7 @@
final String _target;
final BuildInfo _buildInfo;
final bool _initializePlatform;
+ final List<String> _dartDefines;
StreamSubscription<void> _connectedApps;
static const String _kHostName = 'localhost';
@@ -146,7 +149,7 @@
/// Recompile the web application and return whether this was successful.
Future<bool> recompile() async {
if (!_useBuildRunner) {
- await buildWeb(_flutterProject, _target, _buildInfo, _initializePlatform);
+ await buildWeb(_flutterProject, _target, _buildInfo, _initializePlatform, _dartDefines);
return true;
}
_client.startBuild();
@@ -173,6 +176,7 @@
@required bool initializePlatform,
@required String hostname,
@required String port,
+ @required List<String> dartDefines,
}) async {
// workaround for https://github.com/flutter/flutter/issues/38290
if (!flutterProject.dartTool.existsSync()) {
@@ -302,7 +306,7 @@
handler = pipeline.addHandler(proxyHandler('http://localhost:$daemonAssetPort/web/'));
}
} else {
- await buildWeb(flutterProject, target, buildInfo, initializePlatform);
+ await buildWeb(flutterProject, target, buildInfo, initializePlatform, dartDefines);
firstBuildCompleter.complete(true);
}
@@ -325,6 +329,7 @@
target,
buildInfo,
initializePlatform,
+ dartDefines,
);
if (!await firstBuildCompleter.future) {
throw const BuildException();
diff --git a/packages/flutter_tools/lib/src/build_system/targets/dart.dart b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
index 8f00e82..392cc34 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/dart.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/dart.dart
@@ -7,6 +7,7 @@
import '../../base/file_system.dart';
import '../../build_info.dart';
import '../../compile.dart';
+import '../../convert.dart';
import '../../globals.dart';
import '../../project.dart';
import '../build_system.dart';
@@ -50,6 +51,9 @@
/// If provided, must be used along with [kFileSystemScheme].
const String kFileSystemRoots = 'FileSystemRoots';
+/// Defines specified via the `--dart-define` command-line option.
+const String kDartDefines = 'DartDefines';
+
/// The define to control what iOS architectures are built for.
///
/// This is expected to be a comma-separated list of architectures. If not
@@ -208,6 +212,7 @@
extraFrontEndOptions: extraFrontEndOptions,
fileSystemRoots: fileSystemRoots,
fileSystemScheme: fileSystemScheme,
+ dartDefines: parseDartDefines(environment),
);
if (output == null || output.errorCount != 0) {
throw Exception('Errors during snapshot creation: $output');
@@ -357,3 +362,22 @@
AotElfRelease(),
];
}
+
+/// Dart defines are encoded inside [Environment] as a JSON array.
+List<String> parseDartDefines(Environment environment) {
+ if (!environment.defines.containsKey(kDartDefines)) {
+ return const <String>[];
+ }
+
+ final String dartDefinesJson = environment.defines[kDartDefines];
+ try {
+ final List<Object> parsedDefines = jsonDecode(dartDefinesJson);
+ return parsedDefines.cast<String>();
+ } on FormatException catch (_) {
+ throw Exception(
+ 'The value of -D$kDartDefines is not formatted correctly.\n'
+ 'The value must be a JSON-encoded list of strings but was:\n'
+ '$dartDefinesJson'
+ );
+ }
+}
diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart
index 4c4946f..11b01ec 100644
--- a/packages/flutter_tools/lib/src/build_system/targets/web.dart
+++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart
@@ -130,6 +130,7 @@
? PackageMap.globalGeneratedPackagesPath
: PackageMap.globalPackagesPath;
final File outputFile = environment.buildDir.childFile('main.dart.js');
+
final ProcessResult result = await processManager.run(<String>[
artifacts.getArtifactPath(Artifact.engineDartBinary),
artifacts.getArtifactPath(Artifact.dart2jsSnapshot),
@@ -147,6 +148,8 @@
'-Ddart.vm.profile=true'
else
'-Ddart.vm.product=true',
+ for (String dartDefine in parseDartDefines(environment))
+ '-D$dartDefine',
environment.buildDir.childFile('main.dart').path,
]);
if (result.exitCode != 0) {
diff --git a/packages/flutter_tools/lib/src/codegen.dart b/packages/flutter_tools/lib/src/codegen.dart
index bd66869..a85bd86 100644
--- a/packages/flutter_tools/lib/src/codegen.dart
+++ b/packages/flutter_tools/lib/src/codegen.dart
@@ -109,6 +109,7 @@
TargetModel targetModel = TargetModel.flutter,
String initializeFromDill,
String platformDill,
+ List<String> dartDefines,
}) async {
if (fileSystemRoots != null || fileSystemScheme != null || depFilePath != null || targetModel != null || sdkRoot != null || packagesPath != null) {
printTrace('fileSystemRoots, fileSystemScheme, depFilePath, targetModel,'
@@ -147,6 +148,7 @@
depFilePath: depFilePath,
targetModel: targetModel,
initializeFromDill: initializeFromDill,
+ dartDefines: dartDefines,
);
}
}
@@ -172,6 +174,7 @@
String initializeFromDill,
bool runCold = false,
TargetPlatform targetPlatform,
+ @required List<String> dartDefines,
}) async {
codeGenerator.updatePackages(flutterProject);
final ResidentCompiler residentCompiler = ResidentCompiler(
@@ -191,6 +194,7 @@
targetModel: TargetModel.flutter,
unsafePackageSerialization: unsafePackageSerialization,
initializeFromDill: initializeFromDill,
+ dartDefines: dartDefines,
);
if (runCold) {
return residentCompiler;
diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart
index d6aec17..b9f35b5 100644
--- a/packages/flutter_tools/lib/src/commands/attach.dart
+++ b/packages/flutter_tools/lib/src/commands/attach.dart
@@ -61,6 +61,7 @@
usesIpv6Flag();
usesFilesystemOptions(hide: !verboseHelp);
usesFuchsiaOptions(hide: !verboseHelp);
+ usesDartDefines();
argParser
..addOption(
'debug-port',
@@ -274,6 +275,7 @@
target: argResults['target'],
targetModel: TargetModel(argResults['target-model']),
buildMode: getBuildMode(),
+ dartDefines: dartDefines,
);
flutterDevice.observatoryUris = <Uri>[ observatoryUri ];
final List<FlutterDevice> flutterDevices = <FlutterDevice>[flutterDevice];
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart
index b69b1bc..92a8f86 100644
--- a/packages/flutter_tools/lib/src/commands/build_aot.dart
+++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -18,6 +18,7 @@
usesTargetOption();
addBuildModeFlags();
usesPubOption();
+ usesDartDefines();
argParser
..addOption('output-dir', defaultsTo: getAotBuildDirectory())
..addOption('target-platform',
@@ -86,6 +87,7 @@
iosBuildArchs: argResults['ios-arch'].map<DarwinArch>(getIOSArchForName),
extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions],
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
+ dartDefines: dartDefines,
);
return null;
}
diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
index 12d9f36..07e8199 100644
--- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
+++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart
@@ -35,6 +35,7 @@
usesTargetOption();
usesFlavorOption();
usesPubOption();
+ usesDartDefines();
argParser
..addFlag('debug',
negatable: true,
@@ -294,6 +295,7 @@
quiet: true,
reportTimings: false,
iosBuildArchs: <DarwinArch>[DarwinArch.armv7, DarwinArch.arm64],
+ dartDefines: dartDefines,
);
const String appFrameworkName = 'App.framework';
diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart
index e731150..60b5863 100644
--- a/packages/flutter_tools/lib/src/commands/build_web.dart
+++ b/packages/flutter_tools/lib/src/commands/build_web.dart
@@ -18,6 +18,7 @@
usesTargetOption();
usesPubOption();
addBuildModeFlags(excludeDebug: true);
+ usesDartDefines();
argParser.addFlag('web-initialize-platform',
defaultsTo: true,
negatable: true,
@@ -53,7 +54,13 @@
if (buildInfo.isDebug) {
throwToolExit('debug builds cannot be built directly for the web. Try using "flutter run"');
}
- await buildWeb(flutterProject, target, buildInfo, argResults['web-initialize-platform']);
+ await buildWeb(
+ flutterProject,
+ target,
+ buildInfo,
+ argResults['web-initialize-platform'],
+ dartDefines,
+ );
return null;
}
}
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index d37bfe0..fed2bd0 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -424,6 +424,7 @@
viewFilter: isolateFilter,
target: target,
buildMode: options.buildInfo.mode,
+ dartDefines: command?.dartDefines,
);
ResidentRunner runner;
@@ -436,6 +437,7 @@
debuggingOptions: options,
ipv6: ipv6,
stayResident: true,
+ dartDefines: command?.dartDefines,
);
} else if (enableHotReload) {
runner = HotRunner(
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index b410da2..6f10357 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -31,6 +31,7 @@
// Used by run and drive commands.
RunCommandBase({ bool verboseHelp = false }) {
addBuildModeFlags(defaultToRelease: false, verboseHelp: verboseHelp);
+ usesDartDefines();
usesFlavorOption();
argParser
..addFlag('trace-startup',
@@ -426,6 +427,7 @@
experimentalFlags: expFlags,
target: argResults['target'],
buildMode: getBuildMode(),
+ dartDefines: dartDefines,
),
];
// Only support "web mode" with a single web device due to resident runner
@@ -459,6 +461,7 @@
ipv6: ipv6,
debuggingOptions: _createDebuggingOptions(),
stayResident: stayResident,
+ dartDefines: dartDefines,
);
} else {
runner = ColdRunner(
diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart
index 4dfeba8..d31d1d0 100644
--- a/packages/flutter_tools/lib/src/compile.dart
+++ b/packages/flutter_tools/lib/src/compile.dart
@@ -288,6 +288,7 @@
String fileSystemScheme,
String initializeFromDill,
String platformDill,
+ @required List<String> dartDefines,
}) async {
final String frontendServer = artifacts.getArtifactPath(
Artifact.frontendServerSnapshotForEngineDartSdk
@@ -316,6 +317,8 @@
sdkRoot,
'--target=$targetModel',
'-Ddart.developer.causal_async_stacks=$causalAsyncStacks',
+ for (Object dartDefine in dartDefines)
+ '-D$dartDefine',
..._buildModeOptions(buildMode),
if (trackWidgetCreation) '--track-widget-creation',
if (!linkPlatformKernelIn) '--no-link-platform',
@@ -464,6 +467,7 @@
bool unsafePackageSerialization,
List<String> experimentalFlags,
String platformDill,
+ List<String> dartDefines,
}) = DefaultResidentCompiler;
@@ -524,8 +528,10 @@
this.unsafePackageSerialization,
this.experimentalFlags,
this.platformDill,
+ List<String> dartDefines,
}) : assert(sdkRoot != null),
_stdoutHandler = StdoutHandler(consumer: compilerMessageConsumer),
+ dartDefines = dartDefines ?? const <String>[],
// This is a URI, not a file path, so the forward slash is correct even on Windows.
sdkRoot = sdkRoot.endsWith('/') ? sdkRoot : '$sdkRoot/';
@@ -539,6 +545,7 @@
final String initializeFromDill;
final bool unsafePackageSerialization;
final List<String> experimentalFlags;
+ final List<String> dartDefines;
/// The path to the root of the Dart SDK used to compile.
///
@@ -594,9 +601,9 @@
if (_server == null) {
return _compile(
- _mapFilename(request.mainPath, packageUriMapper),
- request.outputPath,
- _mapFilename(request.packagesFilePath ?? packagesPath, /* packageUriMapper= */ null),
+ _mapFilename(request.mainPath, packageUriMapper),
+ request.outputPath,
+ _mapFilename(request.packagesFilePath ?? packagesPath, /* packageUriMapper= */ null),
);
}
@@ -648,6 +655,8 @@
'--incremental',
'--target=$targetModel',
'-Ddart.developer.causal_async_stacks=$causalAsyncStacks',
+ for (Object dartDefine in dartDefines)
+ '-D$dartDefine',
if (outputPath != null) ...<String>[
'--output-dill',
outputPath,
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 0e465b7..9b3ebb6 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -41,6 +41,7 @@
List<String> experimentalFlags,
ResidentCompiler generator,
@required BuildMode buildMode,
+ List<String> dartDefines,
}) : assert(trackWidgetCreation != null),
generator = generator ?? ResidentCompiler(
artifacts.getArtifactPath(
@@ -54,6 +55,7 @@
fileSystemScheme: fileSystemScheme,
targetModel: targetModel,
experimentalFlags: experimentalFlags,
+ dartDefines: dartDefines,
);
/// Create a [FlutterDevice] with optional code generation enabled.
@@ -69,6 +71,7 @@
TargetModel targetModel = TargetModel.flutter,
List<String> experimentalFlags,
ResidentCompiler generator,
+ List<String> dartDefines,
}) async {
ResidentCompiler generator;
final TargetPlatform targetPlatform = await device.targetPlatform;
@@ -86,12 +89,14 @@
targetModel: TargetModel.dartdevc,
experimentalFlags: experimentalFlags,
platformDill: artifacts.getArtifactPath(Artifact.webPlatformKernelDill, mode: buildMode),
+ dartDefines: dartDefines,
);
} else if (flutterProject.hasBuilders) {
generator = await CodeGeneratingResidentCompiler.create(
targetPlatform: targetPlatform,
buildMode: buildMode,
flutterProject: flutterProject,
+ dartDefines: dartDefines,
);
} else {
generator = ResidentCompiler(
@@ -106,6 +111,7 @@
fileSystemScheme: fileSystemScheme,
targetModel: targetModel,
experimentalFlags: experimentalFlags,
+ dartDefines: dartDefines,
);
}
return FlutterDevice(
@@ -119,6 +125,7 @@
targetPlatform: targetPlatform,
generator: generator,
buildMode: buildMode,
+ dartDefines: dartDefines,
);
}
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index cdbad2d..81ec070 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -269,6 +269,20 @@
valueHelp: 'x.y.z');
}
+ void usesDartDefines() {
+ argParser.addMultiOption(
+ 'dart-define',
+ help: 'Passed to the Dart compiler building this application as a -D flag.\n'
+ 'Values supported by this option are compiler implementation specific.\n'
+ 'Multiple defines can be passed by repeating --dart-define multiple times.',
+ valueHelp: 'FOO=bar',
+ hide: true,
+ );
+ }
+
+ /// The values passed via the `--dart-define` option.
+ List<String> get dartDefines => argResults['dart-define'];
+
void usesIsolateFilterOption({ @required bool hide }) {
argParser.addOption('isolate-filter',
defaultsTo: null,
diff --git a/packages/flutter_tools/lib/src/test/test_compiler.dart b/packages/flutter_tools/lib/src/test/test_compiler.dart
index 8bb3a65..dd37a5f 100644
--- a/packages/flutter_tools/lib/src/test/test_compiler.dart
+++ b/packages/flutter_tools/lib/src/test/test_compiler.dart
@@ -103,6 +103,7 @@
// We already ran codegen once at the start, we only need to
// configure builders.
runCold: true,
+ dartDefines: const <String>[],
);
}
return ResidentCompiler(
@@ -113,6 +114,7 @@
compilerMessageConsumer: _reportCompilerMessage,
initializeFromDill: testFilePath,
unsafePackageSerialization: false,
+ dartDefines: const <String>[],
);
}
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
index 51413ad..f0d9524 100644
--- a/packages/flutter_tools/lib/src/web/compile.dart
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -12,6 +12,7 @@
import '../build_system/build_system.dart';
import '../build_system/targets/dart.dart';
import '../build_system/targets/web.dart';
+import '../convert.dart';
import '../globals.dart';
import '../platform_plugins.dart';
import '../plugins.dart';
@@ -21,7 +22,13 @@
/// The [WebCompilationProxy] instance.
WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();
-Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo, bool initializePlatform) async {
+Future<void> buildWeb(
+ FlutterProject flutterProject,
+ String target,
+ BuildInfo buildInfo,
+ bool initializePlatform,
+ List<String> dartDefines,
+) async {
if (!flutterProject.web.existsSync()) {
throwToolExit('Missing index.html.');
}
@@ -42,6 +49,7 @@
kTargetFile: target,
kInitializePlatform: initializePlatform.toString(),
kHasWebPlugins: hasWebPlugins.toString(),
+ kDartDefines: jsonEncode(dartDefines),
},
));
if (!result.success) {
diff --git a/packages/flutter_tools/lib/src/web/web_runner.dart b/packages/flutter_tools/lib/src/web/web_runner.dart
index 09402a3..ad41c5e 100644
--- a/packages/flutter_tools/lib/src/web/web_runner.dart
+++ b/packages/flutter_tools/lib/src/web/web_runner.dart
@@ -23,5 +23,6 @@
@required FlutterProject flutterProject,
@required bool ipv6,
@required DebuggingOptions debuggingOptions,
+ @required List<String> dartDefines,
});
}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
index b11f78e..3709ccf 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_web_test.dart
@@ -57,6 +57,7 @@
fs.path.join('lib', 'main.dart'),
BuildInfo.debug,
false,
+ const <String>[],
), throwsA(isInstanceOf<ToolExit>()));
}));
@@ -69,6 +70,7 @@
ipv6: false,
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
stayResident: true,
+ dartDefines: const <String>[],
);
expect(await runner.run(), 1);
}));
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index b13a303..cb4f01e 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -2,20 +2,30 @@
// 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:args/command_runner.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/common.dart';
+import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/run.dart';
import 'package:flutter_tools/src/device.dart';
+import 'package:flutter_tools/src/features.dart';
+import 'package:flutter_tools/src/project.dart';
+import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/version.dart';
+import 'package:flutter_tools/src/web/web_runner.dart';
import 'package:mockito/mockito.dart';
import '../../src/common.dart';
import '../../src/context.dart';
import '../../src/mocks.dart';
+import '../../src/testbed.dart';
void main() {
group('run', () {
@@ -178,6 +188,63 @@
}, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager,
});
+
+ group('--dart-define option', () {
+ MemoryFileSystem fs;
+ MockProcessManager mockProcessManager;
+ MockWebRunnerFactory mockWebRunnerFactory;
+
+ setUpAll(() {
+ when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
+ return Stream<Device>.fromIterable(<Device>[
+ FakeDevice().._targetPlatform = TargetPlatform.web_javascript,
+ ]);
+ });
+ });
+
+ RunCommand command;
+ List<String> args;
+ setUp(() {
+ command = TestRunCommand();
+ args = <String> [
+ 'run',
+ '--dart-define=FOO=bar',
+ '--no-hot',
+ '--no-pub',
+ ];
+ applyMocksToCommand(command);
+ fs = MemoryFileSystem();
+ mockProcessManager = MockProcessManager();
+ mockWebRunnerFactory = MockWebRunnerFactory();
+ });
+
+ testUsingContext('populates the environment', () async {
+ final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_run_test.');
+ fs.currentDirectory = tempDir;
+
+ final Directory libDir = tempDir.childDirectory('lib');
+ libDir.createSync();
+ final File mainFile = libDir.childFile('main.dart');
+ mainFile.writeAsStringSync('void main() {}');
+
+ final Directory webDir = tempDir.childDirectory('web');
+ webDir.createSync();
+ final File indexFile = libDir.childFile('index.html');
+ indexFile.writeAsStringSync('<h1>Hello</h1>');
+
+ await createTestCommandRunner(command).run(args);
+ expect(mockWebRunnerFactory._dartDefines, <String>['FOO=bar']);
+ }, overrides: <Type, Generator>{
+ FeatureFlags: () => TestFeatureFlags(
+ isWebEnabled: true,
+ ),
+ FileSystem: () => fs,
+ ProcessManager: () => mockProcessManager,
+ DeviceManager: () => mockDeviceManager,
+ FlutterVersion: () => mockStableFlutterVersion,
+ WebRunnerFactory: () => mockWebRunnerFactory,
+ });
+ });
});
}
@@ -207,7 +274,7 @@
class FakeDevice extends Fake implements Device {
static const int kSuccess = 1;
static const int kFailure = -1;
- final TargetPlatform _targetPlatform = TargetPlatform.ios;
+ TargetPlatform _targetPlatform = TargetPlatform.ios;
void _throwToolExit(int code) => throwToolExit(null, exitCode: code);
@@ -263,3 +330,32 @@
return null;
}
}
+
+class MockWebRunnerFactory extends Mock implements WebRunnerFactory {
+ List<String> _dartDefines;
+
+ @override
+ ResidentRunner createWebRunner(
+ FlutterDevice device, {
+ String target,
+ bool stayResident,
+ FlutterProject flutterProject,
+ bool ipv6,
+ DebuggingOptions debuggingOptions,
+ List<String> dartDefines,
+ }) {
+ _dartDefines = dartDefines;
+ return MockWebRunner();
+ }
+}
+
+class MockWebRunner extends Mock implements ResidentRunner {
+ @override
+ Future<int> run({
+ Completer<DebugConnectionInfo> connectionInfoCompleter,
+ Completer<void> appStartedCompleter,
+ String route,
+ }) async {
+ return 0;
+ }
+}
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
index 5e136fb..600e490 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_test.dart
@@ -136,6 +136,7 @@
fileSystemScheme: anyNamed('fileSystemScheme'),
platformDill: anyNamed('platformDill'),
initializeFromDill: anyNamed('initializeFromDill'),
+ dartDefines: anyNamed('dartDefines'),
)).thenAnswer((Invocation invocation) async {
return null;
});
@@ -163,6 +164,7 @@
fileSystemRoots: anyNamed('fileSystemRoots'),
fileSystemScheme: anyNamed('fileSystemScheme'),
linkPlatformKernelIn: anyNamed('linkPlatformKernelIn'),
+ dartDefines: anyNamed('dartDefines'),
)).thenAnswer((Invocation _) async {
return const CompilerOutput('example', 0, <Uri>[]);
});
@@ -191,6 +193,7 @@
fileSystemRoots: anyNamed('fileSystemRoots'),
fileSystemScheme: anyNamed('fileSystemScheme'),
linkPlatformKernelIn: false,
+ dartDefines: anyNamed('dartDefines'),
)).thenAnswer((Invocation _) async {
return const CompilerOutput('example', 0, <Uri>[]);
});
@@ -221,6 +224,7 @@
fileSystemRoots: anyNamed('fileSystemRoots'),
fileSystemScheme: anyNamed('fileSystemScheme'),
linkPlatformKernelIn: false,
+ dartDefines: anyNamed('dartDefines'),
)).thenAnswer((Invocation _) async {
return const CompilerOutput('example', 0, <Uri>[]);
});
@@ -431,6 +435,7 @@
String fileSystemScheme,
String platformDill,
String initializeFromDill,
+ List<String> dartDefines,
}) async {
fs.file(outputFilePath).createSync(recursive: true);
return CompilerOutput(outputFilePath, 0, null);
diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
index 816ff55..7c4b2b4 100644
--- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
+++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
@@ -236,6 +236,80 @@
}, overrides: <Type, Generator>{
ProcessManager: () => MockProcessManager(),
}));
+
+ test('Dart2JSTarget calls dart2js with Dart defines in release mode', () => testbed.run(() async {
+ environment.defines[kBuildMode] = 'release';
+ environment.defines[kDartDefines] = '["FOO=bar","BAZ=qux"]';
+ when(processManager.run(any)).thenAnswer((Invocation invocation) async {
+ return FakeProcessResult(exitCode: 0);
+ });
+ await const Dart2JSTarget().build(environment);
+
+ final List<String> expected = <String>[
+ fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
+ fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
+ '--libraries-spec=' + fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
+ '-O4',
+ '-o',
+ environment.buildDir.childFile('main.dart.js').absolute.path,
+ '--packages=.packages',
+ '-Ddart.vm.product=true',
+ '-DFOO=bar',
+ '-DBAZ=qux',
+ environment.buildDir.childFile('main.dart').absolute.path,
+ ];
+ verify(processManager.run(expected)).called(1);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => MockProcessManager(),
+ }));
+
+ test('Dart2JSTarget calls dart2js with Dart defines in profile mode', () => testbed.run(() async {
+ environment.defines[kBuildMode] = 'profile';
+ environment.defines[kDartDefines] = '["FOO=bar","BAZ=qux"]';
+ when(processManager.run(any)).thenAnswer((Invocation invocation) async {
+ return FakeProcessResult(exitCode: 0);
+ });
+ await const Dart2JSTarget().build(environment);
+
+ final List<String> expected = <String>[
+ fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'dart'),
+ fs.path.join('bin', 'cache', 'dart-sdk', 'bin', 'snapshots', 'dart2js.dart.snapshot'),
+ '--libraries-spec=' + fs.path.join('bin', 'cache', 'flutter_web_sdk', 'libraries.json'),
+ '-O4',
+ '--no-minify',
+ '-o',
+ environment.buildDir.childFile('main.dart.js').absolute.path,
+ '--packages=.packages',
+ '-Ddart.vm.profile=true',
+ '-DFOO=bar',
+ '-DBAZ=qux',
+ environment.buildDir.childFile('main.dart').absolute.path,
+ ];
+ verify(processManager.run(expected)).called(1);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => MockProcessManager(),
+ }));
+
+ test('Dart2JSTarget throws developer-friendly exception on misformatted DartDefines', () => testbed.run(() async {
+ environment.defines[kBuildMode] = 'profile';
+ environment.defines[kDartDefines] = '[misformatted json';
+ try {
+ await const Dart2JSTarget().build(environment);
+ fail('Call to build() must not have succeeded.');
+ } on Exception catch(exception) {
+ expect(
+ '$exception',
+ 'Exception: The value of -D$kDartDefines is not formatted correctly.\n'
+ 'The value must be a JSON-encoded list of strings but was:\n'
+ '[misformatted json',
+ );
+ }
+
+ // Should not attempt to run any processes.
+ verifyNever(processManager.run(any));
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => MockProcessManager(),
+ }));
}
class MockProcessManager extends Mock implements ProcessManager {}
diff --git a/packages/flutter_tools/test/general.shard/compile_batch_test.dart b/packages/flutter_tools/test/general.shard/compile_batch_test.dart
index fabc29f..18ecd0b 100644
--- a/packages/flutter_tools/test/general.shard/compile_batch_test.dart
+++ b/packages/flutter_tools/test/general.shard/compile_batch_test.dart
@@ -25,6 +25,8 @@
MockStdIn mockFrontendServerStdIn;
MockStream mockFrontendServerStdErr;
+ List<String> latestCommand;
+
setUp(() {
mockProcessManager = MockProcessManager();
mockFrontendServer = MockProcess();
@@ -38,7 +40,10 @@
when(mockFrontendServer.stdin).thenReturn(mockFrontendServerStdIn);
when(mockProcessManager.canRun(any)).thenReturn(true);
when(mockProcessManager.start(any)).thenAnswer(
- (Invocation invocation) => Future<Process>.value(mockFrontendServer));
+ (Invocation invocation) {
+ latestCommand = invocation.positionalArguments.first;
+ return Future<Process>.value(mockFrontendServer);
+ });
when(mockFrontendServer.exitCode).thenAnswer((_) async => 0);
});
@@ -55,6 +60,7 @@
mainPath: '/path/to/main.dart',
buildMode: BuildMode.debug,
trackWidgetCreation: false,
+ dartDefines: const <String>[],
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
@@ -79,6 +85,7 @@
buildMode: BuildMode.release,
trackWidgetCreation: false,
aot: true,
+ dartDefines: const <String>[],
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
@@ -103,6 +110,7 @@
mainPath: '/path/to/main.dart',
buildMode: BuildMode.debug,
trackWidgetCreation: false,
+ dartDefines: const <String>[],
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
@@ -130,6 +138,7 @@
mainPath: '/path/to/main.dart',
buildMode: BuildMode.debug,
trackWidgetCreation: false,
+ dartDefines: const <String>[],
);
expect(mockFrontendServerStdIn.getAndClear(), isEmpty);
expect(bufferLogger.errorText, equals('\nCompiler message:\nline1\nline2\n'));
@@ -139,6 +148,27 @@
OutputPreferences: () => OutputPreferences(showColor: false),
Platform: kNoColorTerminalPlatform,
});
+
+ testUsingContext('passes dartDefines to the kernel compiler', () async {
+ // Use unsuccessful result because it's easier to setup in test. We only care about arguments passed to the compiler.
+ when(mockFrontendServer.exitCode).thenAnswer((_) async => 255);
+ when(mockFrontendServer.stdout).thenAnswer((Invocation invocation) => Stream<List<int>>.fromFuture(
+ Future<List<int>>.value(<int>[])
+ ));
+ final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(null);
+ await kernelCompiler.compile(sdkRoot: '/path/to/sdkroot',
+ mainPath: '/path/to/main.dart',
+ buildMode: BuildMode.debug,
+ trackWidgetCreation: false,
+ dartDefines: const <String>['FOO=bar', 'BAZ=qux'],
+ );
+
+ expect(latestCommand, containsAllInOrder(<String>['-DFOO=bar', '-DBAZ=qux']));
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ OutputPreferences: () => OutputPreferences(showColor: false),
+ Platform: kNoColorTerminalPlatform,
+ });
}
class MockProcess extends Mock implements Process {}
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
index 603fee8..a793459 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
@@ -41,6 +41,7 @@
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true,
stayResident: true,
+ dartDefines: const <String>[],
);
},
overrides: <Type, Generator>{
@@ -52,6 +53,7 @@
@required bool initializePlatform,
@required String hostname,
@required String port,
+ @required List<String> dartDefines,
}) async {
return mockWebFs;
},
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
index 05753d3..c30b020 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
@@ -73,6 +73,7 @@
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: true,
+ dartDefines: const <String>[],
);
},
overrides: <Type, Generator>{
@@ -84,6 +85,7 @@
@required bool initializePlatform,
@required String hostname,
@required String port,
+ @required List<String> dartDefines,
}) async {
return mockWebFs;
},
@@ -133,6 +135,7 @@
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: true,
+ dartDefines: const <String>[],
);
expect(profileResidentWebRunner.debuggingEnabled, false);
@@ -150,6 +153,7 @@
debuggingOptions: DebuggingOptions.enabled(BuildInfo.profile),
ipv6: true,
stayResident: true,
+ dartDefines: const <String>[],
);
expect(profileResidentWebRunner.supportsServiceProtocol, false);
@@ -204,6 +208,7 @@
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug),
ipv6: true,
stayResident: false,
+ dartDefines: const <String>[],
);
expect(await residentWebRunner.run(), 0);
@@ -240,6 +245,7 @@
debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, startPaused: true),
ipv6: true,
stayResident: true,
+ dartDefines: const <String>[],
);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
diff --git a/packages/flutter_tools/test/general.shard/web/web_fs_test.dart b/packages/flutter_tools/test/general.shard/web/web_fs_test.dart
index f44ce27..2482140 100644
--- a/packages/flutter_tools/test/general.shard/web/web_fs_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/web_fs_test.dart
@@ -126,6 +126,7 @@
initializePlatform: true,
hostname: null,
port: null,
+ dartDefines: const <String>[],
);
// Since the .packages file is missing in the memory filesystem, this should
// be called.
@@ -155,6 +156,7 @@
initializePlatform: false,
hostname: null,
port: null,
+ dartDefines: const <String>[],
);
// The build daemon is told to build once.
@@ -175,6 +177,7 @@
initializePlatform: false,
hostname: 'foo',
port: '1234',
+ dartDefines: const <String>[],
);
expect(webFs.uri, contains('foo:1234'));
@@ -207,6 +210,7 @@
initializePlatform: false,
hostname: 'foo',
port: '1234',
+ dartDefines: const <String>[],
), throwsA(isInstanceOf<Exception>()));
}));
}
diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart
index eabae35..9905c61 100644
--- a/packages/flutter_tools/test/integration.shard/test_driver.dart
+++ b/packages/flutter_tools/test/integration.shard/test_driver.dart
@@ -486,29 +486,50 @@
pidFile: pidFile,
);
- // Stash the PID so that we can terminate the VM more reliably than using
- // _process.kill() (`flutter` is a shell script so _process itself is a
- // shell, not the flutter tool's Dart process).
- final Map<String, dynamic> connected = await _waitFor(event: 'daemon.connected');
- _processPid = connected['params']['pid'];
+ final Completer<void> prematureExitGuard = Completer<void>();
- // Set this up now, but we don't wait it yet. We want to make sure we don't
- // miss it while waiting for debugPort below.
- final Future<Map<String, dynamic>> started = _waitFor(event: 'app.started', timeout: appStartTimeout);
-
- if (withDebugger) {
- final Map<String, dynamic> debugPort = await _waitFor(event: 'app.debugPort', timeout: appStartTimeout);
- final String wsUriString = debugPort['params']['wsUri'];
- _vmServiceWsUri = Uri.parse(wsUriString);
- await connectToVmService(pauseOnExceptions: pauseOnExceptions);
- if (!startPaused) {
- await resume(waitForNextPause: false);
+ // If the process exits before all of the `await`s below are done, then it
+ // exited prematurely. This causes the currently suspended `await` to
+ // deadlock until the test times out. Instead, this causes the test to fail
+ // fast.
+ unawaited(_process.exitCode.then((_) {
+ if (!prematureExitGuard.isCompleted) {
+ prematureExitGuard.completeError('Process existed prematurely: ${args.join(' ')}');
}
- }
+ }));
- // Now await the started event; if it had already happened the future will
- // have already completed.
- _currentRunningAppId = (await started)['params']['appId'];
+ unawaited(() async {
+ try {
+ // Stash the PID so that we can terminate the VM more reliably than using
+ // _process.kill() (`flutter` is a shell script so _process itself is a
+ // shell, not the flutter tool's Dart process).
+ final Map<String, dynamic> connected = await _waitFor(event: 'daemon.connected');
+ _processPid = connected['params']['pid'];
+
+ // Set this up now, but we don't wait it yet. We want to make sure we don't
+ // miss it while waiting for debugPort below.
+ final Future<Map<String, dynamic>> started = _waitFor(event: 'app.started', timeout: appStartTimeout);
+
+ if (withDebugger) {
+ final Map<String, dynamic> debugPort = await _waitFor(event: 'app.debugPort', timeout: appStartTimeout);
+ final String wsUriString = debugPort['params']['wsUri'];
+ _vmServiceWsUri = Uri.parse(wsUriString);
+ await connectToVmService(pauseOnExceptions: pauseOnExceptions);
+ if (!startPaused) {
+ await resume(waitForNextPause: false);
+ }
+ }
+
+ // Now await the started event; if it had already happened the future will
+ // have already completed.
+ _currentRunningAppId = (await started)['params']['appId'];
+ prematureExitGuard.complete();
+ } catch(error, stackTrace) {
+ prematureExitGuard.completeError(error, stackTrace);
+ }
+ }());
+
+ return prematureExitGuard.future;
}
Future<void> hotRestart({ bool pause = false }) => _restart(fullRestart: true, pause: pause);