artifacts.dart 41 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 6
// @dart = 2.8

7
import 'package:file/memory.dart';
8
import 'package:meta/meta.dart';
9
import 'package:process/process.dart';
10

11
import 'base/common.dart';
12
import 'base/file_system.dart';
13
import 'base/os.dart';
14
import 'base/platform.dart';
15
import 'base/utils.dart';
16
import 'build_info.dart';
17
import 'cache.dart';
18
import 'globals_null_migrated.dart' as globals;
19 20

enum Artifact {
21
  /// The tool which compiles a dart kernel file into native code.
22
  genSnapshot,
23
  /// The flutter tester binary.
24
  flutterTester,
25
  flutterFramework,
26
  flutterXcframework,
27
  /// The framework directory of the macOS desktop.
28
  flutterMacOSFramework,
29
  vmSnapshotData,
30
  isolateSnapshotData,
31
  icuData,
32 33 34 35
  platformKernelDill,
  platformLibrariesJson,
  flutterPatchedSdkPath,
  frontendServerSnapshotForEngineDartSdk,
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
  /// 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,
  /// The root of the cpp client code for Windows desktop and Windows UWP desktop.
  windowsCppClientWrapper,
  /// The root of the Windows UWP desktop sources.
  windowsUwpDesktopPath,
  /// The root of the sky_engine package.
  skyEnginePath,
  /// The location of the macOS engine podspec file.
  flutterMacOSPodspec,

  // 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 {
62
  /// The root directory of the dart SDK.
63
  engineDartSdkPath,
64
  /// The dart binary used to execute any of the required snapshots.
65
  engineDartBinary,
66
  /// The dart snapshot of the dart2js compiler.
67
  dart2jsSnapshot,
68
  /// The dart snapshot of the dartdev compiler.
69
  dartdevcSnapshot,
70
  /// The dart snpashot of the kernel worker compiler.
71
  kernelWorkerSnapshot,
72
  /// The root of the web implementation of the dart SDK.
73
  flutterWebSdk,
74
  /// The libraries JSON file for web release builds.
75
  flutterWebLibrariesJson,
76 77
  /// The summary dill for the dartdevc target.
  webPlatformKernelDill,
78 79
  /// The summary dill with null safety enabled for the dartdevc target.
  webPlatformSoundKernelDill,
80

81 82 83 84 85
  /// The precompiled SDKs and sourcemaps for web debug builds.
  webPrecompiledSdk,
  webPrecompiledSdkSourcemaps,
  webPrecompiledCanvaskitSdk,
  webPrecompiledCanvaskitSdkSourcemaps,
86 87
  webPrecompiledCanvaskitAndHtmlSdk,
  webPrecompiledCanvaskitAndHtmlSdkSourcemaps,
88 89 90 91
  webPrecompiledSoundSdk,
  webPrecompiledSoundSdkSourcemaps,
  webPrecompiledCanvaskitSoundSdk,
  webPrecompiledCanvaskitSoundSdkSourcemaps,
92 93
  webPrecompiledCanvaskitAndHtmlSoundSdk,
  webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps,
94

95 96 97 98
  iosDeploy,
  idevicesyslog,
  idevicescreenshot,
  iproxy,
99
  /// The root of the sky_engine package.
100
  skyEnginePath,
101 102 103

  /// The pub or pub.bat executable
  pubExecutable,
104 105
}

106
String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMode mode ]) {
107
  final String exe = platform == TargetPlatform.windows_x64 ? '.exe' : '';
108 109
  switch (artifact) {
    case Artifact.genSnapshot:
110
      return 'gen_snapshot';
111
    case Artifact.flutterTester:
112
      return 'flutter_tester$exe';
113 114
    case Artifact.flutterFramework:
      return 'Flutter.framework';
115 116
    case Artifact.flutterXcframework:
      return 'Flutter.xcframework';
117 118
    case Artifact.flutterMacOSFramework:
      return 'FlutterMacOS.framework';
119 120 121 122
    case Artifact.vmSnapshotData:
      return 'vm_isolate_snapshot.bin';
    case Artifact.isolateSnapshotData:
      return 'isolate_snapshot.bin';
123 124
    case Artifact.icuData:
      return 'icudtl.dat';
125
    case Artifact.platformKernelDill:
126
      return 'platform_strong.dill';
127 128 129 130 131 132 133
    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';
134 135
    case Artifact.linuxDesktopPath:
      return '';
136 137
    case Artifact.linuxHeaders:
      return 'flutter_linux';
138 139
    case Artifact.windowsCppClientWrapper:
      return 'cpp_client_wrapper';
140 141 142
    case Artifact.windowsUwpDesktopPath:
    case Artifact.windowsDesktopPath:
      return '';
143 144
    case Artifact.skyEnginePath:
      return 'sky_engine';
145 146
    case Artifact.flutterMacOSPodspec:
      return 'FlutterMacOS.podspec';
147 148
    case Artifact.fuchsiaKernelCompiler:
      return 'kernel_compiler.snapshot';
149 150 151 152
    case Artifact.fuchsiaFlutterRunner:
      final String jitOrAot = mode.isJit ? '_jit' : '_aot';
      final String productOrNo = mode.isRelease ? '_product' : '';
      return 'flutter$jitOrAot${productOrNo}_runner-0.far';
153 154 155 156
    case Artifact.fontSubset:
      return 'font-subset$exe';
    case Artifact.constFinder:
      return 'const_finder.dart.snapshot';
157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
  }
  assert(false, 'Invalid artifact $artifact.');
  return null;
}

String _hostArtifactToFileName(HostArtifact artifact, bool windows) {
  final String exe = windows ? '.exe' : '';
  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:
192
      return 'libraries.json';
193 194 195 196 197 198
    case HostArtifact.webPrecompiledSdk:
    case HostArtifact.webPrecompiledCanvaskitSdk:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk:
    case HostArtifact.webPrecompiledSoundSdk:
    case HostArtifact.webPrecompiledCanvaskitSoundSdk:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk:
199
      return 'dart_sdk.js';
200 201 202 203 204 205
    case HostArtifact.webPrecompiledSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps:
    case HostArtifact.webPrecompiledSoundSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
    case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps:
206
      return 'dart_sdk.js.map';
207 208
    case HostArtifact.pubExecutable:
      if (windows) {
209 210 211
        return 'pub.bat';
      }
      return 'pub';
212 213 214 215 216
  }
  assert(false, 'Invalid artifact $artifact.');
  return null;
}

217
class EngineBuildPaths {
218 219 220 221 222
  const EngineBuildPaths({
    @required this.targetEngine,
    @required this.hostEngine,
  }) : assert(targetEngine != null),
       assert(hostEngine != null);
223 224 225 226 227

  final String targetEngine;
  final String hostEngine;
}

228 229
// Manages the engine artifacts of Flutter.
abstract class Artifacts {
230 231
  /// A test-specific implementation of artifacts that returns stable paths for
  /// all artifacts.
232
  ///
233 234
  /// If a [fileSystem] is not provided, creates a new [MemoryFileSystem] instance.
  ///
235
  /// Creates a [LocalEngineArtifacts] if `localEngine` is non-null
236
  @visibleForTesting
237 238
  factory Artifacts.test({String localEngine, FileSystem fileSystem}) {
    fileSystem ??= MemoryFileSystem.test();
239
    if (localEngine != null) {
240
      return _TestLocalEngine(localEngine, fileSystem);
241
    }
242
    return _TestArtifacts(fileSystem);
243
  }
244

245
  static LocalEngineArtifacts getLocalEngine(EngineBuildPaths engineBuildPaths) {
246 247 248 249 250 251 252
    return LocalEngineArtifacts(
      engineBuildPaths.targetEngine,
      engineBuildPaths.hostEngine,
      cache: globals.cache,
      fileSystem: globals.fs,
      processManager: globals.processManager,
      platform: globals.platform,
253
      operatingSystemUtils: globals.os,
254
    );
255 256
  }

257 258 259 260 261 262 263
  /// Returns the requested [artifact] for the [platform], [mode], and [environmentType] combination.
  String getArtifactPath(
    Artifact artifact, {
    TargetPlatform platform,
    BuildMode mode,
    EnvironmentType environmentType,
  });
264

265 266 267 268 269 270
  /// Retrieve a host specific artifact that does not depend on the
  /// current build mode or environment.
  FileSystemEntity getHostArtifact(
    HostArtifact artifact,
  );

271 272
  // Returns which set of engine artifacts is currently used for the [platform]
  // and [mode] combination.
273
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]);
274 275 276

  /// Whether these artifacts correspond to a non-versioned local engine.
  bool get isLocalEngine;
277 278 279
}

/// Manages the engine artifacts downloaded to the local cache.
280
class CachedArtifacts implements Artifacts {
281 282 283 284
  CachedArtifacts({
    @required FileSystem fileSystem,
    @required Platform platform,
    @required Cache cache,
285
    @required OperatingSystemUtils operatingSystemUtils,
286 287
  }) : _fileSystem = fileSystem,
       _platform = platform,
288 289
       _cache = cache,
       _operatingSystemUtils = operatingSystemUtils;
290 291 292 293

  final FileSystem _fileSystem;
  final Platform _platform;
  final Cache _cache;
294
  final OperatingSystemUtils _operatingSystemUtils;
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
  @override
  FileSystemEntity getHostArtifact(
    HostArtifact artifact,
  ) {
    switch (artifact) {
      case HostArtifact.engineDartSdkPath:
        final String path = _dartSdkPath(_fileSystem);
        return _fileSystem.directory(path);
      case HostArtifact.engineDartBinary:
        final String path = _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.flutterWebSdk:
        final String path = _getFlutterWebSdkPath();
        return _fileSystem.directory(path);
      case HostArtifact.flutterWebLibrariesJson:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPlatformKernelDill:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPlatformSoundKernelDill:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSdk:
      case HostArtifact.webPrecompiledSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSdk:
      case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSoundSdk:
      case HostArtifact.webPrecompiledSoundSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.idevicesyslog:
      case HostArtifact.idevicescreenshot:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform.isWindows);
        return _cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName);
        break;
      case HostArtifact.skyEnginePath:
        final Directory dartPackageDirectory = _cache.getCacheDir('pkg');
        final String path = _fileSystem.path.join(dartPackageDirectory.path,  _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.directory(path);
      case HostArtifact.pubExecutable:
        final String path = _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin',  _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.dart2jsSnapshot:
      case HostArtifact.dartdevcSnapshot:
      case HostArtifact.kernelWorkerSnapshot:
        final String path = _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.iosDeploy:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform.isWindows);
        return _cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName);
      case HostArtifact.iproxy:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform.isWindows);
        return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
    }
    assert(false);
    return null;
  }

371
  @override
372 373 374 375 376 377
  String getArtifactPath(
    Artifact artifact, {
    TargetPlatform platform,
    BuildMode mode,
    EnvironmentType environmentType,
  }) {
378 379
    switch (platform) {
      case TargetPlatform.android_arm:
380
      case TargetPlatform.android_arm64:
381 382 383 384
      case TargetPlatform.android_x64:
      case TargetPlatform.android_x86:
        return _getAndroidArtifactPath(artifact, platform, mode);
      case TargetPlatform.ios:
385
        return _getIosArtifactPath(artifact, platform, mode, environmentType);
386 387
      case TargetPlatform.darwin_x64:
      case TargetPlatform.linux_x64:
388
      case TargetPlatform.linux_arm64:
389
      case TargetPlatform.windows_x64:
390
      case TargetPlatform.windows_uwp_x64:
391
        return _getDesktopArtifactPath(artifact, platform, mode);
392 393 394
      case TargetPlatform.fuchsia_arm64:
      case TargetPlatform.fuchsia_x64:
        return _getFuchsiaArtifactPath(artifact, platform, mode);
395
      case TargetPlatform.tester:
396
      case TargetPlatform.web_javascript:
397
      default: // could be null, but that can't be specified as a case.
398
        return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform, _operatingSystemUtils), mode);
399 400 401 402
    }
  }

  @override
403
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) {
404
    return _fileSystem.path.basename(_getEngineArtifactsPath(platform, mode));
405 406
  }

407
  String _getDesktopArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
408 409 410 411
    // 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) {
      final String engineDir = _getEngineArtifactsPath(platform, mode);
412
      return _fileSystem.path.join(engineDir, _artifactToFileName(artifact));
413
    }
414
    return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform(_platform, _operatingSystemUtils), mode);
415 416
  }

417
  String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
418
    final String engineDir = _getEngineArtifactsPath(platform, mode);
419
    switch (artifact) {
420
      case Artifact.frontendServerSnapshotForEngineDartSdk:
421
        assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.');
422
        return _fileSystem.path.join(engineDir, _artifactToFileName(artifact));
423 424
      case Artifact.genSnapshot:
        assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.');
425
        final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
426
        return _fileSystem.path.join(engineDir, hostPlatform, _artifactToFileName(artifact));
427
      default:
428
        return _getHostArtifactPath(artifact, platform, mode);
429 430 431
    }
  }

432 433
  String _getIosArtifactPath(Artifact artifact, TargetPlatform platform,
      BuildMode mode, EnvironmentType environmentType) {
434 435
    switch (artifact) {
      case Artifact.genSnapshot:
436
      case Artifact.flutterXcframework:
437
      case Artifact.frontendServerSnapshotForEngineDartSdk:
438
        final String artifactFileName = _artifactToFileName(artifact);
439
        final String engineDir = _getEngineArtifactsPath(platform, mode);
440
        return _fileSystem.path.join(engineDir, artifactFileName);
441 442 443
      case Artifact.flutterFramework:
        final String engineDir = _getEngineArtifactsPath(platform, mode);
        return _getIosEngineArtifactPath(engineDir, environmentType, _fileSystem);
444
      default:
445
        return _getHostArtifactPath(artifact, platform, mode);
446 447 448
    }
  }

449
  String _getFuchsiaArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
450 451
    final String root = _fileSystem.path.join(
      _cache.getArtifactDirectory('flutter_runner').path,
452 453
      'flutter',
      fuchsiaArchForTargetPlatform(platform),
454
      mode.isRelease ? 'release' : mode.toString(),
455
    );
456
    final String runtime = mode.isJit ? 'jit' : 'aot';
457
    switch (artifact) {
458 459
      case Artifact.genSnapshot:
        final String genSnapshot = mode.isRelease ? 'gen_snapshot_product' : 'gen_snapshot';
460
        return _fileSystem.path.join(root, runtime, 'dart_binaries', genSnapshot);
461 462
      case Artifact.flutterPatchedSdkPath:
        const String artifactFileName = 'flutter_runner_patched_sdk';
463
        return _fileSystem.path.join(root, runtime, artifactFileName);
464 465
      case Artifact.platformKernelDill:
        final String artifactFileName = _artifactToFileName(artifact, platform, mode);
466
        return _fileSystem.path.join(root, runtime, 'flutter_runner_patched_sdk', artifactFileName);
467
      case Artifact.fuchsiaKernelCompiler:
468
        final String artifactFileName = _artifactToFileName(artifact, platform, mode);
469
        return _fileSystem.path.join(root, runtime, 'dart_binaries', artifactFileName);
470
      case Artifact.fuchsiaFlutterRunner:
471
        final String artifactFileName = _artifactToFileName(artifact, platform, mode);
472
        return _fileSystem.path.join(root, runtime, artifactFileName);
473
      default:
474
        return _getHostArtifactPath(artifact, platform, mode);
475 476 477
    }
  }

478
  String _getFlutterPatchedSdkPath(BuildMode mode) {
479 480
    final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
    return _fileSystem.path.join(engineArtifactsPath, 'common',
481
        mode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk');
482 483
  }

484
  String _getFlutterWebSdkPath() {
485
    return _cache.getWebSdkDirectory().path;
486 487
  }

488
  String _getHostArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
489
    assert(platform != null);
490
    switch (artifact) {
491 492 493 494
      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);
495
      case Artifact.flutterTester:
496 497
      case Artifact.vmSnapshotData:
      case Artifact.isolateSnapshotData:
498
      case Artifact.frontendServerSnapshotForEngineDartSdk:
499
      case Artifact.icuData:
500
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
501
        final String platformDirName = getNameForTargetPlatform(platform);
502
        return _fileSystem.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
503
      case Artifact.platformKernelDill:
504
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), _artifactToFileName(artifact));
505
      case Artifact.platformLibrariesJson:
506
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), 'lib', _artifactToFileName(artifact));
507
      case Artifact.flutterPatchedSdkPath:
508
        return _getFlutterPatchedSdkPath(mode);
509
      case Artifact.flutterMacOSFramework:
510 511
      case Artifact.linuxDesktopPath:
      case Artifact.windowsDesktopPath:
512
      case Artifact.flutterMacOSPodspec:
513
      case Artifact.linuxHeaders:
514 515 516 517 518 519 520
        // TODO(jonahwilliams): remove once debug desktop artifacts are uploaded
        // under a separate directory from the host artifacts.
        // https://github.com/flutter/flutter/issues/38935
        String platformDirName = getNameForTargetPlatform(platform);
        if (mode == BuildMode.profile || mode == BuildMode.release) {
          platformDirName = '$platformDirName-${getNameForBuildMode(mode)}';
        }
521 522
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
        return _fileSystem.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
523 524 525
      case Artifact.windowsUwpDesktopPath:
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
         return _fileSystem.path.join(engineArtifactsPath, 'windows-uwp-x64-${getNameForBuildMode(mode)}', _artifactToFileName(artifact, platform, mode));
526 527 528
      case Artifact.windowsCppClientWrapper:
        final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
        return _fileSystem.path.join(engineArtifactsPath, 'windows-x64', _artifactToFileName(artifact, platform, mode));
529
      case Artifact.skyEnginePath:
530 531
        final Directory dartPackageDirectory = _cache.getCacheDir('pkg');
        return _fileSystem.path.join(dartPackageDirectory.path,  _artifactToFileName(artifact));
532 533
      case Artifact.fontSubset:
      case Artifact.constFinder:
534 535 536 537
        return _cache.getArtifactDirectory('engine')
                     .childDirectory(getNameForTargetPlatform(platform))
                     .childFile(_artifactToFileName(artifact, platform, mode))
                     .path;
538 539 540 541 542 543
      default:
        assert(false, 'Artifact $artifact not available for platform $platform.');
        return null;
    }
  }

544
  String _getEngineArtifactsPath(TargetPlatform platform, [ BuildMode mode ]) {
545
    final String engineDir = _cache.getArtifactDirectory('engine').path;
546
    final String platformName = getNameForTargetPlatform(platform);
547
    switch (platform) {
548
      case TargetPlatform.linux_x64:
549
      case TargetPlatform.linux_arm64:
550 551
      case TargetPlatform.darwin_x64:
      case TargetPlatform.windows_x64:
552 553 554 555
        // TODO(jonahwilliams): remove once debug desktop artifacts are uploaded
        // under a separate directory from the host artifacts.
        // https://github.com/flutter/flutter/issues/38935
        if (mode == BuildMode.debug || mode == null) {
556
          return _fileSystem.path.join(engineDir, platformName);
557 558
        }
        final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
559
        return _fileSystem.path.join(engineDir, platformName + suffix);
560 561
      case TargetPlatform.fuchsia_arm64:
      case TargetPlatform.fuchsia_x64:
562
      case TargetPlatform.tester:
563
      case TargetPlatform.web_javascript:
564
        assert(mode == null, 'Platform $platform does not support different build modes.');
565
        return _fileSystem.path.join(engineDir, platformName);
566 567
      case TargetPlatform.ios:
      case TargetPlatform.android_arm:
568
      case TargetPlatform.android_arm64:
569 570
      case TargetPlatform.android_x64:
      case TargetPlatform.android_x86:
571
      case TargetPlatform.windows_uwp_x64:
572
        assert(mode != null, 'Need to specify a build mode for platform $platform.');
573
        final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
574
        return _fileSystem.path.join(engineDir, platformName + suffix);
575 576 577
      case TargetPlatform.android:
        assert(false, 'cannot use TargetPlatform.android to look up artifacts');
        return null;
578 579 580 581
    }
    assert(false, 'Invalid platform $platform.');
    return null;
  }
582 583 584

  @override
  bool get isLocalEngine => false;
585 586
}

587
TargetPlatform _currentHostPlatform(Platform platform, OperatingSystemUtils operatingSystemUtils) {
588
  if (platform.isMacOS) {
589
    return TargetPlatform.darwin_x64;
590 591
  }
  if (platform.isLinux) {
592 593
    return operatingSystemUtils.hostPlatform == HostPlatform.linux_x64 ?
             TargetPlatform.linux_x64 : TargetPlatform.linux_arm64;
594 595
  }
  if (platform.isWindows) {
596
    return TargetPlatform.windows_x64;
597 598 599 600
  }
  throw UnimplementedError('Host OS not supported.');
}

601 602 603 604 605 606 607
String _getIosEngineArtifactPath(String engineDirectory,
    EnvironmentType environmentType, FileSystem fileSystem) {
  final Directory xcframeworkDirectory = fileSystem
      .directory(engineDirectory)
      .childDirectory(_artifactToFileName(Artifact.flutterXcframework));

  if (!xcframeworkDirectory.existsSync()) {
608
    throwToolExit('No xcframework found at ${xcframeworkDirectory.path}. Try running "flutter precache --ios".');
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632
  }
  Directory flutterFrameworkSource;
  for (final Directory platformDirectory
      in xcframeworkDirectory.listSync().whereType<Directory>()) {
    if (!platformDirectory.basename.startsWith('ios-')) {
      continue;
    }
    // ios-x86_64-simulator, ios-armv7_arm64 (Xcode 11), or ios-arm64_armv7 (Xcode 12).
    final bool simulatorDirectory =
        platformDirectory.basename.endsWith('-simulator');
    if ((environmentType == EnvironmentType.simulator && simulatorDirectory) ||
        (environmentType == EnvironmentType.physical && !simulatorDirectory)) {
      flutterFrameworkSource = platformDirectory;
    }
  }
  if (flutterFrameworkSource == null) {
    throwToolExit('No iOS frameworks found in ${xcframeworkDirectory.path}');
  }

  return flutterFrameworkSource
      .childDirectory(_artifactToFileName(Artifact.flutterFramework))
      .path;
}

633 634 635 636 637 638 639 640 641 642 643 644
abstract class LocalEngineArtifacts implements Artifacts {
  factory LocalEngineArtifacts(String engineOutPath, String hostEngineOutPath, {
    @required FileSystem fileSystem,
    @required Cache cache,
    @required ProcessManager processManager,
    @required Platform platform,
    @required OperatingSystemUtils operatingSystemUtils,
  }) = CachedLocalEngineArtifacts;

  String get engineOutPath;
}

645
/// Manages the artifacts of a locally built engine.
646 647
class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
  CachedLocalEngineArtifacts(
648 649 650 651 652 653
    this.engineOutPath,
    this._hostEngineOutPath, {
    @required FileSystem fileSystem,
    @required Cache cache,
    @required ProcessManager processManager,
    @required Platform platform,
654
    @required OperatingSystemUtils operatingSystemUtils,
655 656 657
  }) : _fileSystem = fileSystem,
       _cache = cache,
       _processManager = processManager,
658 659
       _platform = platform,
       _operatingSystemUtils = operatingSystemUtils;
660

661 662 663
  @override
  final String engineOutPath;

664
  final String _hostEngineOutPath;
665 666 667 668
  final FileSystem _fileSystem;
  final Cache _cache;
  final ProcessManager _processManager;
  final Platform _platform;
669
  final OperatingSystemUtils _operatingSystemUtils;
670

671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750

  @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:
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.dart2jsSnapshot:
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.dartdevcSnapshot:
        final String path = _fileSystem.path.join(_dartSdkPath(_fileSystem), 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.kernelWorkerSnapshot:
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.flutterWebSdk:
        final String path = _getFlutterWebSdkPath();
        return _fileSystem.directory(path);
      case HostArtifact.flutterWebLibrariesJson:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPlatformKernelDill:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPlatformSoundKernelDill:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSdk:
      case HostArtifact.webPrecompiledSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSdk:
      case HostArtifact.webPrecompiledCanvaskitSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledSoundSdk:
      case HostArtifact.webPrecompiledSoundSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-sound', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitSoundSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-sound', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdk:
      case HostArtifact.webPrecompiledCanvaskitAndHtmlSoundSdkSourcemaps:
        final String path = _fileSystem.path.join(_getFlutterWebSdkPath(), 'kernel', 'amd-canvaskit-html-sound', _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.idevicesyslog:
      case HostArtifact.idevicescreenshot:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform.isWindows);
        return _cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName);
        break;
      case HostArtifact.skyEnginePath:
        final Directory dartPackageDirectory = _cache.getCacheDir('pkg');
        final String path = _fileSystem.path.join(dartPackageDirectory.path,  _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.directory(path);
      case HostArtifact.pubExecutable:
        final String path = _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin',  _hostArtifactToFileName(artifact, _platform.isWindows));
        return _fileSystem.file(path);
      case HostArtifact.iosDeploy:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform.isWindows);
        return _cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName);
      case HostArtifact.iproxy:
        final String artifactFileName = _hostArtifactToFileName(artifact, _platform.isWindows);
        return _cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName);
    }
    assert(false);
    return null;
  }

751
  @override
752 753 754 755 756 757
  String getArtifactPath(
    Artifact artifact, {
    TargetPlatform platform,
    BuildMode mode,
    EnvironmentType environmentType,
  }) {
758
    platform ??= _currentHostPlatform(_platform, _operatingSystemUtils);
759
    final bool isDirectoryArtifact = artifact == Artifact.flutterPatchedSdkPath;
760
    final String artifactFileName = isDirectoryArtifact ? null : _artifactToFileName(artifact, platform, mode);
761 762
    switch (artifact) {
      case Artifact.genSnapshot:
763
        return _genSnapshotPath();
764 765
      case Artifact.flutterTester:
        return _flutterTesterPath(platform);
766 767
      case Artifact.isolateSnapshotData:
      case Artifact.vmSnapshotData:
768
        return _fileSystem.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', artifactFileName);
769
      case Artifact.icuData:
770 771
      case Artifact.flutterXcframework:
      case Artifact.flutterMacOSFramework:
772
        return _fileSystem.path.join(engineOutPath, artifactFileName);
773
      case Artifact.platformKernelDill:
774
        if (platform == TargetPlatform.fuchsia_x64 || platform == TargetPlatform.fuchsia_arm64) {
775
          return _fileSystem.path.join(engineOutPath, 'flutter_runner_patched_sdk', artifactFileName);
776
        }
777
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), artifactFileName);
778
      case Artifact.platformLibrariesJson:
779
        return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), 'lib', artifactFileName);
780
      case Artifact.flutterFramework:
781 782
        return _getIosEngineArtifactPath(
            engineOutPath, environmentType, _fileSystem);
783
      case Artifact.flutterPatchedSdkPath:
784 785 786 787
        // 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.
788
        if (platform == TargetPlatform.fuchsia_x64 || platform == TargetPlatform.fuchsia_arm64) {
789
          return _fileSystem.path.join(engineOutPath, 'flutter_runner_patched_sdk');
790
        }
791
        return _getFlutterPatchedSdkPath(BuildMode.debug);
792
        return _fileSystem.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName);
793
      case Artifact.skyEnginePath:
794
        return _fileSystem.path.join(_hostEngineOutPath, 'gen', 'dart-pkg', artifactFileName);
795
      case Artifact.flutterMacOSPodspec:
796
        return _fileSystem.path.join(_hostEngineOutPath, _artifactToFileName(artifact));
797
      case Artifact.fuchsiaKernelCompiler:
798
        final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
799 800
        final String modeName = mode.isRelease ? 'release' : mode.toString();
        final String dartBinaries = 'dart_binaries-$modeName-$hostPlatform';
801
        return _fileSystem.path.join(engineOutPath, 'host_bundle', dartBinaries, 'kernel_compiler.dart.snapshot');
802 803 804
      case Artifact.fuchsiaFlutterRunner:
        final String jitOrAot = mode.isJit ? '_jit' : '_aot';
        final String productOrNo = mode.isRelease ? '_product' : '';
805
        return _fileSystem.path.join(engineOutPath, 'flutter$jitOrAot${productOrNo}_runner-0.far');
806 807 808 809
      case Artifact.fontSubset:
        return _fileSystem.path.join(_hostEngineOutPath, artifactFileName);
      case Artifact.constFinder:
        return _fileSystem.path.join(_hostEngineOutPath, 'gen', artifactFileName);
810 811 812 813 814 815
      case Artifact.windowsUwpDesktopPath:
      case Artifact.linuxDesktopPath:
      case Artifact.linuxHeaders:
      case Artifact.windowsDesktopPath:
      case Artifact.windowsCppClientWrapper:
        return _fileSystem.path.join(_hostEngineOutPath, artifactFileName);
816 817 818
      case Artifact.frontendServerSnapshotForEngineDartSdk:
        return _fileSystem.path.join(_hostEngineOutPath, 'gen', artifactFileName);
        break;
819 820 821 822 823 824
    }
    assert(false, 'Invalid artifact $artifact.');
    return null;
  }

  @override
825
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) {
826
    return _fileSystem.path.basename(engineOutPath);
827 828
  }

829
  String _getFlutterPatchedSdkPath(BuildMode buildMode) {
830
    return _fileSystem.path.join(engineOutPath,
831
        buildMode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk');
832 833
  }

834
  String _getFlutterWebSdkPath() {
835
    return _fileSystem.path.join(engineOutPath, 'flutter_web_sdk');
836 837
  }

838
  String _genSnapshotPath() {
839
    const List<String> clangDirs = <String>['.', 'clang_x64', 'clang_x86', 'clang_i386'];
840
    final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot);
841
    for (final String clangDir in clangDirs) {
842 843
      final String genSnapshotPath = _fileSystem.path.join(engineOutPath, clangDir, genSnapshotName);
      if (_processManager.canRun(genSnapshotPath)) {
844
        return genSnapshotPath;
845
      }
846
    }
847
    throw Exception('Unable to find $genSnapshotName');
848 849
  }

850
  String _flutterTesterPath(TargetPlatform platform) {
851
     if (_platform.isLinux) {
852
      return _fileSystem.path.join(engineOutPath, _artifactToFileName(Artifact.flutterTester));
853
    } else if (_platform.isMacOS) {
854
      return _fileSystem.path.join(engineOutPath, 'flutter_tester');
855
    } else if (_platform.isWindows) {
856
      return _fileSystem.path.join(engineOutPath, 'flutter_tester.exe');
857
    }
858
    throw Exception('Unsupported platform $platform.');
859
  }
860 861 862

  @override
  bool get isLocalEngine => true;
863
}
864 865 866 867 868 869 870 871 872 873 874

/// 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({
    @required this.parent,
    this.frontendServer,
875
    this.engineDartBinary,
876 877
    this.platformKernelDill,
    this.flutterPatchedSdk,
878 879 880 881
  }) : assert(parent != null);

  final Artifacts parent;
  final File frontendServer;
882
  final File engineDartBinary;
883 884
  final File platformKernelDill;
  final File flutterPatchedSdk;
885 886

  @override
887 888 889 890 891 892
  String getArtifactPath(
    Artifact artifact, {
    TargetPlatform platform,
    BuildMode mode,
    EnvironmentType environmentType,
  }) {
893 894 895
    if (artifact == Artifact.frontendServerSnapshotForEngineDartSdk && frontendServer != null) {
      return frontendServer.path;
    }
896 897 898 899 900 901
    if (artifact == Artifact.platformKernelDill && platformKernelDill != null) {
      return platformKernelDill.path;
    }
    if (artifact == Artifact.flutterPatchedSdkPath && flutterPatchedSdk != null) {
      return flutterPatchedSdk.path;
    }
902 903 904 905 906 907
    return parent.getArtifactPath(
      artifact,
      platform: platform,
      mode: mode,
      environmentType: environmentType,
    );
908 909 910
  }

  @override
911
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) => parent.getEngineType(platform, mode);
912 913 914

  @override
  bool get isLocalEngine => parent.isLocalEngine;
915 916 917 918 919 920 921 922 923 924

  @override
  FileSystemEntity getHostArtifact(HostArtifact artifact) {
    if (artifact == HostArtifact.engineDartBinary && engineDartBinary != null) {
      return engineDartBinary;
    }
    return parent.getHostArtifact(
      artifact,
    );
  }
925
}
926 927 928 929 930

/// Locate the Dart SDK.
String _dartSdkPath(FileSystem fileSystem) {
  return fileSystem.path.join(Cache.flutterRoot, 'bin', 'cache', 'dart-sdk');
}
931 932

class _TestArtifacts implements Artifacts {
933 934 935 936
  _TestArtifacts(this.fileSystem);

  final FileSystem fileSystem;

937
  @override
938 939 940 941 942 943
  String getArtifactPath(
    Artifact artifact, {
    TargetPlatform platform,
    BuildMode mode,
    EnvironmentType environmentType,
  }) {
944 945 946 947 948 949 950 951
    final StringBuffer buffer = StringBuffer();
    buffer.write(artifact);
    if (platform != null) {
      buffer.write('.$platform');
    }
    if (mode != null) {
      buffer.write('.$mode');
    }
952 953 954
    if (environmentType != null) {
      buffer.write('.$environmentType');
    }
955 956 957 958 959 960 961 962 963 964
    return buffer.toString();
  }

  @override
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) {
    return 'test-engine';
  }

  @override
  bool get isLocalEngine => false;
965 966 967 968 969

  @override
  FileSystemEntity getHostArtifact(HostArtifact artifact) {
    return fileSystem.file(artifact.toString());
  }
970
}
971 972

class _TestLocalEngine extends _TestArtifacts implements LocalEngineArtifacts {
973
  _TestLocalEngine(this.engineOutPath, FileSystem fileSystem) : super(fileSystem);
974 975 976 977 978 979 980

  @override
  bool get isLocalEngine => true;

  @override
  final String engineOutPath;
}