diff --git a/build/dependencies.props b/build/dependencies.props
index 08fffce6d2..ad5a6bdf68 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -55,6 +55,7 @@
4.2.1
3.1.0
1.10.0
+ 1.7.3.4
2.1.1
2.2.1
5.2.6
@@ -80,7 +81,7 @@
1.0.5
1.1.2
2.0.0
- 2.1.0-rc1
+ 2.1.0
1.0.1
15.6.1
3.0.1
@@ -105,7 +106,6 @@
1.3.8
1.0.1
4.7.49
- 1.7.3.4
1.6.1
2.0.3
1.0.1
diff --git a/build/repo.targets b/build/repo.targets
index f0b0c9f336..f2934b2b10 100644
--- a/build/repo.targets
+++ b/build/repo.targets
@@ -165,6 +165,12 @@
+
+
+
+
@@ -207,7 +217,7 @@
-
+
()
+ .Where(p => !p.IsSymbolsArtifact)
+ .ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);
+
+ var factory = new SolutionInfoFactory(Log, BuildEngine5);
+ var props = MSBuildListSplitter.GetNamedProperties(Properties);
+
+ if (!props.TryGetValue("Configuration", out var defaultConfig))
+ {
+ defaultConfig = "Debug";
+ }
+
+ var solutions = factory.Create(Solutions, props, defaultConfig, _cts.Token).OrderBy(f => f.Directory).ToList();
+ Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects");
+
+ if (_cts.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ var repoGraph = new AdjacencyMatrix(solutions.Count);
+ var packageToProjectMap = new Dictionary();
+
+ for (var i = 0; i < solutions.Count; i++)
+ {
+ var sln = repoGraph[i] = solutions[i];
+
+ foreach (var proj in sln.Projects)
+ {
+ if (!proj.IsPackable
+ || proj.FullPath.Contains("samples")
+ || proj.FullPath.Contains("tools/Microsoft.VisualStudio.Web.CodeGeneration.Design"))
+ {
+ continue;
+ }
+
+ var id = new PackageIdentity(proj.PackageId, new NuGetVersion(proj.PackageVersion));
+
+ if (packageToProjectMap.TryGetValue(id, out var otherProj))
+ {
+ Log.LogError($"Both {proj.FullPath} and {otherProj.FullPath} produce {id}");
+ continue;
+ }
+
+ packageToProjectMap.Add(id, proj);
+ }
+
+ var sharedSrc = Path.Combine(sln.Directory, "shared");
+ if (Directory.Exists(sharedSrc))
+ {
+ foreach (var dir in Directory.GetDirectories(sharedSrc, "*.Sources"))
+ {
+ var id = GetDirectoryName(dir);
+ var artifactInfo = packageArtifacts[id];
+ var sharedSrcProj = new ProjectInfo(dir,
+ Array.Empty(),
+ Array.Empty(),
+ true,
+ artifactInfo.PackageInfo.Id,
+ artifactInfo.PackageInfo.Version.ToNormalizedString());
+ sharedSrcProj.SolutionInfo = sln;
+ var identity = new PackageIdentity(artifactInfo.PackageInfo.Id, artifactInfo.PackageInfo.Version);
+ packageToProjectMap.Add(identity, sharedSrcProj);
+ }
+ }
+ }
+
+ if (Log.HasLoggedErrors)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < solutions.Count; i++)
+ {
+ var src = repoGraph[i];
+
+ foreach (var proj in src.Projects)
+ {
+ if (!proj.IsPackable
+ || proj.FullPath.Contains("samples"))
+ {
+ continue;
+ }
+
+ foreach (var dep in proj.Frameworks.SelectMany(f => f.Dependencies.Values))
+ {
+ if (packageToProjectMap.TryGetValue(new PackageIdentity(dep.Id, new NuGetVersion(dep.Version)), out var target))
+ {
+ var j = repoGraph.FindIndex(target.SolutionInfo);
+ repoGraph.SetLink(i, j);
+ }
+ }
+
+ foreach (var toolDep in proj.Tools)
+ {
+ if (packageToProjectMap.TryGetValue(new PackageIdentity(toolDep.Id, new NuGetVersion(toolDep.Version)), out var target))
+ {
+ var j = repoGraph.FindIndex(target.SolutionInfo);
+ repoGraph.SetLink(i, j);
+ }
+ }
+ }
+ }
+
+ var repos = Repositories.ToDictionary(i => i.ItemSpec, i => i, StringComparer.OrdinalIgnoreCase);
+
+ for (var i = 0; i < repoGraph.Count; i++)
+ {
+ var src = repoGraph[i];
+ var repoName = GetDirectoryName(src.Directory);
+ var repo = repos[repoName];
+
+ for (var j = 0; j < repoGraph.Count; j++)
+ {
+ if (j == i) continue;
+ if (repoGraph.HasLink(i, j))
+ {
+ var target = repoGraph[j];
+ var targetRepoName = GetDirectoryName(target.Directory);
+ var targetRepo = repos[targetRepoName];
+
+ if (src.Shipped && !target.Shipped)
+ {
+ Log.LogError($"{repoName} cannot depend on {targetRepoName}. Repos marked as 'Shipped' cannot depend on repos that are rebuilding. Update the configuration in submodule.props.");
+ }
+ }
+ }
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ private static string GetDirectoryName(string path)
+ => Path.GetFileName(path.TrimEnd(new[] { '\\', '/' }));
+
+ private class AdjacencyMatrix
+ {
+ private readonly bool[,] _matrix;
+ private readonly SolutionInfo[] _items;
+
+ public AdjacencyMatrix(int size)
+ {
+ _matrix = new bool[size, size];
+ _items = new SolutionInfo[size];
+ Count = size;
+ }
+
+ public SolutionInfo this[int idx]
+ {
+ get => _items[idx];
+ set => _items[idx] = value;
+ }
+
+ public int FindIndex(SolutionInfo item)
+ {
+ return Array.FindIndex(_items, t => t.Equals(item));
+ }
+
+ public int Count { get; }
+
+ public bool HasLink(int source, int target) => _matrix[source, target];
+
+ public void SetLink(int source, int target)
+ {
+ _matrix[source, target] = true;
+ }
+ }
+ }
+}
diff --git a/build/tasks/CheckVersionOverrides.cs b/build/tasks/CheckVersionOverrides.cs
new file mode 100644
index 0000000000..ad75aa8b2b
--- /dev/null
+++ b/build/tasks/CheckVersionOverrides.cs
@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace RepoTasks
+{
+ public class CheckVersionOverrides : Task
+ {
+ [Required]
+ public string DotNetPackageVersionPropsPath { get; set; }
+
+ [Required]
+ public string DependenciesFile { get; set; }
+
+ public override bool Execute()
+ {
+ Log.LogMessage($"Verifying versions set in {DotNetPackageVersionPropsPath} match expected versions set in {DependenciesFile}");
+
+ var versionOverrides = ProjectRootElement.Open(DotNetPackageVersionPropsPath);
+ var dependencies = ProjectRootElement.Open(DependenciesFile);
+ var pinnedVersions = dependencies.PropertyGroups
+ .Where(p => !string.Equals("Package Versions: Auto", p.Label))
+ .SelectMany(p => p.Properties)
+ .ToDictionary(p => p.Name, p => p.Value, StringComparer.OrdinalIgnoreCase);
+
+ foreach (var prop in versionOverrides.Properties)
+ {
+ if (pinnedVersions.TryGetValue(prop.Name, out var pinnedVersion))
+ {
+ if (!string.Equals(pinnedVersion, prop.Value, StringComparison.OrdinalIgnoreCase))
+ {
+ Log.LogError($"The imported package version props file conflicts with a pinned version variable {prop.Name}. Imported value: {prop.Value}, Pinned value: {pinnedVersion}");
+ }
+ }
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/build/tasks/RepoTasks.tasks b/build/tasks/RepoTasks.tasks
index 6f2032d491..336e4beeda 100644
--- a/build/tasks/RepoTasks.tasks
+++ b/build/tasks/RepoTasks.tasks
@@ -7,6 +7,8 @@
+
+