artifacts.dart 43.4 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
import 'package:file/memory.dart';
6
import 'package:process/process.dart';
7

8
import 'base/common.dart';
9
import 'base/file_system.dart';
10
import 'base/os.dart';
11
import 'base/platform.dart';
12
import 'base/utils.dart';
13
import 'build_info.dart';
14
import 'cache.dart';
15
import 'globals.dart' as globals;
16 17

enum Artifact {
18
  /// The tool which compiles a dart kernel file into native code.
19
  genSnapshot,
20
  /// The flutter tester binary.
21
  flutterTester,
22
  flutterFramework,
23
  flutterXcframework,
24
  /// The framework directory of the macOS desktop.
25
  flutterMacOSFramework,
26
  vmSnapshotData,
27
  isolateSnapshotData,
28
  icuData,
29 30 31 32
  platformKernelDill,
  platformLibrariesJson,
  flutterPatchedSdkPath,
  frontendServerSnapshotForEngineDartSdk,
33 34 35 36 37 38
  /// The root of the Linux desktop sources.
  linuxDesktopPath,
  // The root of the cpp headers for Linux desktop.
  linuxHeaders,
  /// The root of the Windows desktop sources.
  windowsDesktopPath,
39
  /// The root of the cpp client code for Windows desktop.
40
  windowsCppClientWrapper,
41

42 43 44 45 46 47 48 49 50 51 52 53 54 55
  /// The root of the sky_engine package.
  skyEnginePath,

  // Fuchsia artifacts from the engine prebuilts.
  fuchsiaKernelCompiler,
  fuchsiaFlutterRunner,

  /// Tools related to subsetting or icon font files.
  fontSubset,
  constFinder,
}

/// A subset of [Artifact]s that are platform and build mode independent
enum HostArtifact {
56
  /// The root directory of the dart SDK.
57
  engineDartSdkPath,
58
  /// The dart binary used to execute any of the required snapshots.
59
  engineDartBinary,
60
  /// The dart snapshot of the dart2js compiler.
61
  dart2jsSnapshot,
62
  /// The dart snapshot of the dartdev compiler.
63
  dartdevcSnapshot,
64
  /// The dart snapshot of the kernel worker compiler.
65
  kernelWorkerSnapshot,
66
  /// The root of the web implementation of the dart SDK.
67
  flutterWebSdk,
68
  /// The libraries JSON file for web release builds.
69
  flutterWebLibrariesJson,
70 71
  /// The summary dill for the dartdevc target.
  webPlatformKernelDill,
72 73
  /// The summary dill with null safety enabled for the dartdevc target.
  webPlatformSoundKernelDill,
74

75 76 77 78 79
  /// The precompiled SDKs and sourcemaps for web debug builds.
  webPrecompiledSdk,
  webPrecompiledSdkSourcemaps,
  webPrecompiledCanvaskitSdk,
  webPrecompiledCanvaskitSdkSourcemaps,
80 81
  webPrecompiledCanvaskitAndHtmlSdk,
  webPrecompiledCanvaskitAndHtmlSdkSourcemaps,
82 83 84 85
  webPrecompiledSoundSdk,
  webPrecompiledSoundSdkSourcemaps,
  webPrecompiledCanvaskitSoundSdk,
  webPrecompiledCanvaskitSoundSdkSourcemaps,
86 87
  webPrecompiledCanvaskitAndHtmlSoundSdk,
  webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps,
88

89 90 91 92
  iosDeploy,
  idevicesyslog,
  idevicescreenshot,
  iproxy,
93

94
  /// The root of the sky_engine package.
95
  skyEnginePath,
96 97 98 99 100

  // The Impeller shader compiler.
  impellerc,
  // Impeller's tessellation library.
  libtessellator,
101 102
}

103 104 105 106 107 108 109 110
// TODO(knopp): Remove once darwin artifacts are universal and moved out of darwin-x64
String _enginePlatformDirectoryName(TargetPlatform platform) {
  if (platform == TargetPlatform.darwin) {
    return 'darwin-x64';
  }
  return getNameForTargetPlatform(platform);
}

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
// Remove android target platform type.
TargetPlatform? _mapTargetPlatform(TargetPlatform? targetPlatform) {
  switch (targetPlatform) {
    case TargetPlatform.android:
      return TargetPlatform.android_arm64;
    case TargetPlatform.ios:
    case TargetPlatform.darwin:
    case TargetPlatform.linux_x64:
    case TargetPlatform.linux_arm64:
    case TargetPlatform.windows_x64:
    case TargetPlatform.fuchsia_arm64:
    case TargetPlatform.fuchsia_x64:
    case TargetPlatform.tester:
    case TargetPlatform.web_javascript:
    case TargetPlatform.android_arm:
    case TargetPlatform.android_arm64:
    case TargetPlatform.android_x64:
    case TargetPlatform.android_x86:
    case null:
      return targetPlatform;
  }
}

bool _isWindows(TargetPlatform? platform) {
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
  switch (platform) {
    case TargetPlatform.windows_x64:
      return true;
    case TargetPlatform.android:
    case TargetPlatform.android_arm:
    case TargetPlatform.android_arm64:
    case TargetPlatform.android_x64:
    case TargetPlatform.android_x86:
    case TargetPlatform.darwin:
    case TargetPlatform.fuchsia_arm64:
    case TargetPlatform.fuchsia_x64:
    case TargetPlatform.ios:
    case TargetPlatform.linux_arm64:
    case TargetPlatform.linux_x64:
    case TargetPlatform.tester:
    case TargetPlatform.web_javascript:
151
    case null:
152 153 154 155
      return false;
  }
}

156
String? _artifactToFileName(Artifact artifact, [ TargetPlatform? platform, BuildMode? mode ]) {
157
  final String exe = _isWindows(platform) ? '.exe' : '';
158 159
  switch (artifact) {
    case Artifact.genSnapshot:
160
      return 'gen_snapshot';
161
    case Artifact.flutterTester:
162
      return 'flutter_tester$exe';
163 164
    case Artifact.flutterFramework:
      return 'Flutter.framework';
165 166
    case Artifact.flutterXcframework:
      return 'Flutter.xcframework';
167 168
    case Artifact.flutterMacOSFramework:
      return 'FlutterMacOS.framework';
169 170 171 172
    case Artifact.vmSnapshotData:
      return 'vm_isolate_snapshot.bin';
    case Artifact.isolateSnapshotData:
      return 'isolate_snapshot.bin';
173 174
    case Artifact.icuData:
      return 'icudtl.dat';
175
    case Artifact.platformKernelDill:
176
      return 'platform_strong.dill';
177 178 179 180 181 182 183
    case Artifact.platformLibrariesJson:
      return 'libraries.json';
    case Artifact.flutterPatchedSdkPath:
      assert(false, 'No filename for sdk path, should not be invoked');
      return null;
    case Artifact.frontendServerSnapshotForEngineDartSdk:
      return 'frontend_server.dart.snapshot';
184 185
    case Artifact.linuxDesktopPath:
      return '';
186 187
    case Artifact.linuxHeaders:
      return 'flutter_linux';
188 189
    case Artifact.windowsCppClientWrapper:
      return 'cpp_client_wrapper';
190 191
    case Artifact.windowsDesktopPath:
      return '';
192 193
    case Artifact.skyEnginePath:
      return 'sky_engine';
194 195
    case Artifact.fuchsiaKernelCompiler:
      return 'kernel_compiler.snapshot';
196
    case Artifact.fuchsiaFlutterRunner:
197
      final String jitOrAot = mode!.isJit ? '_jit' : '_aot';
198 199
      final String productOrNo = mode.isRelease ? '_product' : '';
      return 'flutter$jitOrAot${productOrNo}_runner-0.far';
200 201 202 203
    case Artifact.fontSubset:
      return 'font-subset$exe';
    case Artifact.constFinder:
      return 'const_finder.dart.snapshot';
204 205 206
  }
}

207 208 209 210 211 212 213 214
String _hostArtifactToFileName(HostArtifact artifact, Platform platform) {
  final String exe = platform.isWindows ? '.exe' : '';
  String dll = '.so';
  if (platform.isWindows) {
    dll = '.dll';
  } else if (platform.isMacOS) {
    dll = '.dylib';
  }
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
  switch (artifact) {
    case HostArtifact.flutterWebSdk:
      return '';
    case HostArtifact.engineDartSdkPath:
      return 'dart-sdk';
    case HostArtifact.engineDartBinary:
      return 'dart$exe';
    case HostArtifact.dart2jsSnapshot:
      return 'dart2js.dart.snapshot';
    case HostArtifact.dartdevcSnapshot:
      return 'dartdevc.dart.snapshot';
    case HostArtifact.kernelWorkerSnapshot:
      return 'kernel_worker.dart.snapshot';
    case HostArtifact.iosDeploy:
      return 'ios-deploy';
    case HostArtifact.idevicesyslog:
      return 'idevicesyslog';
    case HostArtifact.idevicescreenshot:
      return 'idevicescreenshot';
    case HostArtifact.iproxy:
      return 'iproxy';
    case HostArtifact.skyEnginePath:
      return 'sky_engine';
    case HostArtifact.webPlatformKernelDill:
      return 'flutter_ddc_sdk.dill';
    case HostArtifact.webPlatformSoundKernelDill:
      return 'flutter_ddc_sdk_sound.dill';
    case HostArtifact.flutterWebLibrariesJson:
243
      return 'libraries.json';
244 245 246 247 248 249
    case HostArtifact.webPrecompiledSdk:
    case HostArtifact.webPrecompiledCanvaskitSdk:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk:
    case HostArtifact.webPrecompiledSoundSdk:
    case HostArtifact.webPrecompiledCanvaskitSoundSdk:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk:
250
      return 'dart_sdk.js';
251 252 253 254 255 256
    case HostArtifact.webPrecompiledSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps:
    case HostArtifact.webPrecompiledSoundSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps:
257
      return 'dart_sdk.js.map';
258 259 260 261
    case HostArtifact.impellerc:
      return 'impellerc$exe';
    case HostArtifact.libtessellator:
      return 'libtessellator$dll';
262 263 264
  }
}

265
class EngineBuildPaths {
266
  const EngineBuildPaths({
267 268
    required this.targetEngine,
    required this.hostEngine,
269 270
  }) : assert(targetEngine != null),
       assert(hostEngine != null);
271 272 273 274 275

  final String targetEngine;
  final String hostEngine;
}

276 277
// Manages the engine artifacts of Flutter.
abstract class Artifacts {
278 279
  /// A test-specific implementation of artifacts that returns stable paths for
  /// all artifacts.
280
  ///
281 282
  /// If a [fileSystem] is not provided, creates a new [MemoryFileSystem] instance.
  ///
283
  /// Creates a [LocalEngineArtifacts] if `localEngine` is non-null
284
  factory Artifacts.test({String? localEngine, FileSystem? fileSystem}) {
285
    fileSystem ??= MemoryFileSystem.test();
286
    if (localEngine != null) {
287
      return _TestLocalEngine(localEngine, fileSystem);
288
    }
289
    return _TestArtifacts(fileSystem);
290
  }
291

292
  static LocalEngineArtifacts getLocalEngine(EngineBuildPaths engineBuildPaths) {
293 294 295 296 297 298 299
    return LocalEngineArtifacts(
      engineBuildPaths.targetEngine,
      engineBuildPaths.hostEngine,
      cache: globals.cache,
      fileSystem: globals.fs,
      processManager: globals.processManager,
      platform: globals.platform,
300
      operatingSystemUtils: globals.os,
301
    );
302 303
  }

304 305 306
  /// Returns the requested [artifact] for the [platform], [mode], and [environmentType] combination.
  String getArtifactPath(
    Artifact artifact, {
307 308 309
    TargetPlatform? platform,
    BuildMode? mode,
    EnvironmentType? environmentType,
310
  });
311

312 313 314 315 316 317
  /// Retrieve a host specific artifact that does not depend on the
  /// current build mode or environment.
  FileSystemEntity getHostArtifact(
    HostArtifact artifact,
  );

318 319
  // Returns which set of engine artifacts is currently used for the [platform]
  // and [mode] combination.
320
  String getEngineType(TargetPlatform platform, [ BuildMode? mode ]);
321 322 323

  /// Whether these artifacts correspond to a non-versioned local engine.
  bool get isLocalEngine;
324 325 326
}

/// Manages the engine artifacts downloaded to the local cache.
327
class CachedArtifacts implements Artifacts {
328
  CachedArtifacts({
329 330 331 332
    required FileSystem fileSystem,
    required Platform platform,
    required Cache cache,
    required OperatingSystemUtils operatingSystemUtils,
333 334
  }) : _fileSystem = fileSystem,
       _platform = platform,
335 336
       _cache = cache,
       _operatingSystemUtils = operatingSystemUtils;
337 338 339 340

  final FileSystem _fileSystem;
  final Platform _platform;
  final Cache _cache;
341
  final OperatingSystemUtils _operatingSystemUtils;
342

343 344 345 346 347 348
  @override
  FileSystemEntity getHostArtifact(
    HostArtifact artifact,
  ) {
    switch (artifact) {
      case HostArtifact.engineDartSdkPath:
349
        final String path = _dartSdkPath(_cache);
350 351
        return _fileSystem.directory(path);
      case HostArtifact.engineDartBinary:
352
        final String path = _fileSystem.path.join(_dartSdkPath(_cache), 'bin', _hostArtifactToFileName(artifact, _platform));
353 354 355 356 357
        return _fileSystem.file(path);
      case HostArtifact.flutterWebSdk:
        final String path = _getFlutterWebSdkPath();
        return _fileSystem.directory(path);
      case HostArtifact.flutterWebLibrariesJson:
358
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), _hostArtifactToFileName(artifact, _platform));
359 360
        return _fileSystem.file(path);
      case HostArtifact.webPlatformKernelDill:
361
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
362 363
        return _fileSystem.file(path);
      case HostArtifact.webPlatformSoundKernelDill:
364
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
365 366 367
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSdk:
      case HostArtifact.webPrecompiledSdkSourcemaps:
368
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform));
369 370 371
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSdk:
      case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps:
372
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform));
373 374 375
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps:
376
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
377 378 379
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSoundSdk:
      case HostArtifact.webPrecompiledSoundSdkSourcemaps:
380
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform));
381 382 383
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
384
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
385 386 387
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps:
388
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
389 390 391
        return _fileSystem.file(path);
      case HostArtifact.idevicesyslog:
      case HostArtifact.idevicescreenshot:
392
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
393 394 395
        return _cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName);
      case HostArtifact.skyEnginePath:
        final Directory dartPackageDirectory = _cache.getCacheDir('pkg');
396
        final String path = _fileSystem.path.join(dartPackageDirectory.path,  _hostArtifactToFileName(artifact, _platform));
397 398 399 400
        return _fileSystem.directory(path);
      case HostArtifact.dart2jsSnapshot:
      case HostArtifact.dartdevcSnapshot:
      case HostArtifact.kernelWorkerSnapshot:
401
        final String path = _fileSystem.path.join(_dartSdkPath(_cache), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform));
402 403
        return _fileSystem.file(path);
      case HostArtifact.iosDeploy:
404
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
405 406
        return _cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName);
      case HostArtifact.iproxy:
407
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
408
        return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
409 410 411 412 413
      case HostArtifact.impellerc:
      case HostArtifact.libtessellator:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
        final String engineDir = _getEngineArtifactsPath(_currentHostPlatform(_platform, _operatingSystemUtils))!;
        return _fileSystem.file(_fileSystem.path.join(engineDir, artifactFileName));
414 415 416
    }
  }

417
  @override
418 419
  String getArtifactPath(
    Artifact artifact, {
420 421 422
    TargetPlatform? platform,
    BuildMode? mode,
    EnvironmentType? environmentType,
423
  }) {
424
    platform = _mapTargetPlatform(platform);
425
    switch (platform) {
426
      case TargetPlatform.android:
427
      case TargetPlatform.android_arm:
428
      case TargetPlatform.android_arm64:
429 430
      case TargetPlatform.android_x64:
      case TargetPlatform.android_x86:
431
        assert(platform != TargetPlatform.android);
432
        return _getAndroidArtifactPath(artifact, platform!, mode!);
433
      case TargetPlatform.ios:
434
        return _getIosArtifactPath(artifact, platform!, mode, environmentType);
435
      case TargetPlatform.darwin:
436
      case TargetPlatform.linux_x64:
437
      case TargetPlatform.linux_arm64:
438
      case TargetPlatform.windows_x64:
439
        return _getDesktopArtifactPath(artifact, platform, mode);
440 441
      case TargetPlatform.fuchsia_arm64:
      case TargetPlatform.fuchsia_x64:
442
        return _getFuchsiaArtifactPath(artifact, platform!, mode!);
443
      case TargetPlatform.tester:
444
      case TargetPlatform.web_javascript:
445
      case null:
446
        return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform, _operatingSystemUtils), mode);
447 448 449 450
    }
  }

  @override
451 452
  String getEngineType(TargetPlatform platform, [ BuildMode? mode ]) {
    return _fileSystem.path.basename(_getEngineArtifactsPath(platform, mode)!);
453 454
  }

455
  String _getDesktopArtifactPath(Artifact artifact, TargetPlatform? platform, BuildMode? mode) {
456 457 458
    // When platform is null, a generic host platform artifact is being requested
    // and not the gen_snapshot for darwin as a target platform.
    if (platform != null && artifact == Artifact.genSnapshot) {
459
      final String engineDir = _getEngineArtifactsPath(platform, mode)!;
460
      return _fileSystem.path.join(engineDir, _artifactToFileName(artifact));
461
    }
462
    return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform, _operatingSystemUtils), mode);
463 464
  }

465
  String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
466
    final String engineDir = _getEngineArtifactsPath(platform, mode)!;
467 468 469
    switch (artifact) {
      case Artifact.genSnapshot:
        assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.');
470
        final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
471
        return _fileSystem.path.join(engineDir, hostPlatform, _artifactToFileName(artifact));
472
      case Artifact.frontendServerSnapshotForEngineDartSdk:
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
      case Artifact.constFinder:
      case Artifact.flutterFramework:
      case Artifact.flutterMacOSFramework:
      case Artifact.flutterPatchedSdkPath:
      case Artifact.flutterTester:
      case Artifact.flutterXcframework:
      case Artifact.fontSubset:
      case Artifact.fuchsiaFlutterRunner:
      case Artifact.fuchsiaKernelCompiler:
      case Artifact.icuData:
      case Artifact.isolateSnapshotData:
      case Artifact.linuxDesktopPath:
      case Artifact.linuxHeaders:
      case Artifact.platformKernelDill:
      case Artifact.platformLibrariesJson:
      case Artifact.skyEnginePath:
      case Artifact.vmSnapshotData:
      case Artifact.windowsCppClientWrapper:
      case Artifact.windowsDesktopPath:
492
        return _getHostArtifactPath(artifact, platform, mode);
493 494 495
    }
  }

496
  String _getIosArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode? mode, EnvironmentType? environmentType) {
497 498
    switch (artifact) {
      case Artifact.genSnapshot:
499
      case Artifact.flutterXcframework:
500 501
        final String artifactFileName = _artifactToFileName(artifact)!;
        final String engineDir = _getEngineArtifactsPath(platform, mode)!;
502
        return _fileSystem.path.join(engineDir, artifactFileName);
503
      case Artifact.flutterFramework:
504
        final String engineDir = _getEngineArtifactsPath(platform, mode)!;
505
        return _getIosEngineArtifactPath(engineDir, environmentType, _fileSystem);
506
      case Artifact.frontendServerSnapshotForEngineDartSdk:
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
      case Artifact.constFinder:
      case Artifact.flutterMacOSFramework:
      case Artifact.flutterPatchedSdkPath:
      case Artifact.flutterTester:
      case Artifact.fontSubset:
      case Artifact.fuchsiaFlutterRunner:
      case Artifact.fuchsiaKernelCompiler:
      case Artifact.icuData:
      case Artifact.isolateSnapshotData:
      case Artifact.linuxDesktopPath:
      case Artifact.linuxHeaders:
      case Artifact.platformKernelDill:
      case Artifact.platformLibrariesJson:
      case Artifact.skyEnginePath:
      case Artifact.vmSnapshotData:
      case Artifact.windowsCppClientWrapper:
      case Artifact.windowsDesktopPath:
524
        return _getHostArtifactPath(artifact, platform, mode);
525 526 527
    }
  }

528
  String _getFuchsiaArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
529 530
    final String root = _fileSystem.path.join(
      _cache.getArtifactDirectory('flutter_runner').path,
531 532
      'flutter',
      fuchsiaArchForTargetPlatform(platform),
533
      mode.isRelease ? 'release' : mode.toString(),
534
    );
535
    final String runtime = mode.isJit ? 'jit' : 'aot';
536
    switch (artifact) {
537 538
      case Artifact.genSnapshot:
        final String genSnapshot = mode.isRelease ? 'gen_snapshot_product' : 'gen_snapshot';
539
        return _fileSystem.path.join(root, runtime, 'dart_binaries', genSnapshot);
540 541
      case Artifact.flutterPatchedSdkPath:
        const String artifactFileName = 'flutter_runner_patched_sdk';
542
        return _fileSystem.path.join(root, runtime, artifactFileName);
543
      case Artifact.platformKernelDill:
544
        final String artifactFileName = _artifactToFileName(artifact, platform, mode)!;
545
        return _fileSystem.path.join(root, runtime, 'flutter_runner_patched_sdk', artifactFileName);
546
      case Artifact.fuchsiaKernelCompiler:
547
        final String artifactFileName = _artifactToFileName(artifact, platform, mode)!;
548
        return _fileSystem.path.join(root, runtime, 'dart_binaries', artifactFileName);
549
      case Artifact.fuchsiaFlutterRunner:
550
        final String artifactFileName = _artifactToFileName(artifact, platform, mode)!;
551
        return _fileSystem.path.join(root, runtime, artifactFileName);
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
      case Artifact.constFinder:
      case Artifact.flutterFramework:
      case Artifact.flutterMacOSFramework:
      case Artifact.flutterTester:
      case Artifact.flutterXcframework:
      case Artifact.fontSubset:
      case Artifact.frontendServerSnapshotForEngineDartSdk:
      case Artifact.icuData:
      case Artifact.isolateSnapshotData:
      case Artifact.linuxDesktopPath:
      case Artifact.linuxHeaders:
      case Artifact.platformLibrariesJson:
      case Artifact.skyEnginePath:
      case Artifact.vmSnapshotData:
      case Artifact.windowsCppClientWrapper:
      case Artifact.windowsDesktopPath:
568
        return _getHostArtifactPath(artifact, platform, mode);
569 570 571
    }
  }

572
  String _getFlutterPatchedSdkPath(BuildMode? mode) {
573 574
    final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
    return _fileSystem.path.join(engineArtifactsPath, 'common',
575
        mode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk');
576 577
  }

578
  String _getFlutterWebSdkPath() {
579
    return _cache.getWebSdkDirectory().path;
580 581
  }

582
  String _getHostArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode? mode) {
583
    assert(platform != null);
584
    switch (artifact) {
585 586 587 588
      case Artifact.genSnapshot:
        // For script snapshots any gen_snapshot binary will do. Returning gen_snapshot for
        // android_arm in profile mode because it is available on all supported host platforms.
        return _getAndroidArtifactPath(artifact, TargetPlatform.android_arm, BuildMode.profile);
589 590 591 592 593
      case Artifact.frontendServerSnapshotForEngineDartSdk:
        return _fileSystem.path.join(
          _dartSdkPath(_cache), 'bin', 'snapshots',
          _artifactToFileName(artifact),
        );
594
      case Artifact.flutterTester:
595 596
      case Artifact.vmSnapshotData:
      case Artifact.isolateSnapshotData:
597
      case Artifact.icuData:
598
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
599
        final String platformDirName = _enginePlatformDirectoryName(platform);
600
        return _fileSystem.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
601
      case Artifact.platformKernelDill:
602
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), _artifactToFileName(artifact));
603
      case Artifact.platformLibrariesJson:
604
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), 'lib', _artifactToFileName(artifact));
605
      case Artifact.flutterPatchedSdkPath:
606
        return _getFlutterPatchedSdkPath(mode);
607
      case Artifact.flutterMacOSFramework:
608 609
      case Artifact.linuxDesktopPath:
      case Artifact.windowsDesktopPath:
610
      case Artifact.linuxHeaders:
611
        // TODO(zanderso): remove once debug desktop artifacts are uploaded
612 613
        // under a separate directory from the host artifacts.
        // https://github.com/flutter/flutter/issues/38935
614
        String platformDirName = _enginePlatformDirectoryName(platform);
615
        if (mode == BuildMode.profile || mode == BuildMode.release) {
616
          platformDirName = '$platformDirName-${getNameForBuildMode(mode!)}';
617
        }
618 619
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
        return _fileSystem.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
620 621 622
      case Artifact.windowsCppClientWrapper:
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
        return _fileSystem.path.join(engineArtifactsPath, 'windows-x64', _artifactToFileName(artifact, platform, mode));
623
      case Artifact.skyEnginePath:
624 625
        final Directory dartPackageDirectory = _cache.getCacheDir('pkg');
        return _fileSystem.path.join(dartPackageDirectory.path,  _artifactToFileName(artifact));
626 627
      case Artifact.fontSubset:
      case Artifact.constFinder:
628
        return _cache.getArtifactDirectory('engine')
629
                     .childDirectory(_enginePlatformDirectoryName(platform))
630
                     .childFile(_artifactToFileName(artifact, platform, mode)!)
631
                     .path;
632 633 634 635
      case Artifact.flutterFramework:
      case Artifact.flutterXcframework:
      case Artifact.fuchsiaFlutterRunner:
      case Artifact.fuchsiaKernelCompiler:
636
        throw StateError('Artifact $artifact not available for platform $platform.');
637 638 639
    }
  }

640
  String? _getEngineArtifactsPath(TargetPlatform platform, [ BuildMode? mode ]) {
641
    final String engineDir = _cache.getArtifactDirectory('engine').path;
642
    final String platformName = _enginePlatformDirectoryName(platform);
643
    switch (platform) {
644
      case TargetPlatform.linux_x64:
645
      case TargetPlatform.linux_arm64:
646
      case TargetPlatform.darwin:
647
      case TargetPlatform.windows_x64:
648
        // TODO(zanderso): remove once debug desktop artifacts are uploaded
649 650 651
        // under a separate directory from the host artifacts.
        // https://github.com/flutter/flutter/issues/38935
        if (mode == BuildMode.debug || mode == null) {
652
          return _fileSystem.path.join(engineDir, platformName);
653 654
        }
        final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
655
        return _fileSystem.path.join(engineDir, platformName + suffix);
656 657
      case TargetPlatform.fuchsia_arm64:
      case TargetPlatform.fuchsia_x64:
658
      case TargetPlatform.tester:
659
      case TargetPlatform.web_javascript:
660
        assert(mode == null, 'Platform $platform does not support different build modes.');
661
        return _fileSystem.path.join(engineDir, platformName);
662 663
      case TargetPlatform.ios:
      case TargetPlatform.android_arm:
664
      case TargetPlatform.android_arm64:
665 666 667
      case TargetPlatform.android_x64:
      case TargetPlatform.android_x86:
        assert(mode != null, 'Need to specify a build mode for platform $platform.');
668
        final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode!), '-')}' : '';
669
        return _fileSystem.path.join(engineDir, platformName + suffix);
670 671 672
      case TargetPlatform.android:
        assert(false, 'cannot use TargetPlatform.android to look up artifacts');
        return null;
673 674
    }
  }
675 676 677

  @override
  bool get isLocalEngine => false;
678 679
}

680
TargetPlatform _currentHostPlatform(Platform platform, OperatingSystemUtils operatingSystemUtils) {
681
  if (platform.isMacOS) {
682
    return TargetPlatform.darwin;
683 684
  }
  if (platform.isLinux) {
685 686
    return operatingSystemUtils.hostPlatform == HostPlatform.linux_x64 ?
             TargetPlatform.linux_x64 : TargetPlatform.linux_arm64;
687 688
  }
  if (platform.isWindows) {
689
    return TargetPlatform.windows_x64;
690 691 692 693
  }
  throw UnimplementedError('Host OS not supported.');
}

694
String _getIosEngineArtifactPath(String engineDirectory,
695
    EnvironmentType? environmentType, FileSystem fileSystem) {
696 697
  final Directory xcframeworkDirectory = fileSystem
      .directory(engineDirectory)
698
      .childDirectory(_artifactToFileName(Artifact.flutterXcframework)!);
699 700

  if (!xcframeworkDirectory.existsSync()) {
701
    throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter precache --ios".');
702
  }
703
  Directory? flutterFrameworkSource;
704 705 706 707 708
  for (final Directory platformDirectory
      in xcframeworkDirectory.listSync().whereType<Directory>()) {
    if (!platformDirectory.basename.startsWith('ios-')) {
      continue;
    }
709
    // ios-x86_64-simulator, ios-arm64_x86_64-simulator, or ios-arm64.
710
    final bool simulatorDirectory = platformDirectory.basename.endsWith('-simulator');
711 712 713 714 715 716 717 718 719 720
    if ((environmentType == EnvironmentType.simulator && simulatorDirectory) ||
        (environmentType == EnvironmentType.physical && !simulatorDirectory)) {
      flutterFrameworkSource = platformDirectory;
    }
  }
  if (flutterFrameworkSource == null) {
    throwToolExit('No iOS frameworks found in ${xcframeworkDirectory.path}');
  }

  return flutterFrameworkSource
721
      .childDirectory(_artifactToFileName(Artifact.flutterFramework)!)
722 723 724
      .path;
}

725 726
abstract class LocalEngineArtifacts implements Artifacts {
  factory LocalEngineArtifacts(String engineOutPath, String hostEngineOutPath, {
727 728 729 730 731
    required FileSystem fileSystem,
    required Cache cache,
    required ProcessManager processManager,
    required Platform platform,
    required OperatingSystemUtils operatingSystemUtils,
732 733 734 735 736
  }) = CachedLocalEngineArtifacts;

  String get engineOutPath;
}

737
/// Manages the artifacts of a locally built engine.
738 739
class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
  CachedLocalEngineArtifacts(
740 741
    this.engineOutPath,
    this._hostEngineOutPath, {
742 743 744 745 746
    required FileSystem fileSystem,
    required Cache cache,
    required ProcessManager processManager,
    required Platform platform,
    required OperatingSystemUtils operatingSystemUtils,
747 748 749
  }) : _fileSystem = fileSystem,
       _cache = cache,
       _processManager = processManager,
750 751
       _platform = platform,
       _operatingSystemUtils = operatingSystemUtils;
752

753 754 755
  @override
  final String engineOutPath;

756
  final String _hostEngineOutPath;
757 758 759 760
  final FileSystem _fileSystem;
  final Cache _cache;
  final ProcessManager _processManager;
  final Platform _platform;
761
  final OperatingSystemUtils _operatingSystemUtils;
762

763 764 765 766 767 768 769 770 771 772

  @override
  FileSystemEntity getHostArtifact(
    HostArtifact artifact,
  ) {
    switch (artifact) {
      case HostArtifact.engineDartSdkPath:
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk');
        return _fileSystem.directory(path);
      case HostArtifact.engineDartBinary:
773
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', _hostArtifactToFileName(artifact, _platform));
774 775
        return _fileSystem.file(path);
      case HostArtifact.dart2jsSnapshot:
776
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform));
777 778
        return _fileSystem.file(path);
      case HostArtifact.dartdevcSnapshot:
779
        final String path = _fileSystem.path.join(_dartSdkPath(_cache), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform));
780 781
        return _fileSystem.file(path);
      case HostArtifact.kernelWorkerSnapshot:
782
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform));
783 784 785 786 787
        return _fileSystem.file(path);
      case HostArtifact.flutterWebSdk:
        final String path = _getFlutterWebSdkPath();
        return _fileSystem.directory(path);
      case HostArtifact.flutterWebLibrariesJson:
788
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), _hostArtifactToFileName(artifact, _platform));
789 790
        return _fileSystem.file(path);
      case HostArtifact.webPlatformKernelDill:
791
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
792 793
        return _fileSystem.file(path);
      case HostArtifact.webPlatformSoundKernelDill:
794
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform));
795 796 797
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSdk:
      case HostArtifact.webPrecompiledSdkSourcemaps:
798
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform));
799 800 801
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSdk:
      case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps:
802
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform));
803 804 805
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps:
806
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform));
807 808 809
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSoundSdk:
      case HostArtifact.webPrecompiledSoundSdkSourcemaps:
810
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform));
811 812 813
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
814
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform));
815 816 817
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps:
818
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform));
819 820 821
        return _fileSystem.file(path);
      case HostArtifact.idevicesyslog:
      case HostArtifact.idevicescreenshot:
822
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
823 824 825
        return _cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName);
      case HostArtifact.skyEnginePath:
        final Directory dartPackageDirectory = _cache.getCacheDir('pkg');
826
        final String path = _fileSystem.path.join(dartPackageDirectory.path,  _hostArtifactToFileName(artifact, _platform));
827 828
        return _fileSystem.directory(path);
      case HostArtifact.iosDeploy:
829
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
830 831
        return _cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName);
      case HostArtifact.iproxy:
832
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
833
        return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
834 835 836 837
      case HostArtifact.impellerc:
      case HostArtifact.libtessellator:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform);
        return _fileSystem.file(_fileSystem.path.join(_hostEngineOutPath, artifactFileName));
838 839 840
    }
  }

841
  @override
842 843
  String getArtifactPath(
    Artifact artifact, {
844 845 846
    TargetPlatform? platform,
    BuildMode? mode,
    EnvironmentType? environmentType,
847
  }) {
848
    platform ??= _currentHostPlatform(_platform, _operatingSystemUtils);
849
    platform = _mapTargetPlatform(platform);
850
    final bool isDirectoryArtifact = artifact == Artifact.flutterPatchedSdkPath;
851
    final String? artifactFileName = isDirectoryArtifact ? null : _artifactToFileName(artifact, platform, mode);
852 853
    switch (artifact) {
      case Artifact.genSnapshot:
854
        return _genSnapshotPath();
855
      case Artifact.flutterTester:
856
        return _flutterTesterPath(platform!);
857 858
      case Artifact.isolateSnapshotData:
      case Artifact.vmSnapshotData:
859
        return _fileSystem.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', artifactFileName);
860
      case Artifact.icuData:
861 862
      case Artifact.flutterXcframework:
      case Artifact.flutterMacOSFramework:
863
        return _fileSystem.path.join(engineOutPath, artifactFileName);
864
      case Artifact.platformKernelDill:
865
        if (platform == TargetPlatform.fuchsia_x64 || platform == TargetPlatform.fuchsia_arm64) {
866
          return _fileSystem.path.join(engineOutPath, 'flutter_runner_patched_sdk', artifactFileName);
867
        }
868
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), artifactFileName);
869
      case Artifact.platformLibrariesJson:
870
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), 'lib', artifactFileName);
871
      case Artifact.flutterFramework:
872 873
        return _getIosEngineArtifactPath(
            engineOutPath, environmentType, _fileSystem);
874
      case Artifact.flutterPatchedSdkPath:
875 876 877 878
        // When using local engine always use [BuildMode.debug] regardless of
        // what was specified in [mode] argument because local engine will
        // have only one flutter_patched_sdk in standard location, that
        // is happen to be what debug(non-release) mode is using.
879
        if (platform == TargetPlatform.fuchsia_x64 || platform == TargetPlatform.fuchsia_arm64) {
880
          return _fileSystem.path.join(engineOutPath, 'flutter_runner_patched_sdk');
881
        }
882
        return _getFlutterPatchedSdkPath(BuildMode.debug);
883
      case Artifact.skyEnginePath:
884
        return _fileSystem.path.join(_hostEngineOutPath, 'gen', 'dart-pkg', artifactFileName);
885
      case Artifact.fuchsiaKernelCompiler:
886
        final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
887
        final String modeName = mode!.isRelease ? 'release' : mode.toString();
888
        final String dartBinaries = 'dart_binaries-$modeName-$hostPlatform';
889
        return _fileSystem.path.join(engineOutPath, 'host_bundle', dartBinaries, 'kernel_compiler.dart.snapshot');
890
      case Artifact.fuchsiaFlutterRunner:
891
        final String jitOrAot = mode!.isJit ? '_jit' : '_aot';
892
        final String productOrNo = mode.isRelease ? '_product' : '';
893
        return _fileSystem.path.join(engineOutPath, 'flutter$jitOrAot${productOrNo}_runner-0.far');
894 895 896 897
      case Artifact.fontSubset:
        return _fileSystem.path.join(_hostEngineOutPath, artifactFileName);
      case Artifact.constFinder:
        return _fileSystem.path.join(_hostEngineOutPath, 'gen', artifactFileName);
898 899 900 901 902
      case Artifact.linuxDesktopPath:
      case Artifact.linuxHeaders:
      case Artifact.windowsDesktopPath:
      case Artifact.windowsCppClientWrapper:
        return _fileSystem.path.join(_hostEngineOutPath, artifactFileName);
903
      case Artifact.frontendServerSnapshotForEngineDartSdk:
904 905 906
        return _fileSystem.path.join(
          _hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName,
        );
907 908 909 910
    }
  }

  @override
911
  String getEngineType(TargetPlatform platform, [ BuildMode? mode ]) {
912
    return _fileSystem.path.basename(engineOutPath);
913 914
  }

915
  String _getFlutterPatchedSdkPath(BuildMode? buildMode) {
916
    return _fileSystem.path.join(engineOutPath,
917
        buildMode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk');
918 919
  }

920
  String _getFlutterWebSdkPath() {
921
    return _fileSystem.path.join(engineOutPath, 'flutter_web_sdk');
922 923
  }

924
  String _genSnapshotPath() {
925
    const List<String> clangDirs = <String>['.', 'clang_x64', 'clang_x86', 'clang_i386', 'clang_arm64'];
926
    final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot)!;
927
    for (final String clangDir in clangDirs) {
928 929
      final String genSnapshotPath = _fileSystem.path.join(engineOutPath, clangDir, genSnapshotName);
      if (_processManager.canRun(genSnapshotPath)) {
930
        return genSnapshotPath;
931
      }
932
    }
933
    throw Exception('Unable to find $genSnapshotName');
934 935
  }

936
  String _flutterTesterPath(TargetPlatform platform) {
937
    if (_platform.isLinux) {
938
      return _fileSystem.path.join(engineOutPath, _artifactToFileName(Artifact.flutterTester));
939
    } else if (_platform.isMacOS) {
940
      return _fileSystem.path.join(engineOutPath, 'flutter_tester');
941
    } else if (_platform.isWindows) {
942
      return _fileSystem.path.join(engineOutPath, 'flutter_tester.exe');
943
    }
944
    throw Exception('Unsupported platform $platform.');
945
  }
946 947 948

  @override
  bool get isLocalEngine => true;
949
}
950 951 952 953 954 955 956 957 958

/// An implementation of [Artifacts] that provides individual overrides.
///
/// If an artifact is not provided, the lookup delegates to the parent.
class OverrideArtifacts implements Artifacts {
  /// Creates a new [OverrideArtifacts].
  ///
  /// [parent] must be provided.
  OverrideArtifacts({
959
    required this.parent,
960
    this.frontendServer,
961
    this.engineDartBinary,
962 963
    this.platformKernelDill,
    this.flutterPatchedSdk,
964 965 966
  }) : assert(parent != null);

  final Artifacts parent;
967 968 969 970
  final File? frontendServer;
  final File? engineDartBinary;
  final File? platformKernelDill;
  final File? flutterPatchedSdk;
971 972

  @override
973 974
  String getArtifactPath(
    Artifact artifact, {
975 976 977
    TargetPlatform? platform,
    BuildMode? mode,
    EnvironmentType? environmentType,
978
  }) {
979
    if (artifact == Artifact.frontendServerSnapshotForEngineDartSdk && frontendServer != null) {
980
      return frontendServer!.path;
981
    }
982
    if (artifact == Artifact.platformKernelDill && platformKernelDill != null) {
983
      return platformKernelDill!.path;
984 985
    }
    if (artifact == Artifact.flutterPatchedSdkPath && flutterPatchedSdk != null) {
986
      return flutterPatchedSdk!.path;
987
    }
988 989 990 991 992 993
    return parent.getArtifactPath(
      artifact,
      platform: platform,
      mode: mode,
      environmentType: environmentType,
    );
994 995 996
  }

  @override
997
  String getEngineType(TargetPlatform platform, [ BuildMode? mode ]) => parent.getEngineType(platform, mode);
998 999 1000

  @override
  bool get isLocalEngine => parent.isLocalEngine;
1001 1002 1003 1004

  @override
  FileSystemEntity getHostArtifact(HostArtifact artifact) {
    if (artifact == HostArtifact.engineDartBinary && engineDartBinary != null) {
1005
      return engineDartBinary!;
1006 1007 1008 1009 1010
    }
    return parent.getHostArtifact(
      artifact,
    );
  }
1011
}
1012 1013

/// Locate the Dart SDK.
1014 1015
String _dartSdkPath(Cache cache) {
  return cache.getRoot().childDirectory('dart-sdk').path;
1016
}
1017 1018

class _TestArtifacts implements Artifacts {
1019 1020 1021 1022
  _TestArtifacts(this.fileSystem);

  final FileSystem fileSystem;

1023
  @override
1024 1025
  String getArtifactPath(
    Artifact artifact, {
1026 1027 1028
    TargetPlatform? platform,
    BuildMode? mode,
    EnvironmentType? environmentType,
1029
  }) {
1030 1031 1032 1033 1034 1035 1036 1037
    final StringBuffer buffer = StringBuffer();
    buffer.write(artifact);
    if (platform != null) {
      buffer.write('.$platform');
    }
    if (mode != null) {
      buffer.write('.$mode');
    }
1038 1039 1040
    if (environmentType != null) {
      buffer.write('.$environmentType');
    }
1041 1042 1043 1044
    return buffer.toString();
  }

  @override
1045
  String getEngineType(TargetPlatform platform, [ BuildMode? mode ]) {
1046 1047 1048 1049 1050
    return 'test-engine';
  }

  @override
  bool get isLocalEngine => false;
1051 1052 1053 1054 1055

  @override
  FileSystemEntity getHostArtifact(HostArtifact artifact) {
    return fileSystem.file(artifact.toString());
  }
1056
}
1057 1058

class _TestLocalEngine extends _TestArtifacts implements LocalEngineArtifacts {
1059
  _TestLocalEngine(this.engineOutPath, FileSystem fileSystem) : super(fileSystem);
1060 1061 1062 1063 1064 1065 1066

  @override
  bool get isLocalEngine => true;

  @override
  final String engineOutPath;
}