diff --git a/dev/bots/pubspec.yaml b/dev/bots/pubspec.yaml index 096228d7cd..f4710b6fb0 100644 --- a/dev/bots/pubspec.yaml +++ b/dev/bots/pubspec.yaml @@ -70,4 +70,5 @@ dev_dependencies: mockito: 4.0.0 test_api: 0.2.2 + # PUBSPEC CHECKSUM: 422e diff --git a/packages/flutter_tools/bin/fuchsia_attach.dart b/packages/flutter_tools/bin/fuchsia_attach.dart index eb91c2d072..b752efccb2 100644 --- a/packages/flutter_tools/bin/fuchsia_attach.dart +++ b/packages/flutter_tools/bin/fuchsia_attach.dart @@ -15,7 +15,6 @@ import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/attach.dart'; import 'package:flutter_tools/src/commands/doctor.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart'; -import 'package:flutter_tools/src/run_hot.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart'; final ArgParser parser = ArgParser() @@ -100,8 +99,7 @@ Future main(List args) async { platformKernelDill: platformKernelDill, flutterPatchedSdk: flutterPatchedSdk, ), - HotRunnerConfig: () => HotRunnerConfig()..computeDartDependencies = false, - }, + } ); } diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index eb3c4c09b4..a485772a2d 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart @@ -18,6 +18,7 @@ import '../fuchsia/fuchsia_device.dart'; import '../globals.dart'; import '../ios/devices.dart'; import '../ios/simulators.dart'; +import '../project.dart'; import '../protocol_discovery.dart'; import '../resident_runner.dart'; import '../run_cold.dart'; @@ -129,6 +130,7 @@ class AttachCommand extends FlutterCommand { Future runCommand() async { final String ipv4Loopback = InternetAddress.loopbackIPv4.address; final String ipv6Loopback = InternetAddress.loopbackIPv6.address; + final FlutterProject flutterProject = await FlutterProject.current(); Cache.releaseLockEarly(); @@ -228,6 +230,7 @@ class AttachCommand extends FlutterCommand { projectRootPath: argResults['project-root'], dillOutputPath: argResults['output-dill'], ipv6: usesIpv6, + flutterProject: flutterProject, ) : ColdRunner( flutterDevices, @@ -289,6 +292,7 @@ class HotRunnerFactory { String dillOutputPath, bool stayResident = true, bool ipv6 = false, + FlutterProject flutterProject, }) => HotRunner( devices, target: target, @@ -302,6 +306,7 @@ class HotRunnerFactory { dillOutputPath: dillOutputPath, stayResident: stayResident, ipv6: ipv6, + flutterProject: flutterProject, ); } diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index 074caae063..2f93ae59d2 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -19,6 +19,7 @@ import '../convert.dart'; import '../device.dart'; import '../emulator.dart'; import '../globals.dart'; +import '../project.dart'; import '../resident_runner.dart'; import '../run_cold.dart'; import '../run_hot.dart'; @@ -341,6 +342,7 @@ class AppDomain extends Domain { if (await device.isLocalEmulator && !options.buildInfo.supportsEmulator) { throw '${toTitleCase(options.buildInfo.friendlyModeName)} mode is not supported for emulators.'; } + final FlutterProject flutterProject = await FlutterProject.current(); // We change the current working directory for the duration of the `start` command. final Directory cwd = fs.currentDirectory; @@ -368,6 +370,7 @@ class AppDomain extends Domain { dillOutputPath: dillOutputPath, ipv6: ipv6, hostIsIde: true, + flutterProject: flutterProject, ); } else { runner = ColdRunner( diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 507ea456b2..4edaf2162a 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -13,6 +13,7 @@ import '../cache.dart'; import '../device.dart'; import '../globals.dart'; import '../ios/mac.dart'; +import '../project.dart'; import '../resident_runner.dart'; import '../run_cold.dart'; import '../run_hot.dart'; @@ -280,6 +281,7 @@ class RunCommand extends RunCommandBase { // Enable hot mode by default if `--no-hot` was not passed and we are in // debug mode. final bool hotMode = shouldUseHotMode(); + final FlutterProject flutterProject = await FlutterProject.current(); writePidFile(argResults['pid-file']); @@ -389,6 +391,7 @@ class RunCommand extends RunCommandBase { saveCompilationTrace: argResults['train'], stayResident: stayResident, ipv6: ipv6, + flutterProject: flutterProject, ); } else { runner = ColdRunner( diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index 254826ab70..a019bb8e68 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -1093,7 +1093,7 @@ class PubspecDependency extends PubspecLine { /// Generates the File object for the pubspec.yaml file of a given Directory. File _pubspecFor(Directory directory) { - return fs.file('${directory.path}/pubspec.yaml'); + return fs.file(fs.path.join(directory.path, 'pubspec.yaml')); } /// Generates the source of a fake pubspec.yaml file given a list of diff --git a/packages/flutter_tools/lib/src/dart/dependencies.dart b/packages/flutter_tools/lib/src/dart/dependencies.dart deleted file mode 100644 index 378081a92e..0000000000 --- a/packages/flutter_tools/lib/src/dart/dependencies.dart +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// TODO(dnfield): This will be removed when @jonahwilliams' work on build lands -// ignore: deprecated_member_use -import 'package:analyzer/analyzer.dart' as analyzer; - -import '../base/file_system.dart'; -import '../dart/package_map.dart'; - -// List of flutter specific environment configurations. -// See https://github.com/munificent/dep-interface-libraries/blob/master/Proposal.md -// We will populate this list as required. Potentially, all of dart:* libraries -// supported by flutter would end up here. -final List _configurationConstants = ['dart.library.io']; - -String _dottedNameToString(analyzer.DottedName dottedName) { - String result = ''; - for (analyzer.SimpleIdentifier identifier in dottedName.components) { - if (result.isEmpty) { - result += identifier.token.lexeme; - } else { - result += '.' + identifier.token.lexeme; - } - } - return result; -} - -class DartDependencySetBuilder { - DartDependencySetBuilder(String mainScriptPath, String packagesFilePath) - : _mainScriptPath = canonicalizePath(mainScriptPath), - _mainScriptUri = fs.path.toUri(mainScriptPath), - _packagesFilePath = canonicalizePath(packagesFilePath); - - final String _mainScriptPath; - final String _packagesFilePath; - - final Uri _mainScriptUri; - - Set build() { - final List dependencies = [_mainScriptPath, _packagesFilePath]; - final List toProcess = [_mainScriptUri]; - final PackageMap packageMap = PackageMap(_packagesFilePath); - - while (toProcess.isNotEmpty) { - final Uri currentUri = toProcess.removeLast(); - final analyzer.CompilationUnit unit = _parse(currentUri.toFilePath()); - for (analyzer.Directive directive in unit.directives) { - if (!(directive is analyzer.UriBasedDirective)) - continue; - - String uriAsString; - if (directive is analyzer.NamespaceDirective) { - final analyzer.NamespaceDirective namespaceDirective = directive; - // If the directive is a conditional import directive, we should - // select the imported uri based on the condition. - for (analyzer.Configuration configuration in namespaceDirective.configurations) { - if (_configurationConstants.contains(_dottedNameToString(configuration.name))) { - uriAsString = configuration.uri.stringValue; - break; - } - } - } - if (uriAsString == null) { - final analyzer.UriBasedDirective uriBasedDirective = directive; - uriAsString = uriBasedDirective.uri.stringValue; - } - - Uri uri; - try { - uri = Uri.parse(uriAsString); - } on FormatException { - throw DartDependencyException('Unable to parse URI: $uriAsString'); - } - Uri resolvedUri = analyzer.resolveRelativeUri(currentUri, uri); - if (resolvedUri.scheme.startsWith('dart')) - continue; - if (resolvedUri.scheme == 'package') { - final Uri newResolvedUri = packageMap.uriForPackage(resolvedUri); - if (newResolvedUri == null) { - throw DartDependencyException( - 'The following Dart file:\n' - ' ${currentUri.toFilePath()}\n' - '...refers, in an import, to the following library:\n' - ' $resolvedUri\n' - 'That library is in a package that is not known. Maybe you forgot to ' - 'mention it in your pubspec.yaml file?' - ); - } - resolvedUri = newResolvedUri; - } - final String path = canonicalizePath(resolvedUri.toFilePath()); - if (!dependencies.contains(path)) { - if (!fs.isFileSync(path)) { - throw DartDependencyException( - 'The following Dart file:\n' - ' ${currentUri.toFilePath()}\n' - '...refers, in an import, to the following library:\n' - ' $path\n' - 'Unfortunately, that library does not appear to exist on your file system.' - ); - } - dependencies.add(path); - toProcess.add(resolvedUri); - } - } - } - return dependencies.toSet(); - } - - analyzer.CompilationUnit _parse(String path) { - String body; - try { - body = fs.file(path).readAsStringSync(); - } on FileSystemException catch (error) { - throw DartDependencyException( - 'Could not read "$path" when determining Dart dependencies.', - error, - ); - } - try { - return analyzer.parseDirectives(body, name: path); - } on analyzer.AnalyzerError catch (error) { - throw DartDependencyException( - 'When trying to parse this Dart file to find its dependencies:\n' - ' $path\n' - '...the analyzer failed with the following error:\n' - ' ${error.toString().trimRight()}', - error, - ); - } on analyzer.AnalyzerErrorGroup catch (error) { - throw DartDependencyException( - 'When trying to parse this Dart file to find its dependencies:\n' - ' $path\n' - '...the analyzer failed with the following error:\n' - ' ${error.toString().trimRight()}', - error, - ); - } - } -} - -class DartDependencyException implements Exception { - DartDependencyException(this.message, [this.parent]); - final String message; - final Exception parent; - @override - String toString() => message; -} diff --git a/packages/flutter_tools/lib/src/dependency_checker.dart b/packages/flutter_tools/lib/src/dependency_checker.dart deleted file mode 100644 index 8f0aea7446..0000000000 --- a/packages/flutter_tools/lib/src/dependency_checker.dart +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright 2016 The Chromium 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 'asset.dart'; -import 'base/file_system.dart'; -import 'dart/dependencies.dart'; -import 'globals.dart'; - -class DependencyChecker { - DependencyChecker(this.builder, this.assets); - - final DartDependencySetBuilder builder; - final Set _dependencies = {}; - final AssetBundle assets; - - /// Returns [true] if any components have been modified after [threshold] or - /// if it cannot be determined. - bool check(DateTime threshold) { - _dependencies.clear(); - // Build the set of Dart dependencies. - try { - _dependencies.addAll(builder.build()); - } catch (e, st) { - printTrace('DependencyChecker: error determining .dart dependencies:\n$e\n$st'); - return true; - } - // TODO(johnmccutchan): Extract dependencies from the AssetBundle too. - - // Check all dependency modification times. - for (String path in _dependencies) { - final File file = fs.file(path); - final FileStat stat = file.statSync(); - if (stat.type == FileSystemEntityType.notFound) { - printTrace('DependencyChecker: Error stating $path.'); - return true; - } - if (stat.modified.isAfter(threshold)) { - printTrace('DependencyChecker: $path is newer than $threshold'); - return true; - } - } - printTrace('DependencyChecker: nothing is modified after $threshold.'); - return false; - } -} diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index 7940e8bb06..407a51b120 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart @@ -30,8 +30,6 @@ DevFSConfig get devFSConfig => context[DevFSConfig]; /// Common superclass for content copied to the device. abstract class DevFSContent { - bool _exists = true; - /// Return true if this is the first time this method is called /// or if the entry has been modified since this method was last called. bool get isModified; @@ -59,13 +57,6 @@ abstract class DevFSContent { class DevFSFileContent extends DevFSContent { DevFSFileContent(this.file); - static DevFSFileContent clone(DevFSFileContent fsFileContent) { - final DevFSFileContent newFsFileContent = DevFSFileContent(fsFileContent.file); - newFsFileContent._linkTarget = fsFileContent._linkTarget; - newFsFileContent._fileStat = fsFileContent._fileStat; - return newFsFileContent; - } - final FileSystemEntity file; FileSystemEntity _linkTarget; FileStat _fileStat; @@ -218,7 +209,6 @@ abstract class DevFSOperations { Future create(String fsName); Future destroy(String fsName); Future writeFile(String fsName, Uri deviceUri, DevFSContent content); - Future deleteFile(String fsName, Uri deviceUri); } /// An implementation of [DevFSOperations] that speaks to the @@ -261,11 +251,6 @@ class ServiceProtocolDevFSOperations implements DevFSOperations { printTrace('DevFS: Failed to write $deviceUri: $error'); } } - - @override - Future deleteFile(String fsName, Uri deviceUri) async { - // TODO(johnmccutchan): Add file deletion to the devFS protocol. - } } class DevFSException implements Exception { @@ -288,16 +273,14 @@ class _DevFSHttpWriter { int _inFlight = 0; Map _outstanding; Completer _completer; - HttpClient _client; + final HttpClient _client = HttpClient(); Future write(Map entries) async { - _client = HttpClient(); _client.maxConnectionsPerHost = kMaxInFlight; _completer = Completer(); _outstanding = Map.from(entries); _scheduleWrites(); await _completer.future; - _client.close(); } void _scheduleWrites() { @@ -405,9 +388,6 @@ class DevFS { final Map _entries = {}; final Set assetPathsToEvict = {}; - final List>> _pendingOperations = - >>[]; - Uri _baseUri; Uri get baseUri => _baseUri; @@ -453,66 +433,33 @@ class DevFS { DateTime firstBuildTime, bool bundleFirstUpload = false, bool bundleDirty = false, - Set fileFilter, @required ResidentCompiler generator, String dillOutputPath, @required bool trackWidgetCreation, bool fullRestart = false, String projectRootPath, @required String pathToReload, + @required List invalidatedFiles, }) async { assert(trackWidgetCreation != null); assert(generator != null); - // Mark all entries as possibly deleted. - for (DevFSContent content in _entries.values) { - content._exists = false; - } - // Scan workspace, packages, and assets - printTrace('DevFS: Starting sync from $rootDirectory'); - logger.printTrace('Scanning project files'); - await _scanDirectory(rootDirectory, - recursive: true, - fileFilter: fileFilter); - if (fs.isFileSync(_packagesFilePath)) { - printTrace('Scanning package files'); - await _scanPackages(fileFilter); - } if (bundle != null) { printTrace('Scanning asset files'); + // We write the assets into the AssetBundle working dir so that they + // are in the same location in DevFS and the iOS simulator. + final String assetDirectory = getAssetBuildDirectory(); bundle.entries.forEach((String archivePath, DevFSContent content) { - _scanBundleEntry(archivePath, content); + final Uri deviceUri = fs.path.toUri(fs.path.join(assetDirectory, archivePath)); + _entries[deviceUri] = content; }); } - // Handle deletions. - printTrace('Scanning for deleted files'); - final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory()); - final List toRemove = []; - _entries.forEach((Uri deviceUri, DevFSContent content) { - if (!content._exists) { - final Future> operation = - _operations.deleteFile(fsName, deviceUri) - .then>((dynamic v) => v?.cast()); - if (operation != null) - _pendingOperations.add(operation); - toRemove.add(deviceUri); - if (deviceUri.path.startsWith(assetBuildDirPrefix)) { - final String archivePath = deviceUri.path.substring(assetBuildDirPrefix.length); - assetPathsToEvict.add(archivePath); - } - } - }); - if (toRemove.isNotEmpty) { - printTrace('Removing deleted files'); - toRemove.forEach(_entries.remove); - await Future.wait>(_pendingOperations); - _pendingOperations.clear(); - } - // Update modified files - int syncedBytes = 0; + final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory()); final Map dirtyEntries = {}; + + int syncedBytes = 0; _entries.forEach((Uri deviceUri, DevFSContent content) { String archivePath; if (deviceUri.path.startsWith(assetBuildDirPrefix)) @@ -527,30 +474,10 @@ class DevFS { assetPathsToEvict.add(archivePath); } }); - // We run generator even if [dirtyEntries] was empty because we want to - // keep logic of accepting/rejecting generator's output simple: we must - // accept/reject generator's output after every [update] call. Incremental - // run with no changes is supposed to be fast (considering that it is - // initiated by user key press). - final List invalidatedFiles = []; - final Set filesUris = {}; - for (Uri uri in dirtyEntries.keys.toList()) { - if (!uri.path.startsWith(assetBuildDirPrefix)) { - final DevFSContent content = dirtyEntries[uri]; - if (content is DevFSFileContent) { - filesUris.add(uri); - invalidatedFiles.add(content.file.uri.toString()); - syncedBytes -= content.size; - } - } - } - // No need to send source files because all compilation is done on the - // host and result of compilation is single kernel file. - filesUris.forEach(dirtyEntries.remove); - printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files'); if (fullRestart) { generator.reset(); } + printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files'); final CompilerOutput compilerOutput = await generator.recompile( mainPath, invalidatedFiles, @@ -566,228 +493,28 @@ class DevFS { ? fs.path.relative(pathToReload, from: projectRootPath) : pathToReload, ); - if (!dirtyEntries.containsKey(entryUri)) { - final DevFSFileContent content = DevFSFileContent(fs.file(compiledBinary)); - dirtyEntries[entryUri] = content; - syncedBytes += content.size; - } + final DevFSFileContent content = DevFSFileContent(fs.file(compiledBinary)); + syncedBytes += content.size; + dirtyEntries[entryUri] = content; } } + printTrace('Updating files'); if (dirtyEntries.isNotEmpty) { - printTrace('Updating files'); - if (_httpWriter != null) { - try { - await _httpWriter.write(dirtyEntries); - } on SocketException catch (socketException, stackTrace) { - printTrace('DevFS sync failed. Lost connection to device: $socketException'); - throw DevFSException('Lost connection to device.', socketException, stackTrace); - } catch (exception, stackTrace) { - printError('Could not update files on device: $exception'); - throw DevFSException('Sync failed', exception, stackTrace); - } - } else { - // Make service protocol requests for each. - dirtyEntries.forEach((Uri deviceUri, DevFSContent content) { - final Future> operation = - _operations.writeFile(fsName, deviceUri, content) - .then>((dynamic v) => v?.cast()); - if (operation != null) - _pendingOperations.add(operation); - }); - await Future.wait>(_pendingOperations, eagerError: true); - _pendingOperations.clear(); + try { + await _httpWriter.write(dirtyEntries); + } on SocketException catch (socketException, stackTrace) { + printTrace('DevFS sync failed. Lost connection to device: $socketException'); + throw DevFSException('Lost connection to device.', socketException, stackTrace); + } catch (exception, stackTrace) { + printError('Could not update files on device: $exception'); + throw DevFSException('Sync failed', exception, stackTrace); } } - printTrace('DevFS: Sync finished'); return UpdateFSReport(success: true, syncedBytes: syncedBytes, - invalidatedSourcesCount: invalidatedFiles.length); - } - - void _scanFile(Uri deviceUri, FileSystemEntity file) { - final DevFSContent content = _entries.putIfAbsent(deviceUri, () => DevFSFileContent(file)); - content._exists = true; - } - - void _scanBundleEntry(String archivePath, DevFSContent content) { - // We write the assets into the AssetBundle working dir so that they - // are in the same location in DevFS and the iOS simulator. - final Uri deviceUri = fs.path.toUri(fs.path.join(getAssetBuildDirectory(), archivePath)); - - _entries[deviceUri] = content; - content._exists = true; - } - - bool _shouldIgnore(Uri deviceUri) { - final List ignoredUriPrefixes = ['android/', - _asUriPath(getBuildDirectory()), - 'ios/', - '.pub/']; - for (String ignoredUriPrefix in ignoredUriPrefixes) { - if (deviceUri.path.startsWith(ignoredUriPrefix)) - return true; - } - return false; - } - - bool _shouldSkip( - FileSystemEntity file, - String relativePath, - Uri directoryUriOnDevice, { - bool ignoreDotFiles = true, - }) { - if (file is Directory) { - // Skip non-files. - return true; - } - assert((file is Link) || (file is File)); - final String basename = fs.path.basename(file.path); - if (ignoreDotFiles && basename.startsWith('.')) { - // Skip dot files, but not the '.packages' file (even though in dart1 - // mode devfs['.packages'] will be overwritten with synthesized string content). - return basename != '.packages'; - } - return false; - } - - Uri _directoryUriOnDevice(Uri directoryUriOnDevice, Directory directory) { - if (directoryUriOnDevice == null) { - final String relativeRootPath = fs.path.relative(directory.path, from: rootDirectory.path); - if (relativeRootPath == '.') { - directoryUriOnDevice = Uri(); - } else { - directoryUriOnDevice = fs.path.toUri(relativeRootPath); - } - } - return directoryUriOnDevice; - } - - /// Scan all files from the [fileFilter] that are contained in [directory] and - /// pass various filters (e.g. ignoreDotFiles). - Future _scanFilteredDirectory( - Set fileFilter, - Directory directory, { - Uri directoryUriOnDevice, - bool ignoreDotFiles = true, - }) async { - directoryUriOnDevice = - _directoryUriOnDevice(directoryUriOnDevice, directory); - try { - final String absoluteDirectoryPath = canonicalizePath(directory.path); - // For each file in the file filter. - for (String filePath in fileFilter) { - if (!filePath.startsWith(absoluteDirectoryPath)) { - // File is not in this directory. Skip. - continue; - } - final String relativePath = - fs.path.relative(filePath, from: directory.path); - final FileSystemEntity file = fs.file(filePath); - if (_shouldSkip(file, relativePath, directoryUriOnDevice, ignoreDotFiles: ignoreDotFiles)) { - continue; - } - final Uri deviceUri = directoryUriOnDevice.resolveUri(fs.path.toUri(relativePath)); - if (!_shouldIgnore(deviceUri)) - _scanFile(deviceUri, file); - } - } on FileSystemException catch (e) { - _printScanDirectoryError(directory.path, e); - return false; - } - return true; - } - - /// Scan all files in [directory] that pass various filters (e.g. ignoreDotFiles). - Future _scanDirectory( - Directory directory, { - Uri directoryUriOnDevice, - bool recursive = false, - bool ignoreDotFiles = true, - Set fileFilter, - }) async { - directoryUriOnDevice = _directoryUriOnDevice(directoryUriOnDevice, directory); - if ((fileFilter != null) && fileFilter.isNotEmpty) { - // When the fileFilter isn't empty, we can skip crawling the directory - // tree and instead use the fileFilter as the source of potential files. - return _scanFilteredDirectory(fileFilter, - directory, - directoryUriOnDevice: directoryUriOnDevice, - ignoreDotFiles: ignoreDotFiles); - } - try { - final Stream files = - directory.list(recursive: recursive, followLinks: false); - await for (FileSystemEntity file in files) { - if (!devFSConfig.noDirectorySymlinks && (file is Link)) { - // Check if this is a symlink to a directory and skip it. - try { - final FileSystemEntityType linkType = - fs.statSync(file.resolveSymbolicLinksSync()).type; - if (linkType == FileSystemEntityType.directory) - continue; - } on FileSystemException catch (e) { - _printScanDirectoryError(file.path, e); - continue; - } - } - final String relativePath = - fs.path.relative(file.path, from: directory.path); - if (_shouldSkip(file, relativePath, directoryUriOnDevice, ignoreDotFiles: ignoreDotFiles)) { - continue; - } - final Uri deviceUri = directoryUriOnDevice.resolveUri(fs.path.toUri(relativePath)); - if (!_shouldIgnore(deviceUri)) - _scanFile(deviceUri, file); - } - } on FileSystemException catch (e) { - _printScanDirectoryError(directory.path, e); - return false; - } - return true; - } - - void _printScanDirectoryError(String path, Exception e) { - printError( - 'Error while scanning $path.\n' - 'Hot Reload might not work until the following error is resolved:\n' - '$e\n' - ); - } - - Future _scanPackages(Set fileFilter) async { - StringBuffer sb; - final PackageMap packageMap = PackageMap(_packagesFilePath); - - for (String packageName in packageMap.map.keys) { - final Uri packageUri = packageMap.map[packageName]; - final String packagePath = fs.path.fromUri(packageUri); - final Directory packageDirectory = fs.directory(packageUri); - Uri directoryUriOnDevice = fs.path.toUri(fs.path.join('packages', packageName) + fs.path.separator); - bool packageExists = packageDirectory.existsSync(); - - if (!packageExists) { - // If the package directory doesn't exist at all, we ignore it. - continue; - } - - if (fs.path.isWithin(rootDirectory.path, packagePath)) { - // We already scanned everything under the root directory. - directoryUriOnDevice = fs.path.toUri( - fs.path.relative(packagePath, from: rootDirectory.path) + fs.path.separator - ); - } else { - packageExists = - await _scanDirectory(packageDirectory, - directoryUriOnDevice: directoryUriOnDevice, - recursive: true, - fileFilter: fileFilter); - } - if (packageExists) { - sb ??= StringBuffer(); - sb.writeln('$packageName:$directoryUriOnDevice'); - } - } + invalidatedSourcesCount: invalidatedFiles.length); } } + /// Converts a platform-specific file path to a platform-independent Uri path. String _asUriPath(String filePath) => fs.path.toUri(filePath).path + '/'; diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 703d6d708a..88c11d46bf 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -18,9 +18,7 @@ import 'base/utils.dart'; import 'build_info.dart'; import 'codegen.dart'; import 'compile.dart'; -import 'dart/dependencies.dart'; import 'dart/package_map.dart'; -import 'dependency_checker.dart'; import 'devfs.dart'; import 'device.dart'; import 'globals.dart'; @@ -444,10 +442,10 @@ class FlutterDevice { DateTime firstBuildTime, bool bundleFirstUpload = false, bool bundleDirty = false, - Set fileFilter, bool fullRestart = false, String projectRootPath, String pathToReload, + @required List invalidatedFiles, }) async { final Status devFSStatus = logger.startProgress( 'Syncing files to device ${device.name}...', @@ -462,13 +460,13 @@ class FlutterDevice { firstBuildTime: firstBuildTime, bundleFirstUpload: bundleFirstUpload, bundleDirty: bundleDirty, - fileFilter: fileFilter, generator: generator, fullRestart: fullRestart, dillOutputPath: dillOutputPath, trackWidgetCreation: trackWidgetCreation, projectRootPath: projectRootPath, pathToReload: pathToReload, + invalidatedFiles: invalidatedFiles, ); } on DevFSException { devFSStatus.cancel(); @@ -945,21 +943,19 @@ abstract class ResidentRunner { } bool hasDirtyDependencies(FlutterDevice device) { - /// When using the build system, dependency analysis is handled by build - /// runner instead. - if (experimentalBuildEnabled) { - return false; - } - final DartDependencySetBuilder dartDependencySetBuilder = - DartDependencySetBuilder(mainPath, packagesFilePath); - final DependencyChecker dependencyChecker = - DependencyChecker(dartDependencySetBuilder, assetBundle); if (device.package.packagesFile == null || !device.package.packagesFile.existsSync()) { return true; } - final DateTime lastBuildTime = device.package.packagesFile.statSync().modified; - - return dependencyChecker.check(lastBuildTime); + // Leave pubspec null to check all dependencies. + final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator(device.package.packagesFile.path, null); + projectFileInvalidator.findInvalidated(); + final int lastBuildTime = device.package.packagesFile.statSync().modified.millisecondsSinceEpoch; + for (int updateTime in projectFileInvalidator.updateTime.values) { + if (updateTime > lastBuildTime) { + return true; + } + } + return false; } Future preStop() async { } diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 53a4f3681e..e6da33b427 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -7,29 +7,28 @@ import 'dart:async'; import 'package:json_rpc_2/error_code.dart' as rpc_error_code; import 'package:json_rpc_2/json_rpc_2.dart' as rpc; import 'package:meta/meta.dart'; +import 'package:yaml/yaml.dart'; import 'base/common.dart'; import 'base/context.dart'; import 'base/file_system.dart'; import 'base/logger.dart'; +import 'base/platform.dart'; import 'base/terminal.dart'; import 'base/utils.dart'; import 'build_info.dart'; -import 'codegen.dart'; import 'compile.dart'; import 'convert.dart'; -import 'dart/dependencies.dart'; -import 'dart/pub.dart'; +import 'dart/package_map.dart'; import 'devfs.dart'; import 'device.dart'; import 'globals.dart'; +import 'project.dart'; import 'resident_runner.dart'; import 'usage.dart'; import 'vmservice.dart'; class HotRunnerConfig { - /// Should the hot runner compute the minimal Dart dependencies? - bool computeDartDependencies = true; /// Should the hot runner assume that the minimal Dart dependencies do not change? bool stableDartDependencies = false; /// A hook for implementations to perform any necessary initialization prior @@ -71,6 +70,7 @@ class HotRunner extends ResidentRunner { bool saveCompilationTrace = false, bool stayResident = true, bool ipv6 = false, + FlutterProject flutterProject, }) : super(devices, target: target, debuggingOptions: debuggingOptions, @@ -79,14 +79,19 @@ class HotRunner extends ResidentRunner { packagesFilePath: packagesFilePath, saveCompilationTrace: saveCompilationTrace, stayResident: stayResident, - ipv6: ipv6); + ipv6: ipv6) { + fileInvalidator = ProjectFileInvalidator( + packagesFilePath ?? fs.path.absolute(PackageMap.globalPackagesPath), + flutterProject, + ); + } final bool benchmarkMode; final File applicationBinary; final bool hostIsIde; bool _didAttach = false; - Set _dartDependencies; final String dillOutputPath; + ProjectFileInvalidator fileInvalidator; final Map> benchmarkData = >{}; // The initial launch is from a snapshot. @@ -98,54 +103,8 @@ class HotRunner extends ResidentRunner { benchmarkData[name].add(value); } - Future _refreshDartDependencies() async { - if (!hotRunnerConfig.computeDartDependencies) { - // Disabled. - return true; - } - if (_dartDependencies != null) { - // Already computed. - return true; - } - - try { - // Will return immediately if pubspec.yaml is up-to-date. - await pubGet( - context: PubContext.pubGet, - directory: projectRootPath, - ); - } on ToolExit catch (error) { - printError( - 'Unable to reload your application because "flutter packages get" failed to update ' - 'package dependencies.\n' - '$error' - ); - return false; - } - - /// When using the build system, dependency analysis is handled by build - /// runner instead. - if (experimentalBuildEnabled) { - return true; - } - final DartDependencySetBuilder dartDependencySetBuilder = DartDependencySetBuilder(mainPath, packagesFilePath); - try { - _dartDependencies = Set.from(dartDependencySetBuilder.build()); - } on DartDependencyException catch (error) { - printError( - 'Your application could not be compiled, because its dependencies could not be established.\n' - '$error' - ); - return false; - } - return true; - } - - Future _reloadSourcesService( - String isolateId, { - bool force = false, - bool pause = false, - }) async { + Future _reloadSourcesService(String isolateId, + { bool force = false, bool pause = false }) async { // TODO(cbernaschina): check that isolateId is the id of the UI isolate. final OperationResult result = await restart(pauseAfterRestart: pause); if (!result.isOk) { @@ -257,7 +216,6 @@ class HotRunner extends ResidentRunner { // Measure time to perform a hot restart. printStatus('Benchmarking hot restart'); await restart(fullRestart: true); - // TODO(johnmccutchan): Modify script entry point. printStatus('Benchmarking hot reload'); // Measure time to perform a hot reload. await restart(fullRestart: false); @@ -296,12 +254,6 @@ class HotRunner extends ResidentRunner { return 1; } - // Determine the Dart dependencies eagerly. - if (!await _refreshDartDependencies()) { - // Some kind of source level error or missing file in the Dart code. - return 1; - } - firstBuildTime = DateTime.now(); for (FlutterDevice device in flutterDevices) { @@ -336,8 +288,6 @@ class HotRunner extends ResidentRunner { result = await restart(fullRestart: false); } if (!result.isOk) { - // TODO(johnmccutchan): Attempt to determine the number of errors that - // occurred and tighten this message. printStatus('Try again after fixing the above error(s).', emphasis: true); } } else if (lower == 'l') { @@ -364,10 +314,6 @@ class HotRunner extends ResidentRunner { } Future _updateDevFS({ bool fullRestart = false }) async { - if (!await _refreshDartDependencies()) { - // Did not update DevFS because of a Dart source error. - return UpdateFSReport(success: false); - } final bool isFirstUpload = assetBundle.wasBuiltOnce() == false; final bool rebuildBundle = assetBundle.needsBuild(); if (rebuildBundle) { @@ -376,7 +322,7 @@ class HotRunner extends ResidentRunner { if (result != 0) return UpdateFSReport(success: false); } - + final List invalidatedFiles = fileInvalidator.findInvalidated(); final UpdateFSReport results = UpdateFSReport(success: true); for (FlutterDevice device in flutterDevices) { results.incorporateResults(await device.updateDevFS( @@ -386,39 +332,15 @@ class HotRunner extends ResidentRunner { firstBuildTime: firstBuildTime, bundleFirstUpload: isFirstUpload, bundleDirty: isFirstUpload == false && rebuildBundle, - fileFilter: _dartDependencies, fullRestart: fullRestart, projectRootPath: projectRootPath, pathToReload: getReloadPath(fullRestart: fullRestart), + invalidatedFiles: invalidatedFiles, )); } - if (!results.success) { - return results; - } - - if (!hotRunnerConfig.stableDartDependencies) { - // Clear the set after the sync so they are recomputed next time. - _dartDependencies = null; - } return results; } - Future _evictDirtyAssets() { - final List>> futures = >>[]; - for (FlutterDevice device in flutterDevices) { - if (device.devFS.assetPathsToEvict.isEmpty) - continue; - if (device.views.first.uiIsolate == null) { - printError('Application isolate not found for $device'); - continue; - } - for (String assetPath in device.devFS.assetPathsToEvict) - futures.add(device.views.first.uiIsolate.flutterEvictAsset(assetPath)); - device.devFS.assetPathsToEvict.clear(); - } - return Future.wait>(futures); - } - void _resetDirtyAssets() { for (FlutterDevice device in flutterDevices) device.devFS.assetPathsToEvict.clear(); @@ -811,9 +733,9 @@ class HotRunner extends ResidentRunner { return OperationResult(OperationResult.ok.code, reloadMessage); } } - assert(reassembleViews.isNotEmpty); printTrace('Evicting dirty assets'); await _evictDirtyAssets(); + assert(reassembleViews.isNotEmpty); printTrace('Reassembling application'); bool failedReassemble = false; final List> futures = >[]; @@ -886,7 +808,6 @@ class HotRunner extends ResidentRunner { // Only report timings if we reloaded a single view without any errors. if ((reassembleViews.length == 1) && !failedReassemble && shouldReportReloadTime) flutterUsage.sendTiming('hot', 'reload', reloadDuration); - return OperationResult( failedReassemble ? 1 : OperationResult.ok.code, reloadMessage, @@ -965,6 +886,23 @@ class HotRunner extends ResidentRunner { } } + Future _evictDirtyAssets() { + final List>> futures = >>[]; + for (FlutterDevice device in flutterDevices) { + if (device.devFS.assetPathsToEvict.isEmpty) + continue; + if (device.views.first.uiIsolate == null) { + printError('Application isolate not found for $device'); + continue; + } + for (String assetPath in device.devFS.assetPathsToEvict) { + futures.add(device.views.first.uiIsolate.flutterEvictAsset(assetPath)); + } + device.devFS.assetPathsToEvict.clear(); + } + return Future.wait>(futures); + } + @override Future cleanupAfterSignal() async { await stopEchoingDeviceLog(); @@ -988,3 +926,106 @@ class HotRunner extends ResidentRunner { await stopEchoingDeviceLog(); } } + +class ProjectFileInvalidator { + ProjectFileInvalidator(this._packagesPath, this._flutterProject) { + final File packagesFile = fs.file(_packagesPath); + if (packagesFile.existsSync()) { + _packagesUpdateTime = packagesFile.statSync().modified.millisecondsSinceEpoch; + _packageMap = PackageMap(_packagesPath).map; + } else { + _packagesUpdateTime = -1; + _packageMap = const {}; + } + _computePackageMap(_packageMap, _flutterProject); + } + + // Used to avoid watching pubspec directories. This will not change even with pub upgrade, + // because that actually switches the directory and requires a corresponding + // update to .packages + static const String _pubCachePathLinuxAndWindows = '.pub-cache'; + static const String _pubCachePathWindows = 'Pub/Cache'; + + Map _packageMap; + final String _packagesPath; + final FlutterProject _flutterProject; + final Map _updateTime = {}; + int _packagesUpdateTime; + + Map get updateTime => _updateTime; + + @visibleForTesting + Map get packageMap => _packageMap; + + static void _computePackageMap(Map packageMap, FlutterProject flutterProject) { + if (flutterProject != null && flutterProject.pubspecFile.existsSync()) { + try { + final YamlMap pubspec = loadYamlDocument(flutterProject.pubspecFile.readAsStringSync()).contents; + final YamlMap dependencies = pubspec['dependencies']; + final Set relevantDependencies = Set.from(dependencies.keys); + // Remove any packages which were tagged as dev dependenices, + // But don't remove the app itself! + for (String packageName in packageMap.keys.toList()) { + if (!relevantDependencies.contains(packageName) && packageName != flutterProject.manifest.appName) { + packageMap.remove(packageName); + continue; + } + } + } catch (err) { + // If we detect a pubspec formatting problem, fallback to the packages file. + } + } + // Remove any packages which are derived from the pub cache. + for (String packageName in packageMap.keys.toList()) { + final String path = packageMap[packageName].path; + if ((platform.isWindows && path.contains(_pubCachePathWindows)) + || path.contains(_pubCachePathLinuxAndWindows)) { + packageMap.remove(packageName); + } + } + } + + List findInvalidated() { + final File packagesFile = fs.file(_packagesPath); + if (packagesFile.existsSync()) { + final int newPackagesUpdateTime = packagesFile.statSync().modified.millisecondsSinceEpoch; + // Hot reloading with an updated package will often times kill a non-trivial + // appliction. This _might_ work, given certain application size and package + // constraints, so instead of exiting we print a warning so that the user has + // some hint on what went wrong. + if (newPackagesUpdateTime > _packagesUpdateTime) { + printError('Warning: updated dependencies detected. The Flutter application will require a restart to safely use new packages.'); + } + _packagesUpdateTime = newPackagesUpdateTime; + } + final List invalidatedFiles = []; + for (String packageName in _packageMap.keys) { + final Uri packageUri =_packageMap[packageName]; + _scanDirectory(packageUri, invalidatedFiles); + } + return invalidatedFiles; + } + + void _scanDirectory(Uri path, List invalidatedFiles) { + final Directory directory = fs.directory(path); + if (!directory.existsSync()) { + return; + } + for (FileSystemEntity entity in directory.listSync(recursive: true)) { + if (entity.path.endsWith('.dart')) { + final int oldUpdatedAt = _updateTime[entity.path]; + final int updatedAt = fs.statSync(entity.path).modified.millisecondsSinceEpoch; + if (oldUpdatedAt == null || updatedAt > oldUpdatedAt) { + // On windows convert to file uri in expected format. + if (platform.isWindows) { + final Uri uri = Uri.file(entity.path, windows: platform.isWindows); + invalidatedFiles.add(uri.toString()); + } else { + invalidatedFiles.add(entity.path); + } + } + _updateTime[entity.path] = updatedAt; + } + } + } +} diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 9eae2de8c7..a1d023d28a 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -9,7 +9,6 @@ environment: dependencies: # To update these, use "flutter update-packages --force-upgrade". - analyzer: 0.35.3 archive: 2.0.8 args: 1.5.1 bsdiff: 0.1.0 @@ -53,6 +52,7 @@ dependencies: build_modules: 1.0.9 build_daemon: 0.4.2 + analyzer: 0.35.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.0.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" bazel_worker: 0.1.20 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -100,8 +100,8 @@ dev_dependencies: collection: 1.14.11 mockito: 4.0.0 file_testing: 2.1.0 - test: 1.5.3 vm_service_lib: 3.14.2 + test: 1.5.3 build_runner: 1.2.8 build_vm_compilers: 0.1.1+5 build_test: 0.10.6 diff --git a/packages/flutter_tools/test/commands/attach_test.dart b/packages/flutter_tools/test/commands/attach_test.dart index 693d71e3df..87dcb92009 100644 --- a/packages/flutter_tools/test/commands/attach_test.dart +++ b/packages/flutter_tools/test/commands/attach_test.dart @@ -120,6 +120,7 @@ void main() { debuggingOptions: anyNamed('debuggingOptions'), packagesFilePath: anyNamed('packagesFilePath'), usesTerminalUI: anyNamed('usesTerminalUI'), + flutterProject: anyNamed('flutterProject'), ipv6: false, ), ).thenReturn(mockHotRunner); @@ -151,6 +152,7 @@ void main() { debuggingOptions: anyNamed('debuggingOptions'), packagesFilePath: anyNamed('packagesFilePath'), usesTerminalUI: anyNamed('usesTerminalUI'), + flutterProject: anyNamed('flutterProject'), ipv6: false, ), )..called(1); @@ -225,6 +227,7 @@ void main() { debuggingOptions: anyNamed('debuggingOptions'), packagesFilePath: anyNamed('packagesFilePath'), usesTerminalUI: anyNamed('usesTerminalUI'), + flutterProject: anyNamed('flutterProject'), ipv6: false, )).thenReturn(mockHotRunner); @@ -254,6 +257,7 @@ void main() { debuggingOptions: anyNamed('debuggingOptions'), packagesFilePath: anyNamed('packagesFilePath'), usesTerminalUI: anyNamed('usesTerminalUI'), + flutterProject: anyNamed('flutterProject'), ipv6: false, )).called(1); }, overrides: { diff --git a/packages/flutter_tools/test/dart_dependencies_test.dart b/packages/flutter_tools/test/dart_dependencies_test.dart deleted file mode 100644 index dbfdc26509..0000000000 --- a/packages/flutter_tools/test/dart_dependencies_test.dart +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2016 The Chromium 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 'package:flutter_tools/src/dart/dependencies.dart'; -import 'package:flutter_tools/src/base/file_system.dart'; - -import 'src/common.dart'; -import 'src/context.dart'; - -void main() { - group('DartDependencySetBuilder', () { - final String dataPath = fs.path.join( - getFlutterRoot(), - 'packages', - 'flutter_tools', - 'test', - 'data', - 'dart_dependencies_test', - ); - - testUsingContext('good', () { - final String testPath = fs.path.join(dataPath, 'good'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - final Set dependencies = builder.build(); - expect(dependencies.contains(canonicalizePath(mainPath)), isTrue); - expect(dependencies.contains(canonicalizePath(fs.path.join(testPath, 'foo.dart'))), isTrue); - }); - - testUsingContext('syntax_error', () { - final String testPath = fs.path.join(dataPath, 'syntax_error'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - try { - builder.build(); - fail('expect an exception to be thrown.'); - } on DartDependencyException catch (error) { - expect(error.toString(), contains('foo.dart: Expected a string literal')); - } - }); - - testUsingContext('bad_path', () { - final String testPath = fs.path.join(dataPath, 'bad_path'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - try { - builder.build(); - fail('expect an exception to be thrown.'); - } on DartDependencyException catch (error) { - expect(error.toString(), contains('amaze${fs.path.separator}and${fs.path.separator}astonish.dart')); - } - }); - - testUsingContext('bad_package', () { - final String testPath = fs.path.join(dataPath, 'bad_package'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - try { - builder.build(); - fail('expect an exception to be thrown.'); - } on DartDependencyException catch (error) { - expect(error.toString(), contains('rochambeau')); - expect(error.toString(), contains('pubspec.yaml')); - } - }); - - testUsingContext('does not change ASCII casing of path', () { - final String testPath = fs.path.join(dataPath, 'asci_casing'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = DartDependencySetBuilder(mainPath, packagesPath); - final Set deps = builder.build(); - expect(deps, contains(endsWith('This_Import_Has_fuNNy_casING.dart'))); - }); - - testUsingContext('bad_import', () { - final String testPath = fs.path.join(dataPath, 'bad_import'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - try { - builder.build(); - fail('expect an exception to be thrown.'); - } on DartDependencyException catch (error) { - expect(error.toString(), contains('Unable to parse URI')); - } - }); - }); -} diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/.dartignore b/packages/flutter_tools/test/data/dart_dependencies_test/.dartignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages deleted file mode 100644 index 453dc36e85..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/.packages +++ /dev/null @@ -1 +0,0 @@ -self:lib/ diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart deleted file mode 100644 index 6081630a0c..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/This_Import_Has_fuNNy_casING.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -String dummy = 'Hello'; diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml deleted file mode 100644 index 4c1615ac5a..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - exclude: - - '**' diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart deleted file mode 100644 index 176c82d24c..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/main.dart +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2017 The Chromium 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 'This_Import_Has_fuNNy_casING.dart'; - -void main() { - print(dummy); -} diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml deleted file mode 100644 index 99a0109ab6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/asci_casing/pubspec.yaml +++ /dev/null @@ -1 +0,0 @@ -name: self diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml deleted file mode 100644 index 4c1615ac5a..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - exclude: - - '**' diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart deleted file mode 100644 index 7f22c9066b..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/main.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2017 The Chromium 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 'data://object.dart'; diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml deleted file mode 100644 index 99a0109ab6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_import/pubspec.yaml +++ /dev/null @@ -1 +0,0 @@ -name: self diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages deleted file mode 100644 index 453dc36e85..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/.packages +++ /dev/null @@ -1 +0,0 @@ -self:lib/ diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml deleted file mode 100644 index 4c1615ac5a..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - exclude: - - '**' diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart deleted file mode 100644 index c3b231b64e..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/main.dart +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2016 The Chromium 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 'package:rochambeau/you_have_your_orders_now_go_man_go.dart'; - diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml deleted file mode 100644 index 99a0109ab6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_package/pubspec.yaml +++ /dev/null @@ -1 +0,0 @@ -name: self diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages deleted file mode 100644 index 453dc36e85..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/.packages +++ /dev/null @@ -1 +0,0 @@ -self:lib/ diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml deleted file mode 100644 index 4c1615ac5a..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - exclude: - - '**' diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart deleted file mode 100644 index 9c9d21640f..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/main.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2016 The Chromium 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 'amaze/and/astonish.dart'; diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml deleted file mode 100644 index 99a0109ab6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/bad_path/pubspec.yaml +++ /dev/null @@ -1 +0,0 @@ -name: self diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages deleted file mode 100644 index 0b55f79f09..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/.packages +++ /dev/null @@ -1,2 +0,0 @@ -flutter:file:///a/wild/non-existent/directory/has/appeared -sdk-move-test:lib/ diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart deleted file mode 100644 index bcd11b5439..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/lib/main.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// No content diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml deleted file mode 100644 index 9de2fbeeef..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/changed_sdk_location/pubspec.yaml +++ /dev/null @@ -1,9 +0,0 @@ -name: sdk-move-test - -environment: - # The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite. - sdk: ">=2.0.0-dev.68.0 <3.0.0" - -dependencies: - flutter: - sdk: flutter diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/good/.packages deleted file mode 100644 index 453dc36e85..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/good/.packages +++ /dev/null @@ -1 +0,0 @@ -self:lib/ diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml deleted file mode 100644 index 4c1615ac5a..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/good/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - exclude: - - '**' diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart b/packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart deleted file mode 100644 index 6853db73b6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/good/foo.dart +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart b/packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart deleted file mode 100644 index 6853db73b6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/good/lib/bar.dart +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart deleted file mode 100644 index 659a48df0e..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/good/main.dart +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright 2016 The Chromium 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 'foo.dart'; -import 'package:self/bar.dart'; diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml deleted file mode 100644 index 99a0109ab6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/good/pubspec.yaml +++ /dev/null @@ -1 +0,0 @@ -name: self diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages deleted file mode 100644 index 453dc36e85..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/.packages +++ /dev/null @@ -1 +0,0 @@ -self:lib/ diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml deleted file mode 100644 index 4c1615ac5a..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/analysis_options.yaml +++ /dev/null @@ -1,3 +0,0 @@ -analyzer: - exclude: - - '**' diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart deleted file mode 100644 index 6fbabe4861..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/foo.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2016 The Chromium 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 bad programmer! diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart deleted file mode 100644 index 43d48c01ba..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/main.dart +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2016 The Chromium 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 'foo.dart'; diff --git a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml b/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml deleted file mode 100644 index 99a0109ab6..0000000000 --- a/packages/flutter_tools/test/data/dart_dependencies_test/syntax_error/pubspec.yaml +++ /dev/null @@ -1 +0,0 @@ -name: self diff --git a/packages/flutter_tools/test/dependency_checker_test.dart b/packages/flutter_tools/test/dependency_checker_test.dart deleted file mode 100644 index a815ff2649..0000000000 --- a/packages/flutter_tools/test/dependency_checker_test.dart +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2016 The Chromium 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 'package:file/memory.dart'; -import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/cache.dart'; -import 'package:flutter_tools/src/commands/devices.dart'; -import 'package:flutter_tools/src/dart/dependencies.dart'; -import 'package:flutter_tools/src/dependency_checker.dart'; - -import 'src/common.dart'; -import 'src/context.dart'; - -void main() { - group('DependencyChecker', () { - final String dataPath = fs.path.join( - getFlutterRoot(), - 'packages', - 'flutter_tools', - 'test', - 'data', - 'dart_dependencies_test', - ); - - FileSystem testFileSystem; - - setUpAll(() { - Cache.disableLocking(); - }); - - setUp(() { - testFileSystem = MemoryFileSystem(); - }); - - testUsingContext('good', () { - final String testPath = fs.path.join(dataPath, 'good'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String fooPath = fs.path.join(testPath, 'foo.dart'); - final String barPath = fs.path.join(testPath, 'lib', 'bar.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - final DependencyChecker dependencyChecker = - DependencyChecker(builder, null); - - // Set file modification time on all dependencies to be in the past. - final DateTime baseTime = DateTime.now(); - updateFileModificationTime(packagesPath, baseTime, -10); - updateFileModificationTime(mainPath, baseTime, -10); - updateFileModificationTime(fooPath, baseTime, -10); - updateFileModificationTime(barPath, baseTime, -10); - expect(dependencyChecker.check(baseTime), isFalse); - - // Set .packages file modification time to be in the future. - updateFileModificationTime(packagesPath, baseTime, 20); - expect(dependencyChecker.check(baseTime), isTrue); - - // Reset .packages file modification time. - updateFileModificationTime(packagesPath, baseTime, 0); - expect(dependencyChecker.check(baseTime), isFalse); - - // Set 'package:self/bar.dart' file modification time to be in the future. - updateFileModificationTime(barPath, baseTime, 10); - expect(dependencyChecker.check(baseTime), isTrue); - }); - - testUsingContext('syntax error', () { - final String testPath = fs.path.join(dataPath, 'syntax_error'); - final String mainPath = fs.path.join(testPath, 'main.dart'); - final String fooPath = fs.path.join(testPath, 'foo.dart'); - final String packagesPath = fs.path.join(testPath, '.packages'); - - final DartDependencySetBuilder builder = - DartDependencySetBuilder(mainPath, packagesPath); - final DependencyChecker dependencyChecker = - DependencyChecker(builder, null); - - final DateTime baseTime = DateTime.now(); - - // Set file modification time on all dependencies to be in the past. - updateFileModificationTime(packagesPath, baseTime, -10); - updateFileModificationTime(mainPath, baseTime, -10); - updateFileModificationTime(fooPath, baseTime, -10); - - // Dependencies are considered dirty because there is a syntax error in - // the .dart file. - expect(dependencyChecker.check(baseTime), isTrue); - }); - - /// Test a flutter tool move. - /// - /// Tests that the flutter tool doesn't crash and displays a warning when its own location - /// changed since it was last referenced to in a package's .packages file. - testUsingContext('moved flutter sdk', () async { - final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_dependency_checker_test.'); - - // Copy the golden input and let the test run in an isolated temporary in-memory file system. - const LocalFileSystem localFileSystem = LocalFileSystem(); - final Directory sourcePath = localFileSystem.directory(localFileSystem.path.join(dataPath, 'changed_sdk_location')); - copyDirectorySync(sourcePath, tempDir); - fs.currentDirectory = tempDir; - - // Doesn't matter what commands we run. Arbitrarily list devices here. - await createTestCommandRunner(DevicesCommand()).run(['devices']); - expect(testLogger.errorText, contains('.packages')); - tryToDelete(tempDir); - }, overrides: { - FileSystem: () => testFileSystem, - }); - }); -} diff --git a/packages/flutter_tools/test/devfs_test.dart b/packages/flutter_tools/test/devfs_test.dart index 318ed1b6b2..653442b6a8 100644 --- a/packages/flutter_tools/test/devfs_test.dart +++ b/packages/flutter_tools/test/devfs_test.dart @@ -7,10 +7,8 @@ import 'dart:convert'; import 'package:file/file.dart'; import 'package:file/memory.dart'; -import 'package:flutter_tools/src/asset.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/vmservice.dart'; import 'package:json_rpc_2/json_rpc_2.dart' as rpc; @@ -22,16 +20,13 @@ import 'src/mocks.dart'; void main() { FileSystem fs; String filePath; - String filePath2; Directory tempDir; String basePath; DevFS devFS; - final AssetBundle assetBundle = AssetBundleFactory.defaultInstance.createBundle(); setUpAll(() { fs = MemoryFileSystem(); filePath = fs.path.join('lib', 'foo.txt'); - filePath2 = fs.path.join('foo', 'bar.txt'); }); group('DevFSContent', () { @@ -94,362 +89,6 @@ void main() { }); }); - group('devfs local', () { - final MockDevFSOperations devFSOperations = MockDevFSOperations(); - final MockResidentCompiler residentCompiler = MockResidentCompiler(); - - setUpAll(() { - tempDir = _newTempDir(fs); - basePath = tempDir.path; - }); - tearDownAll(_cleanupTempDirs); - - testUsingContext('create dev file system', () async { - // simulate workspace - final File file = fs.file(fs.path.join(basePath, filePath)); - await file.parent.create(recursive: true); - file.writeAsBytesSync([1, 2, 3]); - _packages['my_project'] = fs.path.toUri('lib'); - - // simulate package - await _createPackage(fs, 'somepkg', 'somefile.txt'); - - devFS = DevFS.operations(devFSOperations, 'test', tempDir); - await devFS.create(); - devFSOperations.expectMessages(['create test']); - expect(devFS.assetPathsToEvict, isEmpty); - - UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - - report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: true, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill.track.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - - expect(report.syncedBytes, 22); - expect(report.success, true); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('add new file to local file system', () async { - final File file = fs.file(fs.path.join(basePath, filePath2)); - await file.parent.create(recursive: true); - file.writeAsBytesSync([1, 2, 3, 4, 5, 6, 7]); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('modify existing file on local file system', () async { - UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - final File file = fs.file(fs.path.join(basePath, filePath)); - // Set the last modified time to 5 seconds in the past. - updateFileModificationTime(file.path, DateTime.now(), -5); - report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - await file.writeAsBytes([1, 2, 3, 4, 5, 6]); - report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - // Set the last modified time to 5 seconds in the past. - updateFileModificationTime(file.path, DateTime.now(), -5); - report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: true, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill.track.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - await file.writeAsBytes([1, 2, 3, 4, 5, 6]); - report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: true, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill.track.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('delete a file from the local file system', () async { - final File file = fs.file(fs.path.join(basePath, filePath)); - await file.delete(); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'deleteFile test lib/foo.txt', - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('add new package', () async { - await _createPackage(fs, 'newpkg', 'anotherfile.txt'); - UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - report = await devFS.update( - mainPath: 'lib/foo.txt', - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: true, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill.track.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('add new package with double slashes in URI', () async { - const String packageName = 'doubleslashpkg'; - await _createPackage(fs, packageName, 'somefile.txt', doubleSlash: true); - - final Set fileFilter = {}; - final List pkgUris = [fs.path.toUri(basePath)]..addAll(_packages.values); - for (Uri pkgUri in pkgUris) { - if (!pkgUri.isAbsolute) { - pkgUri = fs.path.toUri(fs.path.join(basePath, pkgUri.path)); - } - fileFilter.addAll(fs.directory(pkgUri) - .listSync(recursive: true) - .whereType() - .map((File file) => canonicalizePath(file.path)) - .toList()); - } - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - fileFilter: fileFilter, - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, isEmpty); - expect(report.syncedBytes, 22); - expect(report.success, true); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('add an asset bundle', () async { - assetBundle.entries['a.txt'] = DevFSStringContent('abc'); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - bundle: assetBundle, - bundleDirty: true, - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test ${_inAssetBuildDirectory(fs, 'a.txt')}', - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, unorderedMatches(['a.txt'])); - devFS.assetPathsToEvict.clear(); - expect(report.syncedBytes, 25); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('add a file to the asset bundle - bundleDirty', () async { - assetBundle.entries['b.txt'] = DevFSStringContent('abcd'); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - bundle: assetBundle, - bundleDirty: true, - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - // Expect entire asset bundle written because bundleDirty is true - devFSOperations.expectMessages([ - 'writeFile test ${_inAssetBuildDirectory(fs, 'a.txt')}', - 'writeFile test ${_inAssetBuildDirectory(fs, 'b.txt')}', - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, unorderedMatches([ - 'a.txt', 'b.txt'])); - devFS.assetPathsToEvict.clear(); - expect(report.syncedBytes, 29); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('add a file to the asset bundle', () async { - assetBundle.entries['c.txt'] = DevFSStringContent('12'); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - bundle: assetBundle, - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'writeFile test ${_inAssetBuildDirectory(fs, 'c.txt')}', - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, unorderedMatches([ - 'c.txt'])); - devFS.assetPathsToEvict.clear(); - expect(report.syncedBytes, 24); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('delete a file from the asset bundle', () async { - assetBundle.entries.remove('c.txt'); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - bundle: assetBundle, - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'deleteFile test ${_inAssetBuildDirectory(fs, 'c.txt')}', - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, unorderedMatches(['c.txt'])); - devFS.assetPathsToEvict.clear(); - expect(report.syncedBytes, 22); - expect(report.success, true); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('delete all files from the asset bundle', () async { - assetBundle.entries.clear(); - final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', - bundle: assetBundle, - bundleDirty: true, - generator: residentCompiler, - pathToReload: 'lib/foo.txt.dill', - trackWidgetCreation: false, - ); - devFSOperations.expectMessages([ - 'deleteFile test ${_inAssetBuildDirectory(fs, 'a.txt')}', - 'deleteFile test ${_inAssetBuildDirectory(fs, 'b.txt')}', - 'writeFile test lib/foo.txt.dill build/app.dill', - ]); - expect(devFS.assetPathsToEvict, unorderedMatches([ - 'a.txt', 'b.txt', - ])); - devFS.assetPathsToEvict.clear(); - expect(report.syncedBytes, 22); - expect(report.success, true); - }, overrides: { - FileSystem: () => fs, - }); - - testUsingContext('delete dev file system', () async { - await devFS.destroy(); - devFSOperations.expectMessages(['destroy test']); - expect(devFS.assetPathsToEvict, isEmpty); - }, overrides: { - FileSystem: () => fs, - }); - }); - group('devfs remote', () { MockVMService vmService; final MockResidentCompiler residentCompiler = MockResidentCompiler(); @@ -484,6 +123,7 @@ void main() { generator: residentCompiler, pathToReload: 'lib/foo.txt.dill', trackWidgetCreation: false, + invalidatedFiles: [], ); vmService.expectMessages([ 'writeFile test lib/foo.txt.dill', @@ -652,6 +292,3 @@ Future _createPackage(FileSystem fs, String pkgName, String pkgFileName, { fs.file(fs.path.join(_tempDirs[0].path, '.packages')).writeAsStringSync(sb.toString()); } -String _inAssetBuildDirectory(FileSystem fs, String filename) { - return '${fs.path.toUri(getAssetBuildDirectory()).path}/$filename'; -} diff --git a/packages/flutter_tools/test/hot_test.dart b/packages/flutter_tools/test/hot_test.dart index a9efb24d94..be36a3d7ed 100644 --- a/packages/flutter_tools/test/hot_test.dart +++ b/packages/flutter_tools/test/hot_test.dart @@ -105,13 +105,13 @@ void main() { firstBuildTime: anyNamed('firstBuildTime'), bundleFirstUpload: anyNamed('bundleFirstUpload'), bundleDirty: anyNamed('bundleDirty'), - fileFilter: anyNamed('fileFilter'), generator: anyNamed('generator'), fullRestart: anyNamed('fullRestart'), dillOutputPath: anyNamed('dillOutputPath'), trackWidgetCreation: anyNamed('trackWidgetCreation'), projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), + invalidatedFiles: anyNamed('invalidatedFiles'), )).thenAnswer((Invocation _) => Future.value( UpdateFSReport(success: true, syncedBytes: 1000, invalidatedSourcesCount: 1))); when(mockDevFs.assetPathsToEvict).thenReturn({}); @@ -122,18 +122,6 @@ void main() { when(mockArtifacts.getArtifactPath(Artifact.flutterPatchedSdkPath)).thenReturn('some/path'); }); - testUsingContext('no setup', () async { - final MockDevice mockDevice = MockDevice(); - when(mockDevice.supportsHotReload).thenReturn(true); - when(mockDevice.supportsHotRestart).thenReturn(true); - final List devices = [ - FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false), - ]; - expect((await HotRunner(devices).restart(fullRestart: true)).isOk, false); - }, overrides: { - Artifacts: () => mockArtifacts, - }); - testUsingContext('Does not hot restart when device does not support it', () async { // Setup mocks final MockDevice mockDevice = MockDevice(); @@ -149,7 +137,7 @@ void main() { expect(result.message, 'hotRestart not supported'); }, overrides: { Artifacts: () => mockArtifacts, - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false), + HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), }); testUsingContext('Does not hot restart when one of many devices does not support it', () async { @@ -171,7 +159,7 @@ void main() { expect(result.message, 'hotRestart not supported'); }, overrides: { Artifacts: () => mockArtifacts, - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false), + HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), }); testUsingContext('Does hot restarts when all devices support it', () async { @@ -193,7 +181,7 @@ void main() { expect(result.message, isNot('hotRestart not supported')); }, overrides: { Artifacts: () => mockArtifacts, - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false), + HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), }); testUsingContext('setup function fails', () async { @@ -226,7 +214,7 @@ void main() { expect(result.message, isNot('setupHotRestart failed')); }, overrides: { Artifacts: () => mockArtifacts, - HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true, computeDartDependencies: false), + HotRunnerConfig: () => TestHotRunnerConfig(successfulSetup: true), }); group('shutdown hook tests', () { @@ -235,7 +223,6 @@ void main() { setUp(() { shutdownTestingConfig = TestHotRunnerConfig( successfulSetup: true, - computeDartDependencies: false, ); }); @@ -283,10 +270,7 @@ class MockDevice extends Mock implements Device { } class TestHotRunnerConfig extends HotRunnerConfig { - TestHotRunnerConfig({@required this.successfulSetup, bool computeDartDependencies = true}) { - this.computeDartDependencies = computeDartDependencies; - } - + TestHotRunnerConfig({@required this.successfulSetup}); bool successfulSetup; bool shutdownHookCalled = false; diff --git a/packages/flutter_tools/test/project_file_invalidator_test.dart b/packages/flutter_tools/test/project_file_invalidator_test.dart new file mode 100644 index 0000000000..c38984eff7 --- /dev/null +++ b/packages/flutter_tools/test/project_file_invalidator_test.dart @@ -0,0 +1,234 @@ +// Copyright 2019 The Chromium 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 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; +import 'package:flutter_tools/src/base/platform.dart'; +import 'package:flutter_tools/src/project.dart'; +import 'package:flutter_tools/src/run_hot.dart'; +import 'package:mockito/mockito.dart'; + +import 'src/common.dart'; +import 'src/context.dart'; + +void main() { + final Platform windowsPlatform = MockPlatform(); + final Platform notWindowsPlatform = MockPlatform(); + final BufferLogger bufferLogger = BufferLogger(); + when(windowsPlatform.isWindows).thenReturn(true); + when(notWindowsPlatform.isWindows).thenReturn(false); + + group('ProjectFileInvalidator linux/mac', () { + final MemoryFileSystem memoryFileSystem = MemoryFileSystem(); + final File packagesFile = memoryFileSystem.file('.packages') + ..createSync() + ..writeAsStringSync(r''' +foo:file:///foo/lib/ +bar:file:///.pub-cache/bar/lib/ +baz:file:///baz/lib/ +test_package:file:///lib/ +'''); + final File pubspecFile = memoryFileSystem.file('pubspec.yaml') + ..createSync() + ..writeAsStringSync(r''' +name: test_package + +dependencies: + foo: any + bar: + +dev_dependencies: + baz: any +'''); + final File mainFile = memoryFileSystem.file('lib/main.dart') + ..createSync(recursive: true) + ..writeAsStringSync(r''' +void main() {} +'''); + final File fooFile = memoryFileSystem.file('foo/lib/foo.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + memoryFileSystem.file('bar/lib/bar.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + final File bazFile = memoryFileSystem.file('baz/lib/baz.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + + testUsingContext('No .packages, no pubspec', () async { + // Instead of setting up multiple filesystems, passing a .packages file which does not exist. + final ProjectFileInvalidator invalidator = ProjectFileInvalidator('.packages-wrong', null); + invalidator.findInvalidated(); + expect(invalidator.packageMap, isEmpty); + expect(invalidator.updateTime, isEmpty); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => notWindowsPlatform, + }); + + testUsingContext('.packages only', () async { + final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, null); + invalidator.findInvalidated(); + expect(invalidator.packageMap, { + 'foo': Uri.parse('file:///foo/lib/'), + // Excluded because it is in pub cache. + // 'bar': Uri.parse('file:///.pub-cache/bar/lib/'), + 'baz': Uri.parse('file:///baz/lib/'), + 'test_package': Uri.parse('file:///lib/'), + }); + expect(invalidator.updateTime, { + '/baz/lib/baz.dart': bazFile.statSync().modified.millisecondsSinceEpoch, + '/lib/main.dart': mainFile.statSync().modified.millisecondsSinceEpoch, + '/foo/lib/foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch, + }); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => notWindowsPlatform, + }); + + testUsingContext('.packages and pubspec', () async { + final FlutterProject flutterProject = await FlutterProject.fromDirectory(pubspecFile.parent); + final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, flutterProject); + invalidator.findInvalidated(); + expect(invalidator.packageMap, { + 'foo': Uri.parse('file:///foo/lib/'), + // Excluded because it is in pub cache. + // 'bar': Uri.parse('file:///.pub-cache/bar/lib/'), + // Excluded because it is a dev dependency/ + // 'baz': Uri.parse('file:///baz/lib/'), + 'test_package': Uri.parse('file:///lib/'), + }); + expect(invalidator.updateTime, { + '/foo/lib/foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch, + '/lib/main.dart':mainFile.statSync().modified.millisecondsSinceEpoch, + }); + expect(invalidator.findInvalidated(), isEmpty); + + // Invalidate main.dart. + mainFile.writeAsStringSync('void main() { }'); + expect(invalidator.findInvalidated(), ['/lib/main.dart']); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => notWindowsPlatform, + }); + + testUsingContext('update to .packages triggers warning', () async { + final FlutterProject flutterProject = await FlutterProject.fromDirectory(pubspecFile.parent); + final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, flutterProject); + invalidator.findInvalidated(); + packagesFile.writeAsStringSync(r''' +foo:file:///foo/lib/ +bar:file:///.pub-cache/bar/lib/ +baz:file:///baz/lib/ +new_dep:file:///new_dep/lib/ +test_package:file:///lib/ +'''); + invalidator.findInvalidated(); + expect(bufferLogger.errorText, 'Warning: updated dependencies detected. The Flutter application will require a restart to safely use new packages.\n'); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => notWindowsPlatform, + Logger: () => bufferLogger, + }); + }); + + group('ProjectFileInvalidator windows', () { + final MemoryFileSystem memoryFileSystem = MemoryFileSystem(style: FileSystemStyle.windows); + // On windows .packages still contains file Uris, albeit ones with the Drive prefix. + final File packagesFile = memoryFileSystem.file(r'C:\.packages') + ..createSync() + ..writeAsStringSync(r''' +foo:file:///C:/foo/lib/ +bar:file:///C:/Pub/Cache/bar/lib/ +baz:file:///C:/baz/lib/ +test_package:file:///C:/lib/ +'''); + memoryFileSystem.file(r'C:\pubspec.yaml') + ..createSync() + ..writeAsStringSync(r''' +name: test_package + +dependencies: + foo: any + bar: + +dev_dependencies: + baz: any +'''); + final File mainFile = memoryFileSystem.file(r'C:\lib\main.dart') + ..createSync(recursive: true) + ..writeAsStringSync(r''' +void main() {} +'''); + final File fooFile = memoryFileSystem.file(r'C:\foo\lib\foo.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + memoryFileSystem.file(r'C:\bar\lib\bar.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + final File bazFile = memoryFileSystem.file(r'C:\baz\lib\baz.dart') + ..createSync(recursive: true) + ..writeAsStringSync(''); + + testUsingContext('No .packages, no pubspec', () async { + // Instead of setting up multiple filesystems, passing a .packages file which does not exist. + final ProjectFileInvalidator invalidator = ProjectFileInvalidator('.packages-wrong', null); + invalidator.findInvalidated(); + expect(invalidator.packageMap, isEmpty); + expect(invalidator.updateTime, isEmpty); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => windowsPlatform, + }); + + testUsingContext('.packages only', () async { + final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, null); + invalidator.findInvalidated(); + expect(invalidator.packageMap, { + 'foo': Uri.file(r'C:\foo\lib\', windows: true), + // Excluded because it is in pub cache. + // 'bar': Uri.parse('file:///Pub/Cache/bar/lib/'), + 'baz': Uri.file(r'C:\baz\lib\', windows: true), + 'test_package': Uri.file(r'C:\lib\', windows: true), + }); + expect(invalidator.updateTime, { + r'C:\baz\lib\baz.dart': bazFile.statSync().modified.millisecondsSinceEpoch, + r'C:\lib\main.dart': mainFile.statSync().modified.millisecondsSinceEpoch, + r'C:\foo\lib\foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch, + }); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => windowsPlatform, + }); + + testUsingContext('.packages and pubspec', () async { + final FlutterProject flutterProject = await FlutterProject.fromDirectory(fs.directory(r'C:\')); + final ProjectFileInvalidator invalidator = ProjectFileInvalidator(packagesFile.path, flutterProject); + invalidator.findInvalidated(); + expect(invalidator.packageMap, { + 'foo': Uri.file(r'C:\foo\lib\', windows: true), + // Excluded because it is in pub cache. + // 'bar': Uri.parse('file:///C:/Pub/Cache/bar/lib/'), + // Excluded because it is a dev dependency/ + // 'baz': Uri.parse('file:///baz/lib/'), + 'test_package': Uri.file(r'C:\lib\', windows: true), + }); + expect(invalidator.updateTime, { + r'C:\lib\main.dart': mainFile.statSync().modified.millisecondsSinceEpoch, + r'C:\foo\lib\foo.dart': fooFile.statSync().modified.millisecondsSinceEpoch, + }); + expect(invalidator.findInvalidated(), isEmpty); + + // Invalidate main.dart. + mainFile.writeAsStringSync('void main() { }'); + expect(invalidator.findInvalidated(), ['file:///C:/lib/main.dart']); + }, overrides: { + FileSystem: () => memoryFileSystem, + Platform: () => windowsPlatform, + }); + }); +} + +class MockPlatform extends Mock implements Platform {} diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index 52c5fc9071..42c9d8f7ff 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -460,12 +460,6 @@ class MockDevFSOperations extends BasicMock implements DevFSOperations { messages.add(message); devicePathToContent[deviceUri] = content; } - - @override - Future deleteFile(String fsName, Uri deviceUri) async { - messages.add('deleteFile $fsName $deviceUri'); - devicePathToContent.remove(deviceUri); - } } class MockResidentCompiler extends BasicMock implements ResidentCompiler {