Unverified Commit 0cb6d598 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate fuchsia sdk and dependencies to null safety (#88920)

parent fec0e19d
......@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import 'package:file/file.dart';
import 'package:process/process.dart';
import '../base/common.dart';
......@@ -25,16 +23,15 @@ import 'fuchsia_sdk.dart';
/// A simple wrapper for the Fuchsia SDK's 'device-finder' tool.
class FuchsiaDevFinder {
FuchsiaDevFinder({
@required FuchsiaArtifacts fuchsiaArtifacts,
@required Logger logger,
@required ProcessManager processManager,
required FuchsiaArtifacts? fuchsiaArtifacts,
required Logger logger,
required ProcessManager processManager,
})
: _fuchsiaArtifacts = fuchsiaArtifacts,
_logger = logger,
_processUtils = ProcessUtils(logger: logger, processManager: processManager);
final FuchsiaArtifacts _fuchsiaArtifacts;
final FuchsiaArtifacts? _fuchsiaArtifacts;
final Logger _logger;
final ProcessUtils _processUtils;
......@@ -42,13 +39,13 @@ class FuchsiaDevFinder {
/// formatted as follows:
///
/// 192.168.42.172 scare-cable-skip-joy
Future<List<String>> list({ Duration timeout }) async {
if (_fuchsiaArtifacts.devFinder == null ||
!_fuchsiaArtifacts.devFinder.existsSync()) {
Future<List<String>?> list({ Duration? timeout }) async {
final File? devFinder = _fuchsiaArtifacts?.devFinder;
if (devFinder == null || !devFinder.existsSync()) {
throwToolExit('Fuchsia device-finder tool not found.');
}
final List<String> command = <String>[
_fuchsiaArtifacts.devFinder.path,
devFinder.path,
'list',
'-full',
if (timeout != null)
......@@ -70,13 +67,13 @@ class FuchsiaDevFinder {
///
/// The string [deviceName] should be the name of the device from the
/// 'list' command, e.g. 'scare-cable-skip-joy'.
Future<String> resolve(String deviceName) async {
if (_fuchsiaArtifacts.devFinder == null ||
!_fuchsiaArtifacts.devFinder.existsSync()) {
Future<String?> resolve(String deviceName) async {
final File? devFinder = _fuchsiaArtifacts?.devFinder;
if (devFinder == null || !devFinder.existsSync()) {
throwToolExit('Fuchsia device-finder tool not found.');
}
final List<String> command = <String>[
_fuchsiaArtifacts.devFinder.path,
devFinder.path,
'resolve',
'-device-limit', '1',
deviceName,
......
......@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import 'package:file/file.dart';
import 'package:process/process.dart';
import '../base/common.dart';
......@@ -32,15 +30,15 @@ import 'fuchsia_sdk.dart';
/// A simple wrapper for the Fuchsia SDK's 'ffx' tool.
class FuchsiaFfx {
FuchsiaFfx({
@required FuchsiaArtifacts fuchsiaArtifacts,
@required Logger logger,
@required ProcessManager processManager,
required FuchsiaArtifacts? fuchsiaArtifacts,
required Logger logger,
required ProcessManager processManager,
}) : _fuchsiaArtifacts = fuchsiaArtifacts,
_logger = logger,
_processUtils =
ProcessUtils(logger: logger, processManager: processManager);
final FuchsiaArtifacts _fuchsiaArtifacts;
final FuchsiaArtifacts? _fuchsiaArtifacts;
final Logger _logger;
final ProcessUtils _processUtils;
......@@ -48,12 +46,13 @@ class FuchsiaFfx {
/// formatted as follows:
///
/// abcd::abcd:abc:abcd:abcd%qemu scare-cable-skip-joy
Future<List<String>> list({Duration timeout}) async {
if (_fuchsiaArtifacts.ffx == null || !_fuchsiaArtifacts.ffx.existsSync()) {
Future<List<String>?> list({Duration? timeout}) async {
final File? ffx = _fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
throwToolExit('Fuchsia ffx tool not found.');
}
final List<String> command = <String>[
_fuchsiaArtifacts.ffx.path,
ffx.path,
if (timeout != null)
...<String>['-T', '${timeout.inSeconds}'],
'target',
......@@ -76,12 +75,13 @@ class FuchsiaFfx {
///
/// The string [deviceName] should be the name of the device from the
/// 'list' command, e.g. 'scare-cable-skip-joy'.
Future<String> resolve(String deviceName) async {
if (_fuchsiaArtifacts.ffx == null || !_fuchsiaArtifacts.ffx.existsSync()) {
Future<String?> resolve(String deviceName) async {
final File? ffx = _fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
throwToolExit('Fuchsia ffx tool not found.');
}
final List<String> command = <String>[
_fuchsiaArtifacts.ffx.path,
ffx.path,
'target',
'list',
'--format',
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import '../artifacts.dart';
......@@ -21,8 +19,8 @@ class FuchsiaKernelCompiler {
/// the Flutter tool should make no use of that fact), and a manifest that
/// refers to them.
Future<void> build({
@required FuchsiaProject fuchsiaProject,
@required String target, // E.g., lib/main.dart
required FuchsiaProject fuchsiaProject,
required String target, // E.g., lib/main.dart
BuildInfo buildInfo = BuildInfo.debug,
}) async {
// TODO(zanderso): Use filesystem root and scheme information from buildInfo.
......@@ -33,20 +31,20 @@ class FuchsiaKernelCompiler {
final String fsRoot = fuchsiaProject.project.directory.path;
final String relativePackagesFile = globals.fs.path.relative(packagesFile, from: fsRoot);
final String manifestPath = globals.fs.path.join(outDir, '$appName.dilpmanifest');
final String kernelCompiler = globals.artifacts.getArtifactPath(
final String? kernelCompiler = globals.artifacts?.getArtifactPath(
Artifact.fuchsiaKernelCompiler,
platform: TargetPlatform.fuchsia_arm64, // This file is not arch-specific.
mode: buildInfo.mode,
);
if (!globals.fs.isFileSync(kernelCompiler)) {
if (kernelCompiler == null || !globals.fs.isFileSync(kernelCompiler)) {
throwToolExit('Fuchsia kernel compiler not found at "$kernelCompiler"');
}
final String platformDill = globals.artifacts.getArtifactPath(
final String? platformDill = globals.artifacts?.getArtifactPath(
Artifact.platformKernelDill,
platform: TargetPlatform.fuchsia_arm64, // This file is not arch-specific.
mode: buildInfo.mode,
);
if (!globals.fs.isFileSync(platformDill)) {
if (platformDill == null || !globals.fs.isFileSync(platformDill)) {
throwToolExit('Fuchsia platform file not found at "$platformDill"');
}
List<String> flags = <String>[
......@@ -64,8 +62,12 @@ class FuchsiaKernelCompiler {
'$multiRootScheme:///$target',
];
final String? engineDartBinaryPath = globals.artifacts?.getHostArtifact(HostArtifact.engineDartBinary).path;
if (engineDartBinaryPath == null) {
throwToolExit('Engine dart binary not found at "$engineDartBinaryPath"');
}
final List<String> command = <String>[
globals.artifacts.getHostArtifact(HostArtifact.engineDartBinary).path,
engineDartBinaryPath,
'--disable-dart-dev',
kernelCompiler,
...flags,
......@@ -87,8 +89,8 @@ class FuchsiaKernelCompiler {
/// Provide flags that are affected by [BuildInfo]
@visibleForTesting
static List<String> getBuildInfoFlags({
@required BuildInfo buildInfo,
@required String manifestPath,
required BuildInfo buildInfo,
required String manifestPath,
}) {
return <String>[
// AOT/JIT:
......
......@@ -2,15 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/net.dart';
import '../base/process.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import '../globals_null_migrated.dart' as globals;
import 'fuchsia_sdk.dart';
......@@ -92,14 +90,15 @@ class FuchsiaPM {
/// [FuchsiaDevFinder.resolve] or [FuchsiaFfx.resolve] and [port] should be an unused port for the
/// http server to bind.
Future<Process> serve(String repoPath, String host, int port) async {
if (globals.fuchsiaArtifacts.pm == null) {
final File? pm = globals.fuchsiaArtifacts?.pm;
if (pm == null) {
throwToolExit('Fuchsia pm tool not found');
}
if (isIPv6Address(host.split('%').first)) {
host = '[$host]';
}
final List<String> command = <String>[
globals.fuchsiaArtifacts.pm.path,
pm.path,
'serve',
'-repo',
repoPath,
......@@ -136,10 +135,11 @@ class FuchsiaPM {
}
Future<bool> _runPMCommand(List<String> args) async {
if (globals.fuchsiaArtifacts.pm == null) {
final File? pm = globals.fuchsiaArtifacts?.pm;
if (pm == null) {
throwToolExit('Fuchsia pm tool not found');
}
final List<String> command = <String>[globals.fuchsiaArtifacts.pm.path, ...args];
final List<String> command = <String>[pm.path, ...args];
final RunResult result = await globals.processUtils.run(command);
return result.exitCode == 0;
}
......@@ -178,7 +178,7 @@ class FuchsiaPackageServer {
final String _host;
final int _port;
Process _process;
Process? _process;
// The name used to reference the server by fuchsia-pkg:// urls.
final String name;
......@@ -196,13 +196,14 @@ class FuchsiaPackageServer {
return false;
}
// initialize a new repo.
if (!await fuchsiaSdk.fuchsiaPM.newrepo(_repo)) {
final FuchsiaPM? fuchsiaPM = fuchsiaSdk?.fuchsiaPM;
if (fuchsiaPM == null || !await fuchsiaPM.newrepo(_repo)) {
globals.printError('Failed to create a new package server repo');
return false;
}
_process = await fuchsiaSdk.fuchsiaPM.serve(_repo, _host, _port);
_process = await fuchsiaPM.serve(_repo, _host, _port);
// Put a completer on _process.exitCode to watch for error.
unawaited(_process.exitCode.whenComplete(() {
unawaited(_process?.exitCode.whenComplete(() {
// If _process is null, then the server was stopped deliberately.
if (_process != null) {
globals.printError('Error running Fuchsia pm tool "serve" command');
......@@ -214,7 +215,7 @@ class FuchsiaPackageServer {
/// Forcefully stops the package server process by sending it SIGTERM.
void stop() {
if (_process != null) {
_process.kill();
_process?.kill();
_process = null;
}
}
......@@ -228,12 +229,12 @@ class FuchsiaPackageServer {
if (_process == null) {
return false;
}
return fuchsiaSdk.fuchsiaPM.publish(_repo, package.path);
return (await fuchsiaSdk?.fuchsiaPM.publish(_repo, package.path)) == true;
}
@override
String toString() {
final String p = (_process == null) ? 'stopped' : 'running ${_process.pid}';
final String p = (_process == null) ? 'stopped' : 'running ${_process?.pid}';
return 'FuchsiaPackageServer at $_host:$_port ($p)';
}
}
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:async';
import '../base/context.dart';
......@@ -11,7 +9,7 @@ import '../base/file_system.dart';
import '../base/io.dart';
import '../base/platform.dart';
import '../convert.dart';
import '../globals.dart' as globals;
import '../globals_null_migrated.dart' as globals;
import 'fuchsia_dev_finder.dart';
import 'fuchsia_ffx.dart';
......@@ -19,7 +17,7 @@ import 'fuchsia_kernel_compiler.dart';
import 'fuchsia_pm.dart';
/// The [FuchsiaSdk] instance.
FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>();
FuchsiaSdk? get fuchsiaSdk => context.get<FuchsiaSdk>();
/// Returns [true] if the current platform supports Fuchsia targets.
bool isFuchsiaSupportedPlatform(Platform platform) {
......@@ -32,26 +30,20 @@ bool isFuchsiaSupportedPlatform(Platform platform) {
/// including a working fx command-line tool in the user's PATH.
class FuchsiaSdk {
/// Interface to the 'pm' tool.
FuchsiaPM get fuchsiaPM => _fuchsiaPM ??= FuchsiaPM();
FuchsiaPM _fuchsiaPM;
late final FuchsiaPM fuchsiaPM = FuchsiaPM();
/// Interface to the 'device-finder' tool.
FuchsiaDevFinder _fuchsiaDevFinder;
FuchsiaDevFinder get fuchsiaDevFinder =>
_fuchsiaDevFinder ??= FuchsiaDevFinder(
late final FuchsiaDevFinder fuchsiaDevFinder = FuchsiaDevFinder(
fuchsiaArtifacts: globals.fuchsiaArtifacts,
logger: globals.logger,
processManager: globals.processManager
);
/// Interface to the 'kernel_compiler' tool.
FuchsiaKernelCompiler _fuchsiaKernelCompiler;
FuchsiaKernelCompiler get fuchsiaKernelCompiler =>
_fuchsiaKernelCompiler ??= FuchsiaKernelCompiler();
late final FuchsiaKernelCompiler fuchsiaKernelCompiler = FuchsiaKernelCompiler();
/// Interface to the 'ffx' tool.
FuchsiaFfx _fuchsiaFfx;
FuchsiaFfx get fuchsiaFfx => _fuchsiaFfx ??= FuchsiaFfx(
late final FuchsiaFfx fuchsiaFfx = FuchsiaFfx(
fuchsiaArtifacts: globals.fuchsiaArtifacts,
logger: globals.logger,
processManager: globals.processManager,
......@@ -60,17 +52,17 @@ class FuchsiaSdk {
/// Returns any attached devices is a newline-denominated String.
///
/// Example output: abcd::abcd:abc:abcd:abcd%qemu scare-cable-skip-joy
Future<String> listDevices({Duration timeout, bool useDeviceFinder = false}) async {
List<String> devices;
Future<String?> listDevices({Duration? timeout, bool useDeviceFinder = false}) async {
List<String>? devices;
if (useDeviceFinder) {
if (globals.fuchsiaArtifacts.devFinder == null ||
!globals.fuchsiaArtifacts.devFinder.existsSync()) {
final File? devFinder = globals.fuchsiaArtifacts?.devFinder;
if (devFinder == null || !devFinder.existsSync()) {
return null;
}
devices = await fuchsiaDevFinder.list(timeout: timeout);
} else {
if (globals.fuchsiaArtifacts.ffx == null ||
!globals.fuchsiaArtifacts.ffx.existsSync()) {
final File? ffx = globals.fuchsiaArtifacts?.ffx;
if (ffx == null || !ffx.existsSync()) {
return null;
}
devices = await fuchsiaFfx.list(timeout: timeout);
......@@ -83,14 +75,14 @@ class FuchsiaSdk {
/// Returns the fuchsia system logs for an attached device where
/// [id] is the IP address of the device.
Stream<String> syslogs(String id) {
Process process;
Stream<String>? syslogs(String id) {
Process? process;
try {
final StreamController<String> controller = StreamController<String>(onCancel: () {
process.kill();
process?.kill();
});
if (globals.fuchsiaArtifacts.sshConfig == null ||
!globals.fuchsiaArtifacts.sshConfig.existsSync()) {
final File? sshConfig = globals.fuchsiaArtifacts?.sshConfig;
if (sshConfig == null || !sshConfig.existsSync()) {
globals.printError('Cannot read device logs: No ssh config.');
globals.printError('Have you set FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR?');
return null;
......@@ -99,7 +91,7 @@ class FuchsiaSdk {
final List<String> cmd = <String>[
'ssh',
'-F',
globals.fuchsiaArtifacts.sshConfig.absolute.path,
sshConfig.absolute.path,
id, // The device's IP.
remoteCommand,
];
......@@ -108,8 +100,8 @@ class FuchsiaSdk {
return;
}
process = newProcess;
process.exitCode.whenComplete(controller.close);
controller.addStream(process.stdout
process?.exitCode.whenComplete(controller.close);
controller.addStream(process!.stdout
.transform(utf8.decoder)
.transform(const LineSplitter()));
});
......@@ -145,10 +137,10 @@ class FuchsiaArtifacts {
// If FUCHSIA_BUILD_DIR is defined, then look for the ssh_config dir
// relative to it. Next, if FUCHSIA_SSH_CONFIG is defined, then use it.
// TODO(zanderso): Consider passing the ssh config path in with a flag.
File sshConfig;
File? sshConfig;
if (globals.platform.environment.containsKey(_kFuchsiaBuildDir)) {
sshConfig = globals.fs.file(globals.fs.path.join(
globals.platform.environment[_kFuchsiaBuildDir], 'ssh-keys', 'ssh_config'));
globals.platform.environment[_kFuchsiaBuildDir]!, 'ssh-keys', 'ssh_config'));
} else if (globals.platform.environment.containsKey(_kFuchsiaSshConfig)) {
sshConfig = globals.fs.file(globals.platform.environment[_kFuchsiaSshConfig]);
}
......@@ -172,19 +164,19 @@ class FuchsiaArtifacts {
/// The location of the SSH configuration file used to interact with a
/// Fuchsia device.
final File sshConfig;
final File? sshConfig;
/// The location of the dev finder tool used to locate connected
/// Fuchsia devices.
final File devFinder;
final File? devFinder;
/// The location of the ffx tool used to locate connected
/// Fuchsia devices.
final File ffx;
final File? ffx;
/// The pm tool.
final File pm;
final File? pm;
/// Returns true if the [sshConfig] file is not null and exists.
bool get hasSshConfig => sshConfig != null && sshConfig.existsSync();
bool get hasSshConfig => sshConfig != null && sshConfig!.existsSync();
}
......@@ -2,10 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:meta/meta.dart';
import '../base/context.dart';
import '../base/platform.dart';
import '../doctor_validator.dart';
......@@ -13,7 +9,7 @@ import '../features.dart';
import 'fuchsia_sdk.dart';
/// The [FuchsiaWorkflow] instance.
FuchsiaWorkflow get fuchsiaWorkflow => context.get<FuchsiaWorkflow>();
FuchsiaWorkflow? get fuchsiaWorkflow => context.get<FuchsiaWorkflow>();
/// The Fuchsia-specific implementation of a [Workflow].
///
......@@ -21,9 +17,9 @@ FuchsiaWorkflow get fuchsiaWorkflow => context.get<FuchsiaWorkflow>();
/// including a working fx command-line tool in the user's PATH.
class FuchsiaWorkflow implements Workflow {
FuchsiaWorkflow({
@required Platform platform,
@required FeatureFlags featureFlags,
@required FuchsiaArtifacts fuchsiaArtifacts,
required Platform platform,
required FeatureFlags featureFlags,
required FuchsiaArtifacts fuchsiaArtifacts,
}) : _platform = platform,
_featureFlags = featureFlags,
_fuchsiaArtifacts = fuchsiaArtifacts;
......@@ -36,7 +32,7 @@ class FuchsiaWorkflow implements Workflow {
bool get appliesToHostPlatform => _featureFlags.isFuchsiaEnabled && (_platform.isLinux || _platform.isMacOS);
bool get shouldUseDeviceFinder {
final String useDeviceFinder = _platform.environment.containsKey('FUCHSIA_DISABLED_ffx_discovery')
final String? useDeviceFinder = _platform.environment.containsKey('FUCHSIA_DISABLED_ffx_discovery')
? _platform.environment['FUCHSIA_DISABLED_ffx_discovery'] : '0';
if (useDeviceFinder == '1') {
return true;
......
......@@ -7,7 +7,6 @@
import 'base/context.dart';
import 'device.dart';
import 'doctor.dart';
import 'fuchsia/fuchsia_sdk.dart';
import 'ios/simulators.dart';
import 'macos/xcdevice.dart';
import 'reporting/crash_reporting.dart';
......@@ -17,7 +16,6 @@ export 'globals_null_migrated.dart';
CrashReporter get crashReporter => context.get<CrashReporter>();
Doctor get doctor => context.get<Doctor>();
DeviceManager get deviceManager => context.get<DeviceManager>();
FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>();
IOSSimulatorUtils get iosSimulatorUtils => context.get<IOSSimulatorUtils>();
XCDevice get xcdevice => context.get<XCDevice>();
......@@ -27,6 +27,7 @@ import 'base/user_messages.dart';
import 'build_system/build_system.dart';
import 'cache.dart';
import 'custom_devices/custom_devices_config.dart';
import 'fuchsia/fuchsia_sdk.dart';
import 'ios/ios_workflow.dart';
import 'ios/plist_parser.dart';
import 'ios/xcodeproj.dart';
......@@ -54,6 +55,7 @@ Signals get signals => context.get<Signals>() ?? LocalSignals.instance;
AndroidStudio? get androidStudio => context.get<AndroidStudio>();
AndroidSdk? get androidSdk => context.get<AndroidSdk>();
FlutterVersion get flutterVersion => context.get<FlutterVersion>()!;
FuchsiaArtifacts? get fuchsiaArtifacts => context.get<FuchsiaArtifacts>();
Usage get flutterUsage => context.get<Usage>()!;
XcodeProjectInterpreter? get xcodeProjectInterpreter => context.get<XcodeProjectInterpreter>();
Xcode? get xcode => context.get<Xcode>();
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -16,10 +14,10 @@ import '../../src/common.dart';
import '../../src/fake_process_manager.dart';
void main() {
FakeFuchsiaArtifacts fuchsiaArtifacts;
BufferLogger logger;
MemoryFileSystem memoryFileSystem;
File deviceFinder;
late FakeFuchsiaArtifacts fuchsiaArtifacts;
late BufferLogger logger;
late MemoryFileSystem memoryFileSystem;
late File deviceFinder;
setUp(() {
fuchsiaArtifacts = FakeFuchsiaArtifacts();
......@@ -134,5 +132,5 @@ void main() {
class FakeFuchsiaArtifacts extends Fake implements FuchsiaArtifacts {
@override
File devFinder;
File? devFinder;
}
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -16,10 +14,10 @@ import '../../src/common.dart';
import '../../src/fake_process_manager.dart';
void main() {
FakeFuchsiaArtifacts fakeFuchsiaArtifacts;
BufferLogger logger;
MemoryFileSystem memoryFileSystem;
File ffx;
late FakeFuchsiaArtifacts fakeFuchsiaArtifacts;
late BufferLogger logger;
late MemoryFileSystem memoryFileSystem;
late File ffx;
setUp(() {
fakeFuchsiaArtifacts = FakeFuchsiaArtifacts();
......@@ -212,5 +210,5 @@ void main() {
class FakeFuchsiaArtifacts extends Fake implements FuchsiaArtifacts {
@override
File ffx;
File? ffx;
}
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.dart';
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/platform.dart';
......
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