Merge branch 'constraint-changes-from-isopack-cache' into devel

This commit is contained in:
David Glasser
2014-11-22 14:21:06 -08:00
7 changed files with 83 additions and 183 deletions

View File

@@ -156,7 +156,7 @@ Tinytest.add("constraint solver - no results", function (test) {
&& error.message.match(/bad-2@1\.0\.0/)
// We shouldn't get shown indirect itself in a pathway: that would just
// be an artifact of there being a path that passes through another
// unibuild. (Note: we might change our mind and decide that all these
// package. (Note: we might change our mind and decide that all these
// lines should end in the relevant constraint, which would probably be
// nice! But in that case, we should test that no line ends with TWO
// mentions of indirect.)

View File

@@ -1,14 +1,3 @@
// Copied from archinfo.matches() in tools/
//
// archMatches("os", "os") => true
// archMatches("web.cordova", "web") => true
// archMatches("web.cordova", "web.cordova") => true
var archMatches = function (arch, baseArch) {
return arch.substr(0, baseArch.length) === baseArch &&
(arch.length === baseArch.length ||
arch.substr(baseArch.length, 1) === ".");
};
ConstraintSolver = {};
// `catalog` has the following methods:
@@ -71,67 +60,40 @@ 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"];
// We rely on sortedness in the constraint solver, since one of the cost
// functions wants to be able to quickly find the earliest or latest version.
var sortedVersions = self.catalog.getSortedVersions(packageName);
_.each(sortedVersions, function (version) {
var versionDef = self.catalog.getVersion(packageName, version);
var unibuilds = {};
_.each(allArchs, function (arch) {
var unitName = packageName + "#" + arch;
unibuilds[unitName] = new ConstraintSolver.UnitVersion(unitName, version);
self.resolver.addUnitVersion(unibuilds[unitName]);
});
var unitVersion = new ConstraintSolver.UnitVersion(packageName, version);
self.resolver.addUnitVersion(unitVersion);
_.each(versionDef.dependencies, function (dep, depName) {
self._ensurePackageInfoLoaded(depName);
_.each(dep.references, function (ref) {
_.each(allArchs, function (arch) {
if (archMatches(arch, ref.arch)) {
var unitName = packageName + "#" + arch;
var unitVersion = unibuilds[unitName];
// "dep" contains a list of references, which describes which unibuilds of
// this unitVersion depend on depName, as well as a constraint, which
// constraints the versions it depends on.
if (! unitVersion)
throw new Error("A non-standard arch " + arch + " for package " + packageName);
var targetUnitName = depName + "#" + arch;
// Add the dependency if needed
if (! ref.weak)
unitVersion.addDependency(targetUnitName);
// Add a constraint if such exists
if (dep.constraint && dep.constraint !== "none") {
var constraint =
self.resolver.getConstraint(targetUnitName, dep.constraint);
unitVersion.addConstraint(constraint);
}
}
});
// The package->package dependency is weak if ALL of the underlying
// unibuild->unibuild dependencies are weak. ie,
// api.use('dep', 'server', { weak: true });
// api.use('dep', 'client');
// is not weak at the package->package level.
var createsDependency = _.any(dep.references, function (ref) {
return !ref.weak;
});
});
// Every unibuild implies that if it is picked, other unibuilds are
// constrained to the same version.
_.each(unibuilds, function (unibuild, unibuildName) {
_.each(unibuilds, function (other, otherUnibuildName) {
if (unibuild === other)
return;
// Add the dependency if needed.
if (createsDependency)
unitVersion.addDependency(depName);
// Constraint is the exact same version of a unibuild
var constraintStr = "=" + version;
var constraint =
self.resolver.getConstraint(otherUnibuildName, constraintStr);
unibuild.addConstraint(constraint);
});
// Add a constraint if needed.
if (dep.constraint && dep.constraint !== "none") {
var constraint = self.resolver.getConstraint(depName, dep.constraint);
unitVersion.addConstraint(constraint);
}
});
});
};
@@ -182,33 +144,26 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
self._ensurePackageInfoLoaded(packageName);
});
// XXX glasser and ekate added this filter to strip some undefineds that
// were causing crashes, but maybe the real answer is that there shouldn't
// have been undefineds?
if (options.previousSolution) {
options.previousSolution =
_.filter(_.flatten(
_.map(options.previousSolution, function (version, packageName) {
return _.map(self._unibuildsForPackage(packageName), function (unitName) {
return self.resolver._unitsVersionsMap[unitName + "@" + version];
});
})), _.identity);
// Replace previousSolution map with a list of the UnitVersions that we know
// about that were mentioned. (_.compact drops unknown UnitVersions.)
options.previousSolution = _.compact(
_.map(options.previousSolution, function (version, packageName) {
return self.resolver.getUnitVersion(packageName, version);
}));
}
// split every package name to one or more archs belonging to that package
// (["foobar"] => ["foobar#os", "foobar#web.browser", ...])
// XXX for now just hardcode in all of the known architectures
var upgradeUnibuilds = {};
// Convert options.upgrade to a map for O(1) access.
// XXX we should probably just change the API so it's passed in this way
var upgradePackages = {};
_.each(options.upgrade, function (packageName) {
_.each(self._unibuildsForPackage(packageName), function (unibuildName) {
upgradeUnibuilds[unibuildName] = true;
});
upgradePackages[packageName] = true;
});
options.upgrade = upgradeUnibuilds;
options.upgrade = upgradePackages;
var dc = self._splitDepsToConstraints(dependencies, constraints);
constraints = self._makeConstraintObjects(constraints);
options.rootDependencies = dc.dependencies;
options.rootDependencies = dependencies;
var resolverOptions = self._getResolverOptions(options);
var res = null;
// If a previous solution existed, try resolving with additional (weak)
@@ -226,7 +181,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
// guide which things are encouraged to be upgraded vs stay the same in the
// heuristic.)
if (!_.isEmpty(options.previousSolution) && _.isEmpty(options.upgrade)) {
var constraintsWithPreviousSolutionLock = _.clone(dc.constraints);
var constraintsWithPreviousSolutionLock = _.clone(constraints);
_.each(options.previousSolution, function (uv) {
constraintsWithPreviousSolutionLock.push(
self.resolver.getConstraint(uv.name, '=' + uv.version));
@@ -235,7 +190,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
// Try running the resolver. If it fails to resolve, that's OK, we'll keep
// working.
res = self.resolver.resolve(
dc.dependencies, constraintsWithPreviousSolutionLock, resolverOptions);
dependencies, constraintsWithPreviousSolutionLock, resolverOptions);
} catch (e) {
if (!(e.constraintSolverError))
throw e;
@@ -246,8 +201,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
// without locking in the previous solution as strict equality.
if (!res) {
try {
res = self.resolver.resolve(
dc.dependencies, dc.constraints, resolverOptions);
res = self.resolver.resolve(dependencies, constraints, resolverOptions);
} catch (e) {
if (!(e.constraintSolverError))
throw e;
@@ -259,8 +213,7 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
// constraints?
if (!res) {
resolverOptions["useRCs"] = true;
res = self.resolver.resolve(
dc.dependencies, dc.constraints, resolverOptions);
res = self.resolver.resolve(dependencies, constraints, resolverOptions);
}
var ret = { answer: resolverResultToPackageMap(res) };
if (resolverOptions.useRCs)
@@ -268,84 +221,24 @@ ConstraintSolver.PackagesResolver.prototype.resolve = function (
return ret;
};
var removeUnibuild = function (unitName) {
return unitName.split('#')[0];
};
var resolverResultToPackageMap = function (choices) {
var packageMap = {};
mori.each(choices, function (nameAndUv) {
var name = mori.first(nameAndUv);
var uv = mori.last(nameAndUv);
// Since we don't yet define the interface for a an app to depend only on
// certain unibuilds of the packages (like only web unibuilds) and we know
// that each unibuild weakly depends on other sibling unibuilds of the same
// version, we can safely output the whole package for each unibuild in the
// result.
packageMap[removeUnibuild(name)] = uv.version;
packageMap[name] = uv.version;
});
return packageMap;
};
// takes dependencies and constraints and rewrites the names from "foo" to
// "foo#os" and "foo#web.browser" and "foo#web.cordova"
// XXX right now creates a dependency for every unibuild it can find
ConstraintSolver.PackagesResolver.prototype._splitDepsToConstraints =
function (inputDeps, inputConstraints) {
ConstraintSolver.PackagesResolver.prototype._makeConstraintObjects = function (
inputConstraints) {
var self = this;
var dependencies = [];
var constraints = [];
_.each(inputDeps, function (packageName) {
_.each(self._unibuildsForPackage(packageName), function (unibuildName) {
dependencies.push(unibuildName);
});
return _.map(inputConstraints, function (constraint) {
return self.resolver.getConstraint(
constraint.name, constraint.constraintString);
});
_.each(inputConstraints, function (constraint) {
_.each(self._unibuildsForPackage(constraint.name), function (unibuildName) {
//XXX: This is kind of dumb -- we make this up, so we can reparse it
//later. Todo: clean this up a bit.
if (!constraint.constraintString) {
var constraintArray = [];
_.each(constraint.constraints, function (c) {
if (c.type == "exact") {
constraintArray.push("+" + c.version);
} else if (c.version) {
constraintArray.push(c.version)
}
});
if (!_.isEmpty(constraintArray)) {
constraint.constraintString =
_.reduce(constraintArray,
function(x, y) {
return x + " || " + y;
});
} else {
constraint.constraintString = "";
}
}
constraints.push(
self.resolver.getConstraint(unibuildName, constraint.constraintString));
});
});
return { dependencies: dependencies, constraints: constraints };
};
ConstraintSolver.PackagesResolver.prototype._unibuildsForPackage =
function (packageName) {
var self = this;
var unibuildPrefix = packageName + "#";
var unibuilds = [];
// XXX hardcode all common architectures assuming that every package has the
// same set of architectures.
_.each(["os", "web.browser", "web.cordova"], function (arch) {
unibuilds.push(unibuildPrefix + arch);
});
return unibuilds;
};
ConstraintSolver.PackagesResolver.prototype._getResolverOptions =

View File

@@ -117,14 +117,13 @@ ConstraintSolver.ConstraintsList.prototype.isSatisfied = function (
return satisfied;
};
ConstraintSolver.ConstraintsList.prototype.toString = function (options) {
ConstraintSolver.ConstraintsList.prototype.toString = function () {
var self = this;
options = options || {};
var strs = [];
self.each(function (c) {
strs.push(c.toString({removeUnibuild: options.removeUnibuild}));
strs.push(c.toString());
});
strs = _.uniq(strs);

View File

@@ -42,9 +42,9 @@ _.extend(ResolverState.prototype, {
self.error = util.format(
"conflict: constraint %s is not satisfied by %s.\n" +
"Constraints on %s come from:\n%s",
constraint.toString({removeUnibuild: true}),
constraint.toString(),
chosen.version,
removeUnibuild(constraint.name),
constraint.name,
self._shownPathwaysForConstraintsIndented(constraint.name));
return self;
}
@@ -60,7 +60,7 @@ _.extend(ResolverState.prototype, {
self.error = util.format(
"conflict: constraints on %s cannot all be satisfied.\n" +
"Constraints come from:\n%s",
removeUnibuild(constraint.name),
constraint.name,
self._shownPathwaysForConstraintsIndented(constraint.name));
} else if (mori.count(newAlternatives) === 1) {
// There's only one choice, so we can immediately choose it.
@@ -83,7 +83,7 @@ _.extend(ResolverState.prototype, {
self = self._clone();
if (!_.has(self._resolver.unitsVersions, unitName)) {
self.error = "unknown package: " + removeUnibuild(unitName);
self.error = "unknown package: " + unitName;
return self;
}
@@ -99,7 +99,7 @@ _.extend(ResolverState.prototype, {
self.error = util.format(
"conflict: constraints on %s cannot be satisfied.\n" +
"Constraints come from:\n%s",
removeUnibuild(unitName),
unitName,
self._shownPathwaysForConstraintsIndented(unitName));
return self;
} else if (mori.count(alternatives) === 1) {
@@ -211,15 +211,6 @@ var filter = function (v, pred) {
return mori.into(mori.vector(), mori.filter(pred, v));
};
// Users are mostly confused by seeing "package#web.browser" instead of just
// "package". Remove it for error messages.
removeUnibuild = function (unitName) {
// For debugging constraint solver issues.
if (process.env.METEOR_SHOW_UNIBUILDS)
return unitName;
return unitName.split('#')[0];
};
// XXX from Underscore.String (http://epeli.github.com/underscore.string/)
// XXX how many copies of this do we have in Meteor?
var startsWith = function(str, starts) {
@@ -229,18 +220,13 @@ var startsWith = function(str, starts) {
var showPathway = function (pathway, dropIfFinal) {
var pathUnits = mori.into_array(mori.map(function (uv) {
return uv.toString({removeUnibuild: true});
return uv.toString();
}, mori.reverse(pathway)));
var dropPrefix = removeUnibuild(dropIfFinal) + '@';
var dropPrefix = dropIfFinal + '@';
while (pathUnits.length && startsWith(_.last(pathUnits), dropPrefix)) {
pathUnits.pop();
}
// This is a bit of a hack: we're using _.uniq in "it's sorted" mode, whose
// implementation is "drop adjacent duplicates". This is what we want (we're
// trying to avoid seeing "foo -> foo" which represents "foo#os ->
// foo#web.browser") even though it's not actually sorted.
pathUnits = _.uniq(pathUnits, true);
return pathUnits.join(' -> ');
};

View File

@@ -270,6 +270,8 @@ ConstraintSolver.UnitVersion = function (name, unitVersion) {
self.name = name;
// Things with different build IDs should represent the same code, so ignore
// them. (Notably: depending on @=1.3.1 should allow 1.3.1+local!)
// XXX we no longer automatically add build IDs to things as part of our build
// process, but this still reflects semver semantics.
self.version = PackageVersion.removeBuildID(unitVersion);
self.dependencies = [];
self.constraints = new ConstraintSolver.ConstraintsList();
@@ -300,11 +302,9 @@ _.extend(ConstraintSolver.UnitVersion.prototype, {
self.constraints = self.constraints.push(constraint);
},
toString: function (options) {
toString: function () {
var self = this;
options = options || {};
var name = options.removeUnibuild ? removeUnibuild(self.name) : self.name;
return name + "@" + self.version;
return self.name + "@" + self.version;
}
});
@@ -332,9 +332,7 @@ ConstraintSolver.Constraint = function (name, versionString) {
ConstraintSolver.Constraint.prototype.toString = function (options) {
var self = this;
options = options || {};
var name = options.removeUnibuild ? removeUnibuild(self.name) : self.name;
return name + "@" + self.constraintString;
return self.name + "@" + self.constraintString;
};

View File

@@ -1936,6 +1936,30 @@ _.extend(PackageSource.prototype, {
_.each(arch.implies, _.partial(processUse, true));
});
_.each(self.pluginInfo, function (info) {
_.each(info.use, function (spec) {
var parsedSpec = utils.splitConstraint(spec);
if (!_.has(dependencies, parsedSpec.package)) {
dependencies[parsedSpec.package] = {
constraint: null,
references: []
};
allConstraints[parsedSpec.package] = [];
}
var d = dependencies[parsedSpec.package];
if (parsedSpec.constraint) {
allConstraints[parsedSpec.package].push(parsedSpec.constraints);
if (d.constraint === null) {
d.constraint = parsedSpec.constraint;
} else if (d.constraint !== parsedSpec.constraint) {
failed = true;
}
}
d.references.push({arch: 'plugin'});
});
});
if (failed && options.logError) {
_.each(allConstraints, function (constraints, name) {
constraints = _.uniq(constraints);

View File

@@ -243,7 +243,7 @@ PV.parseConstraint = function (constraintString, options) {
var splitted = constraintString.split('@');
var name = splitted[0];
var versionString = splitted[1];
var versionString = splitted[1] || '';
if (splitted.length > 2) {
// throw error complaining about @