diff --git a/packages/flutter_tools/lib/src/commands/create_base.dart b/packages/flutter_tools/lib/src/commands/create_base.dart index 9d272b786ae34..02e37fae6430b 100644 --- a/packages/flutter_tools/lib/src/commands/create_base.dart +++ b/packages/flutter_tools/lib/src/commands/create_base.dart @@ -132,9 +132,27 @@ abstract class CreateBase extends FlutterCommand { ); } + /// Pattern for a Windows file system drive (e.g. "D:"). + /// + /// `dart:io` does not recognize strings matching this pattern as absolute + /// paths, as they have no top level back-slash; however, users often specify + /// this + @visibleForTesting + static final RegExp kWindowsDrivePattern = RegExp(r'^[a-zA-Z]:$'); + /// The output directory of the command. @protected + @visibleForTesting Directory get projectDir { + final String argProjectDir = argResults!.rest.first; + if (globals.platform.isWindows && kWindowsDrivePattern.hasMatch(argProjectDir)) { + throwToolExit( + 'You attempted to create a flutter project at the path "$argProjectDir", which is the name of a drive. This ' + 'is usually a mistake--you probably want to specify a containing directory, like "$argProjectDir\\app_name". ' + 'If you really want it at the drive root, re-run the command with the root directory after the drive, like ' + '"$argProjectDir\\".', + ); + } return globals.fs.directory(argResults!.rest.first); } diff --git a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart index 310dddb20d246..a09b4efbdbc50 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/create_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/create_test.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io' as io; import 'package:args/command_runner.dart'; import 'package:file_testing/file_testing.dart'; @@ -113,6 +114,37 @@ void main() { expect(Uuid.isValidUUID(fromString: identifier), isTrue); }); + testUsingContext('tool exits on Windows if given a drive letter without a path', () async { + // Must use LocalFileSystem as it is dependent on dart:io handling of + // Windows paths, which the MemoryFileSystem does not implement + final Directory workingDir = globals.fs.directory(r'X:\path\to\working\dir'); + // Must use [io.IOOverrides] as directory.absolute depends on Directory.current + // from dart:io. + await io.IOOverrides.runZoned>( + () async { + // Verify IOOverrides is working + expect(io.Directory.current, workingDir); + final CreateCommand command = CreateCommand(); + final CommandRunner runner = createTestCommandRunner(command); + const String driveName = 'X:'; + await expectToolExitLater( + runner.run([ + 'create', + '--project-name', + 'test_app', + '--offline', + driveName, + ]), + contains('You attempted to create a flutter project at the path "$driveName"'), + ); + }, + getCurrentDirectory: () => workingDir, + ); + }, overrides: { + Logger: () => BufferLogger.test(), + }, skip: !io.Platform.isWindows // [intended] relies on Windows file system + ); + // Verify that we create a default project ('app') that is // well-formed. testUsingContext('can create a default project', () async { diff --git a/packages/flutter_tools/test/general.shard/create_config_test.dart b/packages/flutter_tools/test/general.shard/create_config_test.dart index 1699de1528d80..4d41ec9e965c7 100644 --- a/packages/flutter_tools/test/general.shard/create_config_test.dart +++ b/packages/flutter_tools/test/general.shard/create_config_test.dart @@ -19,4 +19,13 @@ void main() { expect(isValidPackageName('Foo_bar'), false); }); + + test('kWindowsDrivePattern', () { + expect(CreateBase.kWindowsDrivePattern.hasMatch(r'D:\'), isFalse); + expect(CreateBase.kWindowsDrivePattern.hasMatch(r'z:\'), isFalse); + expect(CreateBase.kWindowsDrivePattern.hasMatch(r'\d:'), isFalse); + expect(CreateBase.kWindowsDrivePattern.hasMatch(r'ef:'), isFalse); + expect(CreateBase.kWindowsDrivePattern.hasMatch(r'D:'), isTrue); + expect(CreateBase.kWindowsDrivePattern.hasMatch(r'c:'), isTrue); + }); } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy