blob: 326c36a61978e0f412c4921f3c0fd7a6688722ee [file]
// 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.
part of generate;
class PackageInfo {
final String name;
final Directory packageDir;
final Directory importDir;
final List<File> mojomFiles;
PackageInfo(this.name, this.packageDir, this.importDir, this.mojomFiles);
}
/// This class finds .mojom files under [_mojomRootDir] that have a
/// 'DartPackage' annotation. It locates the packages named in the annotations
/// and generates a list of [PackageInfo] records.
class MojomFinder {
static final Stopwatch _stopwatch = new Stopwatch();
static dev.Counter _mojomMs;
Directory _mojomRootDir;
Directory _dartRootDir;
List<String> _skip;
// Cache of mappings from package name to Dart source location.
HashMap<String, Directory> _packageLocations;
MojomFinder(this._mojomRootDir, this._dartRootDir, this._skip) {
_packageLocations = new HashMap<String, Directory>();
if (_mojomMs == null) {
_mojomMs = new dev.Counter("mojom searching",
"Time(ms) searching for .mojom files with DartPackage annotations.");
dev.Metrics.register(_mojomMs);
}
}
/// Look for .mojom files in the tree that have a DartPackage annotation.
/// Return a list of PackageInfo records describing packages and the .mojom
/// files that they own.
Future<List<PackageInfo>> find() async {
_stopwatch.start();
var packageInfos = new Map<String, PackageInfo>();
var mojomRootList = _mojomRootDir.list(recursive: true, followLinks: false);
await for (var entry in mojomRootList) {
if (entry is! File) continue;
if (_shouldSkip(entry)) continue;
if (!isMojom(entry.path)) continue;
String package = await _extractDartPackageAttribute(entry);
if (package == null) continue;
log.info("MojomFinder: found package $package");
if (packageInfos.containsKey(package)) {
packageInfos[package].mojomFiles.add(entry);
continue;
}
Directory dartSourceDir = await _findDartSourceDir(package);
if (dartSourceDir == null) {
log.warning(".mojom had annotation '$package', but no package by that "
"name under $_dartRootDir could be found.");
continue;
}
log.info("MojomFinder: found $package at $dartSourceDir");
var packageInfo =
new PackageInfo(package, dartSourceDir, _mojomRootDir, [entry]);
packageInfos[package] = packageInfo;
}
_stopwatch.stop();
_mojomMs.value += _stopwatch.elapsedMilliseconds;
_stopwatch.reset();
return packageInfos.values.toList();
}
// Extract a DartPackage attribute from a .mojom file.
Future<String> _extractDartPackageAttribute(File mojom) async {
String contents = await mojom.readAsString();
int dpIndex = contents.indexOf('DartPackage');
if (dpIndex == -1) return null;
// There must be a '[' before 'DartPackage', and there can't be a ']'
// in between.
int openSbIndex = contents.lastIndexOf('[', dpIndex);
if (openSbIndex == -1) return null;
int closeSbIndex = contents.lastIndexOf(']', dpIndex);
if (closeSbIndex > openSbIndex) return null;
int eqIndex = contents.indexOf('=', dpIndex);
if (eqIndex == -1) return null;
int openQuoteIndex = contents.indexOf('"', eqIndex);
if (openQuoteIndex == -1) return null;
int closeQuoteIndex = -1;
int searchIndex = openQuoteIndex + 1;
while (closeQuoteIndex == -1) {
closeQuoteIndex = contents.indexOf('"', searchIndex);
if (closeQuoteIndex == -1) break;
if (contents[closeQuoteIndex - 1] == '\\') {
searchIndex = closeQuoteIndex + 1;
closeQuoteIndex = -1;
}
}
if (closeQuoteIndex == -1) return null;
return contents.substring(openQuoteIndex + 1, closeQuoteIndex);
}
// Finds where the Dart package named [package] lives. Looks immediately
// under [_dartRootDir].
Future<Directory> _findDartSourceDir(String package) async {
if (_packageLocations.containsKey(package)) {
return _packageLocations[package];
}
var packagePath = path.join(_dartRootDir.path, package);
Directory packageDir = new Directory(packagePath);
if (await packageDir.exists()) {
log.info("Found dart package $package at $packagePath");
_packageLocations[package] = packageDir;
return packageDir;
}
// If the directory doesn't exist, look for a pubspec.yaml file with a
// 'name:' field matching the package name.
Directory dir = await _findDartSourceDirByPubspec(package);
if (dir != null) {
_packageLocations[package] = dir;
return dir;
} else {
log.info("$packagePath not found");
return null;
}
}
Future<Directory> _findDartSourceDirByPubspec(String package) async {
await for (var entry in _dartRootDir.list(recursive: true)) {
if (entry is! File) continue;
if (_shouldSkip(entry)) continue;
if (!isPubspecYaml(entry.path)) continue;
String contents = await entry.readAsString();
var pubspec = yaml.loadYaml(contents);
String name = pubspec['name'];
if (name == package) {
log.info("Found dart package $package at ${entry.parent}");
return entry.parent;
}
}
return null;
}
bool _shouldSkip(File f) => containsPrefix(f.path, _skip);
}