Unverified Commit 545ec9ef authored by Martin Kustermann's avatar Martin Kustermann Committed by GitHub

Add support for NDK discovery and add --prefer-shared-library option (#12788)

* Add support for NDK discovery and add --prefer-shared-library option

We would like to be able to use native tools (e.g. simpleperf, gdb) with
precompiled flutter apps.  The native tools work much better with *.so
files instead of the custom formats the Dart VM uses by default.

The reason for using blobs / instruction snapshots is that we do not
want to force flutter users to install the Android NDK.

This CL adds a `--prefer-shared-library` flag to e.g. `flutter build
apk` which will use the NDK compiler (if available) to turn the
precompiled app assembly file to an `*.so` file.  If the NDK compiler is
not available it will default to the default behavior.

* Rebase, add test for NDK detection, augment flutter.gradle with @Input for flag

* Use InMemoryFileSystem for test

* Remove unused import

* Address some analyzer warnings
parent 14b5cb04
......@@ -235,6 +235,10 @@ class FlutterPlugin implements Plugin<Project> {
if (project.hasProperty('extra-gen-snapshot-options')) {
extraGenSnapshotOptionsValue = project.property('extra-gen-snapshot-options')
}
Boolean preferSharedLibraryValue = false
if (project.hasProperty('prefer-shared-library')) {
preferSharedLibraryValue = project.property('prefer-shared-library')
}
project.android.applicationVariants.all { variant ->
String flutterBuildMode = buildModeFor(variant.buildType)
......@@ -253,6 +257,7 @@ class FlutterPlugin implements Plugin<Project> {
localEngineSrcPath this.localEngineSrcPath
targetPath target
previewDart2 previewDart2Value
preferSharedLibrary preferSharedLibraryValue
sourceDir project.file(project.flutter.source)
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
}
......@@ -266,6 +271,7 @@ class FlutterPlugin implements Plugin<Project> {
localEngineSrcPath this.localEngineSrcPath
targetPath target
previewDart2 previewDart2Value
preferSharedLibrary preferSharedLibraryValue
sourceDir project.file(project.flutter.source)
intermediateDir project.file("${project.buildDir}/${AndroidProject.FD_INTERMEDIATES}/flutter/${variant.name}")
extraFrontEndOptions extraFrontEndOptionsValue
......@@ -298,6 +304,8 @@ abstract class BaseFlutterTask extends DefaultTask {
String targetPath
@Optional @Input
Boolean previewDart2
@Optional @Input
Boolean preferSharedLibrary
File sourceDir
File intermediateDir
@Optional @Input
......@@ -338,10 +346,13 @@ abstract class BaseFlutterTask extends DefaultTask {
args "--preview-dart-2"
}
if (extraFrontEndOptions != null) {
args "--extra-front-end-options", "${extraFrontEndOptions}"
args "--extra-front-end-options", "${extraFrontEndOptions}"
}
if (extraGenSnapshotOptions != null) {
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
args "--extra-gen-snapshot-options", "${extraGenSnapshotOptions}"
}
if (preferSharedLibrary) {
args "--prefer-shared-library"
}
args "--${buildMode}"
}
......@@ -392,15 +403,19 @@ class FlutterTask extends BaseFlutterTask {
CopySpec getAssets() {
return project.copySpec {
from "${intermediateDir}/app.flx"
from "${intermediateDir}/snapshot_blob.bin"
from "${intermediateDir}/app.flx"
from "${intermediateDir}/snapshot_blob.bin"
if (buildMode != 'debug') {
if (preferSharedLibrary) {
from "${intermediateDir}/app.so"
} else {
from "${intermediateDir}/vm_snapshot_data"
from "${intermediateDir}/vm_snapshot_instr"
from "${intermediateDir}/isolate_snapshot_data"
from "${intermediateDir}/isolate_snapshot_instr"
}
}
}
}
}
FileCollection readDependencies(File dependenciesFile) {
......
......@@ -57,63 +57,131 @@ String getAdbPath([AndroidSdk existingSdk]) {
}
class AndroidSdk {
AndroidSdk(this.directory) {
AndroidSdk(this.directory, [this.ndkDirectory, this.ndkCompiler,
this.ndkCompilerArgs]) {
_init();
}
/// The path to the Android SDK.
final String directory;
/// The path to the NDK (can be `null`).
final String ndkDirectory;
/// The path to the NDK compiler (can be `null`).
final String ndkCompiler;
/// The mandatory arguments to the NDK compiler (can be `null`).
final List<String> ndkCompilerArgs;
List<AndroidSdkVersion> _sdkVersions;
AndroidSdkVersion _latestVersion;
static AndroidSdk locateAndroidSdk() {
String androidHomeDir;
if (config.containsKey('android-sdk')) {
androidHomeDir = config.getValue('android-sdk');
} else if (platform.environment.containsKey(kAndroidHome)) {
androidHomeDir = platform.environment[kAndroidHome];
} else if (platform.isLinux) {
if (homeDirPath != null)
androidHomeDir = fs.path.join(homeDirPath, 'Android', 'Sdk');
} else if (platform.isMacOS) {
if (homeDirPath != null)
androidHomeDir = fs.path.join(homeDirPath, 'Library', 'Android', 'sdk');
} else if (platform.isWindows) {
if (homeDirPath != null)
androidHomeDir = fs.path.join(homeDirPath, 'AppData', 'Local', 'Android', 'sdk');
String findAndroidHomeDir() {
String androidHomeDir;
if (config.containsKey('android-sdk')) {
androidHomeDir = config.getValue('android-sdk');
} else if (platform.environment.containsKey(kAndroidHome)) {
androidHomeDir = platform.environment[kAndroidHome];
} else if (platform.isLinux) {
if (homeDirPath != null)
androidHomeDir = fs.path.join(homeDirPath, 'Android', 'Sdk');
} else if (platform.isMacOS) {
if (homeDirPath != null)
androidHomeDir = fs.path.join(homeDirPath, 'Library', 'Android', 'sdk');
} else if (platform.isWindows) {
if (homeDirPath != null)
androidHomeDir = fs.path.join(homeDirPath, 'AppData', 'Local', 'Android', 'sdk');
}
if (androidHomeDir != null) {
if (validSdkDirectory(androidHomeDir))
return androidHomeDir;
if (validSdkDirectory(fs.path.join(androidHomeDir, 'sdk')))
return fs.path.join(androidHomeDir, 'sdk');
}
// in build-tools/$version/aapt
final List<File> aaptBins = os.whichAll('aapt');
for (File aaptBin in aaptBins) {
// Make sure we're using the aapt from the SDK.
aaptBin = fs.file(aaptBin.resolveSymbolicLinksSync());
final String dir = aaptBin.parent.parent.parent.path;
if (validSdkDirectory(dir))
return dir;
}
// in platform-tools/adb
final List<File> adbBins = os.whichAll('adb');
for (File adbBin in adbBins) {
// Make sure we're using the adb from the SDK.
adbBin = fs.file(adbBin.resolveSymbolicLinksSync());
final String dir = adbBin.parent.parent.path;
if (validSdkDirectory(dir))
return dir;
}
return null;
}
if (androidHomeDir != null) {
if (validSdkDirectory(androidHomeDir))
return new AndroidSdk(androidHomeDir);
if (validSdkDirectory(fs.path.join(androidHomeDir, 'sdk')))
return new AndroidSdk(fs.path.join(androidHomeDir, 'sdk'));
String findNdk(String androidHomeDir) {
final String ndkDirectory = fs.path.join(androidHomeDir, 'ndk-bundle');
if (fs.isDirectorySync(ndkDirectory)) {
return ndkDirectory;
}
return null;
}
// in build-tools/$version/aapt
final List<File> aaptBins = os.whichAll('aapt');
for (File aaptBin in aaptBins) {
// Make sure we're using the aapt from the SDK.
aaptBin = fs.file(aaptBin.resolveSymbolicLinksSync());
final String dir = aaptBin.parent.parent.parent.path;
if (validSdkDirectory(dir))
return new AndroidSdk(dir);
String findNdkCompiler(String ndkDirectory) {
String directory;
if (platform.isLinux) {
directory = 'linux-x86_64';
} else if (platform.isMacOS) {
directory = 'darwin-x86_64';
}
if (directory != null) {
final String ndkCompiler = fs.path.join(ndkDirectory,
'toolchains', 'arm-linux-androideabi-4.9', 'prebuilt', directory,
'bin', 'arm-linux-androideabi-gcc');
if (fs.isFileSync(ndkCompiler)) {
return ndkCompiler;
}
}
return null;
}
// in platform-tools/adb
final List<File> adbBins = os.whichAll('adb');
for (File adbBin in adbBins) {
// Make sure we're using the adb from the SDK.
adbBin = fs.file(adbBin.resolveSymbolicLinksSync());
final String dir = adbBin.parent.parent.path;
if (validSdkDirectory(dir))
return new AndroidSdk(dir);
List<String> computeNdkCompilerArgs(String ndkDirectory) {
final String armPlatform = fs.path.join(ndkDirectory, 'platforms',
'android-9', 'arch-arm');
if (fs.isDirectorySync(armPlatform)) {
return <String>['--sysroot', armPlatform];
}
return null;
}
// No dice.
printTrace('Unable to locate an Android SDK.');
return null;
final String androidHomeDir = findAndroidHomeDir();
if (androidHomeDir == null) {
// No dice.
printTrace('Unable to locate an Android SDK.');
return null;
}
// Try to find the NDK compiler. If we can't find it, it's also ok.
final String ndkDir = findNdk(androidHomeDir);
String ndkCompiler;
List<String> ndkCompilerArgs;
if (ndkDir != null) {
ndkCompiler = findNdkCompiler(ndkDir);
if (ndkCompiler != null) {
ndkCompilerArgs = computeNdkCompilerArgs(ndkDir);
if (ndkCompilerArgs == null) {
ndkCompiler = null;
}
}
}
return new AndroidSdk(androidHomeDir, ndkDir, ndkCompiler, ndkCompilerArgs);
}
static bool validSdkDirectory(String dir) {
......
......@@ -119,6 +119,14 @@ class AndroidWorkflow extends DoctorValidator implements Workflow {
messages.add(new ValidationMessage('Android SDK at ${androidSdk.directory}'));
messages.add(new ValidationMessage(androidSdk.ndkDirectory == null
? 'Unable to locate Android NDK.\n'
: 'Android NDK at ${androidSdk.ndkDirectory}'));
messages.add(new ValidationMessage(androidSdk.ndkCompiler == null
? 'Unable to locate compiler in Android NDK.\n'
: 'Compiler in Android NDK at ${androidSdk.ndkCompiler}'));
String sdkVersionText;
if (androidSdk.latestVersion != null) {
sdkVersionText = 'Android SDK ${androidSdk.latestVersion.buildToolsVersionName}';
......
......@@ -4,6 +4,7 @@
import 'dart:async';
import '../android/android_sdk.dart';
import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart';
......@@ -289,12 +290,17 @@ Future<Null> _buildGradleProjectV2(String gradle, BuildInfo buildInfo, String ta
if (target != null) {
command.add('-Ptarget=$target');
}
if (buildInfo.previewDart2)
if (buildInfo.previewDart2) {
command.add('-Ppreview-dart-2=true');
if (buildInfo.extraFrontEndOptions != null)
command.add('-Pextra-front-end-options=${buildInfo.extraFrontEndOptions}');
if (buildInfo.extraGenSnapshotOptions != null)
command.add('-Pextra-gen-snapshot-options=${buildInfo.extraGenSnapshotOptions}');
}
if (buildInfo.preferSharedLibrary && androidSdk.ndkCompiler != null) {
command.add('-Pprefer-shared-library=true');
}
command.add(assembleTask);
final int exitCode = await runCommandAndStreamOutput(
command,
......
......@@ -13,7 +13,8 @@ class BuildInfo {
const BuildInfo(this.mode, this.flavor,
{this.previewDart2,
this.extraFrontEndOptions,
this.extraGenSnapshotOptions});
this.extraGenSnapshotOptions,
this.preferSharedLibrary});
final BuildMode mode;
/// Represents a custom Android product flavor or an Xcode scheme, null for
......@@ -33,6 +34,9 @@ class BuildInfo {
/// Extra command-line options for gen_snapshot.
final String extraGenSnapshotOptions;
// Whether to prefer AOT compiling to a *so file.
final bool preferSharedLibrary;
static const BuildInfo debug = const BuildInfo(BuildMode.debug, null);
static const BuildInfo profile = const BuildInfo(BuildMode.profile, null);
static const BuildInfo release = const BuildInfo(BuildMode.release, null);
......
......@@ -4,6 +4,7 @@
import 'dart:async';
import '../android/android_sdk.dart';
import '../artifacts.dart';
import '../base/build.dart';
import '../base/common.dart';
......@@ -48,7 +49,9 @@ class BuildAotCommand extends BuildSubCommand {
allowMultiple: true,
splitCommas: true,
hide: true,
);
)
..addFlag('prefer-shared-library', negatable: false,
help: 'Whether to prefer compiling to a *.so file (android only).');
}
@override
......@@ -80,6 +83,7 @@ class BuildAotCommand extends BuildSubCommand {
previewDart2: argResults['preview-dart-2'],
extraFrontEndOptions: argResults[FlutterOptions.kExtraFrontEndOptions],
extraGenSnapshotOptions: argResults[FlutterOptions.kExtraGenSnapshotOptions],
preferSharedLibrary: argResults['prefer-shared-library'],
);
status?.stop();
......@@ -110,6 +114,7 @@ Future<String> buildAotSnapshot(
bool previewDart2: false,
List<String> extraFrontEndOptions,
List<String> extraGenSnapshotOptions,
bool preferSharedLibrary: false,
}) async {
outputPath ??= getAotBuildDirectory();
try {
......@@ -122,6 +127,7 @@ Future<String> buildAotSnapshot(
previewDart2: previewDart2,
extraFrontEndOptions: extraFrontEndOptions,
extraGenSnapshotOptions: extraGenSnapshotOptions,
preferSharedLibrary: preferSharedLibrary,
);
} on String catch (error) {
// Catch the String exceptions thrown from the `runCheckedSync` methods below.
......@@ -140,6 +146,7 @@ Future<String> _buildAotSnapshot(
bool previewDart2: false,
List<String> extraFrontEndOptions,
List<String> extraGenSnapshotOptions,
bool preferSharedLibrary: false,
}) async {
outputPath ??= getAotBuildDirectory();
if (!isAotBuildMode(buildMode) && !interpreter) {
......@@ -161,6 +168,16 @@ Future<String> _buildAotSnapshot(
final String isolateSnapshotData = fs.path.join(outputDir.path, 'isolate_snapshot_data');
final String isolateSnapshotInstructions = fs.path.join(outputDir.path, 'isolate_snapshot_instr');
final String dependencies = fs.path.join(outputDir.path, 'snapshot.d');
final String assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S');
final String assemblyO = fs.path.join(outputDir.path, 'snapshot_assembly.o');
final String assemblySo = fs.path.join(outputDir.path, 'app.so');
final bool compileToSharedLibrary =
preferSharedLibrary && androidSdk.ndkCompiler != null;
if (preferSharedLibrary && !compileToSharedLibrary) {
printStatus(
'Could not find NDK compiler. Not building in shared library mode');
}
final String vmEntryPoints = artifacts.getArtifactPath(
Artifact.dartVmEntryPointsTxt,
......@@ -192,20 +209,22 @@ Future<String> _buildAotSnapshot(
// These paths are used only on iOS.
String snapshotDartIOS;
String assembly;
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
outputPaths.addAll(<String>[
vmSnapshotData,
isolateSnapshotData,
]);
if (compileToSharedLibrary) {
outputPaths.add(assemblySo);
} else {
outputPaths.addAll(<String>[
vmSnapshotData,
isolateSnapshotData,
]);
}
break;
case TargetPlatform.ios:
snapshotDartIOS = artifacts.getArtifactPath(Artifact.snapshotDart, platform, buildMode);
assembly = fs.path.join(outputDir.path, 'snapshot_assembly.S');
inputPaths.add(snapshotDartIOS);
break;
case TargetPlatform.darwin_x64:
......@@ -260,16 +279,23 @@ Future<String> _buildAotSnapshot(
final String kIsolateSnapshotDataC = fs.path.join(outputDir.path, '$kIsolateSnapshotData.c');
final String kVmSnapshotDataO = fs.path.join(outputDir.path, '$kVmSnapshotData.o');
final String kIsolateSnapshotDataO = fs.path.join(outputDir.path, '$kIsolateSnapshotData.o');
final String assemblyO = fs.path.join(outputDir.path, 'snapshot_assembly.o');
switch (platform) {
case TargetPlatform.android_arm:
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
if (compileToSharedLibrary) {
genSnapshotCmd.add('--snapshot_kind=app-aot-assembly');
genSnapshotCmd.add('--assembly=$assembly');
outputPaths.add(assemblySo);
} else {
genSnapshotCmd.addAll(<String>[
'--snapshot_kind=app-aot-blobs',
'--vm_snapshot_instructions=$vmSnapshotInstructions',
'--isolate_snapshot_instructions=$isolateSnapshotInstructions',
]);
}
genSnapshotCmd.addAll(<String>[
'--snapshot_kind=app-aot-blobs',
'--vm_snapshot_instructions=$vmSnapshotInstructions',
'--isolate_snapshot_instructions=$isolateSnapshotInstructions',
'--no-sim-use-hardfp', // Android uses the softfloat ABI.
'--no-use-integer-division', // Not supported by the Pixel in 32-bit mode.
]);
......@@ -396,6 +422,19 @@ Future<String> _buildAotSnapshot(
linkCommand.add(assemblyO);
}
await runCheckedAsync(linkCommand);
} else {
if (compileToSharedLibrary) {
// A word of warning: Instead of compiling via two steps, to a .o file and
// then to a .so file we use only one command. When using two commands
// gcc will end up putting a .eh_frame and a .debug_frame into the shared
// library. Without stripping .debug_frame afterwards, unwinding tools
// based upon libunwind use just one and ignore the contents of the other
// (which causes it to not look into the other section and therefore not
// find the correct unwinding information).
await runCheckedAsync(<String>[androidSdk.ndkCompiler]
..addAll(androidSdk.ndkCompilerArgs)
..addAll(<String>[ '-shared', '-nostdlib', '-o', assemblySo, assembly ]));
}
}
// Compute and record build fingerprint.
......
......@@ -11,9 +11,13 @@ class BuildApkCommand extends BuildSubCommand {
BuildApkCommand() {
usesTargetOption();
addBuildModeFlags();
argParser.addFlag('preview-dart-2', negatable: false);
usesFlavorOption();
usesPubOption();
argParser
..addFlag('preview-dart-2', negatable: false)
..addFlag('prefer-shared-library', negatable: false,
help: 'Whether to prefer compiling to a *.so file (android only).');
}
@override
......
......@@ -161,7 +161,10 @@ abstract class FlutterCommand extends Command<Null> {
: null,
extraGenSnapshotOptions: argParser.options.containsKey(FlutterOptions.kExtraGenSnapshotOptions)
? argResults[FlutterOptions.kExtraGenSnapshotOptions]
: null);
: null,
preferSharedLibrary: argParser.options.containsKey('prefer-shared-library')
? argResults['prefer-shared-library']
: false);
}
void setupApplicationPackages() {
......
......@@ -5,6 +5,8 @@
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:test/test.dart';
import '../src/context.dart';
......@@ -21,12 +23,14 @@ void main() {
tearDown(() {
sdkDir?.deleteSync(recursive: true);
sdkDir = null;
});
testUsingContext('parse sdk', () {
sdkDir = _createSdkDirectory();
final AndroidSdk sdk = new AndroidSdk(sdkDir.path);
Config.instance.setValue('android-sdk', sdkDir.path);
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.latestVersion, isNotNull);
expect(sdk.latestVersion.sdkLevel, 23);
}, overrides: <Type, Generator>{
......@@ -35,17 +39,71 @@ void main() {
testUsingContext('parse sdk N', () {
sdkDir = _createSdkDirectory(withAndroidN: true);
final AndroidSdk sdk = new AndroidSdk(sdkDir.path);
Config.instance.setValue('android-sdk', sdkDir.path);
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.latestVersion, isNotNull);
expect(sdk.latestVersion.sdkLevel, 24);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
});
group('ndk', () {
const <String, String>{
'linux': 'linux-x86_64',
'macos': 'darwin-x86_64',
}.forEach((String os, String osDir) {
testUsingContext('detection on $os', () {
sdkDir = _createSdkDirectory(
withAndroidN: true, withNdkDir: osDir, withNdkSysroot: true);
Config.instance.setValue('android-sdk', sdkDir.path);
final String realSdkDir = sdkDir.path;
final String realNdkDir = fs.path.join(realSdkDir, 'ndk-bundle');
final String realNdkCompiler = fs.path.join(
realNdkDir,
'toolchains',
'arm-linux-androideabi-4.9',
'prebuilt',
osDir,
'bin',
'arm-linux-androideabi-gcc');
final String realNdkSysroot =
fs.path.join(realNdkDir, 'platforms', 'android-9', 'arch-arm');
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir);
expect(sdk.ndkDirectory, realNdkDir);
expect(sdk.ndkCompiler, realNdkCompiler);
expect(sdk.ndkCompilerArgs, <String>['--sysroot', realNdkSysroot]);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: () => new FakePlatform(operatingSystem: os),
});
});
for (String os in <String>['linux', 'macos']) {
testUsingContext('detection on $os (no ndk available)', () {
sdkDir = _createSdkDirectory(withAndroidN: true);
Config.instance.setValue('android-sdk', sdkDir.path);
final String realSdkDir = sdkDir.path;
final AndroidSdk sdk = AndroidSdk.locateAndroidSdk();
expect(sdk.directory, realSdkDir);
expect(sdk.ndkDirectory, null);
expect(sdk.ndkCompiler, null);
expect(sdk.ndkCompilerArgs, null);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: () => new FakePlatform(operatingSystem: os),
});
}
});
});
}
Directory _createSdkDirectory({ bool withAndroidN: false }) {
Directory _createSdkDirectory(
{bool withAndroidN: false, String withNdkDir, bool withNdkSysroot: false}) {
final Directory dir = fs.systemTempDirectory.createTempSync('android-sdk');
_createSdkFile(dir, 'platform-tools/adb');
......@@ -63,6 +121,23 @@ Directory _createSdkDirectory({ bool withAndroidN: false }) {
_createSdkFile(dir, 'platforms/android-N/build.prop', contents: _buildProp);
}
if (withNdkDir != null) {
final String ndkCompiler = fs.path.join(
'ndk-bundle',
'toolchains',
'arm-linux-androideabi-4.9',
'prebuilt',
withNdkDir,
'bin',
'arm-linux-androideabi-gcc');
_createSdkFile(dir, ndkCompiler);
}
if (withNdkSysroot) {
final String armPlatform =
fs.path.join('ndk-bundle', 'platforms', 'android-9', 'arch-arm');
_createDir(dir, armPlatform);
}
return dir;
}
......@@ -74,6 +149,11 @@ void _createSdkFile(Directory dir, String filePath, { String contents }) {
}
}
void _createDir(Directory dir, String path) {
final Directory directory = fs.directory(fs.path.join(dir.path, path));
directory.createSync(recursive: true);
}
const String _buildProp = r'''
ro.build.version.incremental=1624448
ro.build.version.sdk=24
......
......@@ -70,6 +70,21 @@ void testUsingContext(String description, dynamic testMethod(), {
ContextInitializer initializeContext: _defaultInitializeContext,
bool skip, // should default to `false`, but https://github.com/dart-lang/test/issues/545 doesn't allow this
}) {
// Ensure we don't rely on the default [Config] constructor which will
// leak a sticky $HOME/.flutter_settings behind!
Directory configDir;
tearDown(() {
configDir?.deleteSync(recursive: true);
configDir = null;
});
Config buildConfig(FileSystem fs) {
configDir = fs.systemTempDirectory.createTempSync('config-dir');
final File settingsFile = fs.file(
fs.path.join(configDir.path, '.flutter_settings'));
return new Config(settingsFile);
}
test(description, () async {
final AppContext testContext = new AppContext();
......@@ -80,7 +95,7 @@ void testUsingContext(String description, dynamic testMethod(), {
..putIfAbsent(FileSystem, () => const LocalFileSystem())
..putIfAbsent(ProcessManager, () => const LocalProcessManager())
..putIfAbsent(Logger, () => new BufferLogger())
..putIfAbsent(Config, () => new Config());
..putIfAbsent(Config, () => buildConfig(testContext[FileSystem]));
// Apply the initializer after seeding the base value above.
initializeContext(testContext);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment