| Index: mojo/public/dart/third_party/analyzer/lib/source/pub_package_map_provider.dart
|
| diff --git a/mojo/public/dart/third_party/analyzer/lib/source/pub_package_map_provider.dart b/mojo/public/dart/third_party/analyzer/lib/source/pub_package_map_provider.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3e9936d1f8abf3c4e6523b03ce637935a811dfaf
|
| --- /dev/null
|
| +++ b/mojo/public/dart/third_party/analyzer/lib/source/pub_package_map_provider.dart
|
| @@ -0,0 +1,171 @@
|
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +library source.pub_package_map_provider;
|
| +
|
| +import 'dart:collection';
|
| +import 'dart:convert';
|
| +import 'dart:core' hide Resource;
|
| +import 'dart:io' as io;
|
| +
|
| +import 'package:analyzer/file_system/file_system.dart';
|
| +import 'package:analyzer/source/package_map_provider.dart';
|
| +import 'package:analyzer/src/generated/engine.dart';
|
| +import 'package:analyzer/src/generated/sdk_io.dart';
|
| +
|
| +/**
|
| + * The function used to run pub list.
|
| + */
|
| +typedef io.ProcessResult RunPubList(Folder folder);
|
| +
|
| +/**
|
| + * Implementation of PackageMapProvider that operates by executing pub.
|
| + */
|
| +class PubPackageMapProvider implements PackageMapProvider {
|
| + static const String PUB_LIST_COMMAND = 'list-package-dirs';
|
| +
|
| + /**
|
| + * The name of the 'pubspec.lock' file, which we assume is the dependency
|
| + * in the event that [PUB_LIST_COMMAND] fails.
|
| + */
|
| + static const String PUBSPEC_LOCK_NAME = 'pubspec.lock';
|
| +
|
| + /**
|
| + * [ResourceProvider] that is used to create the [Folder]s that populate the
|
| + * package map.
|
| + */
|
| + final ResourceProvider resourceProvider;
|
| +
|
| + /**
|
| + * Sdk that we use to find the pub executable.
|
| + */
|
| + final DirectoryBasedDartSdk sdk;
|
| +
|
| + /**
|
| + * The function used to run pub list.
|
| + */
|
| + RunPubList _runPubList;
|
| +
|
| + /**
|
| + * Construct a new instance.
|
| + * A [RunPubList] implementation may be injected for testing
|
| + */
|
| + PubPackageMapProvider(this.resourceProvider, this.sdk, [this._runPubList]) {
|
| + if (_runPubList == null) {
|
| + _runPubList = _runPubListDefault;
|
| + }
|
| + }
|
| +
|
| + @override
|
| + PackageMapInfo computePackageMap(Folder folder) {
|
| + // TODO(paulberry) make this asynchronous so that we can (a) do other
|
| + // analysis while it's in progress, and (b) time out if it takes too long
|
| + // to respond.
|
| + io.ProcessResult result;
|
| + try {
|
| + result = _runPubList(folder);
|
| + } on io.ProcessException catch (exception, stackTrace) {
|
| + AnalysisEngine.instance.logger.logInformation(
|
| + "Error running pub $PUB_LIST_COMMAND\n$exception\n$stackTrace");
|
| + }
|
| + if (result == null || result.exitCode != 0) {
|
| + String exitCode =
|
| + result != null ? 'exit code ${result.exitCode}' : 'null';
|
| + AnalysisEngine.instance.logger
|
| + .logInformation("pub $PUB_LIST_COMMAND failed: $exitCode");
|
| + return computePackageMapError(folder);
|
| + }
|
| + try {
|
| + PackageMapInfo packageMap =
|
| + parsePackageMap(JSON.decode(result.stdout), folder);
|
| + return packageMap;
|
| + } catch (exception, stackTrace) {
|
| + AnalysisEngine.instance.logger.logError(
|
| + "Malformed output from pub $PUB_LIST_COMMAND\n$exception\n$stackTrace");
|
| + }
|
| +
|
| + return computePackageMapError(folder);
|
| + }
|
| +
|
| + /**
|
| + * Create a PackageMapInfo object representing an error condition.
|
| + */
|
| + PackageMapInfo computePackageMapError(Folder folder) {
|
| + // Even if an error occurs, we still need to know the dependencies, so that
|
| + // we'll know when to try running "pub list-package-dirs" again.
|
| + // Unfortunately, "pub list-package-dirs" doesn't tell us dependencies when
|
| + // an error occurs, so just assume there is one dependency, "pubspec.lock".
|
| + List<String> dependencies = <String>[
|
| + resourceProvider.pathContext.join(folder.path, PUBSPEC_LOCK_NAME)
|
| + ];
|
| + return new PackageMapInfo(null, dependencies.toSet());
|
| + }
|
| +
|
| + /**
|
| + * Decode the JSON output from pub into a package map. Paths in the
|
| + * output are considered relative to [folder].
|
| + */
|
| + PackageMapInfo parsePackageMap(Map obj, Folder folder) {
|
| + // The output of pub looks like this:
|
| + // {
|
| + // "packages": {
|
| + // "foo": "path/to/foo",
|
| + // "bar": ["path/to/bar1", "path/to/bar2"],
|
| + // "myapp": "path/to/myapp", // self link is included
|
| + // },
|
| + // "input_files": [
|
| + // "path/to/myapp/pubspec.lock"
|
| + // ]
|
| + // }
|
| + Map<String, List<Folder>> packageMap = new HashMap<String, List<Folder>>();
|
| + Map packages = obj['packages'];
|
| + processPaths(String packageName, List paths) {
|
| + List<Folder> folders = <Folder>[];
|
| + for (var path in paths) {
|
| + if (path is String) {
|
| + Resource resource = folder.getChildAssumingFolder(path);
|
| + if (resource is Folder) {
|
| + folders.add(resource);
|
| + }
|
| + }
|
| + }
|
| + if (folders.isNotEmpty) {
|
| + packageMap[packageName] = folders;
|
| + }
|
| + }
|
| + packages.forEach((key, value) {
|
| + if (value is String) {
|
| + processPaths(key, [value]);
|
| + } else if (value is List) {
|
| + processPaths(key, value);
|
| + }
|
| + });
|
| + Set<String> dependencies = new Set<String>();
|
| + List inputFiles = obj['input_files'];
|
| + if (inputFiles != null) {
|
| + for (var path in inputFiles) {
|
| + if (path is String) {
|
| + dependencies.add(folder.canonicalizePath(path));
|
| + }
|
| + }
|
| + }
|
| + return new PackageMapInfo(packageMap, dependencies);
|
| + }
|
| +
|
| + /**
|
| + * Run pub list to determine the packages and input files.
|
| + */
|
| + io.ProcessResult _runPubListDefault(Folder folder) {
|
| + String executablePath = sdk.pubExecutable.getAbsolutePath();
|
| + List<String> arguments = [PUB_LIST_COMMAND];
|
| + String workingDirectory = folder.path;
|
| + int subprocessId = AnalysisEngine.instance.instrumentationService
|
| + .logSubprocessStart(executablePath, arguments, workingDirectory);
|
| + io.ProcessResult result = io.Process.runSync(executablePath, arguments,
|
| + workingDirectory: workingDirectory);
|
| + AnalysisEngine.instance.instrumentationService.logSubprocessResult(
|
| + subprocessId, result.exitCode, result.stdout, result.stderr);
|
| + return result;
|
| + }
|
| +}
|
|
|