Refactor how Artifacts are located (#8234)

Artifacts are now located in a central place.
This will enable us to downlaod artifacts when we need them (instead of
downloading them all upfront).
This also makes replacing sky_snapshot with gen_snapshot easier.
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
new file mode 100644
index 0000000..0aa9ddc
--- /dev/null
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -0,0 +1,264 @@
+// 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.
+
+import 'base/context.dart';
+import 'base/file_system.dart';
+import 'base/platform.dart';
+import 'build_info.dart';
+import 'globals.dart';
+
+enum Artifact {
+  chromiumDebugKeyStore,
+  classesDexJar,
+  icudtlDat,
+  libskyShellSo,
+  dartIoEntriesTxt,
+  dartVmEntryPointsTxt,
+  dartVmEntryPointsAndroidTxt,
+  genSnapshot,
+  skyShell,
+  skySnapshot,
+  snapshotDart,
+  flutterFramework,
+}
+
+String _artifactToFileName(Artifact artifact) {
+  switch (artifact) {
+    case Artifact.chromiumDebugKeyStore:
+      return 'chromium-debug.keystore';
+    case Artifact.classesDexJar:
+      return 'classes.dex.jar';
+    case Artifact.icudtlDat:
+      return 'icudtl.dat';
+    case Artifact.libskyShellSo:
+      return 'libsky_shell.so';
+    case Artifact.dartIoEntriesTxt:
+      return 'dart_io_entries.txt';
+    case Artifact.dartVmEntryPointsTxt:
+      return 'dart_vm_entry_points.txt';
+    case Artifact.dartVmEntryPointsAndroidTxt:
+      return 'dart_vm_entry_points_android.txt';
+    case Artifact.genSnapshot:
+      return 'gen_snapshot';
+    case Artifact.skySnapshot:
+      return 'sky_snapshot';
+    case Artifact.skyShell:
+      return 'sky_shell';
+    case Artifact.snapshotDart:
+      return 'snapshot.dart';
+    case Artifact.flutterFramework:
+      return 'Flutter.framework';
+  }
+  assert(false, 'Invalid artifact $artifact.');
+  return null;
+}
+
+// Manages the engine artifacts of Flutter.
+abstract class Artifacts {
+  static Artifacts get instance => context[Artifacts];
+
+  static void useLocalEngine(String engineSrcPath, String engineOutPath) {
+    context.setVariable(Artifacts, new LocalEngineArtifacts(engineSrcPath, engineOutPath));
+  }
+
+  // Returns the requested [artifact] for the [platform] and [mode] combination.
+  String getArtifactPath(Artifact artifact, [TargetPlatform platform, BuildMode mode]);
+
+  // Returns which set of engine artifacts is currently used for the [platform]
+  // and [mode] combination.
+  String getEngineType(TargetPlatform platform, [BuildMode mode]);
+}
+
+/// Manages the engine artifacts downloaded to the local cache.
+class CachedArtifacts extends Artifacts {
+
+  @override
+  String getArtifactPath(Artifact artifact, [TargetPlatform platform, BuildMode mode]) {
+    platform ??= _currentHostPlatform;
+    switch (platform) {
+      case TargetPlatform.android_arm:
+      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:
+      case TargetPlatform.linux_x64:
+        assert(mode == null, 'Platform $platform does not support different build modes.');
+        return _getHostArtifactPath(artifact, platform);
+    }
+    assert(false, 'Invalid platform $platform.');
+    return null;
+  }
+
+  @override
+  String getEngineType(TargetPlatform platform, [BuildMode mode]){
+    return fs.path.basename(_getEngineArtifactsPath(platform, mode));
+  }
+
+  String _getAndroidArtifactPath(Artifact artifact, TargetPlatform platform, BuildMode mode) {
+    String engineDir = _getEngineArtifactsPath(platform, mode);
+    switch (artifact) {
+      case Artifact.chromiumDebugKeyStore:
+      case Artifact.classesDexJar:
+      case Artifact.icudtlDat:
+      case Artifact.libskyShellSo:
+        return fs.path.join(engineDir, _artifactToFileName(artifact));
+      case Artifact.dartIoEntriesTxt:
+      case Artifact.dartVmEntryPointsTxt:
+      case Artifact.dartVmEntryPointsAndroidTxt:
+        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.');
+        String hostPlatform = getNameForHostPlatform(getCurrentHostPlatform());
+        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) {
+    String engineDir = _getEngineArtifactsPath(platform, mode);
+    switch (artifact) {
+      case Artifact.dartIoEntriesTxt:
+      case Artifact.dartVmEntryPointsTxt:
+      case Artifact.genSnapshot:
+      case Artifact.snapshotDart:
+      case Artifact.flutterFramework:
+        return fs.path.join(engineDir, _artifactToFileName(artifact));
+      default:
+        assert(false, 'Artifact $artifact not available for platform $platform.');
+        return null;
+    }
+  }
+
+  String _getHostArtifactPath(Artifact artifact, TargetPlatform platform) {
+    switch (artifact) {
+      case Artifact.skyShell:
+      case Artifact.skySnapshot:
+      case Artifact.icudtlDat:
+        String engineArtifactsPath = cache.getArtifactDirectory('engine').path;
+        String platformDirName = getNameForTargetPlatform(platform);
+        return fs.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact));
+      default:
+        assert(false, 'Artifact $artifact not available for platform $platform.');
+        return null;
+    }
+  }
+
+  String _getEngineArtifactsPath(TargetPlatform platform, [BuildMode mode]) {
+    String engineDir = cache.getArtifactDirectory('engine').path;
+    String platformName = getNameForTargetPlatform(platform);
+    switch (platform) {
+      case TargetPlatform.linux_x64:
+      case TargetPlatform.darwin_x64:
+        assert(mode == null, 'Platform $platform does not support different build modes.');
+        return fs.path.join(engineDir, platformName);
+      case TargetPlatform.ios:
+      case TargetPlatform.android_arm:
+      case TargetPlatform.android_x64:
+      case TargetPlatform.android_x86:
+        assert(mode != null, 'Need to specify a build mode for platform $platform.');
+        String suffix = mode != BuildMode.debug ? '-${getModeName(mode)}' : '';
+        return fs.path.join(engineDir, platformName + suffix);
+    }
+    assert(false, 'Invalid platform $platform.');
+    return null;
+  }
+
+  TargetPlatform get _currentHostPlatform {
+    if (platform.isMacOS)
+      return TargetPlatform.darwin_x64;
+    if (platform.isLinux)
+      return TargetPlatform.linux_x64;
+    throw new UnimplementedError('Host OS not supported.');
+  }
+}
+
+/// Manages the artifacts of a locally built engine.
+class LocalEngineArtifacts extends Artifacts {
+  final String _engineSrcPath;
+  final String engineOutPath; // TODO(goderbauer): This should be private.
+
+  LocalEngineArtifacts(this._engineSrcPath, this.engineOutPath);
+
+  @override
+  String getArtifactPath(Artifact artifact, [TargetPlatform platform, BuildMode mode]) {
+    switch (artifact) {
+      case Artifact.chromiumDebugKeyStore:
+        return fs.path.join(_engineSrcPath, 'build', 'android', 'ant', _artifactToFileName(artifact));
+      case Artifact.dartIoEntriesTxt:
+        return fs.path.join(_engineSrcPath, 'dart', 'runtime', 'bin', _artifactToFileName(artifact));
+      case Artifact.dartVmEntryPointsTxt:
+        return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
+      case Artifact.dartVmEntryPointsAndroidTxt:
+        return fs.path.join(_engineSrcPath, 'flutter', 'runtime', _artifactToFileName(artifact));
+      case Artifact.snapshotDart:
+        return fs.path.join(_engineSrcPath, 'flutter', 'lib', 'snapshot', _artifactToFileName(artifact));
+      case Artifact.classesDexJar:
+        return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', _artifactToFileName(artifact));
+      case Artifact.icudtlDat:
+        return fs.path.join(engineOutPath, _artifactToFileName(artifact));
+      case Artifact.libskyShellSo:
+        String abi = _getAbiDirectory(platform);
+        return fs.path.join(engineOutPath, 'gen', 'flutter', 'shell', 'platform', 'android', 'android', fs.path.join('android', 'libs', abi, _artifactToFileName(artifact)));
+      case Artifact.genSnapshot:
+        return _genSnapshotPath(platform);
+      case Artifact.skyShell:
+        return _skyShellPath(platform);
+      case Artifact.skySnapshot:
+        return _skySnapshotPath();
+      case Artifact.flutterFramework:
+        return fs.path.join(engineOutPath, _artifactToFileName(artifact));
+    }
+    assert(false, 'Invalid artifact $artifact.');
+    return null;
+  }
+
+  @override
+  String getEngineType(TargetPlatform platform, [BuildMode mode]) {
+    return fs.path.basename(engineOutPath);
+  }
+
+  String _genSnapshotPath(TargetPlatform platform) {
+    String clang;
+    if (platform == TargetPlatform.ios) {
+      clang = 'clang_x64';
+    } else {
+      clang = getCurrentHostPlatform() == HostPlatform.darwin_x64 ? 'clang_i386' : 'clang_x86';
+    }
+    return fs.path.join(engineOutPath, clang, _artifactToFileName(Artifact.genSnapshot));
+  }
+
+  String _skySnapshotPath() {
+    String clangPath = fs.path.join(engineOutPath, 'clang_x64', _artifactToFileName(Artifact.skySnapshot));
+    if (fs.isFileSync(clangPath))
+      return clangPath;
+    return fs.path.join(engineOutPath, _artifactToFileName(Artifact.skySnapshot));
+  }
+
+  String _skyShellPath(TargetPlatform platform) {
+    if (getCurrentHostPlatform() == HostPlatform.linux_x64) {
+      return fs.path.join(engineOutPath, _artifactToFileName(Artifact.skyShell));
+    } else if (getCurrentHostPlatform() == HostPlatform.darwin_x64) {
+      return fs.path.join(engineOutPath, 'SkyShell.app', 'Contents', 'MacOS', 'SkyShell');
+    }
+    throw new Exception('Unsupported platform $platform.');
+  }
+
+  String _getAbiDirectory(TargetPlatform platform) {
+    switch (platform) {
+      case TargetPlatform.android_arm:
+        return 'armeabi-v7a';
+      case TargetPlatform.android_x64:
+        return 'x86_64';
+      case TargetPlatform.android_x86:
+        return 'x86';
+      default:
+        throw new Exception('Unsupported platform $platform.');
+    }
+  }
+}