Skip to content

Commit b08eb9d

Browse files
srawlinsCommit Queue
authored and
Commit Queue
committed
DAS plugins: Launch a shared plugin, configured from analysis options
This change is the final piece in enabling plugins in the new style to be launched from a specification in analysis options. Work towards #53402 * We support both one legacy analyzer plugin (the current max), and a set of new analyzer plugins, which are combined and launched in one shared plugin isolate. * Make PluginLocator.pluginMap private. * Add a parameter to PluginManager.addPluginToContextRoot: isLegacyPlugin. This method is used for both legacy and new plugins, but has slightly different behavior, finding where the plugin files are. Change-Id: I6644aecd4283eea22586ffd051a01b0ec8987fc5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/395360 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent bd76c51 commit b08eb9d

File tree

11 files changed

+194
-63
lines changed

11 files changed

+194
-63
lines changed

pkg/analysis_server/lib/src/plugin/plugin_locator.dart

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class PluginLocator {
2121
/// The resource provider used to access the file system.
2222
final ResourceProvider resourceProvider;
2323

24-
final Map<String, String?> pluginMap = {};
24+
final Map<String, String?> _pluginMap = {};
2525

2626
/// Initialize a newly created plugin locator to use the given
2727
/// [resourceProvider] to access the file system.
@@ -36,18 +36,12 @@ class PluginLocator {
3636
///
3737
/// The content of the plugin directory is not validated.
3838
String? findPlugin(String packageRoot) {
39-
return pluginMap.putIfAbsent(packageRoot, () => _findPlugin(packageRoot));
40-
}
41-
42-
/// The implementation of [findPlugin].
43-
String? _findPlugin(String packageRoot) {
44-
var packageFolder = resourceProvider.getFolder(packageRoot);
45-
var pluginFolder = packageFolder
46-
.getChildAssumingFolder(toolsFolderName)
47-
.getChildAssumingFolder(defaultPluginFolderName);
48-
if (pluginFolder.exists) {
49-
return pluginFolder.path;
50-
}
51-
return null;
39+
return _pluginMap.putIfAbsent(packageRoot, () {
40+
var pluginFolder = resourceProvider
41+
.getFolder(packageRoot)
42+
.getChildAssumingFolder(toolsFolderName)
43+
.getChildAssumingFolder(defaultPluginFolderName);
44+
return pluginFolder.exists ? pluginFolder.path : null;
45+
});
5246
}
5347
}

pkg/analysis_server/lib/src/plugin/plugin_manager.dart

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -341,17 +341,20 @@ class PluginManager {
341341
///
342342
/// If the plugin had not yet been started, then it will be started by this
343343
/// method.
344+
///
345+
/// Specify whether this is a legacy plugin with [isLegacyPlugin].
344346
Future<void> addPluginToContextRoot(
345347
analyzer.ContextRoot contextRoot,
346-
String path,
347-
) async {
348+
String path, {
349+
required bool isLegacyPlugin,
350+
}) async {
348351
var plugin = _pluginMap[path];
349352
var isNew = false;
350353
if (plugin == null) {
351354
isNew = true;
352355
PluginFiles pluginFiles;
353356
try {
354-
pluginFiles = filesFor(path);
357+
pluginFiles = filesFor(path, isLegacyPlugin: isLegacyPlugin);
355358
} catch (exception, stackTrace) {
356359
plugin = DiscoveredPluginInfo(
357360
path,
@@ -464,10 +467,14 @@ class PluginManager {
464467

465468
/// Returns the files associated with the plugin at the given [pluginPath].
466469
///
470+
/// In some cases, the plugin's sources are copied to a special directory. If
471+
/// [pluginPath] does not include a `pubspec.yaml` file, we do not. If
472+
/// [pluginPath] exists in a [BlazeWorkspace], we do not.
473+
///
467474
/// Throws a [PluginException] if there is a problem that prevents the plugin
468475
/// from being executing.
469476
@visibleForTesting
470-
PluginFiles filesFor(String pluginPath) {
477+
PluginFiles filesFor(String pluginPath, {required bool isLegacyPlugin}) {
471478
var pluginFolder = resourceProvider.getFolder(pluginPath);
472479
var pubspecFile = pluginFolder.getChildAssumingFile(file_paths.pubspecYaml);
473480
if (!pubspecFile.exists) {
@@ -482,16 +489,16 @@ class PluginManager {
482489
return _computeFiles(pluginFolder, workspace: workspace);
483490
}
484491

492+
if (!isLegacyPlugin) {
493+
return _computeFiles(pluginFolder, pubCommand: 'upgrade');
494+
}
495+
485496
// Copy the plugin directory to a unique subdirectory of the plugin
486497
// manager's state location. The subdirectory's name is selected such that
487498
// it will be invariant across sessions, reducing the number of times we
488499
// copy the plugin contents, and the number of times we run `pub`.
489-
var stateFolder = resourceProvider.getStateLocation('.plugin_manager');
490-
if (stateFolder == null) {
491-
throw PluginException('No state location, so plugin could not be copied');
492-
}
493-
var stateName = _uniqueDirectoryName(pluginPath);
494-
var parentFolder = stateFolder.getChildAssumingFolder(stateName);
500+
501+
var parentFolder = pluginStateFolder(pluginPath);
495502
if (parentFolder.exists) {
496503
var executionFolder = parentFolder.getChildAssumingFolder(
497504
pluginFolder.shortName,
@@ -518,6 +525,19 @@ class PluginManager {
518525
return plugins;
519526
}
520527

528+
/// Returns the "plugin state" folder for a plugin at [pluginPath].
529+
///
530+
/// This is a directory under the state location for '.plugin_manager', named
531+
/// with a hash based on [pluginPath].
532+
Folder pluginStateFolder(String pluginPath) {
533+
var stateFolder = resourceProvider.getStateLocation('.plugin_manager');
534+
if (stateFolder == null) {
535+
throw PluginException('No state location, so plugin could not be copied');
536+
}
537+
var stateName = _uniqueDirectoryName(pluginPath);
538+
return stateFolder.getChildAssumingFolder(stateName);
539+
}
540+
521541
/// The given [contextRoot] is no longer being analyzed.
522542
void removedContextRoot(analyzer.ContextRoot contextRoot) {
523543
var plugins = _pluginMap.values.toList();

pkg/analysis_server/lib/src/plugin/plugin_watcher.dart

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import 'dart:convert';
66

77
import 'package:analysis_server/src/plugin/plugin_locator.dart';
88
import 'package:analysis_server/src/plugin/plugin_manager.dart';
9+
import 'package:analysis_server/src/plugin2/generator.dart';
910
import 'package:analyzer/dart/analysis/context_root.dart';
1011
import 'package:analyzer/file_system/file_system.dart';
1112
import 'package:analyzer/src/dart/analysis/driver.dart';
1213
import 'package:analyzer/src/dart/sdk/sdk.dart';
14+
import 'package:analyzer/src/util/file_paths.dart' as file_paths;
1315

1416
/// An object that watches the results produced by analysis drivers to identify
1517
/// references to previously unseen packages and, if those packages have plugins
@@ -33,16 +35,18 @@ class PluginWatcher implements DriverWatcher {
3335
PluginWatcher(this.resourceProvider, this.manager)
3436
: _locator = PluginLocator(resourceProvider);
3537

36-
/// The context manager has just added the given analysis [driver]. This
37-
/// method must be called before the driver has been allowed to perform any
38-
/// analysis.
3938
@override
4039
void addedDriver(AnalysisDriver driver) {
4140
var contextRoot = driver.analysisContext!.contextRoot;
4241
_driverInfo[driver] = _DriverInfo(contextRoot, <String>[
4342
contextRoot.root.path,
4443
_getSdkPath(driver),
4544
]);
45+
46+
// We temporarily support both "legacy plugins" and (new) "plugins." We
47+
// restrict the number of legacy plugins to 1, for performance reasons.
48+
// At some point, we will stop adding legacy plugins to the context root.
49+
4650
for (var hostPackageName in driver.enabledLegacyPluginNames) {
4751
//
4852
// Determine whether the package exists and defines a plugin.
@@ -67,10 +71,36 @@ class PluginWatcher implements DriverWatcher {
6771
manager.addPluginToContextRoot(
6872
driver.analysisContext!.contextRoot,
6973
pluginPath,
74+
isLegacyPlugin: true,
7075
);
7176
}
7277

73-
// TODO(srawlins): Generate package for plugin configurations, with PluginPackageGenerator.
78+
// Now we add any specified (new) plugins to the context, as a single
79+
// "legacy plugin" shared entrypoint.
80+
var pluginConfigurations = driver.pluginConfigurations;
81+
// Add a shared entrypoint plugin to the context root, only if one or more
82+
// plugins are specified in analysis options.
83+
if (pluginConfigurations.isNotEmpty) {
84+
var contextRoot = driver.analysisContext!.contextRoot;
85+
var packageGenerator = PluginPackageGenerator(pluginConfigurations);
86+
// The path here just needs to be unique per context root.
87+
var sharedPluginFolder = manager.pluginStateFolder(contextRoot.root.path)
88+
..create();
89+
sharedPluginFolder
90+
.getChildAssumingFile(file_paths.pubspecYaml)
91+
.writeAsStringSync(packageGenerator.generatePubspec());
92+
var libFolder = sharedPluginFolder.getChildAssumingFolder('bin')
93+
..create();
94+
libFolder
95+
.getChildAssumingFile('plugin.dart')
96+
.writeAsStringSync(packageGenerator.generateEntrypoint());
97+
98+
manager.addPluginToContextRoot(
99+
contextRoot,
100+
sharedPluginFolder.path,
101+
isLegacyPlugin: false,
102+
);
103+
}
74104
}
75105

76106
/// The context manager has just removed the given analysis [driver].

pkg/analysis_server/lib/src/plugin2/analyzer_version.g.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
// Instead, run 'dart pkg/analysis_server/tool/generate_analyzer_version.dart'
88
// to update this file.
99

10-
/// The version of the analyzer that matches the analyzer code used by the
11-
/// analysis_server package.
12-
var analyzerVersion = '7.0.0-dev';
10+
/// The version of the analyzer_plugin package that matches the analyzer_plugin
11+
/// code used by the analysis_server package.
12+
var analyzerPluginVersion = '0.12.0';
13+
14+
/// The version of the analyzer package that matches the analyzer code used by
15+
/// the analysis_server package.
16+
var analyzerVersion = '7.0.0';

pkg/analysis_server/lib/src/plugin2/generator.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ class PluginPackageGenerator {
2222
var imports = [
2323
"'package:analysis_server_plugin/src/plugin_server.dart'",
2424
"'package:analyzer/file_system/physical_file_system.dart'",
25-
"'package:analyzer_plugin/starter.dart'",
25+
"'package:analyzer_plugin/src/channel/isolate_channel.dart'",
2626
for (var configuration in _pluginConfigurations)
2727
"'package:${configuration.name}/main.dart' as ${configuration.name}",
2828
];
2929

30-
var buffer = StringBuffer("import 'dart:isolate';");
30+
var buffer = StringBuffer("import 'dart:isolate';\n");
3131
for (var import in imports..sort()) {
3232
buffer.writeln('import $import;');
3333
}
@@ -45,8 +45,9 @@ Future<void> main(List<String> args, SendPort sendPort) async {
4545
buffer.write('''
4646
],
4747
);
48-
await startPlugin();
49-
ServerPluginStarter(wrangler).start(sendPort);
48+
await pluginServer.initialize();
49+
var channel = PluginIsolateChannel(sendPort);
50+
pluginServer.start(channel);
5051
}
5152
''');
5253

@@ -62,6 +63,7 @@ name: plugin_entrypoint
6263
version: 0.0.1
6364
dependencies:
6465
analyzer: '$analyzerVersion'
66+
analyzer_plugin: '$analyzerPluginVersion'
6567
''');
6668

6769
for (var configuration in _pluginConfigurations) {

pkg/analysis_server/lib/src/utilities/mocks.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,9 @@ class TestPluginManager implements PluginManager {
164164
@override
165165
Future<void> addPluginToContextRoot(
166166
analyzer.ContextRoot contextRoot,
167-
String path,
168-
) async {
167+
String path, {
168+
required bool isLegacyPlugin,
169+
}) async {
169170
contextRootPlugins.putIfAbsent(contextRoot, () => []).add(path);
170171
}
171172

0 commit comments

Comments
 (0)