diff options
author | Pascal Rapicault | 2009-04-18 20:32:55 +0000 |
---|---|---|
committer | Pascal Rapicault | 2009-04-18 20:32:55 +0000 |
commit | 101af48fe53dfac2854546a87c7cb22e88ad9abd (patch) | |
tree | c7d5da16072b72bd11a70fa420c2dd813815af42 /bundles/org.eclipse.equinox.p2.director | |
parent | 574079e890f27d1f7055543db56962a41990a5b6 (diff) | |
download | rt.equinox.p2-101af48fe53dfac2854546a87c7cb22e88ad9abd.tar.gz rt.equinox.p2-101af48fe53dfac2854546a87c7cb22e88ad9abd.tar.xz rt.equinox.p2-101af48fe53dfac2854546a87c7cb22e88ad9abd.zip |
Bug 262503 - [planner] Replace ResolutionHelper with SAT4J based solutionafter_bug262503
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.director')
3 files changed, 272 insertions, 60 deletions
diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java index 616d604f3..5fabf3aca 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java @@ -56,6 +56,8 @@ public class Projector { private Collection alreadyInstalledIUs; private boolean considerMetaRequirements; + private IInstallableUnit entryPoint; + private Map fragments = new HashMap(); static class AbstractVariable { public String toString() { @@ -126,8 +128,9 @@ public class Projector { this.considerMetaRequirements = considerMetaRequirements; } - public void encode(IInstallableUnit metaIu, IInstallableUnit[] alreadyExistingRoots, IInstallableUnit[] newRoots, IProgressMonitor monitor) { + public void encode(IInstallableUnit entryPointIU, IInstallableUnit[] alreadyExistingRoots, IInstallableUnit[] newRoots, IProgressMonitor monitor) { alreadyInstalledIUs = Arrays.asList(alreadyExistingRoots); + this.entryPoint = entryPointIU; try { long start = 0; if (DEBUG) { @@ -159,15 +162,15 @@ public class Projector { throw new OperationCanceledException(); } IInstallableUnit iuToEncode = (IInstallableUnit) iusToEncode.next(); - if (iuToEncode != metaIu) { + if (iuToEncode != entryPointIU) { processIU(iuToEncode, false); } } createConstraintsForSingleton(); - createMustHave(metaIu, alreadyExistingRoots, newRoots); + createMustHave(entryPointIU, alreadyExistingRoots, newRoots); - createOptimizationFunction(metaIu); + createOptimizationFunction(entryPointIU); if (DEBUG) { long stop = System.currentTimeMillis(); Tracing.debug("Projection complete: " + (stop - start)); //$NON-NLS-1$ @@ -312,6 +315,9 @@ public class Projector { if (!isApplicable(req)) return; List matches = getApplicableMatches(req); + if (isHostRequirement(iu, req)) { + rememberHostMatches(iu, matches); + } if (!req.isOptional()) { if (matches.isEmpty()) { missingRequirement(iu, req); @@ -380,7 +386,7 @@ public class Projector { private void expandRequirementsWithPatches(IInstallableUnit iu, Collector applicablePatches, boolean isRootIu) throws ContradictionException { //Unmodified dependencies - Map unchangedRequirements = new HashMap(iu.getRequiredCapabilities().length); + Map unchangedRequirements = new HashMap(getRequiredCapabilities(iu).length); for (Iterator iterator = applicablePatches.iterator(); iterator.hasNext();) { IInstallableUnitPatch patch = (IInstallableUnitPatch) iterator.next(); IRequiredCapability[][] reqs = mergeRequirements(iu, patch); @@ -413,6 +419,9 @@ public class Projector { if (isApplicable(reqs[i][1])) { IRequiredCapability req = reqs[i][1]; List matches = getApplicableMatches(req); + if (isHostRequirement(iu, req)) { + rememberHostMatches(iu, matches); + } if (!req.isOptional()) { if (matches.isEmpty()) { missingRequirement(patch, req); @@ -443,6 +452,9 @@ public class Projector { if (isApplicable(reqs[i][0])) { IRequiredCapability req = reqs[i][0]; List matches = getApplicableMatches(req); + if (isHostRequirement(iu, req)) { + rememberHostMatches(iu, matches); + } if (!req.isOptional()) { if (matches.isEmpty()) { dependencyHelper.implication(new Object[] {iu}).implies(patch).named(new Explanation.HardRequirement(iu, null)); @@ -487,6 +499,9 @@ public class Projector { } IRequiredCapability req = (IRequiredCapability) entry.getKey(); List matches = getApplicableMatches(req); + if (isHostRequirement(iu, req)) { + rememberHostMatches(iu, matches); + } if (!req.isOptional()) { if (matches.isEmpty()) { if (requiredPatches.isEmpty()) { @@ -749,6 +764,8 @@ public class Projector { Object var = i.next(); if (var instanceof IInstallableUnit) { IInstallableUnit iu = (IInstallableUnit) var; + if (iu == entryPoint) + continue; solution.add(iu); } } @@ -770,16 +787,6 @@ public class Projector { return solution; } - public Set getExplanationFor(IInstallableUnit iu) { - //TODO if the iu is resolved then return null. - //TODO if the iu is in an unknown state, then return a special value in the set - try { - return dependencyHelper.whyNot(iu); - } catch (TimeoutException e) { - return Collections.EMPTY_SET; - } - } - public Set getExplanation(IProgressMonitor monitor) { ExplanationJob job = new ExplanationJob(); job.schedule(); @@ -807,4 +814,46 @@ public class Projector { } return job.getExplanationResult(); } + + public Map getFragmentAssociation() { + Map resolvedFragments = new HashMap(fragments.size()); + for (Iterator iterator = fragments.entrySet().iterator(); iterator.hasNext();) { + Entry fragment = (Entry) iterator.next(); + if (!dependencyHelper.getBooleanValueFor(fragment.getKey())) + continue; + Set potentialHosts = (Set) fragment.getValue(); + List resolvedHost = new ArrayList(potentialHosts.size()); + for (Iterator iterator2 = potentialHosts.iterator(); iterator2.hasNext();) { + Object host = iterator2.next(); + if (dependencyHelper.getBooleanValueFor(host)) + resolvedHost.add(host); + } + if (resolvedHost.size() != 0) + resolvedFragments.put(fragment.getKey(), resolvedHost); + } + return resolvedFragments; + } + + private void rememberHostMatches(IInstallableUnit fragment, List matches) { + Set existingMatches = (Set) fragments.get(fragment); + if (existingMatches == null) { + existingMatches = new HashSet(); + fragments.put(fragment, existingMatches); + existingMatches.addAll(matches); + } + existingMatches.retainAll(matches); + } + + private boolean isHostRequirement(IInstallableUnit iu, IRequiredCapability req) { + if (!(iu instanceof IInstallableUnitFragment)) + return false; + IInstallableUnitFragment fragment = (IInstallableUnitFragment) iu; + IRequiredCapability[] reqs = fragment.getHost(); + for (int i = 0; i < reqs.length; i++) { + if (req == reqs[i]) + return true; + } + return true; + } + }
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java index b2b1814eb..8efd839fd 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java @@ -343,9 +343,7 @@ public class SimplePlanner implements IPlanner { LogHelper.log(s); s = Status.OK_STATUS; - Collection newState = projector.extractSolution(); - newState.remove(updatedPlan[0]); - return newState; + return projector; } finally { sub.done(); } @@ -355,22 +353,17 @@ public class SimplePlanner implements IPlanner { SubMonitor sub = SubMonitor.convert(monitor, ExpandWork); sub.setTaskName(Messages.Director_Task_Resolving_Dependencies); try { - //Get the solution of the initial request + //Get the solution for the initial request Object resolutionResult = getSolutionFor(profileChangeRequest, context, sub.newChild(ExpandWork / 2)); if (resolutionResult instanceof ProvisioningPlan) return (ProvisioningPlan) resolutionResult; - //Compute the set of operands based on the solution obtained previously - Collection newState = (Collection) resolutionResult; + Collection newState = ((Projector) resolutionResult).extractSolution(); Collection fullState = new ArrayList(); fullState.addAll(newState); - ResolutionHelper newStateHelper = new ResolutionHelper(createSelectionContext(profileChangeRequest.getProfileProperties()), null); - newState = newStateHelper.attachCUs(newState); + newState = ResolutionHelper.attachFragments(newState, ((Projector) resolutionResult).getFragmentAssociation()); - ResolutionHelper oldStateHelper = new ResolutionHelper(createSelectionContext(profileChangeRequest.getProfile().getProperties()), null); - Collection oldState = oldStateHelper.attachCUs(profileChangeRequest.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection()); - - ProvisioningPlan temporaryPlan = generateProvisioningPlan(oldState, newState, profileChangeRequest, null); + ProvisioningPlan temporaryPlan = generatePlan((Projector) resolutionResult, newState, profileChangeRequest); //Create a plan for installing necessary pieces to complete the installation (e.g touchpoint actions) return createInstallerPlan(profileChangeRequest.getProfile(), profileChangeRequest, fullState, newState, temporaryPlan, context, sub.newChild(ExpandWork / 2)); @@ -472,14 +465,7 @@ public class SimplePlanner implements IPlanner { return new ProvisioningPlan(externalInstallerStatus, initialRequest, new ProvisioningPlan(externalInstallerStatus, agentRequest, null)); } - Collection newState = (Collection) externalInstallerPlan; - ResolutionHelper newStateHelper = new ResolutionHelper(createSelectionContext(agentRequest.getProfileProperties()), null); - newState = newStateHelper.attachCUs(newState); - - ResolutionHelper oldStateHelper = new ResolutionHelper(createSelectionContext(agentProfile.getProperties()), null); - Collection oldState = oldStateHelper.attachCUs(agentRequest.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection()); - - initialPlan.setInstallerPlan(generateProvisioningPlan(oldState, newState, agentRequest, null)); + initialPlan.setInstallerPlan(generatePlan((Projector) externalInstallerPlan, null, agentRequest)); return initialPlan; } @@ -517,35 +503,57 @@ public class SimplePlanner implements IPlanner { agentRequest.removeInstallableUnits(new IInstallableUnit[] {previousMetaRequirementIU}); agentRequest.addInstallableUnits(new IInstallableUnit[] {metaRequirementIU}); - ProvisioningContext agentCtx = initialContext; //TODO I believe that we are passing too much here + ProvisioningContext agentCtx = new ProvisioningContext(new URI[0]); ArrayList extraIUs = new ArrayList(unattachedState); - extraIUs.addAll(profile.available(InstallableUnitQuery.ANY, new Collector(), new NullProgressMonitor()).toCollection()); agentCtx.setExtraIUs(extraIUs); - agentCtx.setProperty(INCLUDE_PROFILE_IUS, Boolean.FALSE.toString()); Object agentSolution = getSolutionFor(agentRequest, agentCtx, monitor.newChild(3)); if (agentSolution instanceof ProvisioningPlan && ((ProvisioningPlan) agentSolution).getStatus().getSeverity() == IStatus.ERROR) { MultiStatus agentStatus = new MultiStatus(DirectorActivator.PI_DIRECTOR, 0, Messages.Planner_actions_and_software_incompatible, null); agentStatus.add(((ProvisioningPlan) agentSolution).getStatus()); return new ProvisioningPlan(agentStatus, initialRequest, new ProvisioningPlan(agentStatus, agentRequest, null)); } - Collection agentState = (Collection) agentSolution; - agentState.remove(metaRequirementIU); - ResolutionHelper agentStateHelper = new ResolutionHelper(createSelectionContext(initialRequest.getProfileProperties()), null); - agentState = agentStateHelper.attachCUs(agentState); - ResolutionHelper initialStateHelper = new ResolutionHelper(createSelectionContext(initialRequest.getProfile().getProperties()), null); - Collection initialState = initialStateHelper.attachCUs(initialRequest.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection()); - ProvisioningPlan agentPlan = generateProvisioningPlan(initialState, agentState, agentRequest, null); + //Compute the installer plan. It is the difference between what is currently in the profile and the solution we just computed + Collection agentState = ((Projector) agentSolution).extractSolution(); + agentState.remove(metaRequirementIU); //Remove the fake IU + agentState = ResolutionHelper.attachFragments(agentState, ((Projector) agentSolution).getFragmentAssociation()); - ResolutionHelper newStateHelper = new ResolutionHelper(createSelectionContext(initialRequest.getProfileProperties()), null); - expectedState = newStateHelper.attachCUs(expectedState); + ProvisioningContext NO_REPO_CONTEXT = new ProvisioningContext(new URI[0]); + NO_REPO_CONTEXT.setArtifactRepositories(new URI[0]); + //...This computes the attachment of what is currently in the profile + Object initialSolution = getSolutionFor(new ProfileChangeRequest(new EverythingOptionalProfile(initialRequest.getProfile())), NO_REPO_CONTEXT, new NullProgressMonitor()); + if (initialSolution instanceof ProvisioningPlan) { + LogHelper.log(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, "The resolution of the previous state contained in profile " + initialRequest.getProfile().getProfileId() + " version " + initialRequest.getProfile().getTimestamp() + " failed to resolve.")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + } + Collection initialState = ((Projector) initialSolution).extractSolution(); + initialState = ResolutionHelper.attachFragments(initialState, ((Projector) initialSolution).getFragmentAssociation()); - ResolutionHelper oldStateHelper = new ResolutionHelper(createSelectionContext(initialRequest.getProfileProperties()), null); //Here we purposefully use the new properties - Set iusAfterActionInstall = new HashSet((Collection) agentSolution); - iusAfterActionInstall.addAll(initialRequest.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection()); - Collection oldState = oldStateHelper.attachCUs(iusAfterActionInstall); + ProvisioningPlan agentPlan = generateProvisioningPlan(initialState, agentState, initialRequest, null); + + //Compute the installation plan. It is the difference between the state after the installer plan has run and the expectedState. + return generateProvisioningPlan(agentState, expectedState, initialRequest, agentPlan); + } + + //Compute the set of operands based on the solution obtained previously + private ProvisioningPlan generatePlan(Projector newSolution, Collection newState, ProfileChangeRequest request) { + //Compute the attachment of the new state if not provided + if (newState == null) { + newState = newSolution.extractSolution(); + newState = ResolutionHelper.attachFragments(newState, newSolution.getFragmentAssociation()); + } + ProvisioningContext NO_REPO_CONTEXT = new ProvisioningContext(new URI[0]); + NO_REPO_CONTEXT.setArtifactRepositories(new URI[0]); + + //Compute the attachment of the previous state + Object initialSolution = getSolutionFor(new ProfileChangeRequest(new EverythingOptionalProfile(request.getProfile())), NO_REPO_CONTEXT, new NullProgressMonitor()); + if (initialSolution instanceof ProvisioningPlan) { + LogHelper.log(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, "The resolution of the previous state contained in profile " + request.getProfile().getProfileId() + " version " + request.getProfile().getTimestamp() + " failed to resolve.")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + } + Collection initialState = ((Projector) initialSolution).extractSolution(); + initialState = ResolutionHelper.attachFragments(initialState, ((Projector) initialSolution).getFragmentAssociation()); - return generateProvisioningPlan(oldState, expectedState, initialRequest, agentPlan); + //Generate the plan + return generateProvisioningPlan(initialState, newState, request, null); } private IInstallableUnit getPreviousIUForMetaRequirements(IProfile profile, String iuId, IProgressMonitor monitor) { @@ -601,17 +609,18 @@ public class SimplePlanner implements IPlanner { profileChangeRequest.setInstallableUnitProfileProperty((IInstallableUnit) object.getKey(), INCLUSION_RULES, PlannerHelper.createStrictInclusionRule((IInstallableUnit) object.getKey())); } //Remove the iu properties associated to the ius removed and the iu properties being removed as well - for (Iterator iterator = alreadyInstalled.iterator(); iterator.hasNext();) { - IInstallableUnit iu = (IInstallableUnit) iterator.next(); - for (int i = 0; i < removed.length; i++) { - if (iu.equals(removed[i])) { - profileChangeRequest.removeInstallableUnitProfileProperty(removed[i], INCLUSION_RULES); - iterator.remove(); - break; + if (removed.length != 0) { + for (Iterator iterator = alreadyInstalled.iterator(); iterator.hasNext();) { + IInstallableUnit iu = (IInstallableUnit) iterator.next(); + for (int i = 0; i < removed.length; i++) { + if (iu.equals(removed[i])) { + profileChangeRequest.removeInstallableUnitProfileProperty(removed[i], INCLUSION_RULES); + iterator.remove(); + break; + } } } } - ArrayList gatheredRequirements = new ArrayList(); //Process all the IUs being added @@ -700,4 +709,71 @@ public class SimplePlanner implements IPlanner { Collection results = resultsMap.values(); return (IInstallableUnit[]) results.toArray(new IInstallableUnit[results.size()]); } + + //helper class to trick the resolver to believe that everything is optional + private static class EverythingOptionalProfile implements IProfile { + private IProfile profile; + + public EverythingOptionalProfile(IProfile p) { + profile = p; + } + + public Collector available(Query query, Collector collector, IProgressMonitor monitor) { + return profile.available(query, collector, monitor); + } + + public Map getInstallableUnitProperties(IInstallableUnit iu) { + return profile.getInstallableUnitProperties(iu); + } + + public String getInstallableUnitProperty(IInstallableUnit iu, String key) { + if (INCLUSION_RULES.equals(key)) + return PlannerHelper.createOptionalInclusionRule(iu); + return profile.getInstallableUnitProperty(iu, key); + } + + public Map getLocalProperties() { + return profile.getLocalProperties(); + } + + public String getLocalProperty(String key) { + return profile.getLocalProperty(key); + } + + public IProfile getParentProfile() { + return profile.getParentProfile(); + } + + public String getProfileId() { + return profile.getProfileId(); + } + + public Map getProperties() { + return profile.getProperties(); + } + + public String getProperty(String key) { + return profile.getProperty(key); + } + + public String[] getSubProfileIds() { + return profile.getSubProfileIds(); + } + + public long getTimestamp() { + return profile.getTimestamp(); + } + + public boolean hasSubProfiles() { + return profile.hasSubProfiles(); + } + + public boolean isRootProfile() { + return profile.isRootProfile(); + } + + public Collector query(Query query, Collector collector, IProgressMonitor monitor) { + return profile.query(query, collector, monitor); + } + } } diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/resolution/ResolutionHelper.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/resolution/ResolutionHelper.java index 04bca9d51..ff706c6aa 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/resolution/ResolutionHelper.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/resolution/ResolutionHelper.java @@ -11,6 +11,7 @@ package org.eclipse.equinox.internal.p2.resolution; import java.util.*; +import java.util.Map.Entry; import org.eclipse.equinox.internal.p2.director.DirectorActivator; import org.eclipse.equinox.internal.p2.director.RecommendationDescriptor; import org.eclipse.equinox.internal.provisional.p2.core.Version; @@ -186,6 +187,92 @@ public class ResolutionHelper { return result; } + public static Collection attachFragments(Collection toAttach, Map fragmentsToIUs) { + Map fragmentBindings = new HashMap(); + //Build a map inverse of the one provided in input (host --> List of fragments) + Map iusToFragment = new HashMap(fragmentsToIUs.size()); + for (Iterator iterator = fragmentsToIUs.entrySet().iterator(); iterator.hasNext();) { + Entry mapping = (Entry) iterator.next(); + IInstallableUnitFragment fragment = (IInstallableUnitFragment) mapping.getKey(); + List existingMatches = (List) mapping.getValue(); + + for (Iterator iterator2 = existingMatches.iterator(); iterator2.hasNext();) { + Object host = iterator2.next(); + List potentialFragments = (List) iusToFragment.get(host); + if (potentialFragments == null) { + potentialFragments = new ArrayList(); + iusToFragment.put(host, potentialFragments); + } + potentialFragments.add(fragment); + } + } + + for (Iterator iterator = iusToFragment.entrySet().iterator(); iterator.hasNext();) { + Entry entry = (Entry) iterator.next(); + IInstallableUnit hostIU = (IInstallableUnit) entry.getKey(); + List potentialIUFragments = (List) entry.getValue(); + ArrayList applicableFragments = new ArrayList(); + for (Iterator iterator2 = potentialIUFragments.iterator(); iterator2.hasNext();) { + IInstallableUnit dependentIU = (IInstallableUnitFragment) iterator2.next(); + if (hostIU.equals(dependentIU) || !dependentIU.isFragment()) + continue; + + IInstallableUnitFragment potentialFragment = (IInstallableUnitFragment) dependentIU; + + // Check to make sure the host meets the requirements of the fragment + IRequiredCapability reqsFromFragment[] = potentialFragment.getHost(); + boolean match = true; + boolean requirementMatched = false; + for (int l = 0; l < reqsFromFragment.length && match == true; l++) { + requirementMatched = false; + if (hostIU.satisfies(reqsFromFragment[l])) + requirementMatched = true; + if (requirementMatched == false) { + match = false; + break; + } + + } + if (match) { + applicableFragments.add(potentialFragment); + } + } + + IInstallableUnitFragment theFragment = null; + int specificityLevel = 0; + for (Iterator iterator4 = applicableFragments.iterator(); iterator4.hasNext();) { + IInstallableUnitFragment fragment = (IInstallableUnitFragment) iterator4.next(); + if (fragment.getHost().length > specificityLevel) { + theFragment = fragment; + specificityLevel = fragment.getHost().length; + } + } + if (theFragment != null) + fragmentBindings.put(hostIU, theFragment); + } + //build the collection of resolved IUs + Collection result = new HashSet(toAttach.size()); + for (Iterator iterator = toAttach.iterator(); iterator.hasNext();) { + IInstallableUnit iu = (IInstallableUnit) iterator.next(); + if (iu == null) + continue; + //just return fragments as they are + if (iu.isFragment()) { + result.add(iu); + continue; + } + //return a new IU that combines the IU with its bound fragments + IInstallableUnitFragment fragment = (IInstallableUnitFragment) fragmentBindings.get(iu); + IInstallableUnitFragment[] fragments; + if (fragment == null) + fragments = NO_FRAGMENTS; + else + fragments = new IInstallableUnitFragment[] {fragment}; + result.add(MetadataFactory.createResolvedInstallableUnit(iu, fragments)); + } + return result; + } + public boolean isResolved(IInstallableUnit iu) { return state.getBundle(iu.getId(), Version.toOSGiVersion(iu.getVersion())).isResolved(); } |