diff --git a/build/artifacts.props b/build/artifacts.props
index c909b76092..a1df1c0583 100644
--- a/build/artifacts.props
+++ b/build/artifacts.props
@@ -23,9 +23,11 @@
+
+
@@ -40,12 +42,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -66,6 +89,10 @@
+
+
+
+
@@ -85,6 +112,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -106,6 +152,11 @@
+
+
+
+
+
@@ -119,6 +170,7 @@
+
diff --git a/build/external-dependencies.props b/build/external-dependencies.props
index a959419737..fe426974c5 100644
--- a/build/external-dependencies.props
+++ b/build/external-dependencies.props
@@ -164,16 +164,11 @@
-
-
-
-
-
@@ -188,12 +183,6 @@
-
-
-
-
-
-
@@ -208,27 +197,10 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -241,32 +213,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -292,9 +244,7 @@
-
-
-
+
@@ -304,8 +254,6 @@
-
-
@@ -337,7 +285,6 @@
-
diff --git a/build/repo.targets b/build/repo.targets
index 32e0796db9..85b652c3e1 100644
--- a/build/repo.targets
+++ b/build/repo.targets
@@ -164,6 +164,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
@@ -29,7 +38,6 @@
-
@@ -38,24 +46,16 @@
-
-
-
-
-
-
-
-
diff --git a/build/tasks/CheckRepoGraph.cs b/build/tasks/CheckRepoGraph.cs
new file mode 100644
index 0000000000..5502d7316c
--- /dev/null
+++ b/build/tasks/CheckRepoGraph.cs
@@ -0,0 +1,214 @@
+// 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.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.IO;
+using System.Text;
+using System.Threading;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using NuGet.Frameworks;
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+using RepoTools.BuildGraph;
+using RepoTasks.ProjectModel;
+using RepoTasks.Utilities;
+
+namespace RepoTasks
+{
+ public class CheckRepoGraph : Task, ICancelableTask
+ {
+ private readonly CancellationTokenSource _cts = new CancellationTokenSource();
+
+ [Required]
+ public ITaskItem[] Solutions { get; set; }
+
+ [Required]
+ public ITaskItem[] Artifacts { get; set; }
+
+ [Required]
+ public ITaskItem[] Repositories { get; set; }
+
+ [Required]
+ public string Properties { get; set; }
+
+ public void Cancel()
+ {
+ _cts.Cancel();
+ }
+
+ public override bool Execute()
+ {
+ var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
+ .OfType()
+ .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/RepoTasks.tasks b/build/tasks/RepoTasks.tasks
index 6f2032d491..1122b7026d 100644
--- a/build/tasks/RepoTasks.tasks
+++ b/build/tasks/RepoTasks.tasks
@@ -6,6 +6,7 @@
+
diff --git a/modules/AADIntegration b/modules/AADIntegration
index 4342e5cae2..a35250fe3a 160000
--- a/modules/AADIntegration
+++ b/modules/AADIntegration
@@ -1 +1 @@
-Subproject commit 4342e5cae29c45c84e27df28218f27c6cec6cc24
+Subproject commit a35250fe3ad8125a4c6fd67feb4055e9470fe558
diff --git a/modules/AzureIntegration b/modules/AzureIntegration
index b51db1fc91..c710aa8c26 160000
--- a/modules/AzureIntegration
+++ b/modules/AzureIntegration
@@ -1 +1 @@
-Subproject commit b51db1fc91cfe792e4f1518b728c41a0af4730d3
+Subproject commit c710aa8c2663e474c270ec48dd9dc999d38d9a46
diff --git a/modules/Diagnostics b/modules/Diagnostics
index 98785fb2f5..977f85f9cc 160000
--- a/modules/Diagnostics
+++ b/modules/Diagnostics
@@ -1 +1 @@
-Subproject commit 98785fb2f5ddb31e298d3f52069adfd233375c65
+Subproject commit 977f85f9cc9ca2685389a740bface9e18fcf5bdd
diff --git a/modules/Identity b/modules/Identity
index 5403ec47ec..d18de6b00e 160000
--- a/modules/Identity
+++ b/modules/Identity
@@ -1 +1 @@
-Subproject commit 5403ec47ecfe5cbc2c904253fc1cfbd64675eba6
+Subproject commit d18de6b00e13f06bae43c621f17e39ed2bec4069
diff --git a/modules/JavaScriptServices b/modules/JavaScriptServices
index 8c84e35392..436cdb0e96 160000
--- a/modules/JavaScriptServices
+++ b/modules/JavaScriptServices
@@ -1 +1 @@
-Subproject commit 8c84e353922319ff104c35afa2d6baa186aa010f
+Subproject commit 436cdb0e967ab5b3e3903bb876bbd3edef844172
diff --git a/modules/MetaPackages b/modules/MetaPackages
index 4e1b907743..10e735d420 160000
--- a/modules/MetaPackages
+++ b/modules/MetaPackages
@@ -1 +1 @@
-Subproject commit 4e1b90774367d96595eb5e990510b6ba1300ee4e
+Subproject commit 10e735d42002db959e1a769a3362c84ec0ef1c27
diff --git a/modules/Proxy b/modules/Proxy
index 90d90c12d6..8ec1eb26bb 160000
--- a/modules/Proxy
+++ b/modules/Proxy
@@ -1 +1 @@
-Subproject commit 90d90c12d61580be1738a78fde0b03c0d25338a0
+Subproject commit 8ec1eb26bbf238eac08161a83632a6260f02d234
diff --git a/modules/StaticFiles b/modules/StaticFiles
index 66573b187d..5c775c9579 160000
--- a/modules/StaticFiles
+++ b/modules/StaticFiles
@@ -1 +1 @@
-Subproject commit 66573b187dc9a915b7726b93753b2f260b9b4b7a
+Subproject commit 5c775c957991cb9e5d20c9cf3c67d56dc92d80ba
diff --git a/modules/WebHooks b/modules/WebHooks
index 5815691af6..e554b1e9f9 160000
--- a/modules/WebHooks
+++ b/modules/WebHooks
@@ -1 +1 @@
-Subproject commit 5815691af6d8c6f0841a14575609e09f5d0e35c8
+Subproject commit e554b1e9f9a07dd9af35d55bcd6f7159fc6cf752
diff --git a/modules/WebSockets b/modules/WebSockets
index b3356b90b2..a1085ed31c 160000
--- a/modules/WebSockets
+++ b/modules/WebSockets
@@ -1 +1 @@
-Subproject commit b3356b90b20e1fdea3c49cef8f4bbbe0b991e731
+Subproject commit a1085ed31cebed4d4fa1f10cd662a462a87d41f1