public Dictionary<string, Dependency> ResolveDependencies(bool useLatest)
{
List<Dependency> unresolved = new List<Dependency>();
Dictionary<string, Dependency> candidates = new Dictionary<string, Dependency>();
Dictionary<string, Dependency> dependencyMap = LoadDependencies(true, true);
// Set of each versioned dependencies for each version-less dependency key.
// e.g if foo depends upon bar, map[bar] = {foo}.
var reverseDependencyTree = new Dictionary<string, HashSet<string>>();
var warnings = new HashSet<string>();
// All dependencies are added to the "unresolved" list.
unresolved.AddRange(dependencyMap.Values);
do
{
Dictionary<string, Dependency> nextUnresolved =
new Dictionary<string, Dependency>();
foreach (Dependency dep in unresolved)
{
var currentDep = dep;
// Whether the dependency has been resolved and therefore should be removed
// from the unresolved list.
bool remove = true;
// check for existing candidate
Dependency candidate;
Dependency newCandidate;
if (candidates.TryGetValue(currentDep.VersionlessKey, out candidate))
{
if (currentDep.IsAcceptableVersion(candidate.BestVersion))
{
remove = true;
// save the most restrictive dependency in the
// candidate
if (currentDep.IsNewer(candidate))
{
candidates[currentDep.VersionlessKey] = currentDep;
}
}
else
{
// in general, we need to iterate
remove = false;
// refine one or both dependencies if they are
// non-concrete.
bool possible = false;
if (currentDep.Version.Contains("+") && candidate.IsNewer(currentDep))
{
possible = currentDep.RefineVersionRange(candidate);
}
// only try if the candidate is less than the depenceny
if (candidate.Version.Contains("+") && currentDep.IsNewer(candidate))
{
possible = possible || candidate.RefineVersionRange(currentDep);
}
if (possible)
{
// add all the dependency constraints back to make
// sure all are met.
foreach (Dependency d in dependencyMap.Values)
{
if (d.VersionlessKey == candidate.VersionlessKey)
{
if (!nextUnresolved.ContainsKey(d.Key))
{
nextUnresolved.Add(d.Key, d);
}
}
}
}
else if (!possible && useLatest)
{
// Reload versions of the dependency has they all have been
// removed.
newCandidate = (currentDep.IsNewer(candidate) ?
currentDep : candidate);
newCandidate = newCandidate.HasPossibleVersions ? newCandidate :
FindCandidate(newCandidate);
candidates[newCandidate.VersionlessKey] = newCandidate;
currentDep = newCandidate;
remove = true;
// Due to a dependency being via multiple modules we track
// whether a warning has already been reported and make sure it's
// only reported once.
if (!warnings.Contains(currentDep.VersionlessKey)) {
Log("WARNING: No compatible versions of " +
currentDep.VersionlessKey + " required by (" +
String.Join(
", ", (new List<string>(
reverseDependencyTree[
currentDep.VersionlessKey])).ToArray()) +
"), will try using the latest version " +
currentDep.BestVersion);
warnings.Add(currentDep.VersionlessKey);
}
}
else if (!possible)
{
throw new ResolutionException("Cannot resolve " +
currentDep + " and " + candidate);
}
}
}
else
{
candidate = FindCandidate(currentDep);
if (candidate != null)
{
candidates.Add(candidate.VersionlessKey, candidate);
remove = true;
}
else
{
throw new ResolutionException("Cannot resolve " +
currentDep);
}
}
// If the dependency has been found.
if (remove)
{
// Add all transitive dependencies to resolution list.
foreach (Dependency d in GetDependencies(currentDep))
{
if (!nextUnresolved.ContainsKey(d.Key))
{
Log("For " + currentDep.Key + " adding dep " + d.Key,
verbose: true);
HashSet<string> parentNames;
if (!reverseDependencyTree.TryGetValue(d.VersionlessKey,
out parentNames)) {
parentNames = new HashSet<string>();
}
parentNames.Add(currentDep.Key);
reverseDependencyTree[d.VersionlessKey] = parentNames;
nextUnresolved.Add(d.Key, d);
}
}
}
else
{
if (!nextUnresolved.ContainsKey(currentDep.Key))
{
nextUnresolved.Add(currentDep.Key, currentDep);
}
}
}
unresolved.Clear();
unresolved.AddRange(nextUnresolved.Values);
nextUnresolved.Clear();
}
while (unresolved.Count > 0);
return candidates;
}