artifacts.dart 20.5 KB
Newer Older
1 2 3 4
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'package:meta/meta.dart';

7 8 9
import 'base/context.dart';
import 'base/file_system.dart';
import 'base/platform.dart';
10
import 'base/process_manager.dart';
11
import 'base/utils.dart';
12
import 'build_info.dart';
13
import 'dart/sdk.dart';
14 15 16
import 'globals.dart';

enum Artifact {
17
  /// The tool which compiles a dart kernel file into native code.
18
  genSnapshot,
19
  /// The flutter tester binary.
20
  flutterTester,
21 22
  snapshotDart,
  flutterFramework,
23
  /// The framework directory of the macOS desktop.
24
  flutterMacOSFramework,
25
  vmSnapshotData,
26 27 28 29 30
  isolateSnapshotData,
  platformKernelDill,
  platformLibrariesJson,
  flutterPatchedSdkPath,
  frontendServerSnapshotForEngineDartSdk,
31
  /// The root directory of the dartk SDK.
32
  engineDartSdkPath,
33
  /// The dart binary used to execute any of the required snapshots.
34
  engineDartBinary,
35
  /// The dart snapshot of the dart2js compiler.
36
  dart2jsSnapshot,
37
  /// The dart snapshot of the dartdev compiler.
38
  dartdevcSnapshot,
39
  /// The dart snpashot of the kernel worker compiler.
40
  kernelWorkerSnapshot,
41
  /// The root of the web implementation of the dart SDK.
42
  flutterWebSdk,
43 44 45 46 47 48 49 50
  iosDeploy,
  ideviceinfo,
  ideviceId,
  idevicename,
  idevicesyslog,
  idevicescreenshot,
  ideviceinstaller,
  iproxy,
51 52 53 54 55 56
  /// The root of the Linux desktop sources.
  linuxDesktopPath,
  /// The root of the Windows desktop sources.
  windowsDesktopPath,
  /// The root of the sky_engine package
  skyEnginePath,
57 58
  /// The location of the macOS engine podspec file.
  flutterMacOSPodspec,
59 60
}

61
String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMode mode ]) {
62 63
  switch (artifact) {
    case Artifact.genSnapshot:
64
      return 'gen_snapshot';
65
    case Artifact.flutterTester:
66 67 68
      if (platform == TargetPlatform.windows_x64) {
        return 'flutter_tester.exe';
      }
69
      return 'flutter_tester';
70 71 72 73
    case Artifact.snapshotDart:
      return 'snapshot.dart';
    case Artifact.flutterFramework:
      return 'Flutter.framework';
74 75
    case Artifact.flutterMacOSFramework:
      return 'FlutterMacOS.framework';
76 77 78 79
    case Artifact.vmSnapshotData:
      return 'vm_isolate_snapshot.bin';
    case Artifact.isolateSnapshotData:
      return 'isolate_snapshot.bin';
80
    case Artifact.platformKernelDill:
81
      return 'platform_strong.dill';
82 83 84 85 86
    case Artifact.platformLibrariesJson:
      return 'libraries.json';
    case Artifact.flutterPatchedSdkPath:
      assert(false, 'No filename for sdk path, should not be invoked');
      return null;
87 88 89
    case Artifact.flutterWebSdk:
      assert(false, 'No filename for web sdk path, should not be invoked');
      return null;
90 91
    case Artifact.engineDartSdkPath:
      return 'dart-sdk';
92 93
    case Artifact.frontendServerSnapshotForEngineDartSdk:
      return 'frontend_server.dart.snapshot';
94
    case Artifact.engineDartBinary:
95 96 97
      if (platform == TargetPlatform.windows_x64) {
        return 'dart.exe';
      }
98
      return 'dart';
99
    case Artifact.dart2jsSnapshot:
100
      return 'dart2js.dart.snapshot';
101 102
    case Artifact.dartdevcSnapshot:
      return 'dartdevc.dart.snapshot';
103
    case Artifact.kernelWorkerSnapshot:
104
      return 'kernel_worker.dart.snapshot';
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
    case Artifact.iosDeploy:
      return 'ios-deploy';
    case Artifact.ideviceinfo:
      return 'ideviceinfo';
    case Artifact.ideviceId:
      return 'idevice_id';
    case Artifact.idevicename:
      return 'idevicename';
    case Artifact.idevicesyslog:
      return 'idevicesyslog';
    case Artifact.idevicescreenshot:
      return 'idevicescreenshot';
    case Artifact.ideviceinstaller:
      return 'ideviceinstaller';
    case Artifact.iproxy:
      return 'iproxy';
121 122 123 124 125 126
    case Artifact.linuxDesktopPath:
      return '';
    case Artifact.windowsDesktopPath:
      return '';
    case Artifact.skyEnginePath:
      return 'sky_engine';
127 128
    case Artifact.flutterMacOSPodspec:
      return 'FlutterMacOS.podspec';
129 130 131 132 133
  }
  assert(false, 'Invalid artifact $artifact.');
  return null;
}

134
class EngineBuildPaths {
135 136 137 138 139
  const EngineBuildPaths({
    @required this.targetEngine,
    @required this.hostEngine,
  }) : assert(targetEngine != null),
       assert(hostEngine != null);
140 141 142 143 144

  final String targetEngine;
  final String hostEngine;
}

145 146
// Manages the engine artifacts of Flutter.
abstract class Artifacts {
147
  static Artifacts get instance => context.get<Artifacts>();
148

149
  static LocalEngineArtifacts getLocalEngine(String engineSrcPath, EngineBuildPaths engineBuildPaths) {
150
    return LocalEngineArtifacts(engineSrcPath, engineBuildPaths.targetEngine, engineBuildPaths.hostEngine);
151 152 153
  }

  // Returns the requested [artifact] for the [platform] and [mode] combination.
154
  String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode });
155 156 157

  // Returns which set of engine artifacts is currently used for the [platform]
  // and [mode] combination.
158
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]);
159 160 161 162 163 164
}

/// Manages the engine artifacts downloaded to the local cache.
class CachedArtifacts extends Artifacts {

  @override
165
  String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) {
166 167
    switch (platform) {
      case TargetPlatform.android_arm:
168
      case TargetPlatform.android_arm64:
169 170 171 172 173 174
      case TargetPlatform.android_x64:
      case TargetPlatform.android_x86:
        return _getAndroidArtifactPath(artifact, platform, mode);
      case TargetPlatform.ios:
        return _getIosArtifactPath(artifact, platform, mode);
      case TargetPlatform.darwin_x64:
175
        return _getDarwinArtifactPath(artifact, platform, mode);
176
      case TargetPlatform.linux_x64:
177
      case TargetPlatform.fuchsia:
178
      case TargetPlatform.windows_x64:
179
      case TargetPlatform.tester:
180
      case TargetPlatform.web_javascript:
181 182
      default: // could be null, but that can't be specified as a case.
        return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform, mode);
183 184 185 186
    }
  }

  @override
187
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) {
188 189 190
    return fs.path.basename(_getEngineArtifactsPath(platform, mode));
  }

191 192 193 194 195 196 197 198 199 200
  String _getDarwinArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
    // 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);
      return fs.path.join(engineDir, _artifactToFileName(artifact));
    }
    return _getHostArtifactPath(artifact, platform ?? _currentHostPlatform, mode);
  }

201
  String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
202
    final String engineDir = _getEngineArtifactsPath(platform, mode);
203
    switch (artifact) {
204
      case Artifact.frontendServerSnapshotForEngineDartSdk:
205 206 207 208
        assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.');
        return fs.path.join(engineDir, _artifactToFileName(artifact));
      case Artifact.genSnapshot:
        assert(mode != BuildMode.debug, 'Artifact $artifact only available in non-debug mode.');
209
        final String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
210 211 212 213 214 215 216 217
        return fs.path.join(engineDir, hostPlatform, _artifactToFileName(artifact));
      default:
        assert(false, 'Artifact $artifact not available for platform $platform.');
        return null;
    }
  }

  String _getIosArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
218
    final String artifactFileName = _artifactToFileName(artifact);
219 220 221 222
    switch (artifact) {
      case Artifact.genSnapshot:
      case Artifact.snapshotDart:
      case Artifact.flutterFramework:
223
      case Artifact.frontendServerSnapshotForEngineDartSdk:
224 225 226 227 228 229 230 231 232 233 234 235 236 237
        final String engineDir = _getEngineArtifactsPath(platform, mode);
        return fs.path.join(engineDir, artifactFileName);
      case Artifact.ideviceId:
      case Artifact.ideviceinfo:
      case Artifact.idevicescreenshot:
      case Artifact.idevicesyslog:
      case Artifact.idevicename:
        return cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName).path;
      case Artifact.iosDeploy:
        return cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName).path;
      case Artifact.ideviceinstaller:
        return cache.getArtifactDirectory('ideviceinstaller').childFile(artifactFileName).path;
      case Artifact.iproxy:
        return cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName).path;
238 239 240 241 242 243
      default:
        assert(false, 'Artifact $artifact not available for platform $platform.');
        return null;
    }
  }

244
  String _getFlutterPatchedSdkPath(BuildMode mode) {
245
    final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
246 247
    return fs.path.join(engineArtifactsPath, 'common',
        mode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk');
248 249
  }

250 251 252 253
  String _getFlutterWebSdkPath() {
    return cache.getWebSdkDirectory().path;
  }

254
  String _getHostArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
255
    assert(platform != null);
256
    switch (artifact) {
257 258 259 260
      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);
261
      case Artifact.flutterTester:
262 263
      case Artifact.vmSnapshotData:
      case Artifact.isolateSnapshotData:
264
      case Artifact.frontendServerSnapshotForEngineDartSdk:
265 266
        final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
        final String platformDirName = getNameForTargetPlatform(platform);
267
        return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
268 269
      case Artifact.engineDartSdkPath:
        return dartSdkPath;
270
      case Artifact.engineDartBinary:
271
        return fs.path.join(dartSdkPath, 'bin', _artifactToFileName(artifact, platform));
272
      case Artifact.platformKernelDill:
273
        return fs.path.join(_getFlutterPatchedSdkPath(mode), _artifactToFileName(artifact));
274
      case Artifact.platformLibrariesJson:
275
        return fs.path.join(_getFlutterPatchedSdkPath(mode), 'lib', _artifactToFileName(artifact));
276
      case Artifact.flutterPatchedSdkPath:
277
        return _getFlutterPatchedSdkPath(mode);
278 279
      case Artifact.flutterWebSdk:
        return _getFlutterWebSdkPath();
280 281
      case Artifact.dart2jsSnapshot:
        return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
282 283
      case Artifact.dartdevcSnapshot:
        return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
284 285
      case Artifact.kernelWorkerSnapshot:
        return fs.path.join(dartSdkPath, 'bin', 'snapshots', _artifactToFileName(artifact));
286
      case Artifact.flutterMacOSFramework:
287 288
      case Artifact.linuxDesktopPath:
      case Artifact.windowsDesktopPath:
289
      case Artifact.flutterMacOSPodspec:
290 291 292 293 294 295 296
        // 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)}';
        }
297 298
        final String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
        return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
299 300 301
      case Artifact.skyEnginePath:
        final Directory dartPackageDirectory = cache.getCacheDir('pkg');
        return fs.path.join(dartPackageDirectory.path,  _artifactToFileName(artifact));
302 303 304 305 306 307
      default:
        assert(false, 'Artifact $artifact not available for platform $platform.');
        return null;
    }
  }

308
  String _getEngineArtifactsPath(TargetPlatform platform, [ BuildMode mode ]) {
309 310
    final String engineDir = cache.getArtifactDirectory('engine').path;
    final String platformName = getNameForTargetPlatform(platform);
311 312 313
    switch (platform) {
      case TargetPlatform.linux_x64:
      case TargetPlatform.darwin_x64:
314
      case TargetPlatform.windows_x64:
315 316 317 318 319 320 321 322
        // 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) {
          return fs.path.join(engineDir, platformName);
        }
        final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
        return fs.path.join(engineDir, platformName + suffix);
323
      case TargetPlatform.fuchsia:
324
      case TargetPlatform.tester:
325
      case TargetPlatform.web_javascript:
326 327 328 329
        assert(mode == null, 'Platform $platform does not support different build modes.');
        return fs.path.join(engineDir, platformName);
      case TargetPlatform.ios:
      case TargetPlatform.android_arm:
330
      case TargetPlatform.android_arm64:
331 332 333
      case TargetPlatform.android_x64:
      case TargetPlatform.android_x86:
        assert(mode != null, 'Need to specify a build mode for platform $platform.');
334
        final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
335 336 337 338 339 340 341
        return fs.path.join(engineDir, platformName + suffix);
    }
    assert(false, 'Invalid platform $platform.');
    return null;
  }

  TargetPlatform get _currentHostPlatform {
342
    if (platform.isMacOS) {
343
      return TargetPlatform.darwin_x64;
344 345
    }
    if (platform.isLinux) {
346
      return TargetPlatform.linux_x64;
347 348
    }
    if (platform.isWindows) {
349
      return TargetPlatform.windows_x64;
350
    }
351
    throw UnimplementedError('Host OS not supported.');
352 353 354 355 356
  }
}

/// Manages the artifacts of a locally built engine.
class LocalEngineArtifacts extends Artifacts {
357 358
  LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath, this._hostEngineOutPath);

359 360
  final String _engineSrcPath;
  final String engineOutPath; // TODO(goderbauer): This should be private.
361
  final String _hostEngineOutPath;
362 363

  @override
364
  String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) {
365
    final String artifactFileName = _artifactToFileName(artifact);
366 367
    switch (artifact) {
      case Artifact.snapshotDart:
368
        return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', artifactFileName);
369
      case Artifact.genSnapshot:
370
        return _genSnapshotPath();
371 372
      case Artifact.flutterTester:
        return _flutterTesterPath(platform);
373 374
      case Artifact.isolateSnapshotData:
      case Artifact.vmSnapshotData:
375
        return fs.path.join(engineOutPath, 'gen', 'flutter', 'lib', 'snapshot', artifactFileName);
376
      case Artifact.platformKernelDill:
377
        return fs.path.join(_getFlutterPatchedSdkPath(mode), artifactFileName);
378
      case Artifact.platformLibrariesJson:
379
        return fs.path.join(_getFlutterPatchedSdkPath(mode), 'lib', artifactFileName);
380
      case Artifact.flutterFramework:
381
        return fs.path.join(engineOutPath, artifactFileName);
382
      case Artifact.flutterMacOSFramework:
383
        return fs.path.join(engineOutPath, artifactFileName);
384
      case Artifact.flutterPatchedSdkPath:
385 386 387 388 389
        // 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.
        return _getFlutterPatchedSdkPath(BuildMode.debug);
390 391
      case Artifact.flutterWebSdk:
        return _getFlutterWebSdkPath();
392
      case Artifact.frontendServerSnapshotForEngineDartSdk:
393
        return fs.path.join(_hostEngineOutPath, 'gen', artifactFileName);
394 395
      case Artifact.engineDartSdkPath:
        return fs.path.join(_hostEngineOutPath, 'dart-sdk');
396
      case Artifact.engineDartBinary:
397
        return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', artifactFileName);
398
      case Artifact.dart2jsSnapshot:
399
        return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName);
400
      case Artifact.dartdevcSnapshot:
401
        return fs.path.join(dartSdkPath, 'bin', 'snapshots', artifactFileName);
402
      case Artifact.kernelWorkerSnapshot:
403 404 405 406 407 408 409 410 411 412 413 414 415
        return fs.path.join(_hostEngineOutPath, 'dart-sdk', 'bin', 'snapshots', artifactFileName);
      case Artifact.ideviceId:
      case Artifact.ideviceinfo:
      case Artifact.idevicename:
      case Artifact.idevicescreenshot:
      case Artifact.idevicesyslog:
        return cache.getArtifactDirectory('libimobiledevice').childFile(artifactFileName).path;
      case Artifact.ideviceinstaller:
        return cache.getArtifactDirectory('ideviceinstaller').childFile(artifactFileName).path;
      case Artifact.iosDeploy:
        return cache.getArtifactDirectory('ios-deploy').childFile(artifactFileName).path;
      case Artifact.iproxy:
        return cache.getArtifactDirectory('usbmuxd').childFile(artifactFileName).path;
416
      case Artifact.linuxDesktopPath:
417
        return fs.path.join(_hostEngineOutPath, artifactFileName);
418
      case Artifact.windowsDesktopPath:
419
        return fs.path.join(_hostEngineOutPath, artifactFileName);
420
      case Artifact.skyEnginePath:
421
        return fs.path.join(_hostEngineOutPath, 'gen', 'dart-pkg', artifactFileName);
422 423
      case Artifact.flutterMacOSPodspec:
        return fs.path.join(_hostEngineOutPath, _artifactToFileName(artifact));
424 425 426 427 428 429
    }
    assert(false, 'Invalid artifact $artifact.');
    return null;
  }

  @override
430
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) {
431 432 433
    return fs.path.basename(engineOutPath);
  }

434 435 436
  String _getFlutterPatchedSdkPath(BuildMode buildMode) {
    return fs.path.join(engineOutPath,
        buildMode == BuildMode.release ? 'flutter_patched_sdk_product' : 'flutter_patched_sdk');
437 438
  }

439 440 441 442
  String _getFlutterWebSdkPath() {
    return fs.path.join(engineOutPath, 'flutter_web_sdk');
  }

443
  String _genSnapshotPath() {
444
    const List<String> clangDirs = <String>['.', 'clang_x64', 'clang_x86', 'clang_i386'];
445 446 447
    final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot);
    for (String clangDir in clangDirs) {
      final String genSnapshotPath = fs.path.join(engineOutPath, clangDir, genSnapshotName);
448
      if (processManager.canRun(genSnapshotPath)) {
449
        return genSnapshotPath;
450
      }
451
    }
452
    throw Exception('Unable to find $genSnapshotName');
453 454
  }

455
  String _flutterTesterPath(TargetPlatform platform) {
456
    if (getCurrentHostPlatform() == HostPlatform.linux_x64) {
457
      return fs.path.join(engineOutPath, _artifactToFileName(Artifact.flutterTester));
458
    } else if (getCurrentHostPlatform() == HostPlatform.darwin_x64) {
459
      return fs.path.join(engineOutPath, 'flutter_tester');
460 461
    } else if (getCurrentHostPlatform() == HostPlatform.windows_x64) {
      return fs.path.join(engineOutPath, 'flutter_tester.exe');
462
    }
463
    throw Exception('Unsupported platform $platform.');
464 465
  }
}
466 467 468 469 470 471 472 473 474 475 476

/// 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,
477
    this.engineDartBinary,
478 479
    this.platformKernelDill,
    this.flutterPatchedSdk,
480 481 482 483
  }) : assert(parent != null);

  final Artifacts parent;
  final File frontendServer;
484
  final File engineDartBinary;
485 486
  final File platformKernelDill;
  final File flutterPatchedSdk;
487 488

  @override
489
  String getArtifactPath(Artifact artifact, { TargetPlatform platform, BuildMode mode }) {
490 491 492
    if (artifact == Artifact.frontendServerSnapshotForEngineDartSdk && frontendServer != null) {
      return frontendServer.path;
    }
493 494 495
    if (artifact == Artifact.engineDartBinary && engineDartBinary != null) {
      return engineDartBinary.path;
    }
496 497 498 499 500 501
    if (artifact == Artifact.platformKernelDill && platformKernelDill != null) {
      return platformKernelDill.path;
    }
    if (artifact == Artifact.flutterPatchedSdkPath && flutterPatchedSdk != null) {
      return flutterPatchedSdk.path;
    }
502
    return parent.getArtifactPath(artifact, platform: platform, mode: mode);
503 504 505
  }

  @override
506
  String getEngineType(TargetPlatform platform, [ BuildMode mode ]) => parent.getEngineType(platform, mode);
507
}