Tell resolver about ECVs of overridden versions

Otherwise trying to use a package with constraint "livedata@1.0.0" when
you are loading livedata 1.0.1 from a local package will fail.
This commit is contained in:
David Glasser
2014-08-12 15:23:33 -07:00
parent 66f21a64a2
commit 0822033ca5
4 changed files with 82 additions and 12 deletions

View File

@@ -57,6 +57,12 @@ ConstraintSolver.PackagesResolver.prototype._ensurePackageInfoLoaded = function
ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function (
packageName) {
var self = this;
// XXX in theory there might be different archs but in practice they are
// always "os", "web.browser" and "web.cordova". Fix this once we
// actually have different archs used.
var allArchs = ["os", "web.browser", "web.cordova"];
// XXX is sortedness actually relevant? is there a minor optimization here
// where we can only talk to self.catalog once?
var sortedVersions = self.catalog.getSortedVersions(packageName);
@@ -66,10 +72,6 @@ ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function (
var unibuilds = {};
// XXX in theory there might be different archs but in practice they are
// always "os", "web.browser" and "web.cordova". Fix this once we
// actually have different archs used.
var allArchs = ["os", "web.browser", "web.cordova"];
_.each(allArchs, function (arch) {
var unitName = packageName + "#" + arch;
unibuilds[unitName] = new ConstraintSolver.UnitVersion(
@@ -121,6 +123,16 @@ ConstraintSolver.PackagesResolver.prototype._loadPackageInfo = function (
});
});
});
// We need to be aware of the earliestCompatibleVersion values for any
// packages that are overridden by local packages, in order to evaluate
// 'compatible-with' constraints that name that version.
_.each(self.catalog.getForgottenECVs(packageName), function (ecv, version) {
_.each(allArchs, function (arch) {
var unitName = packageName + '#' + arch;
self.resolver.addExtraECV(unitName, version, ecv);
});
});
};
// dependencies - an array of string names of packages (not slices)

View File

@@ -213,15 +213,16 @@ ConstraintSolver.ConstraintsList.prototype.edgeMatchingVersionsFor = function (
}
if (c.type === "compatible-with") {
var uv = resolver.getUnitVersion(packageName, c.version);
if (uv) {
var thisECV = resolver.getEarliestCompatibleVersion(
packageName, c.version);
if (thisECV) {
if (earliestCompatibleVersion &&
earliestCompatibleVersion !== uv.earliestCompatibleVersion) {
earliestCompatibleVersion !== thisECV) {
// Two constraints name versions with different ECV. Nothing can be
// compatible with both!
impossible = true;
} else if (! earliestCompatibleVersion) {
earliestCompatibleVersion = uv.earliestCompatibleVersion;
earliestCompatibleVersion = thisECV;
}
// else the ECV matches, which is great.
} else {

View File

@@ -34,6 +34,16 @@ ConstraintSolver.Resolver = function () {
// Refs to all constraints. Mapping String -> instance
self._constraints = {};
// Let's say that we that package P is available from source at version X.Y.Z.
// Then that's the only version that can actually be chosen by the resolver,
// and so it's the only version included as a UnitVersion. But let's say
// another unit depends on it with a 'compatible-with' dependency "@A.B.C". We
// need to be able to figure out the earliestCompatibleVersion of A.B.C, even
// though A.B.C is not a valid (selectable) UnitVersion. We store them here.
//
// Maps String unitName -> String version -> String earliestCompatibleVersion
self._extraECVs = {};
};
ConstraintSolver.Resolver.prototype.addUnitVersion = function (unitVersion) {
@@ -78,6 +88,36 @@ ConstraintSolver.Resolver.prototype.getConstraint =
new ConstraintSolver.Constraint(name, versionConstraint);
};
ConstraintSolver.Resolver.prototype.addExtraECV = function (
unitName, version, earliestCompatibleVersion) {
var self = this;
check(unitName, String);
check(version, String);
check(earliestCompatibleVersion, String);
if (!_.has(self._extraECVs, unitName)) {
self._extraECVs[unitName] = {};
}
self._extraECVs[unitName][version] = earliestCompatibleVersion;
};
ConstraintSolver.Resolver.prototype.getEarliestCompatibleVersion = function (
unitName, version) {
var self = this;
var uv = self.getUnitVersion(unitName, version);
if (uv) {
return uv.earliestCompatibleVersion;
}
if (!_.has(self._extraECVs, unitName)) {
return null;
}
if (!_.has(self._extraECVs[unitName], version)) {
return null;
}
return self._extraECVs[unitName][version];
};
// options: Object:
// - costFunction: function (state, options) - given a state evaluates its cost
// - estimateCostFunction: function (state) - given a state, evaluates the
@@ -552,19 +592,18 @@ ConstraintSolver.Constraint.prototype.isSatisfied = function (candidateUV,
if (self.type === "at-least")
return true;
var myUV = resolver.getUnitVersion(self.name, self.version);
var myECV = resolver.getEarliestCompatibleVersion(self.name, self.version);
// If the constraint is "@1.2.3" and 1.2.3 doesn't exist, then nothing can
// match. This is because we don't know the ECV (compatibility class) of
// 1.2.3!
if (!myUV)
if (!myECV)
return false;
// To be compatible, the two versions must have the same
// earliestCompatibleVersion. If the earliestCompatibleVersions haven't been
// overridden from their default, this means that the two versions have the
// same major version number.
return myUV.earliestCompatibleVersion ===
candidateUV.earliestCompatibleVersion;
return myECV === candidateUV.earliestCompatibleVersion;
};
// Returns any unit version satisfying the constraint in the resolver

View File

@@ -192,6 +192,10 @@ var CompleteCatalog = function () {
// XXX: use a future in the future maybe
self.refreshing = false;
// See the documentation of the _extraECVs field in ConstraintSolver.Resolver.
// Maps packageName -> version -> its ECV
self.forgottenECVs = {};
// We inherit from the protolog class, since we are a catalog.
BaseCatalog.call(self);
};
@@ -234,6 +238,12 @@ _.extend(CompleteCatalog.prototype, {
self.refresh();
},
reset: function () {
var self = this;
BaseCatalog.prototype.reset.call(self);
self.forgottenECVs = {};
},
// Given a set of constraints, returns a det of dependencies that satisfy the
// constraint.
//
@@ -415,6 +425,11 @@ _.extend(CompleteCatalog.prototype, {
_.extend(self.effectiveLocalPackages, self.localPackages);
},
getForgottenECVs: function (packageName) {
var self = this;
return self.forgottenECVs[packageName];
},
// Add all packages in self.effectiveLocalPackages to the catalog,
// first removing any existing packages that have the same name.
//
@@ -430,7 +445,10 @@ _.extend(CompleteCatalog.prototype, {
_.each(self.effectiveLocalPackages, function (dir, packageName) {
if (!_.has(self.versions, packageName))
return;
self.forgottenECVs[packageName] = {};
_.each(self.versions[packageName], function (record) {
self.forgottenECVs[packageName][record.version] =
record.earliestCompatibleVersion;
removedVersionIds[record._id] = true;
});
delete self.versions[packageName];