artifacts.dart 8.11 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2015 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 'dart:async';
import 'dart:io';

8
import 'package:archive/archive.dart';
9
import 'package:path/path.dart' as path;
10

11
import 'base/process.dart';
Devon Carew's avatar
Devon Carew committed
12
import 'build_configuration.dart';
13
import 'globals.dart';
14

15 16 17 18 19 20 21 22 23
String _getNameForHostPlatform(HostPlatform platform) {
  switch (platform) {
    case HostPlatform.linux:
      return 'linux-x64';
    case HostPlatform.mac:
      return 'darwin-x64';
  }
}

Devon Carew's avatar
Devon Carew committed
24
String getNameForTargetPlatform(TargetPlatform platform) {
25
  switch (platform) {
26
    case TargetPlatform.android_arm:
27
      return 'android-arm';
28 29
    case TargetPlatform.ios:
      return 'ios';
30
    case TargetPlatform.darwin_x64:
Ian Hickson's avatar
Ian Hickson committed
31
      return 'darwin-x64';
32
    case TargetPlatform.linux_x64:
33 34 35 36 37 38 39
      return 'linux-x64';
  }
}

enum ArtifactType {
  snapshot,
  shell,
40
  mojo,
41
  androidClassesJar,
42 43 44
  androidIcuData,
  androidKeystore,
  androidLibSkyShell,
45
  iosXcodeProject,
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
}

class Artifact {
  const Artifact._({
    this.name,
    this.fileName,
    this.type,
    this.hostPlatform,
    this.targetPlatform
  });

  final String name;
  final String fileName;
  final ArtifactType type;
  final HostPlatform hostPlatform;
  final TargetPlatform targetPlatform;

  String get platform {
    if (targetPlatform != null)
Devon Carew's avatar
Devon Carew committed
65
      return getNameForTargetPlatform(targetPlatform);
66 67 68 69 70
    if (hostPlatform != null)
      return _getNameForHostPlatform(hostPlatform);
    assert(false);
    return null;
  }
71
}
72

73
class ArtifactStore {
74
  static const List<Artifact> knownArtifacts = const <Artifact>[
Devon Carew's avatar
Devon Carew committed
75
    // tester
76
    const Artifact._(
Adam Barth's avatar
Adam Barth committed
77
      name: 'Flutter Tester',
78 79
      fileName: 'sky_shell',
      type: ArtifactType.shell,
80
      targetPlatform: TargetPlatform.linux_x64
81
    ),
Devon Carew's avatar
Devon Carew committed
82 83

    // snapshotters
84 85 86 87 88 89 90 91 92 93 94 95
    const Artifact._(
      name: 'Sky Snapshot',
      fileName: 'sky_snapshot',
      type: ArtifactType.snapshot,
      hostPlatform: HostPlatform.linux
    ),
    const Artifact._(
      name: 'Sky Snapshot',
      fileName: 'sky_snapshot',
      type: ArtifactType.snapshot,
      hostPlatform: HostPlatform.mac
    ),
Devon Carew's avatar
Devon Carew committed
96 97

    // mojo
98
    const Artifact._(
99 100 101
      name: 'Flutter for Mojo',
      fileName: 'flutter.mojo',
      type: ArtifactType.mojo,
102
      targetPlatform: TargetPlatform.android_arm
103 104
    ),
    const Artifact._(
105 106 107
      name: 'Flutter for Mojo',
      fileName: 'flutter.mojo',
      type: ArtifactType.mojo,
108
      targetPlatform: TargetPlatform.linux_x64
109
    ),
Devon Carew's avatar
Devon Carew committed
110 111

    // android-arm
112 113
    const Artifact._(
      name: 'Compiled Java code',
114 115
      fileName: 'classes.dex.jar',
      type: ArtifactType.androidClassesJar,
116
      targetPlatform: TargetPlatform.android_arm
117 118 119 120 121
    ),
    const Artifact._(
      name: 'ICU data table',
      fileName: 'icudtl.dat',
      type: ArtifactType.androidIcuData,
122
      targetPlatform: TargetPlatform.android_arm
123 124 125 126 127
    ),
    const Artifact._(
      name: 'Key Store',
      fileName: 'chromium-debug.keystore',
      type: ArtifactType.androidKeystore,
128
      targetPlatform: TargetPlatform.android_arm
129 130 131 132 133
    ),
    const Artifact._(
      name: 'Compiled C++ code',
      fileName: 'libsky_shell.so',
      type: ArtifactType.androidLibSkyShell,
134
      targetPlatform: TargetPlatform.android_arm
135
    ),
Devon Carew's avatar
Devon Carew committed
136 137

    // iOS
138 139 140 141 142 143
    const Artifact._(
      name: 'iOS Runner (Xcode Project)',
      fileName: 'FlutterXcode.zip',
      type: ArtifactType.iosXcodeProject,
      targetPlatform: TargetPlatform.ios
    ),
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
  ];

  static Artifact getArtifact({
    ArtifactType type,
    HostPlatform hostPlatform,
    TargetPlatform targetPlatform
  }) {
    for (Artifact artifact in ArtifactStore.knownArtifacts) {
      if (type != null &&
          type != artifact.type)
        continue;
      if (hostPlatform != null &&
          artifact.hostPlatform != null &&
          hostPlatform != artifact.hostPlatform)
        continue;
      if (targetPlatform != null &&
          artifact.targetPlatform != null &&
          targetPlatform != artifact.targetPlatform)
        continue;
      return artifact;
    }
    return null;
  }

168 169
  // These values are initialized by FlutterCommandRunner on startup.
  static String flutterRoot;
Ian Hickson's avatar
Ian Hickson committed
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
  static String packageRoot = 'packages';

  static bool get isPackageRootValid {
    return FileSystemEntity.isDirectorySync(packageRoot);
  }

  static void ensurePackageRootIsValid() {
    if (!isPackageRootValid) {
      String message = '$packageRoot is not a valid directory.';
      if (packageRoot == 'packages') {
        if (FileSystemEntity.isFileSync('pubspec.yaml'))
          message += '\nDid you run `pub get` in this directory?';
        else
          message += '\nDid you run this command from the same directory as your pubspec.yaml file?';
      }
185
      printError(message);
Ian Hickson's avatar
Ian Hickson committed
186 187 188
      throw new ProcessExit(2);
    }
  }
189

190
  static String _engineRevision;
191

192
  static String get engineRevision {
193
    if (_engineRevision == null) {
194
      File revisionFile = new File(path.join(flutterRoot, 'bin', 'cache', 'engine.version'));
195
      if (revisionFile.existsSync())
196
        _engineRevision = revisionFile.readAsStringSync().trim();
197
    }
198
    return _engineRevision;
199 200
  }

201 202 203 204 205 206 207 208 209
  static Directory getBaseCacheDir() {
    if (flutterRoot == null) {
      printError('FLUTTER_ROOT not specified. Cannot find artifact cache.');
      throw new ProcessExit(2);
    }
    Directory cacheDir = new Directory(path.join(flutterRoot, 'bin', 'cache', 'artifacts'));
    if (!cacheDir.existsSync()) {
      printError('${cacheDir.path} does not exist. Cannot find artifact cache.');
      throw new ProcessExit(2);
210
    }
211
    return cacheDir;
212 213
  }

Devon Carew's avatar
Devon Carew committed
214
  static String getPath(Artifact artifact) {
215 216 217 218 219 220 221 222
    File cachedFile = new File(path.join(
        getBaseCacheDir().path, 'engine', artifact.platform, artifact.fileName
    ));
    if (!cachedFile.existsSync()) {
      printError('File not found in the platform artifacts: ${cachedFile.path}');
      throw new ProcessExit(2);
    }
    return cachedFile.path;
223 224
  }

225 226
  /// Download a file from the given URL and return the bytes.
  static Future<List<int>> _downloadFile(Uri url) async {
227
    printStatus('Downloading $url.');
228

229
    HttpClient httpClient = new HttpClient();
230
    HttpClientRequest request = await httpClient.getUrl(url);
231
    HttpClientResponse response = await request.close();
232
    printTrace('Received response statusCode=${response.statusCode}');
233 234
    if (response.statusCode != 200)
      throw new Exception(response.reasonPhrase);
235 236

    BytesBuilder responseBody = new BytesBuilder(copy: false);
237
    await for (List<int> chunk in response)
238 239
      responseBody.add(chunk);

240 241 242 243
    return responseBody.takeBytes();
  }

  /// Download a file from the given url and write it to the cache.
244 245
  /// If [unzip] is true, treat the url as a zip file, and unzip it to the
  /// directory given.
Ian Hickson's avatar
Ian Hickson committed
246
  static Future<Null> _downloadFileToCache(Uri url, FileSystemEntity cachedFile, bool unzip) async {
247 248 249 250
    if (!cachedFile.parent.existsSync())
      cachedFile.parent.createSync(recursive: true);

    List<int> fileBytes = await _downloadFile(url);
251 252 253 254 255 256 257 258 259 260 261 262 263
    if (unzip) {
      if (cachedFile is Directory && !cachedFile.existsSync())
        cachedFile.createSync(recursive: true);

      Archive archive = new ZipDecoder().decodeBytes(fileBytes);
      for (ArchiveFile archiveFile in archive) {
        File subFile = new File(path.join(cachedFile.path, archiveFile.name));
        subFile.writeAsBytesSync(archiveFile.content, flush: true);
      }
    } else {
      File asFile = new File(cachedFile.path);
      asFile.writeAsBytesSync(fileBytes, flush: true);
    }
264 265
  }

266
  static Future<String> getThirdPartyFile(String urlStr, String cacheSubdir, bool unzip) async {
267
    Uri url = Uri.parse(urlStr);
268
    Directory baseDir = getBaseCacheDir();
269 270 271 272 273
    Directory cacheDir = new Directory(path.join(
        baseDir.path, 'third_party', cacheSubdir));
    File cachedFile = new File(
        path.join(cacheDir.path, url.pathSegments[url.pathSegments.length-1]));
    if (!cachedFile.existsSync()) {
274
      try {
275
        await _downloadFileToCache(url, cachedFile, unzip);
276
      } catch (e) {
277
        printError('Failed to fetch third-party artifact: $url: $e');
278 279 280 281 282
        throw new ProcessExit(2);
      }
    }
    return cachedFile.path;
  }
283
}