blob: 4cdd6930ae7c987d467ef326ba11c01cd2a78e9d [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 mojom.utils;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:crypto/crypto.dart' as crypto;
import 'package:path/path.dart' as path;
bool isMojomDart(String path) => path.endsWith('.mojom.dart');
bool isMojom(String path) => path.endsWith('.mojom');
bool isDotMojoms(String path) => path.endsWith(".mojoms");
String makeAbsolute(String p) =>
path.isAbsolute(p) ? path.normalize(p) : path.normalize(path.absolute(p));
String makeRelative(String p) => path.isAbsolute(p)
? path.normalize(path.relative(p, from: Directory.current.path))
: path.normalize(p);
/// An Error for problems on the command line.
class CommandLineError {
final _msg;
CommandLineError(this._msg);
toString() => "Command Line Error: $_msg";
}
/// An Error for failures of the bindings generation script.
class GenerationError {
final _msg;
GenerationError(this._msg);
toString() => "Generation Error: $_msg";
}
/// An Error for failing to fetch a .mojom file.
class FetchError {
final _msg;
FetchError(this._msg);
toString() => "Fetch Error: $_msg";
}
class SubDirIterData {
Directory subdir;
SubDirIterData(this.subdir);
}
typedef Future SubDirAction(SubDirIterData data, Directory subdir);
typedef bool DirectoryFilter(Directory d);
/// Iterates over subdirectories of |directory| that satisfy |filter| if one
/// is given. Applies |action| to each subdirectory passing it |data| and the
/// subdirectory. Recurses into further subdirectories if |recursive| is true.
subDirIter(Directory directory, SubDirIterData data, SubDirAction action,
{DirectoryFilter filter, bool recursive: false}) async {
await for (var subdir in directory.list(recursive: recursive)) {
if (subdir is! Directory) continue;
if ((filter != null) && !filter(subdir)) continue;
if (data != null) {
data.subdir = subdir;
}
await action(data, subdir);
}
}
Future<String> _downloadUrl(HttpClient httpClient, Uri uri) async {
try {
var request = await httpClient.getUrl(uri);
var response = await request.close();
if (response.statusCode >= 400) {
var msg = "Failed to download $uri\nCode ${response.statusCode}";
if (response.reasonPhrase != null) {
msg = "$msg: ${response.reasonPhrase}";
}
throw new FetchError(msg);
}
var fileString = new StringBuffer();
await for (String contents in response.transform(UTF8.decoder)) {
fileString.write(contents);
}
return fileString.toString();
} catch (e) {
throw new FetchError("$e");
}
}
/// Fetch file at [uri] using [httpClient] if needed. Throw a [FetchError] if
/// the file is not successfully fetched.
Future<String> fetchUri(HttpClient httpClient, Uri uri) async {
if (uri.scheme.startsWith('http')) {
return _downloadUrl(httpClient, uri);
}
try {
File f = new File.fromUri(uri);
return await f.readAsString();
} catch (e) {
throw new FetchError("$e");
}
}
markFileReadOnly(String file) async {
if (!Platform.isLinux && !Platform.isMacOS) {
String os = Platform.operatingSystem;
throw 'Setting file $file read-only is not supported on $os.';
}
var process = await Process.run('chmod', ['a-w', file]);
if (process.exitCode != 0) {
print('Setting file $file read-only failed: ${process.stderr}');
}
}
Future<DateTime> getModificationTime(Uri uri) async {
if (uri.scheme.startsWith('http')) {
return new DateTime.now();
}
File f = new File.fromUri(uri);
var stat = await f.stat();
return stat.modified;
}
Future<List<int>> fileSHA1(File f) async {
var sha1 = new crypto.SHA1();
await for (var bytes in f.openRead()) {
sha1.add(bytes);
}
return sha1.close();
}
Future<bool> equalSHA1(File file1, File file2) async {
List<int> file1sha1 = await fileSHA1(file1);
List<int> file2sha1 = await fileSHA1(file2);
if (file1sha1.length != file2sha1.length) return false;
for (int i = 0; i < file1sha1.length; i++) {
if (file1sha1[i] != file2sha1[i]) return false;
}
return true;
}
/// If the files are the same, returns 0.
/// Otherwise, returns a negative number if f1 less recently modified than f2,
/// or a positive number if f1 is more recently modified than f2.
Future<int> compareFiles(File f1, File f2) async {
FileStat f1stat = await f1.stat();
FileStat f2stat = await f2.stat();
if ((f1stat.size != f2stat.size) || !await equalSHA1(f1, f2)) {
return (f1stat.modified.isBefore(f2stat.modified)) ? -1 : 1;
}
return 0;
}