local_engine.dart 7.88 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
// 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 'package:package_config/package_config.dart';

import '../artifacts.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../base/user_messages.dart' hide userMessages;
13
import '../cache.dart';
14 15
import '../dart/package_map.dart';

16
/// A strategy for locating the `out/` directory of a local engine build.
17 18 19 20
///
/// The flutter tool can be run with the output files of one or more engine builds
/// replacing the cached artifacts. Typically this is done by setting the
/// `--local-engine` command line flag to the name of the desired engine variant
21
/// (e.g. "host_debug_unopt"). Provided that the `flutter/` and `engine/` directories
22 23 24 25 26 27
/// are located adjacent to one another, the output folder will be located
/// automatically.
///
/// For scenarios where the engine is not adjacent to flutter, the
/// `--local-engine-src-path` can be provided to give an exact path.
///
28
/// For more information on local engines, see README.md.
29 30
class LocalEngineLocator {
  LocalEngineLocator({
31 32 33 34 35
    required Platform platform,
    required Logger logger,
    required FileSystem fileSystem,
    required String flutterRoot,
    required UserMessages userMessages,
36 37 38 39
  }) : _platform = platform,
       _logger = logger,
       _fileSystem = fileSystem,
       _flutterRoot = flutterRoot,
40
        _userMessages = userMessages;
41 42 43 44 45 46 47 48

  final Platform _platform;
  final Logger _logger;
  final FileSystem _fileSystem;
  final String _flutterRoot;
  final UserMessages _userMessages;

  /// Returns the engine build path of a local engine if one is located, otherwise `null`.
49 50 51 52 53 54 55
  Future<EngineBuildPaths?> findEnginePath({
    String? engineSourcePath,
    String? localEngine,
    String? localHostEngine,
    String? localWebSdk,
    String? packagePath,
  }) async {
56
    engineSourcePath ??= _platform.environment[kFlutterEngineEnvironmentVariableName];
57 58 59
    if (engineSourcePath == null && localEngine == null && localWebSdk == null && packagePath == null) {
      return null;
    }
60

61
    if (engineSourcePath == null) {
62
      try {
63 64 65 66 67 68
        if (localEngine != null) {
          engineSourcePath = _findEngineSourceByBuildPath(localEngine);
        }
        if (localWebSdk != null) {
          engineSourcePath ??= _findEngineSourceByBuildPath(localWebSdk);
        }
69
        engineSourcePath ??= await _findEngineSourceByPackageConfig(packagePath);
70 71
      } on FileSystemException catch (e) {
        _logger.printTrace('Local engine auto-detection file exception: $e');
72 73
        engineSourcePath = null;
      }
74

75 76 77 78 79 80 81 82 83 84 85 86 87 88
      // If engineSourcePath is still not set, try to determine it by flutter root.
      engineSourcePath ??= _tryEnginePath(
        _fileSystem.path.join(_fileSystem.directory(_flutterRoot).parent.path, 'engine', 'src'),
      );
    }

    if (engineSourcePath != null && _tryEnginePath(engineSourcePath) == null) {
      throwToolExit(
        _userMessages.runnerNoEngineBuildDirInPath(engineSourcePath),
        exitCode: 2,
      );
    }

    if (engineSourcePath != null) {
89
      _logger.printTrace('Local engine source at $engineSourcePath');
90 91 92 93 94 95
      return _findEngineBuildPath(
        engineSourcePath: engineSourcePath,
        localEngine: localEngine,
        localWebSdk: localWebSdk,
        localHostEngine: localHostEngine,
      );
96
    }
97
    if (localEngine != null || localWebSdk != null) {
98 99 100 101 102 103 104
      throwToolExit(
        _userMessages.runnerNoEngineSrcDir(
          kFlutterEnginePackageName,
          kFlutterEngineEnvironmentVariableName,
        ),
        exitCode: 2,
      );
105 106 107 108
    }
    return null;
  }

109
  String? _findEngineSourceByBuildPath(String buildPath) {
110 111 112
    // 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
113 114 115
    if (_fileSystem.path.isAbsolute(buildPath)) {
      final Directory buildDirectory = _fileSystem.directory(buildPath);
      final Directory outDirectory = buildDirectory.parent;
116
      final Directory srcDirectory = outDirectory.parent;
117
      if (buildDirectory.existsSync() && outDirectory.basename == 'out' && srcDirectory.basename == 'src') {
118 119 120 121 122 123 124
        _logger.printTrace('Parsed engine source from local engine as ${srcDirectory.path}.');
        return srcDirectory.path;
      }
    }
    return null;
  }

125
  Future<String?> _findEngineSourceByPackageConfig(String? packagePath) async {
126 127
    final PackageConfig packageConfig = await loadPackageConfigWithLogging(
      _fileSystem.file(
128
        // TODO(zanderso): update to package_config
129 130 131 132 133 134
        packagePath ?? _fileSystem.path.join('.packages'),
      ),
      logger: _logger,
      throwOnError: false,
    );
    // Skip if sky_engine is the version in bin/cache.
135
    Uri? engineUri = packageConfig[kFlutterEnginePackageName]?.packageUriRoot;
136 137 138 139 140 141 142 143 144
    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/
145 146 147 148 149 150 151 152 153 154 155
    String? engineSourcePath;
    final String? engineUriPath = engineUri?.path;
    if (engineUriPath != null) {
      engineSourcePath = _fileSystem.directory(engineUriPath)
        .parent
        .parent
        .parent
        .parent
        .parent
        .parent
        .path;
156
      if (engineSourcePath == _fileSystem.path.dirname(engineSourcePath) || engineSourcePath.isEmpty) {
157 158 159 160 161 162 163 164 165 166 167 168 169
        engineSourcePath = null;
        throwToolExit(
          _userMessages.runnerNoEngineSrcDir(
            kFlutterEnginePackageName,
            kFlutterEngineEnvironmentVariableName,
          ),
          exitCode: 2,
        );
      }
    }
    return engineSourcePath;
  }

170 171 172 173 174 175
  EngineBuildPaths _findEngineBuildPath({
    required String engineSourcePath,
    String? localEngine,
    String? localWebSdk,
    String? localHostEngine,
  }) {
176 177
    if (localEngine == null && localWebSdk == null) {
      throwToolExit(_userMessages.runnerLocalEngineOrWebSdkRequired, exitCode: 2);
178 179
    }

180 181 182
    String? engineBuildPath;
    String? engineHostBuildPath;
    if (localEngine != null) {
183
      engineBuildPath = _fileSystem.path.normalize(_fileSystem.path.join(engineSourcePath, 'out', localEngine));
184 185 186 187
      if (!_fileSystem.isDirectorySync(engineBuildPath)) {
        throwToolExit(_userMessages.runnerNoEngineBuild(engineBuildPath), exitCode: 2);
      }

188
      if (localHostEngine == null) {
189
        throwToolExit(_userMessages.runnerLocalEngineRequiresHostEngine);
190
      }
191
      engineHostBuildPath = _fileSystem.path.normalize(
192
        _fileSystem.path.join(_fileSystem.path.dirname(engineBuildPath), localHostEngine),
193 194 195 196
      );
      if (!_fileSystem.isDirectorySync(engineHostBuildPath)) {
        throwToolExit(_userMessages.runnerNoEngineBuild(engineHostBuildPath), exitCode: 2);
      }
197 198
    }

199 200
    String? webSdkPath;
    if (localWebSdk != null) {
201
      webSdkPath = _fileSystem.path.normalize(_fileSystem.path.join(engineSourcePath, 'out', localWebSdk));
202 203 204
      if (!_fileSystem.isDirectorySync(webSdkPath)) {
        throwToolExit(_userMessages.runnerNoWebSdk(webSdkPath), exitCode: 2);
      }
205 206
    }

207
    return EngineBuildPaths(targetEngine: engineBuildPath, webSdk: webSdkPath, hostEngine: engineHostBuildPath);
208 209
  }

210
  String? _tryEnginePath(String enginePath) {
211 212 213 214 215 216
    if (_fileSystem.isDirectorySync(_fileSystem.path.join(enginePath, 'out'))) {
      return enginePath;
    }
    return null;
  }
}