From 402eab798308293b5d3d848873f9256abd52a61a Mon Sep 17 00:00:00 2001 From: David Greenspan Date: Wed, 21 Jan 2015 15:22:59 -0800 Subject: [PATCH] Lift isSatisfied out of resolver.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we axe the rest of resolver.js, we still need this piece. It would be as simple as VersionConstraint#isSatisfiedBy(version) except for the fact that we put the logic here about whether to allow prereleases, which necessitates the “package” and “options” arguments and makes this a constraint-solver-specific utility. In the future, we can put that logic somewhere else, but for now keeping it preserves the current behavior. --- .../constraint-solver/constraint-solver.js | 76 +++++++++++++++++++ packages/constraint-solver/resolver.js | 69 +---------------- tools/package-version-parser.js | 13 +++- 3 files changed, 88 insertions(+), 70 deletions(-) diff --git a/packages/constraint-solver/constraint-solver.js b/packages/constraint-solver/constraint-solver.js index f47b85cc65..5011e719f7 100644 --- a/packages/constraint-solver/constraint-solver.js +++ b/packages/constraint-solver/constraint-solver.js @@ -318,3 +318,79 @@ var getCostFunction = function (resolver, options) { } }; }; + +// - package: String package name +// - vConstraint: a PackageVersion.VersionConstraint, or an object +// with an `alternatives` property lifted from one. +// - version: version String +// - options: any object with an "anticipatedPrereleases" property. +CS.isConstraintSatisfied = function (package, vConstraint, version, options) { + + var prereleaseNeedingLicense = false; + + // We try not to allow "pre-release" versions (versions with a '-') unless + // they are explicitly mentioned. If the `anticipatedPrereleases` option is + // `true` set, all pre-release versions are allowed. Otherwise, + // anticipatedPrereleases lists pre-release versions that are always allow + // (this corresponds to pre-release versions mentioned explicitly in + // *top-level* constraints). + // + // Otherwise, if `candidateUV` is a pre-release, it needs to be "licensed" by + // being mentioned by name in *this* constraint or matched by an inexact + // constraint whose version also has a '-'. + // + // Note that a constraint "@2.0.0" can never match a version "2.0.1-rc.1" + // unless anticipatedPrereleases allows it, even if another constraint found + // in the graph (but not at the top level) explicitly mentions "2.0.1-rc.1". + // Why? The constraint solver assumes that adding a constraint to the resolver + // state can't make previously impossible choices now possible. If + // pre-releases mentioned anywhere worked, then applying the constraint + // "@2.0.0" followed by "@=2.0.1-rc.1" would result in "2.0.1-rc.1" ruled + // first impossible and then possible again. That will break this algorith, so + // we have to fix the meaning based on something known at the start of the + // search. (We could try to apply our prerelease-avoidance tactics solely in + // the cost functions, but then it becomes a much less strict rule.) + if (options.anticipatedPrereleases !== true + && /-/.test(version)) { + var isAnticipatedPrerelease = ( + _.has(options.anticipatedPrereleases, package) && + _.has(options.anticipatedPrereleases[package], version)); + if (! isAnticipatedPrerelease) { + prereleaseNeedingLicense = true; + } + } + + return _.some(vConstraint.alternatives, function (simpleConstraint) { + var type = simpleConstraint.type; + + if (type === "any-reasonable") { + return ! prereleaseNeedingLicense; + } else if (type === "exactly") { + var cVersion = simpleConstraint.versionString; + return (cVersion === version); + } else if (type === 'compatible-with') { + var cv = PV.parse(simpleConstraint.versionString); + var v = PV.parse(version); + + if (prereleaseNeedingLicense && ! /-/.test(cv.raw)) { + return false; + } + + // If the candidate version is less than the version named in the + // constraint, we are not satisfied. + if (PV.lessThan(v, cv)) { + return false; + } + + // To be compatible, the two versions must have the same major version + // number. + if (v.major !== cv.major) { + return false; + } + + return true; + } else { + throw Error("Unknown constraint type: " + type); + } + }); +}; diff --git a/packages/constraint-solver/resolver.js b/packages/constraint-solver/resolver.js index bf758c2e82..fac12bb0df 100644 --- a/packages/constraint-solver/resolver.js +++ b/packages/constraint-solver/resolver.js @@ -321,73 +321,8 @@ ConstraintSolver.Constraint.prototype.isSatisfied = function ( candidateUV.name); } - var prereleaseNeedingLicense = false; - - // We try not to allow "pre-release" versions (versions with a '-') unless - // they are explicitly mentioned. If the `anticipatedPrereleases` option is - // `true` set, all pre-release versions are allowed. Otherwise, - // anticipatedPrereleases lists pre-release versions that are always allow - // (this corresponds to pre-release versions mentioned explicitly in - // *top-level* constraints). - // - // Otherwise, if `candidateUV` is a pre-release, it needs to be "licensed" by - // being mentioned by name in *this* constraint or matched by an inexact - // constraint whose version also has a '-'. - // - // Note that a constraint "@2.0.0" can never match a version "2.0.1-rc.1" - // unless anticipatedPrereleases allows it, even if another constraint found - // in the graph (but not at the top level) explicitly mentions "2.0.1-rc.1". - // Why? The constraint solver assumes that adding a constraint to the resolver - // state can't make previously impossible choices now possible. If - // pre-releases mentioned anywhere worked, then applying the constraint - // "@2.0.0" followed by "@=2.0.1-rc.1" would result in "2.0.1-rc.1" ruled - // first impossible and then possible again. That will break this algorith, so - // we have to fix the meaning based on something known at the start of the - // search. (We could try to apply our prerelease-avoidance tactics solely in - // the cost functions, but then it becomes a much less strict rule.) - if (resolveContext.anticipatedPrereleases !== true - && /-/.test(candidateUV.version)) { - var isAnticipatedPrerelease = ( - _.has(resolveContext.anticipatedPrereleases, self.name) && - _.has(resolveContext.anticipatedPrereleases[self.name], - candidateUV.version)); - if (! isAnticipatedPrerelease) { - prereleaseNeedingLicense = true; - } - } - - return _.some(self.alternatives, function (simpleConstraint) { - var type = simpleConstraint.type; - - if (type === "any-reasonable") { - return ! prereleaseNeedingLicense; - } else if (type === "exactly") { - var version = simpleConstraint.versionString; - return (version === candidateUV.version); - } else if (type === 'compatible-with') { - var version = simpleConstraint.versionString; - - if (prereleaseNeedingLicense && ! /-/.test(version)) { - return false; - } - - // If the candidate version is less than the version named in the - // constraint, we are not satisfied. - if (PackageVersion.lessThan(candidateUV.version, version)) { - return false; - } - - // To be compatible, the two versions must have the same major version - // number. - if (candidateUV.majorVersion !== PackageVersion.majorVersion(version)) { - return false; - } - - return true; - } else { - throw Error("Unknown constraint type: " + type); - } - }); + return ConstraintSolver.isConstraintSatisfied( + self.name, self, candidateUV.version, resolveContext); }; // An object that records the general context of a resolve call. It can be diff --git a/tools/package-version-parser.js b/tools/package-version-parser.js index ee16734a5d..ec3ee5b7a3 100644 --- a/tools/package-version-parser.js +++ b/tools/package-version-parser.js @@ -181,6 +181,7 @@ var prereleaseIdentifierToFraction = function (prerelease) { }; // Takes in two meteor versions. Returns true if the first one is less than the second. +// Versions are strings or PackageVersion objects. PV.lessThan = function (versionOne, versionTwo) { return PV.compare(versionOne, versionTwo) < 0; }; @@ -195,10 +196,16 @@ PV.majorVersion = function (versionString) { }; // Takes in two meteor versions. Returns 0 if equal, 1 if v1 is greater, -1 if -// v2 is greater. +// v2 is greater. Versions are strings or PackageVersion objects. PV.compare = function (versionOne, versionTwo) { - var v1 = PV.parse(versionOne); - var v2 = PV.parse(versionTwo); + var v1 = versionOne; + if (typeof v1 === 'string') { + v1 = PV.parse(v1); + } + var v2 = versionTwo; + if (typeof v2 === 'string') { + v2 = PV.parse(v2); + } // If the semver parts are different, use the semver library to compare, // ignoring wrap numbers. (The semver library will ignore the build ID