blob: 85d51cfd47cf9aa73f2c5d2c5bbbd4e893e9adaa [file] [log] [blame]
// 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.
library sky_tools.artifacts;
import 'dart:async';
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
final Logger _logging = new Logger('sky_tools.artifacts');
enum Artifact { FlutterCompiler, SkyViewerMojo, }
class ArtifactStore {
static String packageRoot;
static String _engineRevision;
static String get engineRevision {
if (_engineRevision == null)
_engineRevision = new File(path.join(packageRoot, 'sky_engine', 'REVISION')).readAsStringSync();
return _engineRevision;
}
// Keep in sync with https://github.com/flutter/engine/blob/master/sky/tools/big_red_button.py#L50
static String googleStorageUrl(String category, String platform) {
return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${engineRevision}/';
}
static Future _downloadFile(String url, File file) async {
_logging.fine('Downloading $url to ${file.path}');
HttpClient httpClient = new HttpClient();
HttpClientRequest request = await httpClient.getUrl(Uri.parse(url));
HttpClientResponse response = await request.close();
_logging.fine('Received response');
if (response.statusCode != 200) throw new Exception(response.reasonPhrase);
IOSink sink = file.openWrite();
await sink.addStream(response);
await sink.close();
_logging.fine('Wrote file');
}
static Future<Directory> _cacheDir() async {
Directory cacheDir = new Directory(path.join(packageRoot, 'sky_tools', 'cache'));
if (!await cacheDir.exists()) {
await cacheDir.create(recursive: true);
}
return cacheDir;
}
static Future<Directory> _engineSpecificCacheDir() async {
Directory cacheDir = await _cacheDir();
// For now, all downloaded artifacts are release mode host binaries so use
// a path that mirrors a local release build.
// TODO(jamesr): Add support for more configurations.
String config = 'Release';
Directory engineSpecificDir = new Directory(path.join(cacheDir.path, 'sky_engine', engineRevision, config));
if (!await engineSpecificDir.exists()) {
await engineSpecificDir.create(recursive: true);
}
return engineSpecificDir;
}
// Whether the artifact needs to be marked as executable on disk.
static bool _needsToBeExecutable(Artifact artifact) {
return artifact == Artifact.FlutterCompiler;
}
static Future<String> getPath(Artifact artifact) async {
Directory cacheDir = await _engineSpecificCacheDir();
String category, name;
switch (artifact) {
case Artifact.FlutterCompiler:
category = 'shell';
name = 'sky_snapshot';
break;
case Artifact.SkyViewerMojo:
category = 'viewer';
name = 'sky_viewer.mojo';
break;
}
File cachedFile = new File(path.join(cacheDir.path, name));
if (!await cachedFile.exists()) {
_logging.info('Downloading ${name} from the cloud, one moment please...');
if (!Platform.isLinux)
throw new Exception('Platform unsupported.');
String url = googleStorageUrl(category, 'linux-x64') + name;
await _downloadFile(url, cachedFile);
if (_needsToBeExecutable(artifact)) {
ProcessResult result = await Process.run('chmod', ['u+x', cachedFile.path]);
if (result.exitCode != 0) throw new Exception(result.stderr);
}
}
return cachedFile.path;
}
static Future clear() async {
Directory cacheDir = await _cacheDir();
_logging.fine('Clearing cache directory ${cacheDir.path}');
await cacheDir.delete(recursive: true);
}
static Future populate() async {
for (Artifact artifact in Artifact.values) {
_logging.fine('Populating cache with $artifact');
await getPath(artifact);
}
}
}