From b7e30cfc26040df3fac1467ecb4efb8acc5ae49c Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 20 Apr 2020 14:02:49 -0700 Subject: [PATCH] [flutter_tools] update compilation to use package config (#54467) --- .../lib/src/build_runner/devfs_web.dart | 31 +++- .../src/build_runner/resident_web_runner.dart | 34 ++-- .../lib/src/build_system/targets/dart.dart | 18 +- .../lib/src/build_system/targets/web.dart | 36 ++-- packages/flutter_tools/lib/src/codegen.dart | 16 +- packages/flutter_tools/lib/src/compile.dart | 156 ++++-------------- packages/flutter_tools/lib/src/devfs.dart | 32 ++-- .../lib/src/resident_runner.dart | 13 +- packages/flutter_tools/lib/src/run_hot.dart | 90 ++++++++-- .../lib/src/test/flutter_platform.dart | 2 +- .../lib/src/test/flutter_web_platform.dart | 2 +- .../lib/src/test/test_compiler.dart | 34 +++- .../build_system/targets/web_test.dart | 2 +- .../general.shard/compile_batch_test.dart | 13 ++ .../compile_expression_test.dart | 10 +- .../compile_incremental_test.dart | 32 ++-- .../test/general.shard/devfs_test.dart | 17 +- .../test/general.shard/hot_test.dart | 3 +- .../package_uri_mapper_test.dart | 87 ---------- .../project_file_invalidator_test.dart | 95 +++++++---- .../general.shard/resident_runner_test.dart | 13 +- .../resident_web_runner_test.dart | 28 ++-- .../general.shard/test_compiler_test.dart | 27 ++- .../general.shard/web/devfs_web_test.dart | 7 +- packages/flutter_tools/test/src/mocks.dart | 3 +- 25 files changed, 420 insertions(+), 381 deletions(-) delete mode 100644 packages/flutter_tools/test/general.shard/package_uri_mapper_test.dart diff --git a/packages/flutter_tools/lib/src/build_runner/devfs_web.dart b/packages/flutter_tools/lib/src/build_runner/devfs_web.dart index 2e3a9d9257e..b3d935e6fe1 100644 --- a/packages/flutter_tools/lib/src/build_runner/devfs_web.dart +++ b/packages/flutter_tools/lib/src/build_runner/devfs_web.dart @@ -373,7 +373,11 @@ class WebAssetServer implements AssetReader { /// Write a single file into the in-memory cache. void writeFile(String filePath, String contents) { - _files[filePath] = Uint8List.fromList(utf8.encode(contents)); + writeBytes(filePath, utf8.encode(contents) as Uint8List); + } + + void writeBytes(String filePath, Uint8List contents) { + _files[filePath] = contents; } /// Update the in-memory asset server with the provided source and manifest files. @@ -620,6 +624,9 @@ class WebDevFS implements DevFS { @override DateTime lastCompiled; + @override + PackageConfig lastPackageConfig; + // We do not evict assets on the web. @override Set get assetPathsToEvict => const {}; @@ -668,7 +675,7 @@ class WebDevFS implements DevFS { @override Future update({ - String mainPath, + Uri mainUri, String target, AssetBundle bundle, DateTime firstBuildTime, @@ -681,20 +688,23 @@ class WebDevFS implements DevFS { String pathToReload, List invalidatedFiles, bool skipAssets = false, + @required PackageConfig packageConfig, }) async { assert(trackWidgetCreation != null); assert(generator != null); - final String outputDirectoryPath = globals.fs.file(mainPath).parent.path; + lastPackageConfig = packageConfig; + final File mainFile = globals.fs.file(mainUri); + final String outputDirectoryPath = mainFile.parent.path; if (bundleFirstUpload) { webAssetServer.entrypointCacheDirectory = globals.fs.directory(outputDirectoryPath); generator.addFileSystemRoot(outputDirectoryPath); - final String entrypoint = globals.fs.path.basename(mainPath); - webAssetServer.writeFile(entrypoint, globals.fs.file(mainPath).readAsStringSync()); + final String entrypoint = globals.fs.path.basename(mainFile.path); + webAssetServer.writeBytes(entrypoint, mainFile.readAsBytesSync()); + webAssetServer.writeBytes('require.js', requireJS.readAsBytesSync()); + webAssetServer.writeBytes('stack_trace_mapper.js', stackTraceMapper.readAsBytesSync()); webAssetServer.writeFile('manifest.json', '{"info":"manifest not generated in run mode."}'); webAssetServer.writeFile('flutter_service_worker.js', '// Service worker not loaded in run mode.'); - webAssetServer.writeFile('require.js', requireJS.readAsStringSync()); - webAssetServer.writeFile('stack_trace_mapper.js', stackTraceMapper.readAsStringSync()); webAssetServer.writeFile( 'main.dart.js', generateBootstrapScript( @@ -727,11 +737,14 @@ class WebDevFS implements DevFS { // mapping the file name, this is done via an additional file root and // specicial hard-coded scheme. final CompilerOutput compilerOutput = await generator.recompile( - 'org-dartlang-app:///' + globals.fs.path.basename(mainPath), + Uri( + scheme: 'org-dartlang-app', + path: '/' + mainUri.pathSegments.last, + ), invalidatedFiles, outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation), - packagesFilePath: packagesFilePath, + packageConfig: packageConfig, ); if (compilerOutput == null || compilerOutput.errorCount > 0) { return UpdateFSReport(success: false); diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart index 1c3acdfd383..e85641207b7 100644 --- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:dwds/dwds.dart'; import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import 'package:vm_service/vm_service.dart' as vmservice; import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart' hide StackTrace; @@ -21,7 +22,6 @@ import '../base/terminal.dart'; import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; -import '../compile.dart'; import '../convert.dart'; import '../dart/pub.dart'; import '../devfs.dart'; @@ -558,7 +558,7 @@ class _ResidentWebRunner extends ResidentWebRunner { // Flutter web projects need to include a generated main entrypoint to call the // appropriate bootstrap method and inject plugins. // Keep this in sync with build_system/targets/web.dart. - Future _generateEntrypoint(String main, String packagesPath) async { + Future _generateEntrypoint(Uri mainUri, PackageConfig packageConfig) async { File result = _generatedEntrypointDirectory?.childFile('web_entrypoint.dart'); if (_generatedEntrypointDirectory == null) { _generatedEntrypointDirectory ??= globals.fs.systemTempDirectory.createTempSync('flutter_tools.') @@ -569,18 +569,20 @@ class _ResidentWebRunner extends ResidentWebRunner { .any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey)); await injectPlugins(flutterProject, checkProjects: true); - final PackageUriMapper packageUriMapper = PackageUriMapper(main, packagesPath, null, null); - final String generatedPath = globals.fs.currentDirectory + final Uri generatedUri = globals.fs.currentDirectory .childDirectory('lib') .childFile('generated_plugin_registrant.dart') - .absolute.path; - final Uri generatedImport = packageUriMapper.map(generatedPath); - String importedEntrypoint = packageUriMapper.map(main)?.toString(); + .absolute.uri; + final Uri generatedImport = packageConfig.toPackageUri(generatedUri); + Uri importedEntrypoint = packageConfig.toPackageUri(mainUri); // Special handling for entrypoints that are not under lib, such as test scripts. if (importedEntrypoint == null) { - final String parent = globals.fs.file(main).parent.path; + final String parent = globals.fs.file(mainUri).parent.path; flutterDevices.first.generator.addFileSystemRoot(parent); - importedEntrypoint = 'org-dartlang-app:///${globals.fs.path.basename(main)}'; + importedEntrypoint = Uri( + scheme: 'org-dartlang-app', + path: '/' + mainUri.pathSegments.last, + ); } final String entrypoint = [ @@ -604,7 +606,7 @@ class _ResidentWebRunner extends ResidentWebRunner { ].join('\n'); result.writeAsStringSync(entrypoint); } - return result.path; + return result.absolute.uri; } Future _updateDevFS({bool fullRestart = false}) async { @@ -617,18 +619,21 @@ class _ResidentWebRunner extends ResidentWebRunner { return UpdateFSReport(success: false); } } - final List invalidatedFiles = - await projectFileInvalidator.findInvalidated( + final InvalidationResult invalidationResult = await projectFileInvalidator.findInvalidated( lastCompiled: device.devFS.lastCompiled, urisToMonitor: device.devFS.sources, packagesPath: packagesFilePath, + packageConfig: device.devFS.lastPackageConfig, ); final Status devFSStatus = globals.logger.startProgress( 'Syncing files to device ${device.device.name}...', timeout: timeoutConfiguration.fastOperation, ); final UpdateFSReport report = await device.devFS.update( - mainPath: await _generateEntrypoint(mainPath, packagesFilePath), + mainUri: await _generateEntrypoint( + globals.fs.file(mainPath).absolute.uri, + invalidationResult.packageConfig, + ), target: target, bundle: assetBundle, firstBuildTime: firstBuildTime, @@ -638,7 +643,8 @@ class _ResidentWebRunner extends ResidentWebRunner { dillOutputPath: dillOutputPath, projectRootPath: projectRootPath, pathToReload: getReloadPath(fullRestart: fullRestart), - invalidatedFiles: invalidatedFiles, + invalidatedFiles: invalidationResult.uris, + packageConfig: invalidationResult.packageConfig, trackWidgetCreation: true, ); devFSStatus.stop(); diff --git a/packages/flutter_tools/lib/src/build_system/targets/dart.dart b/packages/flutter_tools/lib/src/build_system/targets/dart.dart index 102f63a871e..b7bbcd439ef 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/dart.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/dart.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:package_config/package_config.dart'; + import '../../artifacts.dart'; import '../../base/build.dart'; import '../../base/file_system.dart'; @@ -196,7 +198,7 @@ class KernelSnapshot extends Target { } final BuildMode buildMode = getBuildModeForName(environment.defines[kBuildMode]); final String targetFile = environment.defines[kTargetFile] ?? globals.fs.path.join('lib', 'main.dart'); - final String packagesPath = environment.projectDir.childFile('.packages').path; + final File packagesFile = environment.projectDir.childFile('.packages'); final String targetFileAbsolute = globals.fs.file(targetFile).absolute.path; // everything besides 'false' is considered to be enabled. final bool trackWidgetCreation = environment.defines[kTrackWidgetCreation] != 'false'; @@ -229,6 +231,17 @@ class KernelSnapshot extends Target { forceLinkPlatform = false; } + final PackageConfig packageConfig = await loadPackageConfigUri( + packagesFile.absolute.uri, + loader: (Uri uri) { + final File file = globals.fs.file(uri); + if (!file.existsSync()) { + return null; + } + return file.readAsBytes(); + } + ); + final CompilerOutput output = await compiler.compile( sdkRoot: globals.artifacts.getArtifactPath( Artifact.flutterPatchedSdkPath, @@ -240,7 +253,7 @@ class KernelSnapshot extends Target { trackWidgetCreation: trackWidgetCreation && buildMode == BuildMode.debug, targetModel: targetModel, outputFilePath: environment.buildDir.childFile('app.dill').path, - packagesPath: packagesPath, + packagesPath: packagesFile.path, linkPlatformKernelIn: forceLinkPlatform || buildMode.isPrecompiled, mainPath: targetFileAbsolute, depFilePath: environment.buildDir.childFile('kernel_snapshot.d').path, @@ -248,6 +261,7 @@ class KernelSnapshot extends Target { fileSystemRoots: fileSystemRoots, fileSystemScheme: fileSystemScheme, dartDefines: parseDartDefines(environment), + packageConfig: packageConfig, ); if (output == null || output.errorCount != 0) { throw Exception('Errors during snapshot creation: $output'); diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart index cbaa2a0f04e..4937bd9c935 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/web.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart @@ -3,12 +3,12 @@ // found in the LICENSE file. import 'package:crypto/crypto.dart'; +import 'package:package_config/package_config.dart'; import '../../artifacts.dart'; import '../../base/file_system.dart'; import '../../base/io.dart'; import '../../build_info.dart'; -import '../../compile.dart'; import '../../dart/package_map.dart'; import '../../globals.dart' as globals; import '../build_system.dart'; @@ -56,35 +56,39 @@ class WebEntrypointTarget extends Target { final String targetFile = environment.defines[kTargetFile]; final bool shouldInitializePlatform = environment.defines[kInitializePlatform] == 'true'; final bool hasPlugins = environment.defines[kHasWebPlugins] == 'true'; - final String importPath = globals.fs.path.absolute(targetFile); + final Uri importUri = environment.fileSystem.file(targetFile).absolute.uri; + final PackageConfig packageConfig = await loadPackageConfigUri( + environment.projectDir.childFile('.packages').absolute.uri, + loader: (Uri uri) { + final File file = environment.fileSystem.file(uri); + if (!file.existsSync()) { + return null; + } + return file.readAsBytes(); + } + ); - // Use the package uri mapper to find the correct package-scheme import path + // Use the PackageConfig to find the correct package-scheme import path // for the user application. If the application has a mix of package-scheme // and relative imports for a library, then importing the entrypoint as a // file-scheme will cause said library to be recognized as two distinct // libraries. This can cause surprising behavior as types from that library // will be considered distinct from each other. - final PackageUriMapper packageUriMapper = PackageUriMapper( - importPath, - PackageMap.globalPackagesPath, - null, - null, - ); - // By construction, this will only be null if the .packages file does not // have an entry for the user's application or if the main file is // outside of the lib/ directory. - final String mainImport = packageUriMapper.map(importPath)?.toString() - ?? globals.fs.file(importPath).absolute.uri.toString(); + final String mainImport = packageConfig.toPackageUri(importUri)?.toString() + ?? importUri.toString(); String contents; if (hasPlugins) { - final String generatedPath = environment.projectDir + final Uri generatedUri = environment.projectDir .childDirectory('lib') .childFile('generated_plugin_registrant.dart') - .absolute.path; - final String generatedImport = packageUriMapper.map(generatedPath)?.toString() - ?? globals.fs.file(generatedPath).absolute.uri.toString(); + .absolute + .uri; + final String generatedImport = packageConfig.toPackageUri(generatedUri)?.toString() + ?? generatedUri.toString(); contents = ''' import 'dart:ui' as ui; diff --git a/packages/flutter_tools/lib/src/codegen.dart b/packages/flutter_tools/lib/src/codegen.dart index 8e8c9ac3f27..61267680b0c 100644 --- a/packages/flutter_tools/lib/src/codegen.dart +++ b/packages/flutter_tools/lib/src/codegen.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import 'base/context.dart'; import 'build_info.dart'; @@ -72,7 +73,6 @@ class CodeGeneratingKernelCompiler implements KernelCompiler { String outputFilePath, bool linkPlatformKernelIn = false, bool aot = false, - @required BuildMode buildMode, bool trackWidgetCreation, List extraFrontEndOptions, String sdkRoot, @@ -84,6 +84,8 @@ class CodeGeneratingKernelCompiler implements KernelCompiler { String initializeFromDill, String platformDill, List dartDefines, + @required BuildMode buildMode, + @required PackageConfig packageConfig, }) async { final FlutterProject flutterProject = FlutterProject.current(); final CodegenDaemon codegenDaemon = await codeGenerator.daemon(flutterProject); @@ -113,6 +115,7 @@ class CodeGeneratingKernelCompiler implements KernelCompiler { targetModel: targetModel, initializeFromDill: initializeFromDill, dartDefines: dartDefines, + packageConfig: packageConfig, ); } } @@ -170,7 +173,12 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler { } @override - Future recompile(String mainPath, List invalidatedFiles, {String outputPath, String packagesFilePath}) async { + Future recompile( + Uri mainUri, + List invalidatedFiles, { + String outputPath, + PackageConfig packageConfig, + }) async { if (_codegenDaemon.lastStatus != CodegenStatus.Succeeded && _codegenDaemon.lastStatus != CodegenStatus.Failed) { await _codegenDaemon.buildResults.firstWhere((CodegenStatus status) { return status == CodegenStatus.Succeeded || status == CodegenStatus.Failed; @@ -180,10 +188,10 @@ class CodeGeneratingResidentCompiler implements ResidentCompiler { globals.printError('Code generation failed, build may have compile errors.'); } return _residentCompiler.recompile( - mainPath, + mainUri, invalidatedFiles, outputPath: outputPath, - packagesFilePath: packagesFilePath, + packageConfig: packageConfig, ); } diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index 0dafd0b4fb2..94613432af6 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import 'package:usage/uuid/uuid.dart'; import 'artifacts.dart'; @@ -15,7 +16,6 @@ import 'base/terminal.dart'; import 'build_info.dart'; import 'codegen.dart'; import 'convert.dart'; -import 'dart/package_map.dart'; import 'globals.dart' as globals; import 'project.dart'; @@ -193,54 +193,6 @@ class StdoutHandler { } } -/// Converts filesystem paths to package URIs. -class PackageUriMapper { - PackageUriMapper(String scriptPath, String packagesPath, String fileSystemScheme, List fileSystemRoots) { - final Map packageMap = PackageMap(globals.fs.path.absolute(packagesPath), fileSystem: globals.fs).map; - final bool isWindowsPath = globals.platform.isWindows && !scriptPath.startsWith('org-dartlang-app'); - final String scriptUri = Uri.file(scriptPath, windows: isWindowsPath).toString(); - for (final String packageName in packageMap.keys) { - final String prefix = packageMap[packageName].toString(); - // Only perform a multi-root mapping if there are multiple roots. - if (fileSystemScheme != null - && fileSystemRoots != null - && fileSystemRoots.length > 1 - && prefix.contains(fileSystemScheme)) { - _packageName = packageName; - _uriPrefixes = fileSystemRoots - .map((String name) => Uri.file(name, windows:globals.platform.isWindows).toString()) - .toList(); - return; - } - if (scriptUri.startsWith(prefix)) { - _packageName = packageName; - _uriPrefixes = [prefix]; - return; - } - } - } - - String _packageName; - List _uriPrefixes; - - Uri map(String scriptPath) { - if (_packageName == null) { - return null; - } - final String scriptUri = Uri.file(scriptPath, windows: globals.platform.isWindows).toString(); - for (final String uriPrefix in _uriPrefixes) { - if (scriptUri.startsWith(uriPrefix)) { - return Uri.parse('package:$_packageName/${scriptUri.substring(uriPrefix.length)}'); - } - } - return null; - } - - static Uri findUri(String scriptPath, String packagesPath, String fileSystemScheme, List fileSystemRoots) { - return PackageUriMapper(scriptPath, packagesPath, fileSystemScheme, fileSystemRoots).map(scriptPath); - } -} - /// List the preconfigured build options for a given build mode. List buildModeOptions(BuildMode mode) { switch (mode) { @@ -276,17 +228,18 @@ class KernelCompiler { String outputFilePath, String depFilePath, TargetModel targetModel = TargetModel.flutter, - @required BuildMode buildMode, bool linkPlatformKernelIn = false, bool aot = false, - @required bool trackWidgetCreation, List extraFrontEndOptions, - String packagesPath, List fileSystemRoots, String fileSystemScheme, String initializeFromDill, String platformDill, + @required String packagesPath, + @required BuildMode buildMode, + @required bool trackWidgetCreation, @required List dartDefines, + @required PackageConfig packageConfig, }) async { final String frontendServer = globals.artifacts.getArtifactPath( Artifact.frontendServerSnapshotForEngineDartSdk @@ -301,7 +254,7 @@ class KernelCompiler { } Uri mainUri; if (packagesPath != null) { - mainUri = PackageUriMapper.findUri(mainPath, packagesPath, fileSystemScheme, fileSystemRoots); + mainUri = packageConfig.toPackageUri(globals.fs.file(mainPath).uri); } // TODO(jonahwilliams): The output file must already exist, but this seems // unnecessary. @@ -392,16 +345,16 @@ abstract class _CompilationRequest { class _RecompileRequest extends _CompilationRequest { _RecompileRequest( Completer completer, - this.mainPath, + this.mainUri, this.invalidatedFiles, this.outputPath, - this.packagesFilePath, + this.packageConfig, ) : super(completer); - String mainPath; + Uri mainUri; List invalidatedFiles; String outputPath; - String packagesFilePath; + PackageConfig packageConfig; @override Future _run(DefaultResidentCompiler compiler) async => @@ -499,10 +452,10 @@ abstract class ResidentCompiler { /// Binary file name is returned if compilation was successful, otherwise /// null is returned. Future recompile( - String mainPath, + Uri mainUri, List invalidatedFiles, { @required String outputPath, - String packagesFilePath, + @required PackageConfig packageConfig, }); Future compileExpression( @@ -620,56 +573,46 @@ class DefaultResidentCompiler implements ResidentCompiler { @override Future recompile( - String mainPath, + Uri mainUri, List invalidatedFiles, { @required String outputPath, - String packagesFilePath, + @required PackageConfig packageConfig, }) async { - assert (outputPath != null); + assert(outputPath != null); if (!_controller.hasListener) { _controller.stream.listen(_handleCompilationRequest); } final Completer completer = Completer(); _controller.add( - _RecompileRequest(completer, mainPath, invalidatedFiles, outputPath, packagesFilePath) + _RecompileRequest(completer, mainUri, invalidatedFiles, outputPath, packageConfig) ); return completer.future; } Future _recompile(_RecompileRequest request) async { _stdoutHandler.reset(); - - // First time recompile is called we actually have to compile the app from - // scratch ignoring list of invalidated files. - PackageUriMapper packageUriMapper; - if (request.packagesFilePath != null || packagesPath != null) { - packageUriMapper = PackageUriMapper( - request.mainPath, - request.packagesFilePath ?? packagesPath, - fileSystemScheme, - fileSystemRoots, - ); - } - _compileRequestNeedsConfirmation = true; if (_server == null) { return _compile( - _mapFilename(request.mainPath, packageUriMapper), + request.packageConfig.toPackageUri(request.mainUri)?.toString() ?? request.mainUri.toString(), request.outputPath, - _mapFilename(request.packagesFilePath ?? packagesPath, /* packageUriMapper= */ null), ); } - final String inputKey = Uuid().generateV4(); - final String mainUri = request.mainPath != null - ? _mapFilename(request.mainPath, packageUriMapper) + ' ' - : ''; - _server.stdin.writeln('recompile $mainUri$inputKey'); - globals.printTrace('<- recompile $mainUri$inputKey'); + final String mainUri = request.packageConfig.toPackageUri(request.mainUri)?.toString() + ?? request.mainUri.toString(); + _server.stdin.writeln('recompile $mainUri $inputKey'); + globals.printTrace('<- recompile $mainUri $inputKey'); for (final Uri fileUri in request.invalidatedFiles) { - final String message = _mapFileUri(fileUri.toString(), packageUriMapper); + String message; + if (fileUri.scheme == 'package') { + message = fileUri.toString(); + } else { + message = request.packageConfig.toPackageUri(fileUri)?.toString() + ?? fileUri.toString(); + } _server.stdin.writeln(message); globals.printTrace(message); } @@ -699,7 +642,6 @@ class DefaultResidentCompiler implements ResidentCompiler { Future _compile( String scriptUri, String outputPath, - String packagesFilePath, ) async { final String frontendServer = globals.artifacts.getArtifactPath( Artifact.frontendServerSnapshotForEngineDartSdk @@ -726,10 +668,7 @@ class DefaultResidentCompiler implements ResidentCompiler { '--libraries-spec', librariesSpec, ], - if (packagesFilePath != null) ...[ - '--packages', - packagesFilePath, - ] else if (packagesPath != null) ...[ + if (packagesPath != null) ...[ '--packages', packagesPath, ], @@ -916,43 +855,6 @@ class DefaultResidentCompiler implements ResidentCompiler { globals.printTrace('<- reset'); } - String _mapFilename(String filename, PackageUriMapper packageUriMapper) { - return _doMapFilename(filename, packageUriMapper) ?? filename; - } - - String _mapFileUri(String fileUri, PackageUriMapper packageUriMapper) { - String filename; - try { - filename = Uri.parse(fileUri).toFilePath(); - } on UnsupportedError catch (_) { - return fileUri; - } - return _doMapFilename(filename, packageUriMapper) ?? fileUri; - } - - String _doMapFilename(String filename, PackageUriMapper packageUriMapper) { - if (packageUriMapper != null) { - final Uri packageUri = packageUriMapper.map(filename); - if (packageUri != null) { - return packageUri.toString(); - } - } - - if (fileSystemRoots != null) { - for (final String root in fileSystemRoots) { - if (filename.startsWith(root)) { - return Uri( - scheme: fileSystemScheme, path: filename.substring(root.length)) - .toString(); - } - } - } - if (globals.platform.isWindows && fileSystemRoots != null && fileSystemRoots.length > 1) { - return Uri.file(filename, windows: globals.platform.isWindows).toString(); - } - return null; - } - @override Future shutdown() async { // Server was never successfully created. diff --git a/packages/flutter_tools/lib/src/devfs.dart b/packages/flutter_tools/lib/src/devfs.dart index db158004568..6dcaae381ef 100644 --- a/packages/flutter_tools/lib/src/devfs.dart +++ b/packages/flutter_tools/lib/src/devfs.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import 'package:vm_service/vm_service.dart' as vmservice; import 'asset.dart'; @@ -17,7 +18,6 @@ import 'build_info.dart'; import 'bundle.dart'; import 'compile.dart'; import 'convert.dart' show base64, utf8; -import 'dart/package_map.dart'; import 'globals.dart' as globals; import 'vmservice.dart'; @@ -395,32 +395,28 @@ class DevFS { VMService serviceProtocol, this.fsName, this.rootDirectory, { - String packagesFilePath, @required OperatingSystemUtils osUtils, }) : _operations = ServiceProtocolDevFSOperations(serviceProtocol), _httpWriter = _DevFSHttpWriter( fsName, serviceProtocol, osUtils: osUtils, - ), - _packagesFilePath = packagesFilePath ?? globals.fs.path.join(rootDirectory.path, kPackagesFileName); + ); DevFS.operations( this._operations, this.fsName, - this.rootDirectory, { - String packagesFilePath, - }) : _httpWriter = null, - _packagesFilePath = packagesFilePath ?? globals.fs.path.join(rootDirectory.path, kPackagesFileName); + this.rootDirectory, + ) : _httpWriter = null; final DevFSOperations _operations; final _DevFSHttpWriter _httpWriter; final String fsName; final Directory rootDirectory; - final String _packagesFilePath; final Set assetPathsToEvict = {}; List sources = []; DateTime lastCompiled; + PackageConfig lastPackageConfig; Uri _baseUri; Uri get baseUri => _baseUri; @@ -462,23 +458,25 @@ class DevFS { /// /// Returns the number of bytes synced. Future update({ - @required String mainPath, + @required Uri mainUri, + @required ResidentCompiler generator, + @required bool trackWidgetCreation, + @required String pathToReload, + @required List invalidatedFiles, + @required PackageConfig packageConfig, String target, AssetBundle bundle, DateTime firstBuildTime, bool bundleFirstUpload = false, - @required ResidentCompiler generator, String dillOutputPath, - @required bool trackWidgetCreation, bool fullRestart = false, String projectRootPath, - @required String pathToReload, - @required List invalidatedFiles, bool skipAssets = false, }) async { assert(trackWidgetCreation != null); assert(generator != null); final DateTime candidateCompileTime = DateTime.now(); + lastPackageConfig = packageConfig; // Update modified files final String assetBuildDirPrefix = _asUriPath(getAssetBuildDirectory()); @@ -514,10 +512,10 @@ class DevFS { // dill files that depend on the invalidated files. globals.printTrace('Compiling dart to kernel with ${invalidatedFiles.length} updated files'); final CompilerOutput compilerOutput = await generator.recompile( - mainPath, + mainUri, invalidatedFiles, - outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation), - packagesFilePath : _packagesFilePath, + outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: trackWidgetCreation), + packageConfig: packageConfig, ); if (compilerOutput == null || compilerOutput.errorCount > 0) { return UpdateFSReport(success: false); diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 50eac66d59b..816e4884e97 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'package:vm_service/vm_service.dart' as vm_service; import 'package:devtools_server/devtools_server.dart' as devtools_server; import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import 'application_package.dart'; import 'artifacts.dart'; @@ -58,6 +59,7 @@ class FlutterDevice { targetModel: targetModel, experimentalFlags: experimentalFlags, dartDefines: buildInfo.dartDefines, + packagesPath: PackageMap.globalPackagesPath, ); /// Create a [FlutterDevice] with optional code generation enabled. @@ -104,7 +106,8 @@ class FlutterDevice { .absolute.uri.toString(), dartDefines: buildInfo.dartDefines, librariesSpec: globals.fs.file(globals.artifacts - .getArtifactPath(Artifact.flutterWebLibrariesJson)).uri.toString() + .getArtifactPath(Artifact.flutterWebLibrariesJson)).uri.toString(), + packagesPath: PackageMap.globalPackagesPath, ); } else { generator = ResidentCompiler( @@ -121,6 +124,7 @@ class FlutterDevice { experimentalFlags: experimentalFlags, dartDefines: buildInfo.dartDefines, initializeFromDill: globals.fs.path.join(getBuildDirectory(), 'cache.dill'), + packagesPath: PackageMap.globalPackagesPath, ); } @@ -285,7 +289,6 @@ class FlutterDevice { vmService, fsName, rootDirectory, - packagesFilePath: packagesFilePath, osUtils: globals.os, ); return devFS.create(); @@ -548,7 +551,7 @@ class FlutterDevice { } Future updateDevFS({ - String mainPath, + Uri mainUri, String target, AssetBundle bundle, DateTime firstBuildTime, @@ -559,6 +562,7 @@ class FlutterDevice { String pathToReload, @required String dillOutputPath, @required List invalidatedFiles, + @required PackageConfig packageConfig, }) async { final Status devFSStatus = globals.logger.startProgress( 'Syncing files to device ${device.name}...', @@ -567,7 +571,7 @@ class FlutterDevice { UpdateFSReport report; try { report = await devFS.update( - mainPath: mainPath, + mainUri: mainUri, target: target, bundle: bundle, firstBuildTime: firstBuildTime, @@ -579,6 +583,7 @@ class FlutterDevice { projectRootPath: projectRootPath, pathToReload: pathToReload, invalidatedFiles: invalidatedFiles, + packageConfig: packageConfig, ); } on DevFSException { devFSStatus.cancel(); diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 82d1fc19f17..55094f410de 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'package:package_config/package_config.dart'; import 'package:vm_service/vm_service.dart' as vm_service; import 'package:platform/platform.dart'; import 'package:meta/meta.dart'; @@ -17,6 +18,7 @@ import 'build_info.dart'; import 'bundle.dart'; import 'compile.dart'; import 'convert.dart'; +import 'dart/package_map.dart'; import 'devfs.dart'; import 'device.dart'; import 'globals.dart' as globals; @@ -151,13 +153,23 @@ class HotRunner extends ResidentRunner { } @override - Future reloadMethod({String libraryId, String classId}) async { + Future reloadMethod({ String libraryId, String classId }) async { final Stopwatch stopwatch = Stopwatch()..start(); final UpdateFSReport results = UpdateFSReport(success: true); final List invalidated = [Uri.parse(libraryId)]; + final PackageConfig packageConfig = await loadPackageConfigUri( + globals.fs.file(PackageMap.globalPackagesPath).absolute.uri, + loader: (Uri uri) { + final File file = globals.fs.file(uri); + if (!file.existsSync()) { + return null; + } + return file.readAsBytes(); + } + ); for (final FlutterDevice device in flutterDevices) { results.incorporateResults(await device.updateDevFS( - mainPath: mainPath, + mainUri: globals.fs.file(mainPath).absolute.uri, target: target, bundle: assetBundle, firstBuildTime: firstBuildTime, @@ -167,6 +179,7 @@ class HotRunner extends ResidentRunner { projectRootPath: projectRootPath, pathToReload: getReloadPath(fullRestart: false), invalidatedFiles: invalidated, + packageConfig: packageConfig, dillOutputPath: dillOutputPath, )); } @@ -345,6 +358,16 @@ class HotRunner extends ResidentRunner { firstBuildTime = DateTime.now(); final List> startupTasks = >[]; + final PackageConfig packageConfig = await loadPackageConfigUri( + globals.fs.file(PackageMap.globalPackagesPath).absolute.uri, + loader: (Uri uri) { + final File file = globals.fs.file(uri); + if (!file.existsSync()) { + return null; + } + return file.readAsBytes(); + } + ); for (final FlutterDevice device in flutterDevices) { // Here we initialize the frontend_server concurrently with the platform // build, reducing overall initialization time. This is safe because the first @@ -353,11 +376,11 @@ class HotRunner extends ResidentRunner { if (device.generator != null) { startupTasks.add( device.generator.recompile( - mainPath, + globals.fs.file(mainPath).uri, [], outputPath: dillOutputPath ?? getDefaultApplicationKernelPath(trackWidgetCreation: debuggingOptions.buildInfo.trackWidgetCreation), - packagesFilePath : packagesFilePath, + packageConfig: packageConfig, ).then((CompilerOutput output) => output?.errorCount == 0) ); } @@ -405,18 +428,17 @@ class HotRunner extends ResidentRunner { } } - // Picking up first device's compiler as a source of truth - compilers - // for all devices should be in sync. - final List invalidatedFiles = await projectFileInvalidator.findInvalidated( + final InvalidationResult invalidationResult = await projectFileInvalidator.findInvalidated( lastCompiled: flutterDevices[0].devFS.lastCompiled, urisToMonitor: flutterDevices[0].devFS.sources, packagesPath: packagesFilePath, asyncScanning: hotRunnerConfig.asyncScanning, + packageConfig: flutterDevices[0].devFS.lastPackageConfig, ); final UpdateFSReport results = UpdateFSReport(success: true); for (final FlutterDevice device in flutterDevices) { results.incorporateResults(await device.updateDevFS( - mainPath: mainPath, + mainUri: globals.fs.file(mainPath).absolute.uri, target: target, bundle: assetBundle, firstBuildTime: firstBuildTime, @@ -425,7 +447,8 @@ class HotRunner extends ResidentRunner { fullRestart: fullRestart, projectRootPath: projectRootPath, pathToReload: getReloadPath(fullRestart: fullRestart), - invalidatedFiles: invalidatedFiles, + invalidatedFiles: invalidationResult.uris, + packageConfig: invalidationResult.packageConfig, dillOutputPath: dillOutputPath, )); } @@ -1156,6 +1179,17 @@ class HotRunner extends ResidentRunner { } } +/// The result of an invalidation check from [ProjectFileInvalidator]. +class InvalidationResult { + const InvalidationResult({ + this.uris, + this.packageConfig, + }); + + final List uris; + final PackageConfig packageConfig; +} + /// The [ProjectFileInvalidator] track the dependencies for a running /// application to determine when they are dirty. class ProjectFileInvalidator { @@ -1182,10 +1216,11 @@ class ProjectFileInvalidator { // ~2000 files. static const int _kMaxPendingStats = 8; - Future> findInvalidated({ + Future findInvalidated({ @required DateTime lastCompiled, @required List urisToMonitor, @required String packagesPath, + @required PackageConfig packageConfig, bool asyncScanning = false, }) async { assert(urisToMonitor != null); @@ -1194,7 +1229,10 @@ class ProjectFileInvalidator { if (lastCompiled == null) { // Initial load. assert(urisToMonitor.isEmpty); - return []; + return InvalidationResult( + packageConfig: await _createPackageConfig(packagesPath), + uris: [] + ); } final Stopwatch stopwatch = Stopwatch()..start(); @@ -1202,9 +1240,6 @@ class ProjectFileInvalidator { // Don't watch pub cache directories to speed things up a little. for (final Uri uri in urisToMonitor) if (_isNotInPubCache(uri)) uri, - - // We need to check the .packages file too since it is not used in compilation. - _fileSystem.file(packagesPath).uri, ]; final List invalidatedFiles = []; @@ -1233,16 +1268,41 @@ class ProjectFileInvalidator { } } } + // We need to check the .packages file too since it is not used in compilation. + final Uri packageUri = _fileSystem.file(packagesPath).uri; + final DateTime updatedAt = _fileSystem.statSync( + packageUri.toFilePath(windows: _platform.isWindows)).modified; + if (updatedAt != null && updatedAt.isAfter(lastCompiled)) { + invalidatedFiles.add(packageUri); + packageConfig = await _createPackageConfig(packagesPath); + } + _logger.printTrace( 'Scanned through ${urisToScan.length} files in ' '${stopwatch.elapsedMilliseconds}ms' '${asyncScanning ? " (async)" : ""}', ); - return invalidatedFiles; + return InvalidationResult( + packageConfig: packageConfig, + uris: invalidatedFiles, + ); } bool _isNotInPubCache(Uri uri) { return !(_platform.isWindows && uri.path.contains(_pubCachePathWindows)) && !uri.path.contains(_pubCachePathLinuxAndMac); } + + Future _createPackageConfig(String packagesPath) { + return loadPackageConfigUri( + _fileSystem.file(packagesPath).absolute.uri, + loader: (Uri uri) { + final File file = _fileSystem.file(uri); + if (!file.existsSync()) { + return null; + } + return file.readAsBytes(); + } + ); + } } diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 025a4554691..a27aa8f50c7 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -448,7 +448,7 @@ class FlutterPlatform extends PlatformPlugin { if (precompiledDillPath == null && precompiledDillFiles == null) { // Lazily instantiate compiler so it is built only if it is actually used. compiler ??= TestCompiler(buildMode, trackWidgetCreation, flutterProject); - mainDart = await compiler.compile(mainDart); + mainDart = await compiler.compile(globals.fs.file(mainDart).uri); if (mainDart == null) { controller.sink.addError(_getErrorMessage('Compilation failed', testPath, shellPath)); diff --git a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart index fcd7bd592ac..fa153ec3cc9 100644 --- a/packages/flutter_tools/lib/src/test/flutter_web_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_web_platform.dart @@ -869,7 +869,7 @@ class TestGoldenComparator { // Lazily create the compiler _compiler = _compiler ?? compilerFactory(); - final String output = await _compiler.compile(listenerFile.path); + final String output = await _compiler.compile(listenerFile.uri); final List command = [ shellPath, '--disable-observatory', diff --git a/packages/flutter_tools/lib/src/test/test_compiler.dart b/packages/flutter_tools/lib/src/test/test_compiler.dart index dac2fb7015d..c569c379786 100644 --- a/packages/flutter_tools/lib/src/test/test_compiler.dart +++ b/packages/flutter_tools/lib/src/test/test_compiler.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:meta/meta.dart'; +import 'package:package_config/package_config.dart'; import '../artifacts.dart'; import '../base/file_system.dart'; @@ -19,8 +20,9 @@ import '../project.dart'; /// A request to the [TestCompiler] for recompilation. class _CompilationRequest { - _CompilationRequest(this.path, this.result); - String path; + _CompilationRequest(this.mainUri, this.result); + + Uri mainUri; Completer result; } @@ -70,7 +72,7 @@ class TestCompiler { // Whether to report compiler messages. bool _suppressOutput = false; - Future compile(String mainDart) { + Future compile(Uri mainDart) { final Completer completer = Completer(); compilerController.add(_CompilationRequest(mainDart, completer)); return completer.future; @@ -95,13 +97,13 @@ class TestCompiler { Future createCompiler() async { final ResidentCompiler residentCompiler = ResidentCompiler( globals.artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), - packagesPath: PackageMap.globalPackagesPath, buildMode: buildMode, trackWidgetCreation: trackWidgetCreation, compilerMessageConsumer: _reportCompilerMessage, initializeFromDill: testFilePath, unsafePackageSerialization: false, dartDefines: const [], + packagesPath: PackageMap.globalPackagesPath, ); if (flutterProject.hasBuilders) { return CodeGeneratingResidentCompiler.create( @@ -112,6 +114,8 @@ class TestCompiler { return residentCompiler; } + PackageConfig _packageConfig; + // Handle a compilation request. Future _onCompilationRequest(_CompilationRequest request) async { final bool isEmpty = compilationQueue.isEmpty; @@ -122,9 +126,19 @@ class TestCompiler { if (!isEmpty) { return; } + _packageConfig ??= await loadPackageConfigUri( + globals.fs.file(PackageMap.globalPackagesPath).absolute.uri, + loader: (Uri uri) async { + final File file = globals.fs.file(uri); + if (!file.existsSync()) { + return null; + } + return file.readAsBytes(); + } + ); while (compilationQueue.isNotEmpty) { final _CompilationRequest request = compilationQueue.first; - globals.printTrace('Compiling ${request.path}'); + globals.printTrace('Compiling ${request.mainUri}'); final Stopwatch compilerTime = Stopwatch()..start(); bool firstCompile = false; if (compiler == null) { @@ -133,9 +147,10 @@ class TestCompiler { } _suppressOutput = false; final CompilerOutput compilerOutput = await compiler.recompile( - request.path, - [Uri.parse(request.path)], + request.mainUri, + [request.mainUri], outputPath: outputDill.path, + packageConfig: _packageConfig, ); final String outputPath = compilerOutput?.outputFilename; @@ -143,12 +158,13 @@ class TestCompiler { // errors, pass [null] upwards to the consumer and shutdown the // compiler to avoid reusing compiler that might have gotten into // a weird state. + final String path = request.mainUri.toFilePath(windows: globals.platform.isWindows); if (outputPath == null || compilerOutput.errorCount > 0) { request.result.complete(null); await _shutdown(); } else { final File outputFile = globals.fs.file(outputPath); - final File kernelReadyToRun = await outputFile.copy('${request.path}.dill'); + final File kernelReadyToRun = await outputFile.copy('$path.dill'); final File testCache = globals.fs.file(testFilePath); if (firstCompile || !testCache.existsSync() || (testCache.lengthSync() < outputFile.lengthSync())) { // The idea is to keep the cache file up-to-date and include as @@ -161,7 +177,7 @@ class TestCompiler { compiler.accept(); compiler.reset(); } - globals.printTrace('Compiling ${request.path} took ${compilerTime.elapsedMilliseconds}ms'); + globals.printTrace('Compiling $path took ${compilerTime.elapsedMilliseconds}ms'); // Only remove now when we finished processing the element compilationQueue.removeAt(0); } diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart index 6a0380d6778..ee6a1ee404e 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart @@ -140,7 +140,7 @@ void main() { // Import. expect(generated, contains("import 'file:///other/lib/main.dart' as entrypoint;")); - expect(generated, contains("import 'file:///foo/lib/generated_plugin_registrant.dart';")); + expect(generated, contains("import 'package:foo/generated_plugin_registrant.dart';")); })); diff --git a/packages/flutter_tools/test/general.shard/compile_batch_test.dart b/packages/flutter_tools/test/general.shard/compile_batch_test.dart index 88ccfb19e5b..801d8e4d5e0 100644 --- a/packages/flutter_tools/test/general.shard/compile_batch_test.dart +++ b/packages/flutter_tools/test/general.shard/compile_batch_test.dart @@ -11,6 +11,7 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/convert.dart'; import 'package:mockito/mockito.dart'; +import 'package:package_config/package_config.dart'; import 'package:process/process.dart'; import 'package:platform/platform.dart'; @@ -59,6 +60,8 @@ void main() { buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const [], + packageConfig: PackageConfig.empty, + packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); @@ -88,6 +91,8 @@ void main() { trackWidgetCreation: false, aot: true, dartDefines: const [], + packageConfig: PackageConfig.empty, + packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); @@ -121,6 +126,8 @@ void main() { trackWidgetCreation: false, aot: true, dartDefines: const [], + packageConfig: PackageConfig.empty, + packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); @@ -152,6 +159,8 @@ void main() { buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const [], + packageConfig: PackageConfig.empty, + packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); @@ -179,6 +188,8 @@ void main() { buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const [], + packageConfig: PackageConfig.empty, + packagesPath: '.packages', ); expect(mockFrontendServerStdIn.getAndClear(), isEmpty); expect(testLogger.errorText, equals('\nCompiler message:\nline1\nline2\n')); @@ -201,6 +212,8 @@ void main() { buildMode: BuildMode.debug, trackWidgetCreation: false, dartDefines: const ['FOO=bar', 'BAZ=qux'], + packageConfig: PackageConfig.empty, + packagesPath: '.packages', ); expect(latestCommand, containsAllInOrder(['-DFOO=bar', '-DBAZ=qux'])); diff --git a/packages/flutter_tools/test/general.shard/compile_expression_test.dart b/packages/flutter_tools/test/general.shard/compile_expression_test.dart index 9f28534fcd4..4d6e274b069 100644 --- a/packages/flutter_tools/test/general.shard/compile_expression_test.dart +++ b/packages/flutter_tools/test/general.shard/compile_expression_test.dart @@ -10,7 +10,9 @@ import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/convert.dart'; +import 'package:flutter_tools/src/globals.dart' as globals; import 'package:mockito/mockito.dart'; +import 'package:package_config/package_config.dart'; import 'package:process/process.dart'; import 'package:platform/platform.dart'; @@ -74,12 +76,13 @@ void main() { ))); await generator.recompile( - '/path/to/main.dart', + globals.fs.file('/path/to/main.dart').uri, null, /* invalidatedFiles */ outputPath: '/build/', + packageConfig: PackageConfig.empty, ).then((CompilerOutput output) { expect(mockFrontendServerStdIn.getAndClear(), - 'compile /path/to/main.dart\n'); + 'compile file:///path/to/main.dart\n'); verifyNoMoreInteractions(mockFrontendServerStdIn); expect(testLogger.errorText, equals('\nCompiler message:\nline1\nline2\n')); @@ -122,9 +125,10 @@ void main() { // The test manages timing via completers. unawaited( generator.recompile( - '/path/to/main.dart', + Uri.parse('/path/to/main.dart'), null, /* invalidatedFiles */ outputPath: '/build/', + packageConfig: PackageConfig.empty, ).then((CompilerOutput outputCompile) { expect(testLogger.errorText, equals('\nCompiler message:\nline1\nline2\n')); diff --git a/packages/flutter_tools/test/general.shard/compile_incremental_test.dart b/packages/flutter_tools/test/general.shard/compile_incremental_test.dart index a1d9b08bd6c..77aafcbc8e6 100644 --- a/packages/flutter_tools/test/general.shard/compile_incremental_test.dart +++ b/packages/flutter_tools/test/general.shard/compile_incremental_test.dart @@ -11,6 +11,7 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/convert.dart'; import 'package:mockito/mockito.dart'; +import 'package:package_config/package_config.dart'; import 'package:process/process.dart'; import 'package:platform/platform.dart'; @@ -58,9 +59,10 @@ void main() { )); final CompilerOutput output = await generator.recompile( - '/path/to/main.dart', + Uri.parse('/path/to/main.dart'), null /* invalidatedFiles */, outputPath: '/build/', + packageConfig: PackageConfig.empty, ); expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n'); verifyNoMoreInteractions(mockFrontendServerStdIn); @@ -78,9 +80,10 @@ void main() { ); expect(asyncGuard(() => generator.recompile( - '/path/to/main.dart', + Uri.parse('/path/to/main.dart'), null, /* invalidatedFiles */ outputPath: '/build/', + packageConfig: PackageConfig.empty, )), throwsToolExit()); }, overrides: { ProcessManager: () => mockProcessManager, @@ -96,9 +99,10 @@ void main() { ); expect(asyncGuard(() => generator.recompile( - '/path/to/main.dart', + Uri.parse('/path/to/main.dart'), null, /* invalidatedFiles */ outputPath: '/build/', + packageConfig: PackageConfig.empty, )), throwsToolExit()); }, overrides: { ProcessManager: () => mockProcessManager, @@ -112,9 +116,10 @@ void main() { .thenAnswer((Invocation invocation) => streamController.stream); streamController.add(utf8.encode('result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n')); await generator.recompile( - '/path/to/main.dart', + Uri.parse('/path/to/main.dart'), null, /* invalidatedFiles */ outputPath: '/build/', + packageConfig: PackageConfig.empty, ); expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n'); @@ -153,7 +158,12 @@ void main() { streamController.add(utf8.encode( 'result abc\nline0\nline1\nabc\nabc /path/to/main.dart.dill 0\n' )); - await generator.recompile('/path/to/main.dart', null /* invalidatedFiles */, outputPath: '/build/'); + await generator.recompile( + Uri.parse('/path/to/main.dart'), + null /* invalidatedFiles */, + outputPath: '/build/', + packageConfig: PackageConfig.empty, + ); expect(mockFrontendServerStdIn.getAndClear(), 'compile /path/to/main.dart\n'); await _recompile(streamController, generator, mockFrontendServerStdIn, @@ -187,16 +197,18 @@ Future _recompile( streamController.add(utf8.encode(mockCompilerOutput)); }); final CompilerOutput output = await generator.recompile( - null /* mainPath */, + Uri.parse('/path/to/main.dart'), [Uri.parse('/path/to/main.dart')], outputPath: '/build/', + packageConfig: PackageConfig.empty, ); expect(output.outputFilename, equals('/path/to/main.dart.dill')); final String commands = mockFrontendServerStdIn.getAndClear(); - final RegExp re = RegExp(r'^recompile (.*)\n/path/to/main.dart\n(.*)\n$'); - expect(commands, matches(re)); - final Match match = re.firstMatch(commands); - expect(match[1] == match[2], isTrue); + final RegExp whitespace = RegExp(r'\s+'); + final List parts = commands.split(whitespace); + + // Test that uuid matches at beginning and end. + expect(parts[2], equals(parts[4])); mockFrontendServerStdIn.stdInWrites.clear(); } diff --git a/packages/flutter_tools/test/general.shard/devfs_test.dart b/packages/flutter_tools/test/general.shard/devfs_test.dart index f85f99e322e..2970ceccb65 100644 --- a/packages/flutter_tools/test/general.shard/devfs_test.dart +++ b/packages/flutter_tools/test/general.shard/devfs_test.dart @@ -16,6 +16,7 @@ import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/vmservice.dart'; import 'package:mockito/mockito.dart'; +import 'package:package_config/package_config.dart'; import 'package:vm_service/vm_service.dart' as vm_service; import '../src/common.dart'; @@ -160,11 +161,12 @@ void main() { final MockResidentCompiler residentCompiler = MockResidentCompiler(); final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', + mainUri: Uri.parse('lib/foo.txt'), generator: residentCompiler, pathToReload: 'lib/foo.txt.dill', trackWidgetCreation: false, invalidatedFiles: [], + packageConfig: PackageConfig.empty, ); expect(report.syncedBytes, 22); @@ -221,11 +223,12 @@ void main() { expect(devFS.assetPathsToEvict, isEmpty); final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', + mainUri: Uri.parse('lib/foo.txt'), generator: residentCompiler, pathToReload: 'lib/foo.txt.dill', trackWidgetCreation: false, invalidatedFiles: [], + packageConfig: PackageConfig.empty, ); vmService.expectMessages([ 'writeFile test lib/foo.txt.dill', @@ -284,17 +287,18 @@ void main() { any, any, outputPath: anyNamed('outputPath'), - packagesFilePath: anyNamed('packagesFilePath'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) { return Future.value(const CompilerOutput('example', 2, [])); }); final UpdateFSReport report = await devFS.update( - mainPath: 'lib/foo.txt', + mainUri: Uri.parse('lib/foo.txt'), generator: residentCompiler, pathToReload: 'lib/foo.txt.dill', trackWidgetCreation: false, invalidatedFiles: [], + packageConfig: PackageConfig.empty, ); expect(report.success, false); @@ -316,18 +320,19 @@ void main() { any, any, outputPath: anyNamed('outputPath'), - packagesFilePath: anyNamed('packagesFilePath'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) { fs.file('example').createSync(); return Future.value(CompilerOutput('example', 0, [sourceFile.uri])); }); final UpdateFSReport report = await devFS.update( - mainPath: 'lib/main.dart', + mainUri: Uri.parse('lib/main.dart'), generator: residentCompiler, pathToReload: 'lib/foo.txt.dill', trackWidgetCreation: false, invalidatedFiles: [], + packageConfig: PackageConfig.empty, ); expect(report.success, true); diff --git a/packages/flutter_tools/test/general.shard/hot_test.dart b/packages/flutter_tools/test/general.shard/hot_test.dart index 15587d670ab..9a7d34f3725 100644 --- a/packages/flutter_tools/test/general.shard/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/hot_test.dart @@ -103,7 +103,7 @@ void main() { MockLocalEngineArtifacts mockArtifacts; when(mockDevFs.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -115,6 +115,7 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation _) => Future.value( UpdateFSReport(success: true, syncedBytes: 1000, invalidatedSourcesCount: 1))); when(mockDevFs.assetPathsToEvict).thenReturn({}); diff --git a/packages/flutter_tools/test/general.shard/package_uri_mapper_test.dart b/packages/flutter_tools/test/general.shard/package_uri_mapper_test.dart deleted file mode 100644 index c03f3df1278..00000000000 --- a/packages/flutter_tools/test/general.shard/package_uri_mapper_test.dart +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2014 The Flutter 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 'dart:typed_data'; - -import 'package:flutter_tools/src/base/file_system.dart'; -import 'package:flutter_tools/src/compile.dart'; -import 'package:flutter_tools/src/convert.dart'; -import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:mockito/mockito.dart'; - -import '../src/common.dart'; -import '../src/context.dart'; - -const String packagesContents = r''' -xml:file:///Users/flutter_user/.pub-cache/hosted/pub.dartlang.org/xml-3.2.3/lib/ -yaml:file:///Users/flutter_user/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib/ -example:file:///example/lib/ -'''; - -const String multiRootPackagesContents = r''' -xml:file:///Users/flutter_user/.pub-cache/hosted/pub.dartlang.org/xml-3.2.3/lib/ -yaml:file:///Users/flutter_user/.pub-cache/hosted/pub.dartlang.org/yaml-2.1.15/lib/ -example:org-dartlang-app:/ -'''; - -void main() { - MockFileSystem mockFileSystem; - MockFile mockFile; - - setUp(() { - mockFileSystem = MockFileSystem(); - mockFile = MockFile(); - when(mockFileSystem.path).thenReturn(globals.fs.path); - when(mockFileSystem.file(any)).thenReturn(mockFile); - when(mockFile.readAsBytesSync()).thenReturn(utf8.encode(packagesContents) as Uint8List); - }); - - testUsingContext('Can map main.dart to correct package', () async { - final PackageUriMapper packageUriMapper = PackageUriMapper('/example/lib/main.dart', '.packages', null, null); - expect(packageUriMapper.map('/example/lib/main.dart').toString(), - 'package:example/main.dart'); - }, overrides: { - FileSystem: () => mockFileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('single-root maps file from other package to null', () async { - final PackageUriMapper packageUriMapper = PackageUriMapper('/example/lib/main.dart', '.packages', null, null); - expect(packageUriMapper.map('/xml/lib/xml.dart'), null); - }, overrides: { - FileSystem: () => mockFileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('single-root maps non-main file from same package', () async { - final PackageUriMapper packageUriMapper = PackageUriMapper('/example/lib/main.dart', '.packages', null, null); - expect(packageUriMapper.map('/example/lib/src/foo.dart').toString(), - 'package:example/src/foo.dart'); - }, overrides: { - FileSystem: () => mockFileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); - - testUsingContext('multi-root maps main file from same package on multiroot scheme', () async { - final MockFileSystem mockFileSystem = MockFileSystem(); - final MockFile mockFile = MockFile(); - when(mockFileSystem.path).thenReturn(globals.fs.path); - when(mockFileSystem.file(any)).thenReturn(mockFile); - when(mockFile.readAsBytesSync()) - .thenReturn(utf8.encode(multiRootPackagesContents) as Uint8List); - final PackageUriMapper packageUriMapper = PackageUriMapper( - '/example/lib/main.dart', - '.packages', - 'org-dartlang-app', - ['/example/lib/', '/gen/lib/']); - expect(packageUriMapper.map('/example/lib/main.dart').toString(), - 'package:example/main.dart'); - }, overrides: { - FileSystem: () => mockFileSystem, - ProcessManager: () => FakeProcessManager.any(), - }); -} - -class MockFileSystem extends Mock implements FileSystem {} -class MockFile extends Mock implements File {} diff --git a/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart b/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart index 70d50b80506..24bb962adbc 100644 --- a/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart +++ b/packages/flutter_tools/test/general.shard/project_file_invalidator_test.dart @@ -3,84 +3,123 @@ // 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/terminal.dart'; import 'package:flutter_tools/src/run_hot.dart'; +import 'package:package_config/package_config.dart'; import 'package:platform/platform.dart'; import '../src/common.dart'; -import '../src/mocks.dart'; // assumption: tests have a timeout less than 100 days final DateTime inFuture = DateTime.now().add(const Duration(days: 100)); void main() { - BufferLogger bufferLogger; - - setUp(() { - bufferLogger = BufferLogger( - terminal: AnsiTerminal( - stdio: MockStdio(), - platform: FakePlatform(), - ), - outputPreferences: OutputPreferences.test(), - ); - }); - for (final bool asyncScanning in [true, false]) { testWithoutContext('No last compile, asyncScanning: $asyncScanning', () async { + final FileSystem fileSystem = MemoryFileSystem(); final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator( - fileSystem: MemoryFileSystem(), + fileSystem: fileSystem, platform: FakePlatform(), - logger: bufferLogger, + logger: BufferLogger.test(), ); + fileSystem.file('.packages').writeAsStringSync('\n'); expect( - await projectFileInvalidator.findInvalidated( + (await projectFileInvalidator.findInvalidated( lastCompiled: null, urisToMonitor: [], - packagesPath: '', + packagesPath: '.packages', asyncScanning: asyncScanning, - ), + packageConfig: PackageConfig.empty, + )).uris, isEmpty, ); }); testWithoutContext('Empty project, asyncScanning: $asyncScanning', () async { + final FileSystem fileSystem = MemoryFileSystem(); final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator( - fileSystem: MemoryFileSystem(), + fileSystem: fileSystem, platform: FakePlatform(), - logger: bufferLogger, + logger: BufferLogger.test(), ); + fileSystem.file('.packages').writeAsStringSync('\n'); expect( - await projectFileInvalidator.findInvalidated( + (await projectFileInvalidator.findInvalidated( lastCompiled: inFuture, urisToMonitor: [], - packagesPath: '', + packagesPath: '.packages', asyncScanning: asyncScanning, - ), + packageConfig: PackageConfig.empty, + )).uris, isEmpty, ); }); testWithoutContext('Non-existent files are ignored, asyncScanning: $asyncScanning', () async { + final FileSystem fileSystem = MemoryFileSystem(); final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator( fileSystem: MemoryFileSystem(), platform: FakePlatform(), - logger: bufferLogger, + logger: BufferLogger.test(), ); + fileSystem.file('.packages').writeAsStringSync('\n'); expect( - await projectFileInvalidator.findInvalidated( + (await projectFileInvalidator.findInvalidated( lastCompiled: inFuture, urisToMonitor: [Uri.parse('/not-there-anymore'),], - packagesPath: '', + packagesPath: '.packages', asyncScanning: asyncScanning, - ), + packageConfig: PackageConfig.empty, + )).uris, isEmpty, ); }); + + testWithoutContext('Picks up changes to the .packages file and updates PackageConfig' + ', asyncScanning: $asyncScanning', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final PackageConfig packageConfig = PackageConfig.empty; + final ProjectFileInvalidator projectFileInvalidator = ProjectFileInvalidator( + fileSystem: fileSystem, + platform: FakePlatform(), + logger: BufferLogger.test(), + ); + fileSystem.file('.packages') + .writeAsStringSync('\n'); + + final InvalidationResult invalidationResult = await projectFileInvalidator.findInvalidated( + lastCompiled: null, + urisToMonitor: [], + packagesPath: '.packages', + asyncScanning: asyncScanning, + packageConfig: packageConfig, + ); + + expect(invalidationResult.packageConfig, isNot(packageConfig)); + + fileSystem.file('.packages') + .writeAsStringSync('foo:lib/\n'); + final DateTime packagesUpdated = fileSystem.statSync('.packages') + .modified; + + final InvalidationResult nextInvalidationResult = await projectFileInvalidator + .findInvalidated( + lastCompiled: packagesUpdated.subtract(const Duration(seconds: 1)), + urisToMonitor: [], + packagesPath: '.packages', + asyncScanning: asyncScanning, + packageConfig: PackageConfig.empty, + ); + + expect(nextInvalidationResult.uris, contains(Uri.parse('.packages'))); + // The PackagConfig should have been recreated too + expect(nextInvalidationResult.packageConfig, + isNot(invalidationResult.packageConfig)); + }); } } diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index 6065f64d055..5a33104982e 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -44,6 +44,7 @@ void main() { setUp(() { testbed = Testbed(setup: () { + globals.fs.file('.packages').writeAsStringSync('\n'); globals.fs.file(globals.fs.path.join('build', 'app.dill')) ..createSync(recursive: true) ..writeAsStringSync('ABC'); @@ -69,9 +70,8 @@ void main() { when(mockDevFS.assetPathsToEvict).thenReturn({}); // FlutterDevice Mocks. when(mockFlutterDevice.updateDevFS( - // Intentionally provide empty list to match above mock. - invalidatedFiles: [], - mainPath: anyNamed('mainPath'), + invalidatedFiles: anyNamed('invalidatedFiles'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -81,6 +81,7 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), dillOutputPath: anyNamed('dillOutputPath'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { return UpdateFSReport( success: true, @@ -225,7 +226,7 @@ void main() { )); await onAppStart.future; when(mockFlutterDevice.updateDevFS( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -236,6 +237,7 @@ void main() { pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), dillOutputPath: anyNamed('dillOutputPath'), + packageConfig: anyNamed('packageConfig'), )).thenThrow(vm_service.RPCError('something bad happened', 666, '')); final OperationResult result = await residentRunner.restart(fullRestart: false); @@ -330,7 +332,7 @@ void main() { )); await onAppStart.future; when(mockFlutterDevice.updateDevFS( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -341,6 +343,7 @@ void main() { pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), dillOutputPath: anyNamed('dillOutputPath'), + packageConfig: anyNamed('packageConfig'), )).thenThrow(vm_service.RPCError('something bad happened', 666, '')); final OperationResult result = await residentRunner.restart(fullRestart: true); diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart index 8bf049e3585..b018a46b3ff 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart @@ -94,7 +94,7 @@ void main() { globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file(globals.fs.path.join('web', 'index.html')).createSync(recursive: true); when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -106,6 +106,7 @@ void main() { pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), trackWidgetCreation: true, + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation _) async { return UpdateFSReport(success: true, syncedBytes: 0)..invalidatedModules = []; }); @@ -313,7 +314,7 @@ void main() { _setupMocks(); launchChromeInstance(mockChrome); when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -325,9 +326,10 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { // Generated entrypoint file in temp dir. - expect(invocation.namedArguments[#mainPath], contains('entrypoint.dart')); + expect(invocation.namedArguments[#mainUri].toString(), contains('entrypoint.dart')); return UpdateFSReport(success: true) ..invalidatedModules = ['example']; }); @@ -362,9 +364,9 @@ void main() { test('Can hot restart after attaching', () => testbed.run(() async { _setupMocks(); launchChromeInstance(mockChrome); - String entrypointFileName; + Uri entrypointFileUri; when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -376,8 +378,9 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { - entrypointFileName = invocation.namedArguments[#mainPath] as String; + entrypointFileUri = invocation.namedArguments[#mainUri] as Uri; return UpdateFSReport(success: true) ..invalidatedModules = ['example']; }); @@ -389,8 +392,8 @@ void main() { final OperationResult result = await residentWebRunner.restart(fullRestart: true); // Ensure that generated entrypoint is generated correctly. - expect(entrypointFileName, isNotNull); - final String entrypointContents = globals.fs.file(entrypointFileName).readAsStringSync(); + expect(entrypointFileUri, isNotNull); + final String entrypointContents = globals.fs.file(entrypointFileUri).readAsStringSync(); expect(entrypointContents, contains('// Flutter web bootstrap script')); expect(entrypointContents, contains("import 'dart:ui' as ui;")); expect(entrypointContents, contains('await ui.webOnlyInitializePlatform();')); @@ -417,7 +420,7 @@ void main() { _setupMocks(); when(mockFlutterDevice.device).thenReturn(mockWebServerDevice); when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -429,6 +432,7 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { return UpdateFSReport(success: true) ..invalidatedModules = ['example']; @@ -469,7 +473,7 @@ void main() { test('Exits when initial compile fails', () => testbed.run(() async { _setupMocks(); when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -480,6 +484,7 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), + packageConfig: anyNamed('packageConfig'), trackWidgetCreation: true, )).thenAnswer((Invocation _) async { return UpdateFSReport(success: false, syncedBytes: 0)..invalidatedModules = []; @@ -527,7 +532,7 @@ void main() { )); await connectionInfoCompleter.future; when(mockWebDevFS.update( - mainPath: anyNamed('mainPath'), + mainUri: anyNamed('mainUri'), target: anyNamed('target'), bundle: anyNamed('bundle'), firstBuildTime: anyNamed('firstBuildTime'), @@ -538,6 +543,7 @@ void main() { projectRootPath: anyNamed('projectRootPath'), pathToReload: anyNamed('pathToReload'), invalidatedFiles: anyNamed('invalidatedFiles'), + packageConfig: anyNamed('packageConfig'), trackWidgetCreation: true, )).thenAnswer((Invocation _) async { return UpdateFSReport(success: false, syncedBytes: 0)..invalidatedModules = []; diff --git a/packages/flutter_tools/test/general.shard/test_compiler_test.dart b/packages/flutter_tools/test/general.shard/test_compiler_test.dart index c6bc63789aa..4a27324c447 100644 --- a/packages/flutter_tools/test/general.shard/test_compiler_test.dart +++ b/packages/flutter_tools/test/general.shard/test_compiler_test.dart @@ -2,16 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/compile.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/test/test_compiler.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:mockito/mockito.dart'; +import 'package:platform/platform.dart'; import '../src/common.dart'; import '../src/testbed.dart'; +final Platform linuxPlatform = FakePlatform( + operatingSystem: 'linux', + environment: {}, +); + void main() { group(TestCompiler, () { Testbed testbed; @@ -20,6 +27,9 @@ void main() { setUp(() { testbed = Testbed( + overrides: { + Platform: () => linuxPlatform, + }, setup: () async { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); @@ -37,37 +47,42 @@ void main() { test('Reports a dill file when compile is successful', () => testbed.run(() async { when(residentCompiler.recompile( - 'test/foo.dart', + any, [Uri.parse('test/foo.dart')], outputPath: testCompiler.outputDill.path, + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { globals.fs.file('abc.dill').createSync(); return const CompilerOutput('abc.dill', 0, []); }); - expect(await testCompiler.compile('test/foo.dart'), 'test/foo.dart.dill'); - expect(globals.fs.file('test/foo.dart.dill').existsSync(), true); + expect(await testCompiler.compile(Uri.parse('test/foo.dart')), 'test/foo.dart.dill'); + expect(globals.fs.file('test/foo.dart.dill'), exists); })); test('Reports null when a compile fails', () => testbed.run(() async { when(residentCompiler.recompile( - 'test/foo.dart', + any, [Uri.parse('test/foo.dart')], outputPath: testCompiler.outputDill.path, + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { globals.fs.file('abc.dill').createSync(); return const CompilerOutput('abc.dill', 1, []); }); - expect(await testCompiler.compile('test/foo.dart'), null); - expect(globals.fs.file('test/foo.dart.dill').existsSync(), false); + expect(await testCompiler.compile(Uri.parse('test/foo.dart')), null); + expect(globals.fs.file('test/foo.dart.dill'), isNot(exists)); verify(residentCompiler.shutdown()).called(1); })); test('Disposing test compiler shuts down backing compiler', () => testbed.run(() async { testCompiler.compiler = residentCompiler; + expect(testCompiler.compilerController.isClosed, false); + await testCompiler.dispose(); + expect(testCompiler.compilerController.isClosed, true); verify(residentCompiler.shutdown()).called(1); })); diff --git a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart index 911ebf45a2e..db565fe851c 100644 --- a/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devfs_web_test.dart @@ -360,7 +360,7 @@ void main() { any, any, outputPath: anyNamed('outputPath'), - packagesFilePath: anyNamed('packagesFilePath'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { return const CompilerOutput('a', 0, []); }); @@ -402,11 +402,12 @@ void main() { webDevFS.webAssetServer.dartSdkSourcemap.createSync(recursive: true); await webDevFS.update( - mainPath: globals.fs.path.join('lib', 'main.dart'), + mainUri: globals.fs.file(globals.fs.path.join('lib', 'main.dart')).uri, generator: residentCompiler, trackWidgetCreation: true, bundleFirstUpload: true, invalidatedFiles: [], + packageConfig: PackageConfig.empty, ); expect(webDevFS.webAssetServer.getFile('require.js'), isNotNull); @@ -452,7 +453,7 @@ void main() { any, any, outputPath: anyNamed('outputPath'), - packagesFilePath: anyNamed('packagesFilePath'), + packageConfig: anyNamed('packageConfig'), )).thenAnswer((Invocation invocation) async { return const CompilerOutput('a', 0, []); }); diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index 2d7b8a4f6f4..d5df2801045 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io' as io show IOSink, ProcessSignal, Stdout, StdoutException; +import 'package:package_config/package_config.dart'; import 'package:platform/platform.dart'; import 'package:flutter_tools/src/android/android_device.dart'; @@ -662,7 +663,7 @@ class MockResidentCompiler extends BasicMock implements ResidentCompiler { } @override - Future recompile(String mainPath, List invalidatedFiles, { String outputPath, String packagesFilePath }) async { + Future recompile(Uri mainPath, List invalidatedFiles, { String outputPath, PackageConfig packageConfig }) async { globals.fs.file(outputPath).createSync(recursive: true); globals.fs.file(outputPath).writeAsStringSync('compiled_kernel_output'); return CompilerOutput(outputPath, 0, []);