[flutter_tools] Catch lack of flutter tools source missing (#93168)
This commit is contained in:
parent
d21bb19708
commit
e8ec8a0dfb
@ -54,6 +54,7 @@ import 'src/globals.dart' as globals;
|
||||
// Files in `isolated` are intentionally excluded from google3 tooling.
|
||||
import 'src/isolated/mustache_template.dart';
|
||||
import 'src/isolated/resident_web_runner.dart';
|
||||
import 'src/pre_run_validator.dart';
|
||||
import 'src/resident_runner.dart';
|
||||
import 'src/runner/flutter_command.dart';
|
||||
import 'src/web/web_runner.dart';
|
||||
@ -126,6 +127,7 @@ Future<void> main(List<String> args) async {
|
||||
windows: globals.platform.isWindows,
|
||||
);
|
||||
},
|
||||
PreRunValidator: () => PreRunValidator(fileSystem: globals.fs),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import 'macos/cocoapods_validator.dart';
|
||||
import 'macos/xcdevice.dart';
|
||||
import 'macos/xcode.dart';
|
||||
import 'persistent_tool_state.dart';
|
||||
import 'pre_run_validator.dart';
|
||||
import 'project.dart';
|
||||
import 'reporting/crash_reporting.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
@ -257,3 +258,5 @@ FlutterProjectFactory get projectFactory {
|
||||
}
|
||||
|
||||
CustomDevicesConfig get customDevicesConfig => context.get<CustomDevicesConfig>()!;
|
||||
|
||||
PreRunValidator get preRunValidator => context.get<PreRunValidator>() ?? const NoOpPreRunValidator();
|
||||
|
51
packages/flutter_tools/lib/src/pre_run_validator.dart
Normal file
51
packages/flutter_tools/lib/src/pre_run_validator.dart
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'base/common.dart';
|
||||
import 'base/file_system.dart';
|
||||
import 'cache.dart';
|
||||
|
||||
/// A validator that runs before the tool runs any command.
|
||||
abstract class PreRunValidator {
|
||||
factory PreRunValidator({
|
||||
required FileSystem fileSystem,
|
||||
}) => _DefaultPreRunValidator(fileSystem: fileSystem);
|
||||
|
||||
void validate();
|
||||
}
|
||||
|
||||
class _DefaultPreRunValidator implements PreRunValidator {
|
||||
_DefaultPreRunValidator({
|
||||
required this.fileSystem,
|
||||
});
|
||||
|
||||
final FileSystem fileSystem;
|
||||
|
||||
late final Directory _toolsDir = fileSystem.directory(
|
||||
fileSystem.path.join(Cache.flutterRoot!, 'packages', 'flutter_tools'),
|
||||
);
|
||||
|
||||
@override
|
||||
void validate() {
|
||||
// If a user downloads the Flutter SDK via a pre-built archive and there is
|
||||
// an error during extraction, the user could have a valid Dart snapshot of
|
||||
// the tool but not the source directory. We still need the source, so
|
||||
// validate the source directory exists and toolExit if not.
|
||||
if (!_toolsDir.existsSync()) {
|
||||
throwToolExit(
|
||||
'Flutter SDK installation appears corrupted: expected to find the '
|
||||
'directory ${_toolsDir.path} but it does not exist! Please go to '
|
||||
'https://flutter.dev/setup for instructions on how to re-install '
|
||||
'Flutter.',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NoOpPreRunValidator implements PreRunValidator {
|
||||
const NoOpPreRunValidator();
|
||||
|
||||
@override
|
||||
void validate() {}
|
||||
}
|
@ -1251,6 +1251,7 @@ abstract class FlutterCommand extends Command<void> {
|
||||
/// rather than calling [runCommand] directly.
|
||||
@mustCallSuper
|
||||
Future<FlutterCommandResult> verifyThenRunCommand(String? commandPath) async {
|
||||
globals.preRunValidator.validate();
|
||||
// Populate the cache. We call this before pub get below so that the
|
||||
// sky_engine package is available in the flutter cache for pub to find.
|
||||
if (shouldUpdateCache) {
|
||||
|
@ -31,6 +31,8 @@ void main() {
|
||||
expect(fakeStdio.writtenToStdout.length, equals(1));
|
||||
expect(fakeStdio.writtenToStdout.first, contains('__flutter_completion'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Stdio: () => fakeStdio,
|
||||
});
|
||||
|
||||
@ -40,6 +42,8 @@ void main() {
|
||||
expect(fakeStdio.writtenToStdout.length, equals(1));
|
||||
expect(fakeStdio.writtenToStdout.first, contains('__flutter_completion'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Stdio: () => fakeStdio,
|
||||
});
|
||||
|
||||
|
@ -449,6 +449,7 @@ void main() {
|
||||
|
||||
testUsingContext('test without bot', () async {
|
||||
Cache.flutterRoot = '';
|
||||
globals.fs.directory('/packages/flutter_tools').createSync(recursive: true);
|
||||
globals.fs.file('pubspec.yaml').createSync();
|
||||
processManager.addCommand(
|
||||
const FakeCommand(command: <String>['/bin/cache/dart-sdk/bin/dart', '__deprecated_pub', 'run', 'test']),
|
||||
|
@ -19,6 +19,7 @@ import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/dart/pub.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/pre_run_validator.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:test/fake.dart';
|
||||
@ -34,6 +35,13 @@ void main() {
|
||||
TestUsage usage;
|
||||
FakeClock clock;
|
||||
FakeProcessInfo processInfo;
|
||||
MemoryFileSystem fileSystem;
|
||||
FakeProcessManager processManager;
|
||||
PreRunValidator preRunValidator;
|
||||
|
||||
setUpAll(() {
|
||||
Cache.flutterRoot = '/path/to/sdk/flutter';
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
Cache.disableLocking();
|
||||
@ -42,6 +50,9 @@ void main() {
|
||||
clock = FakeClock();
|
||||
processInfo = FakeProcessInfo();
|
||||
processInfo.maxRss = 10;
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
processManager = FakeProcessManager.empty();
|
||||
preRunValidator = PreRunValidator(fileSystem: fileSystem);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
@ -63,6 +74,8 @@ void main() {
|
||||
expect(flutterCommand.hidden, isFalse);
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
Cache: () => cache,
|
||||
});
|
||||
|
||||
@ -79,9 +92,25 @@ void main() {
|
||||
);
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
Cache: () => cache,
|
||||
});
|
||||
|
||||
testUsingContext("throws toolExit if flutter_tools source dir doesn't exist", () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
|
||||
await expectToolExitLater(
|
||||
flutterCommand.run(),
|
||||
contains('Flutter SDK installation appears corrupted'),
|
||||
);
|
||||
},
|
||||
overrides: <Type, Generator>{
|
||||
Cache: () => cache,
|
||||
FileSystem: () => fileSystem,
|
||||
PreRunValidator: () => preRunValidator,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('deprecated command should warn', () async {
|
||||
final FakeDeprecatedCommand flutterCommand = FakeDeprecatedCommand();
|
||||
final CommandRunner<void> runner = createTestCommandRunner(flutterCommand);
|
||||
@ -95,6 +124,9 @@ void main() {
|
||||
'of Flutter.'));
|
||||
expect(flutterCommand.deprecated, isTrue);
|
||||
expect(flutterCommand.hidden, isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('uses the error handling file system', () async {
|
||||
@ -105,6 +137,9 @@ void main() {
|
||||
}
|
||||
);
|
||||
await flutterCommand.run();
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('finds the target file with default values', () async {
|
||||
@ -115,8 +150,8 @@ void main() {
|
||||
|
||||
expect(fakeTargetCommand.cachedTargetFile, 'lib/main.dart');
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('finds the target file with specified value', () async {
|
||||
@ -127,8 +162,8 @@ void main() {
|
||||
|
||||
expect(fakeTargetCommand.cachedTargetFile, 'lib/foo.dart');
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('throws tool exit if specified file does not exist', () async {
|
||||
@ -137,13 +172,15 @@ void main() {
|
||||
|
||||
expect(() async => runner.run(<String>['test', '-t', 'lib/foo.dart']), throwsToolExit());
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
void testUsingCommandContext(String testName, dynamic Function() testBody) {
|
||||
testUsingContext(testName, testBody, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessInfo: () => processInfo,
|
||||
ProcessManager: () => processManager,
|
||||
SystemClock: () => clock,
|
||||
Usage: () => usage,
|
||||
});
|
||||
@ -245,6 +282,9 @@ void main() {
|
||||
'http://127.0.0.1:9105',
|
||||
]);
|
||||
expect(command.devToolsServerAddress.toString(), equals('http://127.0.0.1:9105'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('devToolsServerAddress returns null for bad input', () async {
|
||||
@ -277,6 +317,9 @@ void main() {
|
||||
'127.0.0.1:9101',
|
||||
]);
|
||||
expect(command.devToolsServerAddress, isNull);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
group('signals tests', () {
|
||||
@ -328,6 +371,8 @@ void main() {
|
||||
),
|
||||
]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
ProcessInfo: () => processInfo,
|
||||
Signals: () => FakeSignals(
|
||||
subForSigTerm: signalUnderTest,
|
||||
@ -363,6 +408,8 @@ void main() {
|
||||
signalController.add(mockSignal);
|
||||
await completer.future;
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
ProcessInfo: () => processInfo,
|
||||
Signals: () => FakeSignals(
|
||||
subForSigTerm: signalUnderTest,
|
||||
@ -499,26 +546,35 @@ void main() {
|
||||
}, overrides: <Type, Generator>{
|
||||
Pub: () => FakePub(),
|
||||
Usage: () => usage,
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('use packagesPath to generate BuildInfo', () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(packagesPath: 'foo');
|
||||
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
|
||||
expect(buildInfo.packagesPath, 'foo');
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('use fileSystemScheme to generate BuildInfo', () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(fileSystemScheme: 'foo');
|
||||
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
|
||||
expect(buildInfo.fileSystemScheme, 'foo');
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('use fileSystemRoots to generate BuildInfo', () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(fileSystemRoots: <String>['foo', 'bar']);
|
||||
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
|
||||
expect(buildInfo.fileSystemRoots, <String>['foo', 'bar']);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('includes initializeFromDill in BuildInfo', () async {
|
||||
@ -527,6 +583,9 @@ void main() {
|
||||
await runner.run(<String>['dummy', '--initialize-from-dill=/foo/bar.dill']);
|
||||
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
|
||||
expect(buildInfo.initializeFromDill, '/foo/bar.dill');
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('dds options', () async {
|
||||
@ -535,6 +594,9 @@ void main() {
|
||||
await runner.run(<String>['test', '--dds-port=1']);
|
||||
expect(ddsCommand.enableDds, isTrue);
|
||||
expect(ddsCommand.ddsPort, 1);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('dds options --dds', () async {
|
||||
@ -542,6 +604,9 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
|
||||
await runner.run(<String>['test', '--dds']);
|
||||
expect(ddsCommand.enableDds, isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('dds options --no-dds', () async {
|
||||
@ -549,6 +614,9 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
|
||||
await runner.run(<String>['test', '--no-dds']);
|
||||
expect(ddsCommand.enableDds, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('dds options --disable-dds', () async {
|
||||
@ -556,6 +624,9 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
|
||||
await runner.run(<String>['test', '--disable-dds']);
|
||||
expect(ddsCommand.enableDds, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('dds options --no-disable-dds', () async {
|
||||
@ -563,6 +634,9 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
|
||||
await runner.run(<String>['test', '--no-disable-dds']);
|
||||
expect(ddsCommand.enableDds, isTrue);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('dds options --dds --disable-dds', () async {
|
||||
@ -570,6 +644,9 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
|
||||
await runner.run(<String>['test', '--dds', '--disable-dds']);
|
||||
expect(() => ddsCommand.enableDds, throwsToolExit());
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -162,6 +162,7 @@ void test(String description, FutureOr<void> Function() body, {
|
||||
addTearDown(() async {
|
||||
await globals.localFileSystem.dispose();
|
||||
});
|
||||
|
||||
return body();
|
||||
},
|
||||
skip: skip,
|
||||
|
Loading…
x
Reference in New Issue
Block a user