"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter_tools/lib/src/base/file_system.dart" (13 Nov 2020, 8317 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 import 'package:file/file.dart';
    6 import 'package:file/local.dart' as local_fs;
    7 import 'package:meta/meta.dart';
    8 
    9 import 'common.dart' show throwToolExit;
   10 import 'io.dart';
   11 import 'platform.dart';
   12 import 'process.dart';
   13 import 'signals.dart';
   14 
   15 // package:file/local.dart must not be exported. This exposes LocalFileSystem,
   16 // which we override to ensure that temporary directories are cleaned up when
   17 // the tool is killed by a signal.
   18 export 'package:file/file.dart';
   19 
   20 /// Exception indicating that a file that was expected to exist was not found.
   21 class FileNotFoundException implements IOException {
   22   const FileNotFoundException(this.path);
   23 
   24   final String path;
   25 
   26   @override
   27   String toString() => 'File not found: $path';
   28 }
   29 
   30 /// Various convenience file system methods.
   31 class FileSystemUtils {
   32   FileSystemUtils({
   33     @required FileSystem fileSystem,
   34     @required Platform platform,
   35   }) : _fileSystem = fileSystem,
   36        _platform = platform;
   37 
   38   final FileSystem _fileSystem;
   39 
   40   final Platform _platform;
   41 
   42   /// Create the ancestor directories of a file path if they do not already exist.
   43   void ensureDirectoryExists(String filePath) {
   44     final String dirPath = _fileSystem.path.dirname(filePath);
   45     if (_fileSystem.isDirectorySync(dirPath)) {
   46       return;
   47     }
   48     try {
   49       _fileSystem.directory(dirPath).createSync(recursive: true);
   50     } on FileSystemException catch (e) {
   51       throwToolExit('Failed to create directory "$dirPath": ${e.osError.message}');
   52     }
   53   }
   54 
   55   /// Creates `destDir` if needed, then recursively copies `srcDir` to
   56   /// `destDir`, invoking [onFileCopied], if specified, for each
   57   /// source/destination file pair.
   58   ///
   59   /// Skips files if [shouldCopyFile] returns `false`.
   60   void copyDirectorySync(
   61     Directory srcDir,
   62     Directory destDir, {
   63     bool shouldCopyFile(File srcFile, File destFile),
   64     void onFileCopied(File srcFile, File destFile),
   65   }) {
   66     if (!srcDir.existsSync()) {
   67       throw Exception('Source directory "${srcDir.path}" does not exist, nothing to copy');
   68     }
   69 
   70     if (!destDir.existsSync()) {
   71       destDir.createSync(recursive: true);
   72     }
   73 
   74     for (final FileSystemEntity entity in srcDir.listSync()) {
   75       final String newPath = destDir.fileSystem.path.join(destDir.path, entity.basename);
   76       if (entity is File) {
   77         final File newFile = destDir.fileSystem.file(newPath);
   78         if (shouldCopyFile != null && !shouldCopyFile(entity, newFile)) {
   79           continue;
   80         }
   81         newFile.writeAsBytesSync(entity.readAsBytesSync());
   82         onFileCopied?.call(entity, newFile);
   83       } else if (entity is Directory) {
   84         copyDirectorySync(
   85           entity,
   86           destDir.fileSystem.directory(newPath),
   87           shouldCopyFile: shouldCopyFile,
   88           onFileCopied: onFileCopied,
   89         );
   90       } else {
   91         throw Exception('${entity.path} is neither File nor Directory');
   92       }
   93     }
   94   }
   95 
   96   /// Appends a number to a filename in order to make it unique under a
   97   /// directory.
   98   File getUniqueFile(Directory dir, String baseName, String ext) {
   99     final FileSystem fs = dir.fileSystem;
  100     int i = 1;
  101 
  102     while (true) {
  103       final String name = '${baseName}_${i.toString().padLeft(2, '0')}.$ext';
  104       final File file = fs.file(_fileSystem.path.join(dir.path, name));
  105       if (!file.existsSync()) {
  106         return file;
  107       }
  108       i += 1;
  109     }
  110   }
  111 
  112   /// Appends a number to a directory name in order to make it unique under a
  113   /// directory.
  114   Directory getUniqueDirectory(Directory dir, String baseName) {
  115     final FileSystem fs = dir.fileSystem;
  116     int i = 1;
  117 
  118     while (true) {
  119       final String name = '${baseName}_${i.toString().padLeft(2, '0')}';
  120       final Directory directory = fs.directory(_fileSystem.path.join(dir.path, name));
  121       if (!directory.existsSync()) {
  122         return directory;
  123       }
  124       i += 1;
  125     }
  126   }
  127 
  128   /// Return a relative path if [fullPath] is contained by the cwd, else return an
  129   /// absolute path.
  130   String getDisplayPath(String fullPath) {
  131     final String cwd = _fileSystem.currentDirectory.path + _fileSystem.path.separator;
  132     return fullPath.startsWith(cwd) ? fullPath.substring(cwd.length) : fullPath;
  133   }
  134 
  135   /// Escapes [path].
  136   ///
  137   /// On Windows it replaces all '\' with '\\'. On other platforms, it returns the
  138   /// path unchanged.
  139   String escapePath(String path) => _platform.isWindows ? path.replaceAll(r'\', r'\\') : path;
  140 
  141   /// Returns true if the file system [entity] has not been modified since the
  142   /// latest modification to [referenceFile].
  143   ///
  144   /// Returns true, if [entity] does not exist.
  145   ///
  146   /// Returns false, if [entity] exists, but [referenceFile] does not.
  147   bool isOlderThanReference({
  148     @required FileSystemEntity entity,
  149     @required File referenceFile,
  150   }) {
  151     if (!entity.existsSync()) {
  152       return true;
  153     }
  154     return referenceFile.existsSync()
  155         && referenceFile.statSync().modified.isAfter(entity.statSync().modified);
  156   }
  157 
  158   /// Return the absolute path of the user's home directory.
  159   String get homeDirPath {
  160     String path = _platform.isWindows
  161       ? _platform.environment['USERPROFILE']
  162       : _platform.environment['HOME'];
  163     if (path != null) {
  164       path = _fileSystem.path.absolute(path);
  165     }
  166     return path;
  167   }
  168 }
  169 
  170 /// This class extends [local_fs.LocalFileSystem] in order to clean up
  171 /// directories and files that the tool creates under the system temporary
  172 /// directory when the tool exits either normally or when killed by a signal.
  173 class LocalFileSystem extends local_fs.LocalFileSystem {
  174   LocalFileSystem._(Signals signals, List<ProcessSignal> fatalSignals) :
  175     _signals = signals, _fatalSignals = fatalSignals;
  176 
  177   @visibleForTesting
  178   LocalFileSystem.test({
  179     @required Signals signals,
  180     List<ProcessSignal> fatalSignals = Signals.defaultExitSignals,
  181   }) : this._(signals, fatalSignals);
  182 
  183   // Unless we're in a test of this class's signal hanlding features, we must
  184   // have only one instance created with the singleton LocalSignals instance
  185   // and the catchable signals it considers to be fatal.
  186   static LocalFileSystem _instance;
  187   static LocalFileSystem get instance => _instance ??= LocalFileSystem._(
  188     LocalSignals.instance,
  189     Signals.defaultExitSignals,
  190   );
  191 
  192   Directory _systemTemp;
  193   final Map<ProcessSignal, Object> _signalTokens = <ProcessSignal, Object>{};
  194 
  195   @visibleForTesting
  196   static Future<void> dispose() => LocalFileSystem.instance?._dispose();
  197 
  198   Future<void> _dispose() async {
  199     _tryToDeleteTemp();
  200     for (final MapEntry<ProcessSignal, Object> signalToken in _signalTokens.entries) {
  201       await _signals.removeHandler(signalToken.key, signalToken.value);
  202     }
  203     _signalTokens.clear();
  204   }
  205 
  206   final Signals _signals;
  207   final List<ProcessSignal> _fatalSignals;
  208 
  209   void _tryToDeleteTemp() {
  210     try {
  211       if (_systemTemp?.existsSync() ?? false) {
  212         _systemTemp.deleteSync(recursive: true);
  213       }
  214     } on FileSystemException {
  215       // ignore.
  216     }
  217     _systemTemp = null;
  218   }
  219 
  220   // This getter returns a fresh entry under /tmp, like
  221   // /tmp/flutter_tools.abcxyz, then the rest of the tool creates /tmp entries
  222   // under that, like /tmp/flutter_tools.abcxyz/flutter_build_stuff.123456.
  223   // Right before exiting because of a signal or otherwise, we delete
  224   // /tmp/flutter_tools.abcxyz, not the whole of /tmp.
  225   @override
  226   Directory get systemTempDirectory {
  227     if (_systemTemp == null) {
  228       _systemTemp = super.systemTempDirectory.createTempSync(
  229         'flutter_tools.',
  230       )..createSync(recursive: true);
  231       // Make sure that the temporary directory is cleaned up if the tool is
  232       // killed by a signal.
  233       for (final ProcessSignal signal in _fatalSignals) {
  234         final Object token = _signals.addHandler(
  235           signal,
  236           (ProcessSignal _) {
  237             _tryToDeleteTemp();
  238           },
  239         );
  240         _signalTokens[signal] = token;
  241       }
  242       // Make sure that the temporary directory is cleaned up when the tool
  243       // exits normally.
  244       shutdownHooks?.addShutdownHook(
  245         _tryToDeleteTemp,
  246         ShutdownStage.CLEANUP,
  247       );
  248     }
  249     return _systemTemp;
  250   }
  251 }