diff --git a/packages/flutter_tools/lib/src/runner/local_engine.dart b/packages/flutter_tools/lib/src/runner/local_engine.dart index eab3e568b9c..80f9160bbd8 100644 --- a/packages/flutter_tools/lib/src/runner/local_engine.dart +++ b/packages/flutter_tools/lib/src/runner/local_engine.dart @@ -54,45 +54,8 @@ class LocalEngineLocator { if (engineSourcePath == null && localEngine != null) { try { - final PackageConfig packageConfig = await loadPackageConfigWithLogging( - _fileSystem.file( - // TODO(jonahwilliams): update to package_config - packagePath ?? _fileSystem.path.join('.packages'), - ), - logger: _logger, - throwOnError: false, - ); - // Skip if sky_engine is the version in bin/cache. - Uri engineUri = packageConfig[kFlutterEnginePackageName]?.packageUriRoot; - final String cachedPath = _fileSystem.path.join(_flutterRoot, 'bin', 'cache', 'pkg', kFlutterEnginePackageName, 'lib'); - if (engineUri != null && _fileSystem.identicalSync(cachedPath, engineUri.path)) { - _logger.printTrace('Local engine auto-detection sky_engine in $packagePath is the same version in bin/cache.'); - engineUri = null; - } - // If sky_engine is specified and the engineSourcePath not set, try to - // determine the engineSourcePath by sky_engine setting. A typical engine Uri - // looks like: - // file://flutter-engine-local-path/src/out/host_debug_unopt/gen/dart-pkg/sky_engine/lib/ - if (engineUri?.path != null) { - engineSourcePath = _fileSystem.directory(engineUri.path) - ?.parent - ?.parent - ?.parent - ?.parent - ?.parent - ?.parent - ?.path; - if (engineSourcePath != null && (engineSourcePath == _fileSystem.path.dirname(engineSourcePath) || engineSourcePath.isEmpty)) { - engineSourcePath = null; - throwToolExit( - _userMessages.runnerNoEngineSrcDir( - kFlutterEnginePackageName, - kFlutterEngineEnvironmentVariableName, - ), - exitCode: 2, - ); - } - } + engineSourcePath = _findEngineSourceByLocalEngine(localEngine); + engineSourcePath ??= await _findEngineSourceByPackageConfig(packagePath); } on FileSystemException catch (e) { _logger.printTrace('Local engine auto-detection file exception: $e'); engineSourcePath = null; @@ -113,12 +76,79 @@ class LocalEngineLocator { if (engineSourcePath != null) { _logger.printTrace('Local engine source at $engineSourcePath'); return _findEngineBuildPath(localEngine, engineSourcePath); - } else if (localEngine != null) { - _logger.printTrace('Could not find engine source for $localEngine, skipping'); + } + if (localEngine != null) { + throwToolExit( + _userMessages.runnerNoEngineSrcDir( + kFlutterEnginePackageName, + kFlutterEngineEnvironmentVariableName, + ), + exitCode: 2, + ); } return null; } + String _findEngineSourceByLocalEngine(String localEngine) { + // When the local engine is an absolute path to a variant inside the + // out directory, parse the engine source. + // --local-engine /path/to/cache/builder/src/out/host_debug_unopt + if (_fileSystem.path.isAbsolute(localEngine)) { + final Directory localEngineDirectory = _fileSystem.directory(localEngine); + final Directory outDirectory = localEngineDirectory?.parent; + final Directory srcDirectory = outDirectory?.parent; + if (localEngineDirectory.existsSync() && outDirectory?.basename == 'out' && srcDirectory?.basename == 'src') { + _logger.printTrace('Parsed engine source from local engine as ${srcDirectory.path}.'); + return srcDirectory.path; + } + } + return null; + } + + Future _findEngineSourceByPackageConfig(String packagePath) async { + final PackageConfig packageConfig = await loadPackageConfigWithLogging( + _fileSystem.file( + // TODO(jonahwilliams): update to package_config + packagePath ?? _fileSystem.path.join('.packages'), + ), + logger: _logger, + throwOnError: false, + ); + // Skip if sky_engine is the version in bin/cache. + Uri engineUri = packageConfig[kFlutterEnginePackageName]?.packageUriRoot; + final String cachedPath = _fileSystem.path.join(_flutterRoot, 'bin', 'cache', 'pkg', kFlutterEnginePackageName, 'lib'); + if (engineUri != null && _fileSystem.identicalSync(cachedPath, engineUri.path)) { + _logger.printTrace('Local engine auto-detection sky_engine in $packagePath is the same version in bin/cache.'); + engineUri = null; + } + // If sky_engine is specified and the engineSourcePath not set, try to + // determine the engineSourcePath by sky_engine setting. A typical engine Uri + // looks like: + // file://flutter-engine-local-path/src/out/host_debug_unopt/gen/dart-pkg/sky_engine/lib/ + String engineSourcePath; + if (engineUri?.path != null) { + engineSourcePath = _fileSystem.directory(engineUri.path) + ?.parent + ?.parent + ?.parent + ?.parent + ?.parent + ?.parent + ?.path; + if (engineSourcePath != null && (engineSourcePath == _fileSystem.path.dirname(engineSourcePath) || engineSourcePath.isEmpty)) { + engineSourcePath = null; + throwToolExit( + _userMessages.runnerNoEngineSrcDir( + kFlutterEnginePackageName, + kFlutterEngineEnvironmentVariableName, + ), + exitCode: 2, + ); + } + } + return engineSourcePath; + } + // Determine the host engine directory associated with the local engine: // Strip '_sim_' since there are no host simulator builds. String _getHostEngineBasename(String localEngineBasename) { diff --git a/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart b/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart index 52906b384af..8281014091c 100644 --- a/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/local_engine_test.dart @@ -96,6 +96,79 @@ void main() { expect(logger.traceText, contains('Local engine source at /arbitrary/engine/src')); }); + testWithoutContext('works if --local-engine is specified and --local-engine-src-path ' + 'is determined by --local-engine', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Directory localEngine = fileSystem + .directory('$kArbitraryEngineRoot/src/out/ios_debug/') + ..createSync(recursive: true); + fileSystem.directory('$kArbitraryEngineRoot/src/out/host_debug/').createSync(recursive: true); + + final BufferLogger logger = BufferLogger.test(); + final LocalEngineLocator localEngineLocator = LocalEngineLocator( + fileSystem: fileSystem, + flutterRoot: 'flutter/flutter', + logger: logger, + userMessages: UserMessages(), + platform: FakePlatform(environment: {}), + ); + + expect( + await localEngineLocator.findEnginePath(null, localEngine.path, null), + matchesEngineBuildPaths( + hostEngine: '/arbitrary/engine/src/out/host_debug', + targetEngine: '/arbitrary/engine/src/out/ios_debug', + ), + ); + expect(logger.traceText, contains('Parsed engine source from local engine as /arbitrary/engine/src')); + expect(logger.traceText, contains('Local engine source at /arbitrary/engine/src')); + }); + + testWithoutContext('works if local engine is host engine', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Directory localEngine = fileSystem + .directory('$kArbitraryEngineRoot/src/out/host_debug/') + ..createSync(recursive: true); + + final BufferLogger logger = BufferLogger.test(); + final LocalEngineLocator localEngineLocator = LocalEngineLocator( + fileSystem: fileSystem, + flutterRoot: 'flutter/flutter', + logger: logger, + userMessages: UserMessages(), + platform: FakePlatform(environment: {}), + ); + + expect( + await localEngineLocator.findEnginePath(null, localEngine.path, null), + matchesEngineBuildPaths( + hostEngine: '/arbitrary/engine/src/out/host_debug', + targetEngine: '/arbitrary/engine/src/out/host_debug', + ), + ); + expect(logger.traceText, contains('Local engine source at /arbitrary/engine/src')); + }); + + testWithoutContext('fails if host_debug does not exist', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final Directory localEngine = fileSystem + .directory('$kArbitraryEngineRoot/src/out/ios_debug/') + ..createSync(recursive: true); + + final LocalEngineLocator localEngineLocator = LocalEngineLocator( + fileSystem: fileSystem, + flutterRoot: 'flutter/flutter', + logger: BufferLogger.test(), + userMessages: UserMessages(), + platform: FakePlatform(environment: {}), + ); + + await expectToolExitLater( + localEngineLocator.findEnginePath(null, localEngine.path, null), + contains('No Flutter engine build found at /arbitrary/engine/src/out/host_debug'), + ); + }); + testWithoutContext('works if --local-engine is specified and --local-engine-src-path ' 'is determined by flutter root', () async { final FileSystem fileSystem = MemoryFileSystem.test(); @@ -128,6 +201,24 @@ void main() { ); expect(logger.traceText, contains('Local engine source at flutter/engine/src')); }); + + testWithoutContext('fails if --local-engine is specified and --local-engine-src-path ' + 'cannot be determined', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + + final LocalEngineLocator localEngineLocator = LocalEngineLocator( + fileSystem: fileSystem, + flutterRoot: 'flutter/flutter', + logger: BufferLogger.test(), + userMessages: UserMessages(), + platform: FakePlatform(environment: {}), + ); + + await expectToolExitLater( + localEngineLocator.findEnginePath(null, '/path/to/nothing', null), + contains('Unable to detect local Flutter engine src directory'), + ); + }); } Matcher matchesEngineBuildPaths({