Merge pull request #12262 from harryadel/underscore/constraint-solver

[constraint-solver] Remove underscore
This commit is contained in:
Gabriel Grubba
2023-11-15 11:36:37 -03:00
committed by GitHub
13 changed files with 301 additions and 253 deletions

View File

@@ -273,7 +273,7 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
var solution = r.resolve(args.dependencies, args.constraints, { previousSolution: previousSolution }).answer;
// check that root deps are the same
_.each(args.dependencies, function (dep) {
args.dependencies.forEach(function (dep) {
if (previousSolution[dep])
test.equal(solution[dep], previousSolution[dep], dep);
});
@@ -284,17 +284,17 @@ runBenchmarks && Tinytest.add("constraint solver - benchmark on gems - rails, gi
function getCatalogStub (gems) {
return {
getSortedVersionRecords(name) {
var versions = _.chain(gems)
.filter(function (pv) { return pv.name === name; })
.pluck('number')
var versions = Object.values(gems.filter(function (pv) { return pv.name === name; })
.map(function(gem) {
return gem.number
})
.filter(function (v) {
return PackageVersion.getValidServerVersion(v);
})
.sort(PackageVersion.compare)
.uniq(true)
.value();
return _.map(versions, function (version) {
var gem = _.find(gems, function (pv) {
.sort(PackageVersion.compare));
return versions.map(function (version) {
var gem = gems.find(function (pv) {
return pv.name === name && pv.number === version;
});
@@ -304,7 +304,7 @@ function getCatalogStub (gems) {
dependencies: {}
};
_.each(gem.dependencies, function (dep) {
gem.dependencies.forEach(function (dep) {
var name = dep[0];
var constraint = dep[1];
@@ -332,4 +332,4 @@ function getCatalogStub (gems) {
return result;
}
};
}
}

View File

@@ -38,8 +38,8 @@ Tinytest.add("constraint solver - CatalogCache", function (test) {
var pvs = {};
cache.eachPackageVersion(function (pv, deps) {
check(pv, CS.PackageAndVersion);
check(_.values(deps), [CS.Dependency]);
pvs[pv.package+' '+pv.version] = _.keys(deps).sort();
check(Object.values(deps), [CS.Dependency]);
pvs[`${pv.package} ${pv.version}`] = Object.keys(deps).sort();
});
test.equal(pvs, {'foo 1.0.0': ['bar'],
'foo 1.0.1': ['bar', 'bzzz', 'weakly1', 'weakly2']});
@@ -52,9 +52,9 @@ Tinytest.add("constraint solver - CatalogCache", function (test) {
test.equal(oneVersion.length, 1); // don't know which it is
var foos = [];
_.each(cache.getPackageVersions('foo'), function (v) {
cache.getPackageVersions('foo').forEach(function (v) {
var depMap = cache.getDependencyMap('foo', v);
foos.push([v, _.map(depMap, String).sort()]);
foos.push([v, Object.values(depMap).map(String).sort()]);
});
// versions should come out sorted, just like this.
test.equal(foos,
@@ -81,4 +81,4 @@ Tinytest.add("constraint solver - CatalogCache", function (test) {
return true;
});
test.equal(onePackage.length, 1); // don't know which package it is
});
});

View File

@@ -1,3 +1,6 @@
const has = Npm.require('lodash.has');
const memoize = Npm.require('lodash.memoize');
var CS = ConstraintSolver;
var PV = PackageVersion;
@@ -18,7 +21,7 @@ CS.CatalogCache = function () {
};
CS.CatalogCache.prototype.hasPackageVersion = function (pkg, version) {
return _.has(this._dependencies, pvkey(pkg, version));
return has(this._dependencies, pvkey(pkg, version));
};
CS.CatalogCache.prototype.addPackageVersion = function (p, v, deps) {
@@ -28,11 +31,11 @@ CS.CatalogCache.prototype.addPackageVersion = function (p, v, deps) {
check(deps, [CS.Dependency]);
var key = pvkey(p, v);
if (_.has(this._dependencies, key)) {
if (has(this._dependencies, key)) {
throw new Error("Already have an entry for " + key);
}
if (! _.has(this._versions, p)) {
if (!has(this._versions, p)) {
this._versions[p] = [];
}
this._versions[p].push(v);
@@ -40,9 +43,9 @@ CS.CatalogCache.prototype.addPackageVersion = function (p, v, deps) {
var depsByPackage = {};
this._dependencies[key] = depsByPackage;
_.each(deps, function (d) {
deps.forEach(function (d) {
var p2 = d.packageConstraint.package;
if (_.has(depsByPackage, p2)) {
if (has(depsByPackage, p2)) {
throw new Error("Can't have two dependencies on " + p2 +
" in " + key);
}
@@ -55,7 +58,7 @@ CS.CatalogCache.prototype.addPackageVersion = function (p, v, deps) {
// `d.packageConstraint.package`. (Don't mutate the map.)
CS.CatalogCache.prototype.getDependencyMap = function (p, v) {
var key = pvkey(p, v);
if (! _.has(this._dependencies, key)) {
if (!has(this._dependencies, key)) {
throw new Error("No entry for " + key);
}
return this._dependencies[key];
@@ -64,14 +67,14 @@ CS.CatalogCache.prototype.getDependencyMap = function (p, v) {
// Returns an array of version strings, sorted, possibly empty.
// (Don't mutate the result.)
CS.CatalogCache.prototype.getPackageVersions = function (pkg) {
var result = (_.has(this._versions, pkg) ?
var result = (has(this._versions, pkg) ?
this._versions[pkg] : []);
if ((!result.length) || result.sorted) {
return result;
} else {
// sort in place, and record so that we don't sort redundantly
// (we'll sort again if more versions are pushed onto the array)
var pvParse = _.memoize(PV.parse);
var pvParse = memoize(PV.parse);
result.sort(function (a, b) {
return PV.compare(pvParse(a), pvParse(b));
});
@@ -81,16 +84,16 @@ CS.CatalogCache.prototype.getPackageVersions = function (pkg) {
};
CS.CatalogCache.prototype.hasPackage = function (pkg) {
return _.has(this._versions, pkg);
return has(this._versions, pkg);
};
CS.CatalogCache.prototype.toJSONable = function () {
var self = this;
var data = {};
_.each(self._dependencies, function (depsByPackage, key) {
Object.entries(self._dependencies).forEach(function ([key, depsByPackage]) {
// depsByPackage is a map of String -> Dependency.
// Map over the values to get an array of String.
data[key] = _.map(depsByPackage, function (dep) {
data[key] = Object.values(depsByPackage).map(function (dep) {
return dep.toString();
});
});
@@ -101,12 +104,12 @@ CS.CatalogCache.fromJSONable = function (obj) {
check(obj, { data: Object });
var cache = new CS.CatalogCache();
_.each(obj.data, function (depsArray, pv) {
Object.entries(obj.data).forEach(function ([pv, depsArray]) {
check(depsArray, [String]);
pv = CS.PackageAndVersion.fromString(pv);
cache.addPackageVersion(
pv.package, pv.version,
_.map(depsArray, function (str) {
depsArray.map(function (str) {
return CS.Dependency.fromString(str);
}));
});
@@ -118,8 +121,8 @@ CS.CatalogCache.fromJSONable = function (obj) {
// iteration is stopped. There's no particular order to the iteration.
CS.CatalogCache.prototype.eachPackageVersion = function (iter) {
var self = this;
_.find(self._dependencies, function (value, key) {
var stop = iter(CS.PackageAndVersion.fromString(key), value);
Object.keys(self._dependencies).find(function (key) {
var stop = iter(CS.PackageAndVersion.fromString(key), self._dependencies[key]);
return stop;
});
};
@@ -129,8 +132,8 @@ CS.CatalogCache.prototype.eachPackageVersion = function (iter) {
// If `iter` returns true, iteration is stopped.
ConstraintSolver.CatalogCache.prototype.eachPackage = function (iter) {
var self = this;
_.find(_.keys(self._versions), function (key) {
Object.keys(self._versions).find(function (key) {
var stop = iter(key, self.getPackageVersions(key));
return stop;
});
};
};

View File

@@ -1,3 +1,5 @@
const has = Npm.require('lodash.has');
var PV = PackageVersion;
var CS = ConstraintSolver;
@@ -35,10 +37,10 @@ CS.CatalogLoader = function (fromCatalog, toCatalogCache) {
};
var convertDeps = function (catalogDeps) {
return _.map(catalogDeps, function (dep, pkg) {
return Object.entries(catalogDeps).map(function ([pkg, dep], ) {
// The dependency is strong if any of its "references"
// (for different architectures) are strong.
var isStrong = _.any(dep.references, function (ref) {
var isStrong = dep.references.some(function (ref) {
return !ref.weak;
});
@@ -52,7 +54,7 @@ var convertDeps = function (catalogDeps) {
// Since we don't fetch different versions of a package independently
// at the moment, this helper is where we get our data.
CS.CatalogLoader.prototype._getSortedVersionRecords = function (pkg) {
if (! _.has(this._sortedVersionRecordsCache, pkg)) {
if (!has(this._sortedVersionRecordsCache, pkg)) {
this._sortedVersionRecordsCache[pkg] =
this.catalog.getSortedVersionRecords(pkg);
}
@@ -65,8 +67,8 @@ CS.CatalogLoader.prototype.loadSingleVersion = function (pkg, version) {
var cache = self.catalogCache;
if (! cache.hasPackageVersion(pkg, version)) {
var rec;
if (_.has(self._sortedVersionRecordsCache, pkg)) {
rec = _.find(self._sortedVersionRecordsCache[pkg],
if (has(self._sortedVersionRecordsCache, pkg)) {
rec = self._sortedVersionRecordsCache[pkg].find(
function (r) {
return r.version === version;
});
@@ -84,7 +86,7 @@ CS.CatalogLoader.prototype.loadAllVersions = function (pkg) {
var self = this;
var cache = self.catalogCache;
var versionRecs = self._getSortedVersionRecords(pkg);
_.each(versionRecs, function (rec) {
versionRecs.forEach(function (rec) {
var version = rec.version;
if (! cache.hasPackageVersion(pkg, version)) {
var deps = convertDeps(rec.dependencies);
@@ -106,22 +108,22 @@ CS.CatalogLoader.prototype.loadAllVersionsRecursive = function (packageList) {
var packagesEverEnqueued = {};
var enqueue = function (pkg) {
if (! _.has(packagesEverEnqueued, pkg)) {
if (!has(packagesEverEnqueued, pkg)) {
packagesEverEnqueued[pkg] = true;
loadQueue.push(pkg);
}
};
_.each(packageList, enqueue);
packageList.forEach(enqueue);
while (loadQueue.length) {
var pkg = loadQueue.pop();
self.loadAllVersions(pkg);
_.each(self.catalogCache.getPackageVersions(pkg), function (v) {
self.catalogCache.getPackageVersions(pkg).forEach(function (v) {
var depMap = self.catalogCache.getDependencyMap(pkg, v);
_.each(depMap, function (dep, package2) {
Object.entries(depMap).forEach(function ([package2, dep]) {
enqueue(package2);
});
});
}
};
};

View File

@@ -1,3 +1,7 @@
const has = Npm.require('lodash.has');
const isEqual = Npm.require('lodash.isequal');
const isEmpty = Npm.require('lodash.isempty');
var PV = PackageVersion;
var CS = ConstraintSolver;
@@ -18,7 +22,7 @@ CS.Input = function (dependencies, constraints, catalogCache, options) {
// Convert them to our PackageConstraint class if necessary. (This is
// just top-level constraints from .meteor/packages or running from
// checkout, so it's not a lot of data.)
constraints = _.map(constraints, function (c) {
constraints = constraints.map(function (c) {
if (c instanceof PV.PackageConstraint) {
return c;
} else {
@@ -62,27 +66,27 @@ CS.Input = function (dependencies, constraints, catalogCache, options) {
validatePackageName(packageName);
});
self.catalogCache.eachPackageVersion(function (packageName, depsMap) {
_.each(depsMap, function (deps, depPackageName) {
Object.entries(depsMap).forEach(function ([depPackageName, deps]) {
validatePackageName(depPackageName);
});
});
_.each(self.dependencies, validatePackageName);
_.each(self.upgrade, validatePackageName);
_.each(self.constraints, function (c) {
self.dependencies.forEach(validatePackageName);
self.upgrade.forEach(validatePackageName);
self.constraints.forEach(function (c) {
validatePackageName(c.package);
});
if (self.previousSolution) {
_.each(_.keys(self.previousSolution),
Object.keys(self.previousSolution).forEach(
validatePackageName);
}
self._dependencySet = {}; // package name -> true
_.each(self.dependencies, function (d) {
self.dependencies.forEach(function (d) {
self._dependencySet[d] = true;
});
self._upgradeSet = {};
_.each(self.upgrade, function (u) {
self.upgrade.forEach(function (u) {
self._upgradeSet[u] = true;
});
};
@@ -107,33 +111,33 @@ CS.Input.prototype.isKnownPackage = function (p) {
};
CS.Input.prototype.isRootDependency = function (p) {
return _.has(this._dependencySet, p);
return has(this._dependencySet, p);
};
CS.Input.prototype.isUpgrading = function (p) {
return _.has(this._upgradeSet, p);
return has(this._upgradeSet, p);
};
CS.Input.prototype.isInPreviousSolution = function (p) {
return !! (this.previousSolution && _.has(this.previousSolution, p));
return !! (this.previousSolution && has(this.previousSolution, p));
};
function getMentionedPackages(input) {
var packages = {}; // package -> true
_.each(input.dependencies, function (pkg) {
input.dependencies.forEach(function (pkg) {
packages[pkg] = true;
});
_.each(input.constraints, function (constraint) {
input.constraints.forEach(function (constraint) {
packages[constraint.package] = true;
});
if (input.previousSolution) {
_.each(input.previousSolution, function (version, pkg) {
Object.entries(input.previousSolution).forEach(function ([pkg, version]) {
packages[pkg] = true;
});
}
return _.keys(packages);
return Object.keys(packages);
}
CS.Input.prototype.loadFromCatalog = function (catalogLoader) {
@@ -146,7 +150,7 @@ CS.Input.prototype.loadOnlyPreviousSolution = function (catalogLoader) {
// load just the exact versions from the previousSolution
if (self.previousSolution) {
_.each(self.previousSolution, function (version, pkg) {
Object.entries(self.previousSolution).forEach(function ([pkg, version]) {
catalogLoader.loadSingleVersion(pkg, version);
});
}
@@ -170,7 +174,7 @@ CS.Input.prototype.isEqual = function (otherInput) {
// the same input. So by omitting `catalogCache` we no longer need
// to reload the entire relevant part of the catalog from SQLite on
// every rebuild!
return _.isEqual(
return isEqual(
a.toJSONable(true),
b.toJSONable(true)
);
@@ -180,7 +184,7 @@ CS.Input.prototype.toJSONable = function (omitCatalogCache) {
var self = this;
var obj = {
dependencies: self.dependencies,
constraints: _.map(self.constraints, function (c) {
constraints: self.constraints.map(function (c) {
return c.toString();
})
};
@@ -194,7 +198,7 @@ CS.Input.prototype.toJSONable = function (omitCatalogCache) {
if (self.upgrade.length) {
obj.upgrade = self.upgrade;
}
if (! _.isEmpty(self.anticipatedPrereleases)) {
if (!isEmpty(self.anticipatedPrereleases)) {
obj.anticipatedPrereleases = self.anticipatedPrereleases;
}
if (self.previousSolution !== null) {
@@ -225,7 +229,7 @@ CS.Input.fromJSONable = function (obj) {
return new CS.Input(
obj.dependencies,
_.map(obj.constraints, function (cstr) {
obj.constraints.map(function (cstr) {
return PV.parsePackageConstraint(cstr);
}),
CS.CatalogCache.fromJSONable(obj.catalogCache),
@@ -236,4 +240,4 @@ CS.Input.fromJSONable = function (obj) {
allowIncompatibleUpdate: obj.allowIncompatibleUpdate,
upgradeIndirectDepPatchVersions: obj.upgradeIndirectDepPatchVersions
});
};
};

View File

@@ -1,16 +1,20 @@
const has = Npm.require('lodash.has');
const isString = Npm.require('lodash.isstring');
const isEmpty = Npm.require('lodash.isempty');
var PV = PackageVersion;
var CS = ConstraintSolver;
var makeResolver = function (data) {
var Versions = new LocalCollection;
_.each(data, function (versionDescription) {
data.forEach(function (versionDescription) {
var packageName = versionDescription.shift();
var version = versionDescription.shift();
var deps = versionDescription.shift();
var constructedDeps = {};
_.each(deps, function (constraint, name) {
if (!isEmpty(deps)) {
Object.entries(deps).forEach(function ([name, constraint]) {
constructedDeps[name] = {
constraint: constraint,
references: [
@@ -20,6 +24,7 @@ var makeResolver = function (data) {
]
};
});
}
Versions.insert({ packageName: packageName, version: version,
dependencies: constructedDeps });
});
@@ -73,8 +78,7 @@ var defaultResolver = makeResolver([
// in the returned arrays.
splitArgs = function (deps) {
var dependencies = [], constraints = [];
_.each(deps, function (constr, dep) {
Object.entries(deps).forEach(function ([dep, constr]) {
if (constr && constr.charAt(0) === 'w') {
constr = constr.slice(1);
} else {
@@ -89,7 +93,7 @@ splitArgs = function (deps) {
var testWithResolver = function (test, resolver, f) {
var answerToString = function (answer) {
var pvs = _.map(answer, function (v, p) { return p + ' ' + v; });
var pvs = Object.keys(answer).map(function (p) { return p + ' ' + answer[p]; });
return pvs.sort().join('\n');
};
var t = function (deps, expected, options) {
@@ -306,7 +310,7 @@ Tinytest.add("constraint solver - previousSolution", function (test) {
Tinytest.add("constraint solver - no constraint dependency - anything", function (test) {
var versions = defaultResolver.resolve(["sparkle"], []).answer;
test.isTrue(_.isString(versions.sparkle));
test.isTrue(isString(versions.sparkle));
});
@@ -333,9 +337,9 @@ Tinytest.add("constraint solver - input serialization", function (test) {
test.equal(input1.upgradeIndirectDepPatchVersions, false);
var obj1 = input1.toJSONable();
test.isFalse(_.has(obj1, 'upgrade'));
test.isFalse(_.has(obj1, 'anticipatedPrereleases'));
test.isFalse(_.has(obj1, 'previousSolution'));
test.isFalse(has(obj1, 'upgrade'));
test.isFalse(has(obj1, 'anticipatedPrereleases'));
test.isFalse(has(obj1, 'previousSolution'));
var input2 = CS.Input.fromJSONable(obj1);
var obj2 = input2.toJSONable();
@@ -377,4 +381,4 @@ Tinytest.add("constraint solver - non-existent indirect package", function (test
return error.message.match(/unknown package: bar/);
});
});
});
});

View File

@@ -1,3 +1,5 @@
const isEqual = Npm.require('lodash.isequal');
var PV = PackageVersion;
var CS = ConstraintSolver;
@@ -50,13 +52,17 @@ CS.PackagesResolver.prototype.resolve = function (dependencies, constraints,
var input;
Profile.time("new CS.Input", function () {
const { upgrade,
anticipatedPrereleases,
previousSolution,
allowIncompatibleUpdate,
upgradeIndirectDepPatchVersions } = options;
input = new CS.Input(dependencies, constraints, self.catalogCache,
_.pick(options,
'upgrade',
'anticipatedPrereleases',
'previousSolution',
'allowIncompatibleUpdate',
'upgradeIndirectDepPatchVersions'));
{ upgrade,
anticipatedPrereleases,
previousSolution,
allowIncompatibleUpdate,
upgradeIndirectDepPatchVersions });
});
// The constraint solver avoids re-solving everything from scratch on
@@ -78,14 +84,14 @@ CS.PackagesResolver.prototype.resolve = function (dependencies, constraints,
resultCache = null;
} else if (resultCache &&
resultCache.lastInput &&
_.isEqual(resultCache.lastInput,
isEqual(resultCache.lastInput,
input.toJSONable(true))) {
return resultCache.lastOutput;
}
if (options.supportedIsobuildFeaturePackages) {
_.each(options.supportedIsobuildFeaturePackages, function (versions, pkg) {
_.each(versions, function (version) {
Object.entries(options.supportedIsobuildFeaturePackages).forEach(function ([pkg, versions]) {
versions.forEach(function (version) {
input.catalogCache.addPackageVersion(pkg, version, []);
});
});
@@ -100,7 +106,7 @@ CS.PackagesResolver.prototype.resolve = function (dependencies, constraints,
if (options.previousSolution && options.missingPreviousVersionIsError) {
// see comment where missingPreviousVersionIsError is passed in
Profile.time("check for previous versions in catalog", function () {
_.each(options.previousSolution, function (version, pkg) {
Object.entries(options.previousSolution).forEach(function ([pkg, version]) {
if (! input.catalogCache.hasPackageVersion(pkg, version)) {
CS.throwConstraintSolverError(
"Package version not in catalog: " + pkg + " " + version);
@@ -193,7 +199,7 @@ CS.PackagesResolver._resolveWithInput = function (input, options) {
// with an `alternatives` property lifted from one.
// - version: version String
CS.isConstraintSatisfied = function (pkg, vConstraint, version) {
return _.some(vConstraint.alternatives, function (simpleConstraint) {
return vConstraint.alternatives.some(function (simpleConstraint) {
var type = simpleConstraint.type;
if (type === "any-reasonable") {
@@ -263,4 +269,4 @@ CS.DummyProfile = function (bucket, f) {
};
CS.DummyProfile.time = function (bucket, f) {
return f();
};
};

View File

@@ -2,7 +2,7 @@ var PV = PackageVersion;
var CS = ConstraintSolver;
Tinytest.add("constraint solver - datatypes - Dependency", function (test) {
_.each(["foo", "foo@1.0.0"], function (foo) {
["foo", "foo@1.0.0"].forEach(function (foo) {
var d1 = new CS.Dependency(PV.parsePackageConstraint(foo));
test.equal(d1.packageConstraint.toString(), foo);
test.equal(d1.isWeak, false);

View File

@@ -12,7 +12,7 @@ var CS = ConstraintSolver;
// in that order. If that's not true, these tests will break.
var sortKeys = function (obj) {
var result = {};
_.each(_.keys(obj).sort(), function (k) {
Object.keys(obj).sort().forEach(function (k) {
result[k] = obj[k];
});
return result;
@@ -22,7 +22,7 @@ var formatSolution = function (obj) {
// results into tests.
return JSON.stringify({
answer: sortKeys(obj.answer),
allAnswers: obj.allAnswers && _.map(obj.allAnswers, sortKeys),
allAnswers: obj.allAnswers && obj.allAnswers.map(sortKeys),
neededToUseUnanticipatedPrereleases: obj.neededToUseUnanticipatedPrereleases
}, null, 2);
};
@@ -36,7 +36,7 @@ var doTest = function (test, inputJSONable, outputJSONable, options) {
}
if (typeof outputJSONable.neededToUseUnanticipatedPrereleases !== 'boolean') {
outputJSONable = _.extend(outputJSONable, {
outputJSONable = Object.assign(outputJSONable, {
neededToUseUnanticipatedPrereleases: (
!! outputJSONable.neededToUseUnanticipatedPrereleases)
});

View File

@@ -3,10 +3,20 @@ Package.describe({
version: "1.2.0"
});
Npm.depends({
'lodash.has': '4.5.2',
'lodash.memoize': '4.1.2',
'lodash.isequal': '4.5.0',
'lodash.isempty': '4.4.0',
'lodash.zip': '4.2.0',
'lodash.groupby': '4.6.0',
'lodash.isstring': '4.0.1',
'lodash.isobject': '3.0.2'
});
Package.onUse(function (api) {
api.export('ConstraintSolver');
api.use([
'underscore',
'check',
'package-version-parser',
'logic-solver'
@@ -27,7 +37,6 @@ Package.onTest(function (api) {
'tinytest',
'minimongo',
'package-version-parser',
'underscore',
'check'
]);

View File

@@ -1,3 +1,8 @@
const has = Npm.require('lodash.has');
const zip = Npm.require('lodash.zip');
const memoize = Npm.require('lodash.memoize');
const groupBy = Npm.require('lodash.groupby');
var CS = ConstraintSolver;
var PV = PackageVersion;
@@ -16,10 +21,10 @@ CS.Solver = function (input, options) {
self.errors = []; // [String]
self.pricer = new CS.VersionPricer();
self.getConstraintFormula = _.memoize(_getConstraintFormula,
function (p, vConstraint) {
return p + "@" + vConstraint.raw;
});
self.getConstraintFormula = memoize(_getConstraintFormula,
function (p, vConstraint) {
return p + "@" + vConstraint.raw;
});
self.options = options || {};
self.Profile = (self.options.Profile || CS.DummyProfile);
@@ -38,7 +43,7 @@ CS.Solver = function (input, options) {
CS.Solver.prototype.throwAnyErrors = function () {
if (this.errors.length) {
var multiline = _.any(this.errors, function (e) {
var multiline = this.errors.some(function (e) {
return /\n/.test(e);
});
CS.throwConstraintSolverError(this.errors.join(
@@ -48,7 +53,7 @@ CS.Solver.prototype.throwAnyErrors = function () {
CS.Solver.prototype.getVersions = function (pkg) {
var self = this;
if (_.has(self.analysis.allowedVersions, pkg)) {
if (has(self.analysis.allowedVersions, pkg)) {
return self.analysis.allowedVersions[pkg];
} else {
return self.input.catalogCache.getPackageVersions(pkg);
@@ -90,20 +95,20 @@ CS.Solver.prototype.analyze = function () {
// track such packages in packagesWithNoAllowedVersions so that we
// throw a good error later.
Profile.time("analyze allowed versions", function () {
_.each(_.groupBy(input.constraints, 'package'), function (cs, p) {
Object.entries(groupBy(input.constraints, 'package')).forEach(function ([p, cs]) {
var versions = cache.getPackageVersions(p);
if (! versions.length) {
if (!versions.length) {
// deal with wholly unknown packages later
return;
}
_.each(cs, function (constr) {
versions = _.filter(versions, function (v) {
cs.forEach(function (constr) {
versions = versions.filter(function (v) {
return CS.isConstraintSatisfied(p, constr.versionConstraint, v);
});
});
if (! versions.length) {
analysis.packagesWithNoAllowedVersions[p] = _.filter(cs, function (c) {
return !! c.constraintString;
if (!versions.length) {
analysis.packagesWithNoAllowedVersions[p] = cs.filter(function (c) {
return !!c.constraintString;
});
}
analysis.allowedVersions[p] = versions;
@@ -118,11 +123,11 @@ CS.Solver.prototype.analyze = function () {
analysis.previousRootDepVersions = [];
Profile.time("analyze root dependencies", function () {
_.each(input.dependencies, function (p) {
if (! input.isKnownPackage(p)) {
input.dependencies.forEach(function (p) {
if (!input.isKnownPackage(p)) {
analysis.unknownRootDeps.push(p);
} else if (input.isInPreviousSolution(p) &&
! input.isUpgrading(p)) {
!input.isUpgrading(p)) {
analysis.previousRootDepVersions.push(new CS.PackageAndVersion(
p, input.previousSolution[p]));
}
@@ -130,7 +135,7 @@ CS.Solver.prototype.analyze = function () {
// throw if there are unknown packages in root deps
if (analysis.unknownRootDeps.length) {
_.each(analysis.unknownRootDeps, function (p) {
analysis.unknownRootDeps.forEach(function (p) {
if (CS.isIsobuildFeaturePackage(p)) {
self.errors.push(
'unsupported Isobuild feature "' + p +
@@ -171,21 +176,21 @@ CS.Solver.prototype.analyze = function () {
var markReachable = function (p) {
analysis.reachablePackages[p] = true;
_.each(self.getVersions(p), function (v) {
_.each(cache.getDependencyMap(p, v), function (dep) {
self.getVersions(p).forEach(function (v) {
Object.values(cache.getDependencyMap(p, v)).forEach(function (dep) {
// `dep` is a CS.Dependency
var p2 = dep.packageConstraint.package;
if (! input.isKnownPackage(p2)) {
if (!input.isKnownPackage(p2)) {
// record this package so we will generate a variable
// for it. we'll try not to select it, and ultimately
// throw an error if we are forced to.
if (! _.has(analysis.unknownPackages, p2)) {
if (!has(analysis.unknownPackages, p2)) {
analysis.unknownPackages[p2] = [];
}
analysis.unknownPackages[p2].push(pvVar(p, v));
} else {
if (! dep.isWeak) {
if (! _.has(analysis.reachablePackages, p2)) {
if (!dep.isWeak) {
if (!has(analysis.reachablePackages, p2)) {
markReachable(p2);
}
}
@@ -195,7 +200,7 @@ CS.Solver.prototype.analyze = function () {
};
Profile.time("analyze reachability", function () {
_.each(input.dependencies, markReachable);
input.dependencies.forEach(markReachable);
});
////////// ANALYZE CONSTRAINTS
@@ -217,9 +222,9 @@ CS.Solver.prototype.analyze = function () {
// version constraints is a power-tool that should be used sparingly
// by application developers, and never abused by package authors.
var overrides = new Set;
_.each(input.constraints, function (c) {
input.constraints.forEach(function (c) {
if (c.constraintString &&
c.versionConstraint.override) {
c.versionConstraint.override) {
overrides.add(c.package);
}
});
@@ -260,28 +265,28 @@ CS.Solver.prototype.analyze = function () {
}
// top-level constraints
_.each(input.constraints, function (c) {
input.constraints.forEach(function (c) {
if (c.constraintString) {
analysis.constraints.push(new CS.Solver.Constraint(
null, c.package, getVersionConstraint(c),
"constraint#" + analysis.constraints.length));
if (c.versionConstraint.alternatives.length === 1 &&
c.versionConstraint.alternatives[0].type === 'exactly') {
c.versionConstraint.alternatives[0].type === 'exactly') {
analysis.topLevelEqualityConstrainedPackages[c.package] = true;
}
}
});
// constraints specified in package dependencies
_.each(_.keys(analysis.reachablePackages), function (p) {
_.each(self.getVersions(p), function (v) {
Object.keys(analysis.reachablePackages).forEach(function (p) {
self.getVersions(p).forEach(function (v) {
var pv = pvVar(p, v);
_.each(cache.getDependencyMap(p, v), function (dep) {
Object.values(cache.getDependencyMap(p, v)).forEach(function (dep) {
// `dep` is a CS.Dependency
var p2 = dep.packageConstraint.package;
if (input.isKnownPackage(p2) &&
dep.packageConstraint.constraintString) {
dep.packageConstraint.constraintString) {
analysis.constraints.push(new CS.Solver.Constraint(
pv, p2, getVersionConstraint(dep.packageConstraint),
"constraint#" + analysis.constraints.length));
@@ -295,11 +300,11 @@ CS.Solver.prototype.analyze = function () {
Profile.time("analyze pre-releases", function () {
var unanticipatedPrereleases = [];
_.each(_.keys(analysis.reachablePackages), function (p) {
Object.keys(analysis.reachablePackages).forEach(function (p) {
var anticipatedPrereleases = input.anticipatedPrereleases[p];
_.each(self.getVersions(p), function (v) {
if (/-/.test(v) && ! (anticipatedPrereleases &&
_.has(anticipatedPrereleases, v))) {
self.getVersions(p).forEach(function (v) {
if (/-/.test(v) && !(anticipatedPrereleases &&
has(anticipatedPrereleases, v))) {
unanticipatedPrereleases.push(pvVar(p, v));
}
});
@@ -357,7 +362,7 @@ CS.Solver.Step.prototype.addTerm = function (term, weight) {
if (typeof this.weights === 'number') {
if (weight !== this.weights) {
throw new Error("Can't specify a different weight now: " +
weight + " != " + this.weights);
weight + " != " + this.weights);
}
} else {
this.weights.push(weight);
@@ -374,9 +379,9 @@ var DEBUG = false;
CS.Solver.prototype.minimize = function (step, options) {
var self = this;
if (_.isArray(step)) {
if (Array.isArray(step)) {
// minimize([steps...], options)
_.each(step, function (st) {
step.forEach(function (st) {
self.minimize(st, options);
});
return;
@@ -389,7 +394,7 @@ CS.Solver.prototype.minimize = function (step, options) {
var costWeights_ = arguments[2];
var options_ = arguments[3];
if (costWeights_ && typeof costWeights_ === 'object' &&
! _.isArray(costWeights_)) {
!Array.isArray(costWeights_)) {
options_ = costWeights_;
costWeights_ = null;
}
@@ -419,29 +424,29 @@ CS.Solver.prototype.minimize = function (step, options) {
self.setSolution(logic.minimizeWeightedSum(
self.solution, optimized.costTerms, optimized.costWeights, {
progress: function (status, cost) {
if (self.options.nudge) {
self.options.nudge();
progress: function (status, cost) {
if (self.options.nudge) {
self.options.nudge();
}
if (DEBUG) {
if (status === 'improving') {
console.log(cost + " ... trying to improve ...");
} else if (status === 'trying') {
console.log("... trying " + cost + " ... ");
}
if (DEBUG) {
if (status === 'improving') {
console.log(cost + " ... trying to improve ...");
} else if (status === 'trying') {
console.log("... trying " + cost + " ... ");
}
}
},
strategy: (options && options.strategy)
}));
}
},
strategy: (options && options.strategy)
}));
step.optimum = self.solution.getWeightedSum(costTerms, costWeights);
if (DEBUG) {
console.log(step.optimum + " is optimal");
if (step.optimum) {
_.each(costTerms, function (t, i) {
costTerms.forEach(function (t, i) {
var w = (typeof costWeights === 'number' ? costWeights :
costWeights[i]);
costWeights[i]);
if (w && self.solution.evaluate(t)) {
console.log(" " + w + ": " + t);
}
@@ -474,32 +479,38 @@ var groupMutuallyExclusiveTerms = function (costTerms, costWeights) {
// first space. So "foo 1.0.0" becomes "foo " and "foo" stays "foo".
var getTermKey = function (t) {
var firstSpace = t.indexOf(' ');
return firstSpace < 0 ? t : t.slice(0, firstSpace+1);
return firstSpace < 0 ? t : t.slice(0, firstSpace + 1);
};
// costWeights, as usual, may be a number or an array
if (typeof costWeights === 'number') {
return {
costTerms: _.map(_.groupBy(costTerms, getTermKey), function (group) {
costTerms: Object.values(groupBy(costTerms, getTermKey)).map(function (group) {
return Logic.or(group);
}),
costWeights: costWeights
};
} else if (! costTerms.length) {
} else if (!costTerms.length) {
return { costTerms: costTerms, costWeights: costWeights };
} else {
var weightedTerms = _.zip(costWeights, costTerms);
var newWeightedTerms = _.map(_.groupBy(weightedTerms, function (wt) {
var weightedTerms = zip(costWeights, costTerms);
var newWeightedTerms = Object.values(groupBy(weightedTerms, function (wt) {
// construct a string from the weight and term key, for grouping
// purposes. since the weight comes first, there's no ambiguity
// and the separator char could be pretty much anything.
return wt[0] + ' ' + getTermKey(wt[1]);
}), function (wts) {
return [wts[0][0], Logic.or(_.pluck(wts, 1))];
})).map(function (wts) {
return [wts[0][0], Logic.or(wts.map(function(x){
return x[1]
}))];
});
return {
costTerms: _.pluck(newWeightedTerms, 1),
costWeights: _.pluck(newWeightedTerms, 0)
costTerms: newWeightedTerms.map(function(x){
return x[1]
}),
costWeights: newWeightedTerms.map(function(x){
return x[0]
})
};
}
@@ -513,7 +524,7 @@ CS.Solver.prototype.getStepContributions = function (step) {
var solution = self.solution;
var contributions = {};
var weights = step.weights;
_.each(step.terms, function (t, i) {
step.terms.forEach(function (t, i) {
var w = (typeof weights === 'number' ? weights : weights[i]);
if (w && self.solution.evaluate(t)) {
contributions[t] = w;
@@ -523,7 +534,7 @@ CS.Solver.prototype.getStepContributions = function (step) {
};
var addCostsToSteps = function (pkg, versions, costs, steps) {
var pvs = _.map(versions, function (v) {
var pvs = versions.map(function (v) {
return pvVar(pkg, v);
});
for (var j = 0; j < steps.length; j++) {
@@ -546,7 +557,7 @@ var addCostsToSteps = function (pkg, versions, costs, steps) {
// the cost of every version of every package. This function iterates
// over `packages` and puts the result into `Step` objects.
CS.Solver.prototype.getVersionCostSteps = function (stepBaseName, packages,
pricerMode) {
pricerMode) {
var self = this;
var major = new CS.Solver.Step(stepBaseName + '_major');
var minor = new CS.Solver.Step(stepBaseName + '_minor');
@@ -556,7 +567,7 @@ CS.Solver.prototype.getVersionCostSteps = function (stepBaseName, packages,
self.Profile.time(
"calculate " + stepBaseName + " version costs",
function () {
_.each(packages, function (p) {
packages.forEach(function (p) {
var versions = self.getVersions(p);
if (versions.length >= 2) {
var costs = self.pricer.priceVersions(versions, pricerMode);
@@ -574,8 +585,8 @@ CS.Solver.prototype.getVersionCostSteps = function (stepBaseName, packages,
// as `packageAndVersion`. (Actually it's a complicated function of the
// previous and new version.)
CS.Solver.prototype.getVersionDistanceSteps = function (stepBaseName,
packageAndVersions,
takePatches) {
packageAndVersions,
takePatches) {
var self = this;
var incompat = new CS.Solver.Step(stepBaseName + '_incompat');
@@ -587,7 +598,7 @@ CS.Solver.prototype.getVersionDistanceSteps = function (stepBaseName,
self.Profile.time(
"calculate " + stepBaseName + " distance costs",
function () {
_.each(packageAndVersions, function (pvArg) {
packageAndVersions.forEach(function (pvArg) {
var pkg = pvArg.package;
var previousVersion = pvArg.version;
var versions = self.getVersions(pkg);
@@ -595,7 +606,7 @@ CS.Solver.prototype.getVersionDistanceSteps = function (stepBaseName,
var costs = self.pricer.priceVersionsWithPrevious(
versions, previousVersion, takePatches);
addCostsToSteps(pkg, versions, costs,
[incompat, major, minor, patch, rest]);
[incompat, major, minor, patch, rest]);
}
});
});
@@ -606,7 +617,7 @@ CS.Solver.prototype.getVersionDistanceSteps = function (stepBaseName,
CS.Solver.prototype.currentVersionMap = function () {
var self = this;
var pvs = [];
_.each(self.solution.getTrueVars(), function (x) {
self.solution.getTrueVars().forEach(function (x) {
if (x.indexOf(' ') >= 0) {
// all variables with spaces in them are PackageAndVersions
var pv = CS.PackageAndVersion.fromString(x);
@@ -615,11 +626,11 @@ CS.Solver.prototype.currentVersionMap = function () {
});
var versionMap = {};
_.each(pvs, function (pv) {
if (_.has(versionMap, pv.package)) {
pvs.forEach(function (pv) {
if (has(versionMap, pv.package)) {
throw new Error("Assertion failure: Selected two versions of " +
pv.package + ", " +versionMap[pv.package] +
" and " + pv.version);
pv.package + ", " + versionMap[pv.package] +
" and " + pv.version);
}
versionMap[pv.package] = pv.version;
});
@@ -632,7 +643,7 @@ CS.Solver.prototype.currentVersionMap = function () {
CS.Solver.prototype.setSolution = function (solution) {
var self = this;
self.solution = solution;
if (! self.solution) {
if (!self.solution) {
throw new Error("Unexpected unsatisfiability");
}
// When we query a Solution, we always want to treat unknown variables
@@ -671,19 +682,19 @@ CS.Solver.prototype._getAnswer = function (options) {
// require root dependencies
Profile.time("require root dependencies", function () {
_.each(input.dependencies, function (p) {
input.dependencies.forEach(function (p) {
logic.require(p);
});
});
// generate package version variables for known, reachable packages
Profile.time("generate package variables", function () {
_.each(_.keys(analysis.reachablePackages), function (p) {
if (! _.has(analysis.packagesWithNoAllowedVersions, p)) {
var versionVars = _.map(self.getVersions(p),
function (v) {
return pvVar(p, v);
});
Object.keys(analysis.reachablePackages).forEach(function (p) {
if (!has(analysis.packagesWithNoAllowedVersions, p)) {
var versionVars = self.getVersions(p).map(
function (v) {
return pvVar(p, v);
});
// At most one of ["foo 1.0.0", "foo 1.0.1", ...] is true.
logic.require(Logic.atMostOne(versionVars));
// The variable "foo" is true if and only if at least one of the
@@ -695,11 +706,11 @@ CS.Solver.prototype._getAnswer = function (options) {
// generate strong dependency requirements
Profile.time("generate dependency requirements", function () {
_.each(_.keys(analysis.reachablePackages), function (p) {
_.each(self.getVersions(p), function (v) {
_.each(cache.getDependencyMap(p, v), function (dep) {
Object.keys(analysis.reachablePackages).forEach(function (p) {
self.getVersions(p).forEach(function (v) {
Object.values(cache.getDependencyMap(p, v)).forEach(function (dep) {
// `dep` is a CS.Dependency
if (! dep.isWeak) {
if (!dep.isWeak) {
var p2 = dep.packageConstraint.package;
logic.require(Logic.implies(pvVar(p, v), p2));
}
@@ -711,7 +722,7 @@ CS.Solver.prototype._getAnswer = function (options) {
// generate constraints -- but technically don't enforce them, because
// we haven't forced the conflictVars to be false
Profile.time("generate constraints", function () {
_.each(analysis.constraints, function (c) {
analysis.constraints.forEach(function (c) {
// We logically require that EITHER a constraint is marked as a
// conflict OR it comes from a package version that is not selected
// OR its constraint formula must be true.
@@ -719,8 +730,8 @@ CS.Solver.prototype._getAnswer = function (options) {
// then a version of it that satisfies our constraint must be true.)
logic.require(
Logic.or(c.conflictVar,
c.fromVar ? Logic.not(c.fromVar) : [],
self.getConstraintFormula(c.toPackage, c.vConstraint)));
c.fromVar ? Logic.not(c.fromVar) : [],
self.getConstraintFormula(c.toPackage, c.vConstraint)));
});
});
@@ -754,7 +765,7 @@ CS.Solver.prototype._getAnswer = function (options) {
// initial solution before asking the solver if it's possible to
// not use these packages.
Profile.time("forbid packages with no matching versions", function () {
_.each(analysis.packagesWithNoAllowedVersions, function (constrs, p) {
Object.entries(analysis.packagesWithNoAllowedVersions).forEach(function ([p, constrs]) {
var newSolution = logic.solveAssuming(Logic.not(p));
if (newSolution) {
self.setSolution(newSolution);
@@ -762,9 +773,9 @@ CS.Solver.prototype._getAnswer = function (options) {
} else {
var error =
'No version of ' + p + ' satisfies all constraints: ' +
_.map(constrs, function (constr) {
return '@' + constr.constraintString;
}).join(', ');
constrs.map(function (constr) {
return '@' + constr.constraintString;
}).join(', ');
error += '\n' + self.listConstraintsOnPackage(p);
self.errors.push(error);
}
@@ -776,7 +787,7 @@ CS.Solver.prototype._getAnswer = function (options) {
// than 0, we'll throw an error later, after we apply the constraints
// and the cost function, so that we can explain the problem to the
// user in a convincing way.
self.minimize('unknown_packages', _.keys(analysis.unknownPackages));
self.minimize('unknown_packages', Object.keys(analysis.unknownPackages));
// try not to set the conflictVar on any constraint. If the minimum
// is greater than 0, we'll throw an error later, after we've run the
@@ -784,12 +795,14 @@ CS.Solver.prototype._getAnswer = function (options) {
// If there are conflicts, this minimization can be time-consuming
// (several seconds or more). The strategy 'bottom-up' helps by
// looking for solutions with few conflicts first.
self.minimize('conflicts', _.pluck(analysis.constraints, 'conflictVar'),
{ strategy: 'bottom-up' });
self.minimize('conflicts', analysis.constraints.map(function (constraint) {
return constraint.conflictVar
}),
{ strategy: 'bottom-up' });
// Try not to use "unanticipated" prerelease versions
self.minimize('unanticipated_prereleases',
analysis.unanticipatedPrereleases);
analysis.unanticipatedPrereleases);
var previousRootSteps = self.getVersionDistanceSteps(
'previous_root', analysis.previousRootDepVersions);
@@ -798,25 +811,25 @@ CS.Solver.prototype._getAnswer = function (options) {
// the "previous_root_major", "previous_root_minor", etc. steps
var previousRootVersionParts = previousRootSteps.slice(1);
var toUpdate = _.filter(input.upgrade, function (p) {
var toUpdate = input.upgrade.filter(function (p) {
return analysis.reachablePackages[p] === true;
});
// make sure packages that are being updated can still count as
// a previous_root for the purposes of previous_root_incompat
Profile.time("add terms to previous_root_incompat", function () {
_.each(toUpdate, function (p) {
toUpdate.forEach(function (p) {
if (input.isRootDependency(p) && input.isInPreviousSolution(p)) {
var parts = self.pricer.partitionVersions(
self.getVersions(p), input.previousSolution[p]);
_.each(parts.older.concat(parts.higherMajor), function (v) {
parts.older.concat(parts.higherMajor).forEach(function (v) {
previousRootIncompat.addTerm(pvVar(p, v), 1);
});
}
});
});
if (! input.allowIncompatibleUpdate) {
if (!input.allowIncompatibleUpdate) {
// Enforce that we don't make breaking changes to your root dependencies,
// unless you pass --allow-incompatible-update. It will actually be enforced
// farther down, but for now, we want to apply this constraint before handling
@@ -836,20 +849,20 @@ CS.Solver.prototype._getAnswer = function (options) {
self.minimize(previousRootVersionParts);
var otherPrevious = _.filter(_.map(input.previousSolution, function (v, p) {
var otherPrevious = Object.entries(input.previousSolution || []).map(function ([p, v]) {
return new CS.PackageAndVersion(p, v);
}), function (pv) {
}).filter(function (pv) {
var p = pv.package;
return analysis.reachablePackages[p] === true &&
! input.isRootDependency(p);
!input.isRootDependency(p);
});
self.minimize(self.getVersionDistanceSteps(
'previous_indirect', otherPrevious,
input.upgradeIndirectDepPatchVersions));
var newRootDeps = _.filter(input.dependencies, function (p) {
return ! input.isInPreviousSolution(p);
var newRootDeps = input.dependencies.filter(function (p) {
return !input.isInPreviousSolution(p);
});
self.minimize(self.getVersionCostSteps(
@@ -876,10 +889,10 @@ CS.Solver.prototype._getAnswer = function (options) {
// signal. In other words, the user might be better off with some tie-breaker
// that looks only at the important packages anyway.
Profile.time("lock down important versions", function () {
_.each(self.currentVersionMap(), function (v, pkg) {
Object.entries(self.currentVersionMap()).forEach(function ([pkg, v]) {
if (input.isRootDependency(pkg) ||
input.isInPreviousSolution(pkg) ||
input.isUpgrading(pkg)) {
input.isInPreviousSolution(pkg) ||
input.isUpgrading(pkg)) {
logic.require(Logic.implies(pkg, pvVar(pkg, v)));
}
});
@@ -887,10 +900,10 @@ CS.Solver.prototype._getAnswer = function (options) {
// new, indirect packages are the lowest priority
var otherPackages = [];
_.each(_.keys(analysis.reachablePackages), function (p) {
if (! (input.isRootDependency(p) ||
input.isInPreviousSolution(p) ||
input.isUpgrading(p))) {
Object.keys(analysis.reachablePackages).forEach(function (p) {
if (!(input.isRootDependency(p) ||
input.isInPreviousSolution(p) ||
input.isUpgrading(p))) {
otherPackages.push(p);
}
});
@@ -899,17 +912,17 @@ CS.Solver.prototype._getAnswer = function (options) {
'new_indirect', otherPackages,
CS.VersionPricer.MODE_GRAVITY_WITH_PATCHES));
self.minimize('total_packages', _.keys(analysis.reachablePackages));
self.minimize('total_packages', Object.keys(analysis.reachablePackages));
// throw errors about unknown packages
if (self.stepsByName['unknown_packages'].optimum > 0) {
Profile.time("generate error for unknown packages", function () {
var unknownPackages = _.keys(analysis.unknownPackages);
var unknownPackagesNeeded = _.filter(unknownPackages, function (p) {
var unknownPackages = Object.keys(analysis.unknownPackages);
var unknownPackagesNeeded = unknownPackages.filter(function (p) {
return self.solution.evaluate(p);
});
_.each(unknownPackagesNeeded, function (p) {
var requirers = _.filter(analysis.unknownPackages[p], function (pv) {
unknownPackagesNeeded.forEach(function (p) {
var requirers = analysis.unknownPackages[p].filter(function (pv) {
return self.solution.evaluate(pv);
});
var errorStr;
@@ -920,7 +933,7 @@ CS.Solver.prototype._getAnswer = function (options) {
} else {
errorStr = 'unknown package: ' + p;
}
_.each(requirers, function (pv) {
requirers.forEach(function (pv) {
errorStr += '\nRequired by: ' + pv;
});
self.errors.push(errorStr);
@@ -934,36 +947,36 @@ CS.Solver.prototype._getAnswer = function (options) {
self.throwConflicts();
}
if ((! input.allowIncompatibleUpdate) &&
self.stepsByName['previous_root_incompat'].optimum > 0) {
if ((!input.allowIncompatibleUpdate) &&
self.stepsByName['previous_root_incompat'].optimum > 0) {
// we have some "incompatible root changes", where we needed to change a
// version of a root dependency to a new version incompatible with the
// original, but --allow-incompatible-update hasn't been passed in.
// these are in the form of PackageAndVersion strings that we need.
var incompatRootChanges = _.keys(self.getStepContributions(
var incompatRootChanges = Object.keys(self.getStepContributions(
self.stepsByName['previous_root_incompat']));
Profile.time("generate errors for incompatible root change", function () {
var numActualErrors = 0;
_.each(incompatRootChanges, function (pvStr) {
incompatRootChanges.forEach(function (pvStr) {
var pv = CS.PackageAndVersion.fromString(pvStr);
// exclude packages with top-level equality constraints (added by user
// or by the tool pinning a version)
if (! _.has(analysis.topLevelEqualityConstrainedPackages, pv.package)) {
if (!has(analysis.topLevelEqualityConstrainedPackages, pv.package)) {
var prevVersion = input.previousSolution[pv.package];
self.errors.push(
'Potentially incompatible change required to ' +
'top-level dependency: ' +
pvStr + ', was ' + prevVersion + '.\n' +
self.listConstraintsOnPackage(pv.package));
'top-level dependency: ' +
pvStr + ', was ' + prevVersion + '.\n' +
self.listConstraintsOnPackage(pv.package));
numActualErrors++;
}
});
if (numActualErrors) {
self.errors.push(
'To allow potentially incompatible changes to top-level ' +
'dependencies, you must pass --allow-incompatible-update ' +
'on the command line.');
'dependencies, you must pass --allow-incompatible-update ' +
'on the command line.');
}
});
self.throwAnyErrors();
@@ -1001,13 +1014,13 @@ CS.Solver.prototype._getAnswer = function (options) {
// Get a list of package-version variables that satisfy a given constraint.
var getOkVersions = function (toPackage, vConstraint, targetVersions) {
return _.compact(_.map(targetVersions, function (v) {
return (targetVersions.map(function (v) {
if (CS.isConstraintSatisfied(toPackage, vConstraint, v)) {
return pvVar(toPackage, v);
} else {
return null;
}
}));
})).filter(Boolean);
};
// The CS.Solver constructor turns this into a memoized method.
@@ -1031,7 +1044,7 @@ CS.Solver.prototype.listConstraintsOnPackage = function (pkg) {
var result = 'Constraints on package "' + pkg + '":';
_.each(constraints, function (c) {
constraints.forEach(function (c) {
if (c.toPackage === pkg) {
var paths;
if (c.fromVar) {
@@ -1040,7 +1053,7 @@ CS.Solver.prototype.listConstraintsOnPackage = function (pkg) {
} else {
paths = [['top level']];
}
_.each(paths, function (path) {
paths.forEach(function (path) {
result += '\n* ' + (new PV.PackageConstraint(
pkg, c.vConstraint.raw)) + ' <- ' + path.join(' <- ');
});
@@ -1057,15 +1070,15 @@ CS.Solver.prototype.throwConflicts = function () {
var constraints = self.analysis.constraints;
self.Profile.time("generate error about conflicts", function () {
_.each(constraints, function (c) {
constraints.forEach(function (c) {
// c is a CS.Solver.Constraint
if (solution.evaluate(c.conflictVar)) {
// skipped this constraint
var possibleVersions = self.getVersions(c.toPackage);
var chosenVersion = _.find(possibleVersions, function (v) {
var chosenVersion = possibleVersions.find(function (v) {
return solution.evaluate(pvVar(c.toPackage, v));
});
if (! chosenVersion) {
if (!chosenVersion) {
// this can't happen, because for a constraint to be a problem,
// we must have chosen some version of the package it applies to!
throw new Error("Internal error: Version not found");
@@ -1073,7 +1086,7 @@ CS.Solver.prototype.throwConflicts = function () {
var error = (
'Conflict: Constraint ' + (new PV.PackageConstraint(
c.toPackage, c.vConstraint)) +
' is not satisfied by ' + c.toPackage + ' ' + chosenVersion + '.');
' is not satisfied by ' + c.toPackage + ' ' + chosenVersion + '.');
error += '\n' + self.listConstraintsOnPackage(c.toPackage);
@@ -1112,12 +1125,12 @@ CS.Solver.prototype.getPathsToPackageVersion = function (packageAndVersion) {
var versionMap = self.currentVersionMap();
var hasDep = function (p1, p2) {
// Include weak dependencies, because their constraints matter.
return _.has(cache.getDependencyMap(p1, versionMap[p1]), p2);
return has(cache.getDependencyMap(p1, versionMap[p1]), p2);
};
var allPackages = _.keys(versionMap);
var allPackages = Object.keys(versionMap);
var getPaths = function (pv, _ignorePackageSet) {
if (! solution.evaluate(pv.toString())) {
if (!solution.evaluate(pv.toString())) {
return [];
}
var pkg = pv.package;
@@ -1126,20 +1139,20 @@ CS.Solver.prototype.getPathsToPackageVersion = function (packageAndVersion) {
return [[pv]];
}
var newIgnorePackageSet = _.clone(_ignorePackageSet);
var newIgnorePackageSet = Object.assign({}, _ignorePackageSet);
newIgnorePackageSet[pkg] = true;
var paths = [];
var shortestLength = null;
_.each(allPackages, function (p) {
if ((! _.has(newIgnorePackageSet, p)) &&
solution.evaluate(p) &&
hasDep(p, pkg)) {
allPackages.forEach(function (p) {
if ((!has(newIgnorePackageSet, p)) &&
solution.evaluate(p) &&
hasDep(p, pkg)) {
var newPV = new CS.PackageAndVersion(p, versionMap[p]);
_.each(getPaths(newPV, newIgnorePackageSet), function (path) {
getPaths(newPV, newIgnorePackageSet).forEach(function (path) {
var newPath = [pv].concat(path);
if ((! paths.length) || newPath.length < shortestLength) {
if ((!paths.length) || newPath.length < shortestLength) {
paths.push(newPath);
shortestLength = newPath.length;
}
@@ -1166,4 +1179,4 @@ CS.Solver.Constraint = function (fromVar, toPackage, vConstraint, conflictVar) {
check(this.toPackage, String); // package name
check(this.vConstraint, PV.VersionConstraint);
check(this.conflictVar, String);
};
};

View File

@@ -1,3 +1,6 @@
const zip = Npm.require('lodash.zip');
const isObject = Npm.require('lodash.isobject');
var CS = ConstraintSolver;
var PV = PackageVersion;
@@ -7,24 +10,26 @@ Tinytest.add("constraint solver - version pricer", function (test) {
var pricer = new CS.VersionPricer();
var testScanVersions = function (versions, mode, options, expected) {
if (options && _.isArray(options)) {
if (options && Array.isArray(options)) {
expected = options;
options = null;
}
var result, tuples;
// Accepts either a mode like CS.VersionPricer.MODE_UPDATE or
// an object that looks like `{ previous: version }`
if (_.isObject(mode) && mode.previous) {
if (isObject(mode) && mode.previous) {
result = pricer.priceVersionsWithPrevious(versions, mode.previous);
tuples = _.zip(versions, result[0], result[1], result[2], result[3],
tuples = zip(versions, result[0], result[1], result[2], result[3],
result[4]);
} else {
result = pricer.priceVersions(versions, mode, options);
tuples = _.zip(versions, result[0], result[1], result[2], result[3]);
tuples = zip(versions, result[0], result[1], result[2], result[3]);
}
test.equal(tuples.length, expected.length);
test.equal(_.pluck(tuples, 0), versions);
_.each(_.zip(tuples, expected), function (x) {
test.equal(tuples.map(function(x){
return x[0]
}), versions);
zip(tuples, expected).forEach(function (x) {
var tuple = x[0];
var expectedTuple = x[1];
if (typeof expectedTuple[0] !== 'string') {
@@ -200,4 +205,4 @@ Tinytest.add("constraint solver - version pricer", function (test) {
["4.0.0", 1, 3, 0, 0, 1],
["4.0.0_1", 1, 3, 0, 0, 0]]);
});
});

View File

@@ -1,3 +1,5 @@
const memoize = Npm.require('lodash.memoize');
var CS = ConstraintSolver;
var PV = PackageVersion;
@@ -9,7 +11,7 @@ CS.VersionPricer = function () {
//
// The VersionPricer instance stores a memoization table for
// efficiency.
self.getVersionInfo = _.memoize(PV.parse);
self.getVersionInfo = memoize(PV.parse);
};
CS.VersionPricer.MODE_UPDATE = 1;