|
28 | 28 | import java.util.ArrayDeque; |
29 | 29 | import java.util.ArrayList; |
30 | 30 | import java.util.Arrays; |
| 31 | +import java.util.Collection; |
31 | 32 | import java.util.Collections; |
32 | 33 | import java.util.Comparator; |
33 | 34 | import java.util.HashMap; |
|
50 | 51 | import org.eclipse.core.runtime.Status; |
51 | 52 | import org.eclipse.debug.core.ILaunchConfiguration; |
52 | 53 | import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; |
| 54 | +import org.eclipse.osgi.service.resolver.BaseDescription; |
53 | 55 | import org.eclipse.osgi.service.resolver.BundleDescription; |
| 56 | +import org.eclipse.osgi.service.resolver.State; |
| 57 | +import org.eclipse.osgi.service.resolver.StateObjectFactory; |
54 | 58 | import org.eclipse.osgi.util.NLS; |
55 | 59 | import org.eclipse.pde.core.plugin.IMatchRules; |
56 | 60 | import org.eclipse.pde.core.plugin.IPluginBase; |
|
59 | 63 | import org.eclipse.pde.core.plugin.PluginRegistry; |
60 | 64 | import org.eclipse.pde.core.target.ITargetDefinition; |
61 | 65 | import org.eclipse.pde.core.target.ITargetPlatformService; |
| 66 | +import org.eclipse.pde.internal.build.BundleHelper; |
62 | 67 | import org.eclipse.pde.internal.build.IPDEBuildConstants; |
63 | 68 | import org.eclipse.pde.internal.core.DependencyManager; |
64 | 69 | import org.eclipse.pde.internal.core.FeatureModelManager; |
|
75 | 80 | import org.eclipse.pde.internal.launching.PDEMessages; |
76 | 81 | import org.eclipse.pde.launching.IPDELauncherConstants; |
77 | 82 | import org.osgi.framework.Version; |
78 | | -import org.osgi.resource.Resource; |
79 | 83 |
|
80 | 84 | public class BundleLauncherHelper { |
81 | 85 |
|
@@ -166,12 +170,7 @@ private static void addRequiredBundles(Map<IPluginModelBase, String> bundle2star |
166 | 170 | RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, bundle2startLevel); |
167 | 171 |
|
168 | 172 | 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) // |
| 173 | + computeDependencies(bundle2startLevel.keySet(), includeOptional, IPDELauncherConstants.LOCATION_WORKSPACE) // |
175 | 174 | .forEach(p -> addDefaultStartingBundle(bundle2startLevel, p)); |
176 | 175 | } |
177 | 176 |
|
@@ -227,12 +226,8 @@ private static Map<IPluginModelBase, String> getMergedBundleMapFeatureBased(ILau |
227 | 226 | List<String> appRequirements = RequirementHelper.getApplicationLaunchRequirements(configuration); |
228 | 227 | RequirementHelper.addApplicationLaunchRequirements(appRequirements, configuration, launchPlugins, launchPlugins::add); |
229 | 228 |
|
230 | | - // 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, defaultPluginResolution) // |
| 230 | + .map(Objects::requireNonNull).forEach(launchPlugins::add); |
236 | 231 | } |
237 | 232 |
|
238 | 233 | // Create the start levels for the selected plugins and add them to the map |
@@ -546,6 +541,107 @@ private static void addBundleToMap(Map<IPluginModelBase, String> map, IPluginMod |
546 | 541 | map.put(bundle, startData); |
547 | 542 | } |
548 | 543 |
|
| 544 | + // --- dependency resolution --- |
| 545 | + |
| 546 | + private static Stream<IPluginModelBase> computeDependencies(Set<IPluginModelBase> includedPlugins, boolean includeOptional, String defaultPluginResolution) { |
| 547 | + if (includedPlugins.isEmpty()) { |
| 548 | + return Stream.empty(); |
| 549 | + } |
| 550 | + Set<BundleDescription> launchBundles = resolveBundlesInNewStatePreferringIncludedBundles(includedPlugins, defaultPluginResolution); |
| 551 | + State launchState = launchBundles.iterator().next().getContainingState(); |
| 552 | + |
| 553 | + DependencyManager.getImplicitDependencies().stream().map(descriptor -> { |
| 554 | + String versionStr = descriptor.getVersion(); |
| 555 | + Version version = versionStr != null ? Version.parseVersion(versionStr) : null; |
| 556 | + return launchState.getBundle(descriptor.getId(), version); |
| 557 | + }).forEach(launchBundles::add); |
| 558 | + |
| 559 | + DependencyManager.Options[] options = includeOptional // |
| 560 | + ? new DependencyManager.Options[] {DependencyManager.Options.INCLUDE_OPTIONAL_DEPENDENCIES} |
| 561 | + : new DependencyManager.Options[] {}; |
| 562 | + Set<BundleDescription> closure = DependencyManager.findRequirementsClosure(launchBundles, options); |
| 563 | + return closure.stream() // |
| 564 | + .map(bundle -> { |
| 565 | + // Ensure that the preferred plugin-location is respected (therefore not just use PluginRegistry.findModel) |
| 566 | + //TODO: incorporate this when creating the new state?! |
| 567 | + return getRequiredPlugin(bundle.getSymbolicName(), bundle.getVersion().toString(), IMatchRules.PERFECT, defaultPluginResolution); |
| 568 | + }).map(Objects::requireNonNull) //TODO: check if this can ever be null?! |
| 569 | + .filter(p -> !includedPlugins.contains(p)); |
| 570 | + } |
| 571 | + |
| 572 | + private static Set<BundleDescription> resolveBundlesInNewStatePreferringIncludedBundles(Collection<IPluginModelBase> includedPlugins, String defaultPluginResolution) { |
| 573 | + Set<BundleDescription> includedTPBundles = new HashSet<>(); |
| 574 | + Set<BundleDescription> launchBundles = new HashSet<>(); |
| 575 | + State tpState = TargetPlatformHelper.getState(); |
| 576 | + |
| 577 | + StateObjectFactory factory = BundleHelper.getPlatformAdmin().getFactory(); |
| 578 | + State launchState = factory.createState(true); |
| 579 | + //TODO: ensure this is correct?! |
| 580 | + launchState.setPlatformProperties(tpState.getPlatformProperties()); |
| 581 | + |
| 582 | + Comparator<BaseDescription> selectionPolicy = Comparator.<BaseDescription, Boolean> // |
| 583 | + comparing(d -> launchBundles.contains(d.getSupplier())).reversed() //false<true |
| 584 | + .thenComparing(tpState.getResolver().getSelectionPolicy()); |
| 585 | + launchState.getResolver().setSelectionPolicy(selectionPolicy); |
| 586 | + |
| 587 | + // Collect all bundles explicitly included in the launch |
| 588 | + for (IPluginModelBase plugin : includedPlugins) { |
| 589 | + BundleDescription bundle = plugin.getBundleDescription(); |
| 590 | + if (bundle != null) { |
| 591 | + if (bundle.getContainingState() != tpState) { // consistency check |
| 592 | + throw new IllegalStateException("Plugins have different TP state"); //$NON-NLS-1$ |
| 593 | + } |
| 594 | + includedTPBundles.add(bundle); |
| 595 | + BundleDescription launchBundle = factory.createBundleDescription(bundle); |
| 596 | + if (!launchState.addBundle(launchBundle)) { |
| 597 | + throw new IllegalStateException("Failed to add bundle to launch state: " + launchBundle); //$NON-NLS-1$ |
| 598 | + } |
| 599 | + if (!launchBundles.add(launchBundle)) { |
| 600 | + throw new IllegalStateException("Duplicated launch bundle for plugin: " + plugin); //$NON-NLS-1$ |
| 601 | + } |
| 602 | + } |
| 603 | + } |
| 604 | + // Add all other bundles from the tpState and resolve the set of explicitly included bundles against them |
| 605 | + for (BundleDescription bundle : tpState.getBundles()) { |
| 606 | + if (!includedTPBundles.contains(bundle)) { |
| 607 | + BundleDescription launchBundle = factory.createBundleDescription(bundle); |
| 608 | + if (!launchState.addBundle(launchBundle)) { |
| 609 | + throw new IllegalStateException("Failed to add bundle to launch state: " + launchBundle); //$NON-NLS-1$ |
| 610 | + } |
| 611 | + } |
| 612 | + } |
| 613 | + launchState.resolve(false); |
| 614 | + return launchBundles; |
| 615 | + } |
| 616 | + |
| 617 | + // -- start data --- |
| 618 | + |
| 619 | +// private static Comparator<BaseDescription> getDefaultSelectionPolicy(Dictionary<?, ?>[] platformProperties) { |
| 620 | +// Object preferSystem = platformProperties.length == 0 ? null : platformProperties[0].get("osgi.resolver.preferSystemPackages"); //$NON-NLS-1$ |
| 621 | +// boolean preferSystemPackages = preferSystem == null || Boolean.parseBoolean(preferSystem.toString()); |
| 622 | +// String systemBundle = Optional.ofNullable(platformProperties.length == 0 ? null : (String) platformProperties[0].get("osgi.system.bundle")).orElse(EquinoxContainer.NAME); //$NON-NLS-1$ |
| 623 | +// |
| 624 | +// return (BaseDescription d1, BaseDescription d2) -> { |
| 625 | +// // Assume BaseDescription.getName() == BundleDescription.getSymbolicName() |
| 626 | +// if (preferSystemPackages) { |
| 627 | +// if (systemBundle.equals(d1.getSupplier().getSymbolicName()) && !systemBundle.equals(d2.getSupplier().getSymbolicName())) { |
| 628 | +// return -1; |
| 629 | +// } else if (!systemBundle.equals(d1.getSupplier().getSymbolicName()) && systemBundle.equals(d2.getSupplier().getSymbolicName())) { |
| 630 | +// return 1; |
| 631 | +// } |
| 632 | +// } |
| 633 | +// ; |
| 634 | +// if (d1.getSupplier().isResolved() != d2.getSupplier().isResolved()) { |
| 635 | +// return d1.getSupplier().isResolved() ? -1 : 1; |
| 636 | +// } |
| 637 | +// int versionCompare = -(d1.getVersion().compareTo(d2.getVersion())); |
| 638 | +// if (versionCompare != 0) { |
| 639 | +// return versionCompare; |
| 640 | +// } |
| 641 | +// return Long.compare(d1.getSupplier().getBundleId(), d2.getSupplier().getBundleId()); |
| 642 | +// }; |
| 643 | +// } |
| 644 | + |
549 | 645 | public static String getStartData(BundleDescription desc, String defaultStartData) { |
550 | 646 | String runLevel = resolveSystemRunLevelText(desc); |
551 | 647 | String auto = resolveSystemAutoText(desc); |
|
0 commit comments