Skip to content

Commit 81535cc

Browse files
committed
Prefer explicitly included plugins for Eclipse/OSGi launches
When launching an application create a new Equinox resolver state, where explicitly included plugins/bundles are preferred and compute the requirements closure from that state. This avoids adding other providers of a capability if another provider is already (explicitly) included in a launch, just because the former is wired to the requirement in the target-platform state. Potentially this also makes the set of launched plugins more compact. Fixes #1604
1 parent 98a5134 commit 81535cc

2 files changed

Lines changed: 77 additions & 13 deletions

File tree

ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/DependencyManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ private static boolean isTestWorkspaceProject(Resource f) {
262262
*
263263
* @return a set of bundle ids
264264
*/
265-
private static Collection<NameVersionDescriptor> getImplicitDependencies() {
265+
public static Collection<NameVersionDescriptor> getImplicitDependencies() {
266266
try {
267267
ITargetPlatformService service = PDECore.getDefault().acquireService(ITargetPlatformService.class);
268268
if (service != null) {

ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/BundleLauncherHelper.java

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@
5050
import org.eclipse.core.runtime.Status;
5151
import org.eclipse.debug.core.ILaunchConfiguration;
5252
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
53+
import org.eclipse.osgi.service.resolver.BaseDescription;
5354
import org.eclipse.osgi.service.resolver.BundleDescription;
55+
import org.eclipse.osgi.service.resolver.State;
5456
import org.eclipse.osgi.util.NLS;
5557
import org.eclipse.pde.core.plugin.IMatchRules;
5658
import org.eclipse.pde.core.plugin.IPluginBase;
@@ -59,10 +61,12 @@
5961
import org.eclipse.pde.core.plugin.PluginRegistry;
6062
import org.eclipse.pde.core.target.ITargetDefinition;
6163
import org.eclipse.pde.core.target.ITargetPlatformService;
64+
import org.eclipse.pde.internal.build.BundleHelper;
6265
import org.eclipse.pde.internal.build.IPDEBuildConstants;
6366
import org.eclipse.pde.internal.core.DependencyManager;
6467
import org.eclipse.pde.internal.core.FeatureModelManager;
6568
import org.eclipse.pde.internal.core.PDECore;
69+
import org.eclipse.pde.internal.core.PluginModelManager;
6670
import org.eclipse.pde.internal.core.TargetPlatformHelper;
6771
import org.eclipse.pde.internal.core.ifeature.IFeature;
6872
import org.eclipse.pde.internal.core.ifeature.IFeatureChild;
@@ -75,7 +79,6 @@
7579
import org.eclipse.pde.internal.launching.PDEMessages;
7680
import org.eclipse.pde.launching.IPDELauncherConstants;
7781
import org.osgi.framework.Version;
78-
import org.osgi.resource.Resource;
7982

8083
public class BundleLauncherHelper {
8184

@@ -166,12 +169,7 @@ private static void addRequiredBundles(Map<IPluginModelBase, String> bundle2star
166169
RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, bundle2startLevel);
167170

168171
boolean includeOptional = configuration.getAttribute(IPDELauncherConstants.INCLUDE_OPTIONAL, true);
169-
Set<BundleDescription> requiredDependencies = includeOptional //
170-
? DependencyManager.getDependencies(bundle2startLevel.keySet(), DependencyManager.Options.INCLUDE_OPTIONAL_DEPENDENCIES)
171-
: DependencyManager.getDependencies(bundle2startLevel.keySet());
172-
173-
requiredDependencies.stream().map(Resource.class::cast) //
174-
.map(PluginRegistry::findModel).filter(Objects::nonNull) //
172+
computeDependencies(bundle2startLevel.keySet(), includeOptional) //
175173
.forEach(p -> addDefaultStartingBundle(bundle2startLevel, p));
176174
}
177175

@@ -228,11 +226,7 @@ private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILau
228226
RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, launchPlugins, launchPlugins::add);
229227

230228
// Get all required plugins
231-
Set<BundleDescription> additionalBundles = DependencyManager.getDependencies(launchPlugins);
232-
for (BundleDescription bundle : additionalBundles) {
233-
IPluginModelBase plugin = getRequiredPlugin(bundle.getSymbolicName(), bundle.getVersion().toString(), IMatchRules.PERFECT, defaultPluginResolution);
234-
launchPlugins.add(Objects.requireNonNull(plugin));// should never be null
235-
}
229+
computeDependencies(launchPlugins, false).forEach(launchPlugins::add);
236230
}
237231

238232
// Create the start levels for the selected plugins and add them to the map
@@ -546,6 +540,76 @@ private static void addBundleToMap(Map<IPluginModelBase, String> map, IPluginMod
546540
map.put(bundle, startData);
547541
}
548542

543+
// --- dependency resolution ---
544+
545+
private static Stream<IPluginModelBase> computeDependencies(Set<IPluginModelBase> includedPlugins, boolean includeOptional) {
546+
if (includedPlugins.isEmpty()) {
547+
return Stream.empty();
548+
}
549+
// Create and resolve the new 'launch'-state where bundles explicitly included in the launch are preferred. Then compute the requirement closure on that 'launch'-state.
550+
Map<BundleDescription, IPluginModelBase> launchBundlePlugins = new HashMap<>(includedPlugins.size() * 4 / 3 + 1);
551+
Set<BundleDescription> launchBundles = reresolveBundlesPreferringIncludedBundles(includedPlugins, launchBundlePlugins);
552+
State launchState = launchBundles.iterator().next().getContainingState();
553+
554+
DependencyManager.getImplicitDependencies().stream().map(descriptor -> {
555+
String versionStr = descriptor.getVersion();
556+
Version version = versionStr != null ? Version.parseVersion(versionStr) : null;
557+
return launchState.getBundle(descriptor.getId(), version);
558+
}).forEach(launchBundles::add);
559+
560+
DependencyManager.Options[] options = includeOptional //
561+
? new DependencyManager.Options[] {DependencyManager.Options.INCLUDE_OPTIONAL_DEPENDENCIES}
562+
: new DependencyManager.Options[] {};
563+
Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(launchBundles, options);
564+
return closure.stream().map(launchBundlePlugins::get).map(Objects::requireNonNull) //
565+
.filter(p -> !includedPlugins.contains(p));
566+
}
567+
568+
private static Set<BundleDescription> reresolveBundlesPreferringIncludedBundles(Set<IPluginModelBase> includedPlugins, Map<BundleDescription, IPluginModelBase> launchBundlePlugins) {
569+
PluginModelManager modelManager = PDECore.getDefault().getModelManager();
570+
State tpState = modelManager.getState().getState();
571+
572+
State launchState = BundleHelper.getPlatformAdmin().getFactory().createState(true);
573+
launchState.setPlatformProperties(tpState.getPlatformProperties());
574+
575+
// Collect all bundles explicitly included in the launch
576+
for (IPluginModelBase plugin : includedPlugins) {
577+
addPluginBundle(plugin, launchState, launchBundlePlugins, tpState);
578+
}
579+
Set<BundleDescription> launchBundles = new HashSet<>(launchBundlePlugins.keySet());
580+
581+
// Iterate workspace- and TP-models separately to avoid shadowing of TP models by workspace models
582+
Stream.of(modelManager.getWorkspaceModels(), modelManager.getExternalModels()).flatMap(Arrays::stream) //
583+
.filter(IPluginModelBase::isEnabled).filter(p -> !includedPlugins.contains(p)) //
584+
.forEach(plugin -> addPluginBundle(plugin, launchState, launchBundlePlugins, tpState));
585+
586+
launchState.getResolver().setSelectionPolicy(Comparator
587+
// prefer explicitly directly included in the launch
588+
.comparing((BaseDescription d) -> !launchBundles.contains(d.getSupplier())) //false<true
589+
.thenComparing(tpState.getResolver().getSelectionPolicy()));
590+
591+
launchState.resolve(false);
592+
return launchBundles;
593+
}
594+
595+
private static void addPluginBundle(IPluginModelBase plugin, State launchState, Map<BundleDescription, IPluginModelBase> launchBundlePlugin, State tpState) {
596+
BundleDescription bundle = plugin.getBundleDescription();
597+
if (bundle != null) {
598+
if (bundle.getContainingState() != tpState) { // consistency check
599+
throw new IllegalStateException("Plugins have different TP state"); //$NON-NLS-1$
600+
}
601+
BundleDescription launchBundle = launchState.getFactory().createBundleDescription(bundle);
602+
if (!launchState.addBundle(launchBundle)) {
603+
throw new IllegalStateException("Failed to add bundle to launch state: " + launchBundle); //$NON-NLS-1$
604+
}
605+
if (launchBundlePlugin.put(launchBundle, plugin) != null) {
606+
throw new IllegalStateException("Duplicated launch bundle for plugin: " + plugin); //$NON-NLS-1$
607+
}
608+
}
609+
}
610+
611+
// -- start data ---
612+
549613
public static String getStartData(BundleDescription desc, String defaultStartData) {
550614
String runLevel = resolveSystemRunLevelText(desc);
551615
String auto = resolveSystemAutoText(desc);

0 commit comments

Comments
 (0)