diff --git a/packages/flutter_tools/lib/src/base/config.dart b/packages/flutter_tools/lib/src/base/config.dart index 529bb76c1a8..c14b2d2bf55 100644 --- a/packages/flutter_tools/lib/src/base/config.dart +++ b/packages/flutter_tools/lib/src/base/config.dart @@ -14,18 +14,23 @@ import 'utils.dart'; /// A class to abstract configuration files. class Config { /// Constructs a new [Config] object from a file called [name] in the - /// current user's home directory as determined by the [Platform] and - /// [FileSystem]. + /// current user's configuration directory as determined by the [Platform] + /// and [FileSystem]. + /// + /// The configuration directory defaults to $XDG_CONFIG_HOME on Linux and + /// macOS, but falls back to the home directory if a file named + /// `.flutter_$name` already exists there. On other platforms the + /// configuration file will always be a file named `.flutter_$name` in the + /// home directory. factory Config( String name, { @required FileSystem fileSystem, @required Logger logger, @required Platform platform, }) { - final File file = fileSystem.file(fileSystem.path.join( - _userHomePath(platform), - name, - )); + final String filePath = _configPath(platform, fileSystem, name); + final File file = fileSystem.file(filePath); + file.parent.createSync(recursive: true); return Config.createForTesting(file, logger); } @@ -35,7 +40,7 @@ class Config { String name, { @required Directory directory, @required Logger logger, - }) => Config.createForTesting(directory.childFile(name), logger); + }) => Config.createForTesting(directory.childFile('.${kConfigDir}_$name'), logger); /// Test only access to the Config constructor. @visibleForTesting @@ -65,8 +70,24 @@ class Config { } } + /// The default directory name for Flutter's configs. + + /// Configs will be written to the user's config path. If there is already a + /// file with the name `.${kConfigDir}_$name` in the user's home path, that + /// file will be used instead. + static const String kConfigDir = 'flutter'; + + /// Environment variable specified in the XDG Base Directory + /// [specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) + /// to specify the user's configuration directory. + static const String kXdgConfigHome = 'XDG_CONFIG_HOME'; + + /// Fallback directory in the user's home directory if `XDG_CONFIG_HOME` is + /// not defined. + static const String kXdgConfigFalback = '.config'; + /// The default name for the Flutter config file. - static const String kFlutterSettings = '.flutter_settings'; + static const String kFlutterSettings = 'settings'; final Logger _logger; @@ -104,9 +125,22 @@ class Config { // // Note that this is different from FileSystemUtils.homeDirPath. static String _userHomePath(Platform platform) { - final String envKey = platform.operatingSystem == 'windows' - ? 'APPDATA' - : 'HOME'; + final String envKey = platform.isWindows ? 'APPDATA' : 'HOME'; return platform.environment[envKey] ?? '.'; } + + static String _configPath( + Platform platform, FileSystem fileSystem, String name) { + final String homeDirFile = + fileSystem.path.join(_userHomePath(platform), '.${kConfigDir}_$name'); + if (platform.isLinux || platform.isMacOS) { + if (fileSystem.isFileSync(homeDirFile)) { + return homeDirFile; + } + final String configDir = platform.environment[kXdgConfigHome] ?? + fileSystem.path.join(_userHomePath(platform), '.config', kConfigDir); + return fileSystem.path.join(configDir, name); + } + return homeDirFile; + } } diff --git a/packages/flutter_tools/lib/src/persistent_tool_state.dart b/packages/flutter_tools/lib/src/persistent_tool_state.dart index bb3872b0f35..d5963906c32 100644 --- a/packages/flutter_tools/lib/src/persistent_tool_state.dart +++ b/packages/flutter_tools/lib/src/persistent_tool_state.dart @@ -73,7 +73,7 @@ class _DefaultPersistentToolState implements PersistentToolState { logger: logger, ); - static const String _kFileName = '.flutter_tool_state'; + static const String _kFileName = 'tool_state'; static const String _kRedisplayWelcomeMessage = 'redisplay-welcome-message'; static const Map _lastActiveVersionKeys = { Channel.master: 'last-active-master-version', diff --git a/packages/flutter_tools/test/general.shard/config_test.dart b/packages/flutter_tools/test/general.shard/config_test.dart index 295450d8771..425c19f5e9e 100644 --- a/packages/flutter_tools/test/general.shard/config_test.dart +++ b/packages/flutter_tools/test/general.shard/config_test.dart @@ -65,7 +65,7 @@ void main() { testWithoutContext('Config parse error', () { final BufferLogger bufferLogger = BufferLogger.test(); - final File file = memoryFileSystem.file('example') + final File file = memoryFileSystem.file('.flutter_example') ..writeAsStringSync('{"hello":"bar'); config = Config( 'example', @@ -106,6 +106,30 @@ void main() { // Also contains original error message: expect(bufferLogger.errorText, contains('The flutter tool cannot access the file or directory')); }); + + testWithoutContext('Config in home dir is used if it exists', () { + memoryFileSystem.file('.flutter_example').writeAsStringSync('{"hello":"bar"}'); + config = Config( + 'example', + fileSystem: memoryFileSystem, + logger: BufferLogger.test(), + platform: fakePlatform, + ); + expect(config.getValue('hello'), 'bar'); + expect(memoryFileSystem.file('.config/flutter/example').existsSync(), false); + }); + + testWithoutContext('Config is created in config dir if it does not already exist in home dir', () { + config = Config( + 'example', + fileSystem: memoryFileSystem, + logger: BufferLogger.test(), + platform: fakePlatform, + ); + + config.setValue('foo', 'bar'); + expect(memoryFileSystem.file('.config/flutter/example').existsSync(), true); + }); } class FakeFile extends Fake implements File {