|
50 | 50 | import org.eclipse.core.runtime.Status; |
51 | 51 | import org.eclipse.debug.core.ILaunchConfiguration; |
52 | 52 | import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| 53 | +import org.eclipse.osgi.service.resolver.BaseDescription; |
53 | 54 | import org.eclipse.osgi.service.resolver.BundleDescription; |
| 55 | +import org.eclipse.osgi.service.resolver.State; |
54 | 56 | import org.eclipse.osgi.util.NLS; |
55 | 57 | import org.eclipse.pde.core.plugin.IMatchRules; |
56 | 58 | import org.eclipse.pde.core.plugin.IPluginBase; |
|
59 | 61 | import org.eclipse.pde.core.plugin.PluginRegistry; |
60 | 62 | import org.eclipse.pde.core.target.ITargetDefinition; |
61 | 63 | import org.eclipse.pde.core.target.ITargetPlatformService; |
| 64 | +import org.eclipse.pde.internal.build.BundleHelper; |
62 | 65 | import org.eclipse.pde.internal.build.IPDEBuildConstants; |
63 | 66 | import org.eclipse.pde.internal.core.DependencyManager; |
64 | 67 | import org.eclipse.pde.internal.core.FeatureModelManager; |
65 | 68 | import org.eclipse.pde.internal.core.PDECore; |
| 69 | +import org.eclipse.pde.internal.core.PluginModelManager; |
66 | 70 | import org.eclipse.pde.internal.core.TargetPlatformHelper; |
67 | 71 | import org.eclipse.pde.internal.core.ifeature.IFeature; |
68 | 72 | import org.eclipse.pde.internal.core.ifeature.IFeatureChild; |
|
75 | 79 | import org.eclipse.pde.internal.launching.PDEMessages; |
76 | 80 | import org.eclipse.pde.launching.IPDELauncherConstants; |
77 | 81 | import org.osgi.framework.Version; |
78 | | -import org.osgi.resource.Resource; |
79 | 82 |
|
80 | 83 | public class BundleLauncherHelper { |
81 | 84 |
|
@@ -166,12 +169,7 @@ private static void addRequiredBundles(Map<IPluginModelBase, String> bundle2star |
166 | 169 | RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, bundle2startLevel); |
167 | 170 |
|
168 | 171 | 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, true) // |
175 | 173 | .forEach(p -> addDefaultStartingBundle(bundle2startLevel, p)); |
176 | 174 | } |
177 | 175 |
|
@@ -223,16 +221,12 @@ private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILau |
223 | 221 | launchPlugins.addAll(additionalPlugins.keySet()); |
224 | 222 |
|
225 | 223 | if (addRequirements) { |
226 | | - // Add all missing plug-ins required by the application/product set in the config |
| 224 | + // Add all missing plug-ins required by the application/product set in the config |
227 | 225 | List<String> appRequirements = RequirementHelper.getApplicationLaunchRequirements(configuration); |
228 | 226 | RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, launchPlugins, launchPlugins::add); |
229 | 227 |
|
230 | 228 | // 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, isWorkspace(defaultPluginResolution)).forEach(launchPlugins::add); |
236 | 230 | } |
237 | 231 |
|
238 | 232 | // Create the start levels for the selected plugins and add them to the map |
@@ -546,6 +540,79 @@ private static void addBundleToMap(Map<IPluginModelBase, String> map, IPluginMod |
546 | 540 | map.put(bundle, startData); |
547 | 541 | } |
548 | 542 |
|
| 543 | + // --- dependency resolution --- |
| 544 | + |
| 545 | + private static Stream<IPluginModelBase> computeDependencies(Set<IPluginModelBase> includedPlugins, boolean includeOptional, boolean preferWorkspaceBundles) { |
| 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, preferWorkspaceBundles); |
| 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, boolean preferWorkspaceBundles) { |
| 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 bundles explicitly included in the launch |
| 588 | + .comparing((BaseDescription d) -> !launchBundles.contains(d.getSupplier())) //false<true |
| 589 | + .thenComparing(d -> { // choose bundles originating from the preferred location (workspace or TP) |
| 590 | + boolean isWorkspaceBundle = launchBundlePlugins.get(d.getSupplier()).getUnderlyingResource() != null; |
| 591 | + return isWorkspaceBundle != preferWorkspaceBundles; //false<true |
| 592 | + }).thenComparing(tpState.getResolver().getSelectionPolicy())); |
| 593 | + |
| 594 | + launchState.resolve(false); |
| 595 | + return launchBundles; |
| 596 | + } |
| 597 | + |
| 598 | + private static void addPluginBundle(IPluginModelBase plugin, State launchState, Map<BundleDescription, IPluginModelBase> launchBundlePlugin, State tpState) { |
| 599 | + BundleDescription bundle = plugin.getBundleDescription(); |
| 600 | + if (bundle != null) { |
| 601 | + if (bundle.getContainingState() != tpState) { // consistency check |
| 602 | + throw new IllegalStateException("Plugins have different TP state"); //$NON-NLS-1$ |
| 603 | + } |
| 604 | + BundleDescription launchBundle = launchState.getFactory().createBundleDescription(bundle); |
| 605 | + if (!launchState.addBundle(launchBundle)) { |
| 606 | + throw new IllegalStateException("Failed to add bundle to launch state: " + launchBundle); //$NON-NLS-1$ |
| 607 | + } |
| 608 | + if (launchBundlePlugin.put(launchBundle, plugin) != null) { |
| 609 | + throw new IllegalStateException("Duplicated launch bundle for plugin: " + plugin); //$NON-NLS-1$ |
| 610 | + } |
| 611 | + } |
| 612 | + } |
| 613 | + |
| 614 | + // -- start data --- |
| 615 | + |
549 | 616 | public static String getStartData(BundleDescription desc, String defaultStartData) { |
550 | 617 | String runLevel = resolveSystemRunLevelText(desc); |
551 | 618 | String auto = resolveSystemAutoText(desc); |
|
0 commit comments