Index: pkg/analysis_server/lib/src/plugin/plugin_manager.dart |
diff --git a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart |
index 6b4fdc7cd722550722f612627dc8604bb7a42ec1..148cc37ee311317b2139a7a77d8ee973389a1eb5 100644 |
--- a/pkg/analysis_server/lib/src/plugin/plugin_manager.dart |
+++ b/pkg/analysis_server/lib/src/plugin/plugin_manager.dart |
@@ -13,6 +13,8 @@ import 'package:analyzer/file_system/file_system.dart'; |
import 'package:analyzer/instrumentation/instrumentation.dart'; |
import 'package:analyzer/src/generated/bazel.dart'; |
import 'package:analyzer/src/generated/gn.dart'; |
+import 'package:analyzer/src/generated/source.dart'; |
+import 'package:analyzer/src/generated/workspace.dart'; |
import 'package:analyzer/src/util/glob.dart'; |
import 'package:analyzer_plugin/channel/channel.dart'; |
import 'package:analyzer_plugin/protocol/protocol.dart'; |
@@ -26,6 +28,7 @@ import 'package:crypto/crypto.dart'; |
import 'package:meta/meta.dart'; |
import 'package:path/path.dart' as path; |
import 'package:watcher/watcher.dart' as watcher; |
+import 'package:yaml/yaml.dart'; |
/** |
* Information about a plugin that is built-in. |
@@ -336,8 +339,8 @@ class PluginManager { |
PluginInfo plugin = _pluginMap[path]; |
bool isNew = plugin == null; |
if (isNew) { |
- List<String> pluginPaths = _pathsFor(path); |
- if (pluginPaths == null || pluginPaths[1] == null) { |
+ List<String> pluginPaths = pathsFor(path); |
+ if (pluginPaths == null) { |
return; |
} |
plugin = new DiscoveredPluginInfo(path, pluginPaths[0], pluginPaths[1], |
@@ -415,6 +418,46 @@ class PluginManager { |
} |
/** |
+ * Return the execution path and .packages path associated with the plugin at |
+ * the given [path], or `null` if there is a problem that prevents us from |
+ * executing the plugin. |
+ */ |
+ @visibleForTesting |
+ List<String> pathsFor(String pluginPath) { |
+ Folder pluginFolder = resourceProvider.getFolder(pluginPath); |
+ File pubspecFile = pluginFolder.getChildAssumingFile('pubspec.yaml'); |
+ if (!pubspecFile.exists) { |
+ // If there's no pubspec file, then we don't need to copy the package |
+ // because we won't be running pub. |
mfairhurst
2017/07/24 18:00:07
This will prevent the bazel support from working w
|
+ return _computePaths(pluginFolder); |
+ } |
+ Workspace workspace = |
+ BazelWorkspace.find(resourceProvider, pluginFolder.path) ?? |
+ GnWorkspace.find(resourceProvider, pluginFolder.path); |
+ if (workspace != null) { |
+ // Similarly, we won't be running pub if we're in a workspace because |
+ // there is exactly one version of each package. |
+ return _computePaths(pluginFolder, workspace: workspace); |
+ } |
+ // |
+ // Copy the plugin directory to a unique subdirectory of the plugin |
+ // manager's state location. The subdirectory's name is selected such that |
+ // it will be invariant across sessions, reducing the number of times the |
+ // plugin will need to be copied and pub will need to be run. |
+ // |
+ Folder stateFolder = resourceProvider.getStateLocation('.plugin_manager'); |
+ String stateName = _uniqueDirectoryName(pluginPath); |
+ Folder parentFolder = stateFolder.getChildAssumingFolder(stateName); |
+ if (parentFolder.exists) { |
+ Folder executionFolder = |
+ parentFolder.getChildAssumingFolder(pluginFolder.shortName); |
+ return _computePaths(executionFolder); |
+ } |
+ Folder executionFolder = pluginFolder.copyTo(parentFolder); |
+ return _computePaths(executionFolder, runPub: true); |
+ } |
+ |
+ /** |
* Return a list of all of the plugins that are currently associated with the |
* given [contextRoot]. |
*/ |
@@ -518,6 +561,52 @@ class PluginManager { |
]; |
} |
+ /** |
+ * Compute the paths to be returned by the enclosing method given that the |
+ * plugin should exist in the given [pluginFolder]. |
+ */ |
+ List<String> _computePaths(Folder pluginFolder, |
+ {bool runPub: false, Workspace workspace}) { |
+ File pluginFile = pluginFolder |
+ .getChildAssumingFolder('bin') |
+ .getChildAssumingFile('plugin.dart'); |
+ if (!pluginFile.exists) { |
+ return null; |
+ } |
+ File packagesFile = pluginFolder.getChildAssumingFile('.packages'); |
+ if (!packagesFile.exists) { |
+ if (runPub) { |
+ String vmPath = Platform.executable; |
+ String pubPath = path.join(path.dirname(vmPath), 'pub'); |
+ ProcessResult result = Process.runSync(pubPath, <String>['get'], |
+ stderrEncoding: UTF8, |
+ stdoutEncoding: UTF8, |
+ workingDirectory: pluginFolder.path); |
+ if (result.exitCode != 0) { |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.writeln('Failed to run pub get'); |
+ buffer.writeln(' pluginFolder = ${pluginFolder.path}'); |
+ buffer.writeln(' exitCode = ${result.exitCode}'); |
+ buffer.writeln(' stdout = ${result.stdout}'); |
+ buffer.writeln(' stderr = ${result.stderr}'); |
+ instrumentationService.logError(buffer.toString()); |
+ } |
+ if (!packagesFile.exists) { |
+ packagesFile = null; |
+ } |
+ } else if (workspace != null) { |
+ packagesFile = |
+ _createPackagesFile(pluginFolder, workspace.packageUriResolver); |
+ } else { |
+ packagesFile = null; |
+ } |
+ } |
+ if (packagesFile == null) { |
+ return null; |
+ } |
+ return <String>[pluginFile.path, packagesFile.path]; |
+ } |
+ |
WatchEventType _convertChangeType(watcher.ChangeType type) { |
switch (type) { |
case watcher.ChangeType.ADD: |
@@ -536,6 +625,61 @@ class PluginManager { |
} |
/** |
+ * Return a temporary `.packages` file that is appropriate for the plugin in |
+ * the given [pluginFolder]. The [packageUriResolver] is used to determine the |
+ * location of the packages that need to be included in the packages file. |
+ */ |
+ File _createPackagesFile( |
+ Folder pluginFolder, UriResolver packageUriResolver) { |
+ String pluginPath = pluginFolder.path; |
+ Folder stateFolder = resourceProvider.getStateLocation('.plugin_manager'); |
+ String stateName = _uniqueDirectoryName(pluginPath) + '.packages'; |
+ File packagesFile = stateFolder.getChildAssumingFile(stateName); |
+ if (!packagesFile.exists) { |
+ File pluginPubspec = pluginFolder.getChildAssumingFile('pubspec.yaml'); |
+ if (!pluginPubspec.exists) { |
+ return null; |
+ } |
+ |
+ try { |
+ Map<String, String> visitedPackages = <String, String>{}; |
+ path.Context context = resourceProvider.pathContext; |
+ visitedPackages[context.basename(pluginPath)] = |
+ context.join(pluginFolder.path, 'lib'); |
+ List<File> pubspecFiles = <File>[]; |
+ pubspecFiles.add(pluginPubspec); |
+ while (pubspecFiles.isNotEmpty) { |
+ File pubspecFile = pubspecFiles.removeLast(); |
+ for (String packageName in _readDependecies(pubspecFile)) { |
+ if (!visitedPackages.containsKey(packageName)) { |
+ Uri uri = Uri.parse('package:$packageName/$packageName.dart'); |
+ Source packageSource = packageUriResolver.resolveAbsolute(uri); |
+ String libDirPath = context.dirname(packageSource.fullName); |
+ visitedPackages[packageName] = libDirPath; |
+ String pubspecPath = |
+ context.join(context.dirname(libDirPath), 'pubspec.yaml'); |
+ pubspecFiles.add(resourceProvider.getFile(pubspecPath)); |
+ } |
+ } |
+ } |
+ |
+ StringBuffer buffer = new StringBuffer(); |
+ visitedPackages.forEach((String name, String path) { |
+ buffer.write(name); |
+ buffer.write(':'); |
+ buffer.writeln(new Uri.file(path)); |
+ }); |
+ packagesFile.writeAsStringSync(buffer.toString()); |
+ } catch (exception) { |
+ // If we are not able to produce a .packages file, return null so that |
+ // callers will not try to load the plugin. |
+ return null; |
mfairhurst
2017/07/24 18:00:06
should log the exception too though
|
+ } |
+ } |
+ return packagesFile; |
+ } |
+ |
+ /** |
* Return `true` if the plugin with the given [path] has been whitelisted. |
*/ |
bool _isWhitelisted(String path) { |
@@ -548,85 +692,20 @@ class PluginManager { |
} |
/** |
- * Return the execution path and .packages path associated with the plugin at |
- * the given [path], or `null` if there is a problem that prevents us from |
- * executing the plugin. |
+ * Return the names of packages that are listed as dependencies in the given |
+ * [pubspecFile]. |
*/ |
- List<String> _pathsFor(String pluginPath) { |
- /** |
- * Return `true` if the plugin in the give [folder] needs to be copied to a |
- * temporary location so that 'pub' can be run to resolve dependencies. We |
- * need to run `pub` if the plugin contains a `pubspec.yaml` file and is not |
- * in a workspace. |
- */ |
- bool needToCopy(Folder folder) { |
- File pubspecFile = folder.getChildAssumingFile('pubspec.yaml'); |
- if (!pubspecFile.exists) { |
- return false; |
+ Iterable<String> _readDependecies(File pubspecFile) { |
+ YamlDocument document = loadYamlDocument(pubspecFile.readAsStringSync(), |
+ sourceUrl: pubspecFile.toUri()); |
+ YamlNode contents = document.contents; |
+ if (contents is YamlMap) { |
+ YamlNode dependencies = contents['dependencies']; |
mfairhurst
2017/07/24 18:00:06
Is there any way we can get this info by running p
|
+ if (dependencies is YamlMap) { |
+ return dependencies.keys; |
} |
- return BazelWorkspace.find(resourceProvider, folder.path) == null && |
- GnWorkspace.find(resourceProvider, folder.path) == null; |
} |
- |
- /** |
- * Compute the paths to be returned by the enclosing method given that the |
- * plugin should exist in the given [pluginFolder]. |
- */ |
- List<String> computePaths(Folder pluginFolder, {bool runPub: false}) { |
- File pluginFile = pluginFolder |
- .getChildAssumingFolder('bin') |
- .getChildAssumingFile('plugin.dart'); |
- if (!pluginFile.exists) { |
- return null; |
- } |
- File packagesFile = pluginFolder.getChildAssumingFile('.packages'); |
- if (!packagesFile.exists) { |
- if (runPub) { |
- String vmPath = Platform.executable; |
- String pubPath = path.join(path.dirname(vmPath), 'pub'); |
- ProcessResult result = Process.runSync(pubPath, <String>['get'], |
- stderrEncoding: UTF8, |
- stdoutEncoding: UTF8, |
- workingDirectory: pluginFolder.path); |
- if (result.exitCode != 0) { |
- StringBuffer buffer = new StringBuffer(); |
- buffer.writeln('Failed to run pub get'); |
- buffer.writeln(' pluginFolder = ${pluginFolder.path}'); |
- buffer.writeln(' exitCode = ${result.exitCode}'); |
- buffer.writeln(' stdout = ${result.stdout}'); |
- buffer.writeln(' stderr = ${result.stderr}'); |
- instrumentationService.logError(buffer.toString()); |
- } |
- if (!packagesFile.exists) { |
- packagesFile = null; |
- } |
- } else { |
- packagesFile = null; |
- } |
- } |
- return <String>[pluginFile.path, packagesFile?.path]; |
- } |
- |
- Folder pluginFolder = resourceProvider.getFolder(pluginPath); |
- if (!needToCopy(pluginFolder)) { |
- return computePaths(pluginFolder); |
- } |
- // |
- // Copy the plugin directory to a unique subdirectory of the plugin |
- // manager's state location. The subdirectory's name is selected such that |
- // it will be invariant across sessions, reducing the number of times the |
- // plugin will need to be copied and pub will need to be run. |
- // |
- Folder stateFolder = resourceProvider.getStateLocation('.plugin_manager'); |
- String stateName = _uniqueDirectoryName(pluginPath); |
- Folder parentFolder = stateFolder.getChildAssumingFolder(stateName); |
- if (parentFolder.exists) { |
- Folder executionFolder = |
- parentFolder.getChildAssumingFolder(pluginFolder.shortName); |
- return computePaths(executionFolder); |
- } |
- Folder executionFolder = pluginFolder.copyTo(parentFolder); |
- return computePaths(executionFolder, runPub: true); |
+ return const <String>[]; |
} |
/** |