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 @@ + +