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; |
+ } |
+} |