Warn about presence of legacy cache (#3921)
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 4d19989..db3df6f 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -517,6 +517,7 @@
additionalSources: additionalSources,
nativeAssets: nativeAssets,
);
+ cache.maintainCache();
}
/// The location of the snapshot of the dart program at [path] in [package]
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart
index 90bd7d5..181e847 100644
--- a/lib/src/system_cache.dart
+++ b/lib/src/system_cache.dart
@@ -256,7 +256,7 @@
// downloading a package, but might be significant in the fast-case where
// a the cache is already valid.
if (result.didUpdate) {
- _ensureReadme();
+ maintainCache();
}
return result.packageId;
}
@@ -313,7 +313,83 @@
void clean() {
deleteEntry(rootDir);
ensureDir(rootDir);
+ maintainCache();
+ }
+
+ /// Tasks that ensures the cache is in a good condition.
+ /// Should be called whenever an operation updates the cache.
+ void maintainCache() {
+ /// We only want to do this once per run.
+ if (_hasMaintainedCache) return;
+ _hasMaintainedCache = true;
_ensureReadme();
+ _checkOldCacheLocation();
+ }
+
+ /// Check for the presence of a cache at the legacy location
+ /// `%APPDATA$\Pub\Cache`.
+ ///
+ /// If it is present, give a warning and write a DEPRECATED.md in that cache.
+ ///
+ /// If DEPRECATED.md is less than 7 days old, we don't repeat the warning.
+ void _checkOldCacheLocation() {
+ // Background:
+ // Prior to Dart 2.8 the default location for the PUB_CACHE on Windows was:
+ // %APPDATA%\Pub\Cache
+ //
+ // Start Dart 2.8 pub started migrating the default PUB_CACHE location to:
+ // %LOCALAPPDATA%\Pub\Cache
+ // That is:
+ // * If a pub-cache existed in `%LOCALAPPDATA%\Pub\Cache` then it
+ // would be used.
+ // * If a pub-cache existed in `%APPDATA%\Pub\Cache` then it would be
+ // used, unless a pub-cache in `%LOCALAPPDATA%\Pub\Cache` had been found.
+ // * If no pub-cache was found, a new empty pub-cache was created in
+ // `%LOCALAPPDATA%\Pub\Cache`.
+ //
+ // Starting in Dart 3.0 pub will no-longer look for a pub-cache in
+ // `%APPDATA%\Pub\Cache`. Instead it will always use the new location,
+ // `%LOCALAPPDATA%\Pub\Cache`, as default PUB_CACHE location.
+ //
+ // Using `%APPDATA%` caused the pub-cache to be copied with the user-profile,
+ // when using a networked Windows setup where users can login on multiple
+ // machines. This is undesirable because you are moving a lot of bytes over
+ // the network and onto whatever servers are storing the user profiles.
+ //
+ // Thus, we migrated to storing the pub-cache in `%LOCALAPPDATA%`.
+ // And finished the migration in Dart 3 to keep things simple.
+ if (!Platform.isWindows) return;
+
+ final appData = Platform.environment['APPDATA'];
+ if (appData == null) return;
+ final legacyCacheLocation = p.join(appData, 'Pub', 'Cache');
+ final legacyCacheDeprecatedFile =
+ p.join(legacyCacheLocation, 'DEPRECATED.md');
+ final stat = tryStatFile(legacyCacheDeprecatedFile);
+ if ((stat == null ||
+ DateTime.now().difference(stat.changed) > Duration(days: 7)) &&
+ dirExists(legacyCacheLocation)) {
+ log.warning('''
+Found a legacy Pub cache at $legacyCacheLocation. Pub is using $defaultDir.
+
+Consider deleting the legacy cache.
+
+See https://dart.dev/resources/dart-3-migration#other-tools-changes for details.
+''');
+ try {
+ writeTextFile(legacyCacheDeprecatedFile, '''
+As of Dart 3 this pub cache is no longer used by Dart/Flutter.
+
+Consider deleting it, if you are not using Dart versions earlier than 2.8.0.
+
+See https://dart.dev/resources/dart-3-migration#other-tools-changes for details.
+''');
+ } on Exception catch (e) {
+ // Failing to write the DEPRECATED.md file should not disrupt other
+ // operations.
+ log.fine('Failed to write $legacyCacheDeprecatedFile: $e');
+ }
+ }
}
/// Write a README.md file in the root of the cache directory to document the
@@ -324,9 +400,6 @@
/// permission errors because we writing a `README.md` file, in a flow that
/// the user expected wouldn't have issues with a read-only `PUB_CACHE`.
void _ensureReadme() {
- /// We only want to do this once per run.
- if (_hasEnsuredReadme) return;
- _hasEnsuredReadme = true;
final readmePath = p.join(rootDir, 'README.md');
try {
writeTextFile(readmePath, '''
@@ -350,7 +423,7 @@
}
}
- bool _hasEnsuredReadme = false;
+ bool _hasMaintainedCache = false;
}
typedef SourceRegistry = Source Function(String? name);
diff --git a/test/cache/create_readme_test.dart b/test/cache/create_readme_test.dart
index 70a02a6..ffec057 100644
--- a/test/cache/create_readme_test.dart
+++ b/test/cache/create_readme_test.dart
@@ -1,5 +1,6 @@
import 'dart:io';
+import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import '../descriptor.dart' as d;
@@ -34,4 +35,31 @@
d.file('README.md', contains('https://dart.dev/go/pub-cache'))
]).validate();
});
+
+ test('PUB_CACHE/README.md gets created when compiling a snapshot', () async {
+ final server = await servePackages();
+ server.serve(
+ 'foo',
+ '1.0.0',
+ contents: [d.file('bin/foo.dart', "main() {print('Hello');}")],
+ );
+ await runPub(args: ['global', 'activate', 'foo']);
+ File(p.join(d.sandbox, cachePath, 'README.md')).deleteSync();
+ // Replace the created snapshot with one that really doesn't work with the
+ // current dart.
+ await d.dir(cachePath, [
+ d.dir('global_packages', [
+ d.dir('foo', [
+ d.dir(
+ 'bin',
+ [d.outOfDateSnapshot('foo.dart-3.1.2+3.snapshot')],
+ )
+ ])
+ ])
+ ]).create();
+ await runPub(args: ['global', 'run', 'foo'], output: contains('Hello'));
+ await d.dir(cachePath, [
+ d.file('README.md', contains('https://dart.dev/go/pub-cache'))
+ ]).validate();
+ });
}
diff --git a/test/cache/detect_deprecated_dir_test.dart b/test/cache/detect_deprecated_dir_test.dart
new file mode 100644
index 0000000..3320452
--- /dev/null
+++ b/test/cache/detect_deprecated_dir_test.dart
@@ -0,0 +1,34 @@
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:test/test.dart';
+import 'package:test_descriptor/test_descriptor.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+
+void main() async {
+ test('Detects and warns about old cache dir', skip: !Platform.isWindows,
+ () async {
+ await d.dir('APPDATA', [
+ d.dir('Pub', [d.dir('Cache')])
+ ]).create();
+ final server = await servePackages();
+ server.serve('foo', '1.0.0');
+ await d.appDir(dependencies: {'foo': '^1.0.0'}).create();
+ await pubGet(
+ warning: contains('Found a legacy Pub cache at'),
+ environment: {'APPDATA': d.path('APPDATA')},
+ );
+ expect(
+ File(p.join(sandbox, 'APPDATA', 'Pub', 'Cache', 'DEPRECATED.md'))
+ .existsSync(),
+ isTrue,
+ );
+ server.serve('foo', '2.0.0');
+ await d.appDir(dependencies: {'foo': '^2.0.0'}).create();
+ await pubGet(
+ warning: isNot(contains('Found a legacy Pub cache')),
+ );
+ });
+}