From 88e66f2b219db153a0b9de6907ac4256543aba41 Mon Sep 17 00:00:00 2001 From: Nate McMaster Date: Wed, 6 Dec 2017 16:30:07 -0800 Subject: [PATCH] Prepare the 2.0.5 patch and add task to ensure repo versions cascade --- .gitmodules | 6 +- build/artifacts.props | 117 ---------- build/dependencies.props | 221 ++++++++++++++---- build/repo.targets | 6 + build/submodules.props | 56 +++-- build/tasks/AnalyzeBuildGraph.cs | 7 +- build/tasks/CheckRepoGraph.cs | 214 +++++++++++++++++ build/tasks/ProjectModel/ProjectInfo.cs | 4 +- .../tasks/ProjectModel/ProjectInfoFactory.cs | 11 +- build/tasks/ProjectModel/SolutionInfo.cs | 8 + .../tasks/ProjectModel/SolutionInfoFactory.cs | 4 +- build/tasks/RepoTasks.tasks | 1 + modules/Identity | 2 +- modules/JavaScriptServices | 2 +- modules/Mvc | 2 +- modules/MvcPrecompilation | 2 +- modules/Scaffolding | 2 +- modules/Templating | 2 +- 18 files changed, 462 insertions(+), 205 deletions(-) create mode 100644 build/tasks/CheckRepoGraph.cs diff --git a/.gitmodules b/.gitmodules index cf4535ebdb..a220bfb2f7 100644 --- a/.gitmodules +++ b/.gitmodules @@ -68,8 +68,8 @@ branch = release/2.0.0 [submodule "modules/Mvc"] path = modules/Mvc - url = https://github.com/aspnet/Mvc.git - branch = release/2.0.0 + url = https://github.com/aspnet/Mvc-Private.git + branch = release/2.0.0-MSRC [submodule "modules/MvcPrecompilation"] path = modules/MvcPrecompilation url = https://github.com/aspnet/MvcPrecompilation.git @@ -169,4 +169,4 @@ [submodule "modules/Templating"] path = modules/Templating url = https://github.com/aspnet/Templating-Private.git - branch = release/2.0.0 + branch = release/2.0.0-MSRC diff --git a/build/artifacts.props b/build/artifacts.props index 0564ae2f0b..cc7872a533 100644 --- a/build/artifacts.props +++ b/build/artifacts.props @@ -10,67 +10,7 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -79,7 +19,6 @@ - @@ -90,65 +29,11 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -158,7 +43,5 @@ - - diff --git a/build/dependencies.props b/build/dependencies.props index d262b69550..c7caac4ed3 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -28,54 +28,13 @@ https://dotnet.myget.org/F/dotnet-core/api/v3/index.json - 2.0.2-servicing-25728-02 - 2.0.0 + 2.0.3 + 2.0.1 $(CoreSetupPackageVersion) - - - - - - RuntimeFrameworkVersion - netcoreapp2.0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -153,6 +112,53 @@ + + + + + KRB2004 + RuntimeFrameworkVersion + netcoreapp2.0 + + + KRB2004 + MicrosoftNETCoreApp20PackageVersion + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -290,6 +296,116 @@ not building again in this patch. --> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -338,9 +454,24 @@ not building again in this patch. - https://dotnet.myget.org/F/aspnetcore-master/api/v3/index.json + https://dotnet.myget.org/F/aspnet-2-0-2-october2017-patch-public/api/v3/index.json + + + + + + + + + + + + + https://dotnet.myget.org/F/aspnetcore-master/api/v3/index.json + + diff --git a/build/repo.targets b/build/repo.targets index b4329c0155..fdbc47fecd 100644 --- a/build/repo.targets +++ b/build/repo.targets @@ -142,6 +142,12 @@ + + + + + true + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/tasks/AnalyzeBuildGraph.cs b/build/tasks/AnalyzeBuildGraph.cs index 4a5555b82f..550bca775f 100644 --- a/build/tasks/AnalyzeBuildGraph.cs +++ b/build/tasks/AnalyzeBuildGraph.cs @@ -59,9 +59,12 @@ namespace RepoTasks var factory = new SolutionInfoFactory(Log, BuildEngine5); var props = MSBuildListSplitter.GetNamedProperties(Properties); - Log.LogMessage(MessageImportance.High, $"Beginning cross-repo analysis on {Solutions.Length} solutions. Hang tight..."); + if (!props.TryGetValue("Configuration", out var defaultConfig)) + { + defaultConfig = "Debug"; + } - var solutions = factory.Create(Solutions, props, _cts.Token); + var solutions = factory.Create(Solutions, props, defaultConfig, _cts.Token); Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects"); if (_cts.IsCancellationRequested) 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/ProjectModel/ProjectInfo.cs b/build/tasks/ProjectModel/ProjectInfo.cs index 1dd4339185..9aed38898b 100644 --- a/build/tasks/ProjectModel/ProjectInfo.cs +++ b/build/tasks/ProjectModel/ProjectInfo.cs @@ -10,7 +10,6 @@ namespace RepoTasks.ProjectModel internal class ProjectInfo { public ProjectInfo(string fullPath, - string projectExtensionsPath, IReadOnlyList frameworks, IReadOnlyList tools, bool isPackable, @@ -28,7 +27,6 @@ namespace RepoTasks.ProjectModel FullPath = fullPath; FileName = Path.GetFileName(fullPath); Directory = Path.GetDirectoryName(FullPath); - ProjectExtensionsPath = projectExtensionsPath ?? Path.Combine(Directory, "obj"); IsPackable = isPackable; PackageId = packageId; PackageVersion = packageVersion; @@ -36,7 +34,6 @@ namespace RepoTasks.ProjectModel public string FullPath { get; } public string FileName { get; } - public string ProjectExtensionsPath { get; } public string Directory { get; } public string PackageId { get; } public string PackageVersion { get; } @@ -44,5 +41,6 @@ namespace RepoTasks.ProjectModel public IReadOnlyList Frameworks { get; } public IReadOnlyList Tools { get; } + public SolutionInfo SolutionInfo { get; internal set; } } } diff --git a/build/tasks/ProjectModel/ProjectInfoFactory.cs b/build/tasks/ProjectModel/ProjectInfoFactory.cs index 5c739f1784..78d5adc5fd 100644 --- a/build/tasks/ProjectModel/ProjectInfoFactory.cs +++ b/build/tasks/ProjectModel/ProjectInfoFactory.cs @@ -27,7 +27,6 @@ namespace RepoTasks.ProjectModel { var project = GetProject(path, projectCollection); var instance = project.CreateProjectInstance(ProjectInstanceSettings.ImmutableWithFastItemLookup); - var projExtPath = instance.GetPropertyValue("MSBuildProjectExtensionsPath"); var targetFrameworks = instance.GetPropertyValue("TargetFrameworks"); var targetFramework = instance.GetPropertyValue("TargetFramework"); @@ -59,11 +58,17 @@ namespace RepoTasks.ProjectModel var tools = GetTools(instance).ToArray(); bool.TryParse(instance.GetPropertyValue("IsPackable"), out var isPackable); + + if (isPackable) + { + // the default packable setting is disabled for projects referencing this package. + isPackable = !frameworks.SelectMany(f => f.Dependencies.Keys).Any(d => d.Equals("Microsoft.NET.Test.Sdk", StringComparison.OrdinalIgnoreCase)); + } + var packageId = instance.GetPropertyValue("PackageId"); var packageVersion = instance.GetPropertyValue("PackageVersion"); return new ProjectInfo(path, - projExtPath, frameworks, tools, isPackable, @@ -88,6 +93,8 @@ namespace RepoTasks.ProjectModel var globalProps = new Dictionary() { ["DesignTimeBuild"] = "true", + // Isolate the project from post-restore side effects + ["ExcludeRestorePackageImports"] = "true", }; var project = new Project(xml, diff --git a/build/tasks/ProjectModel/SolutionInfo.cs b/build/tasks/ProjectModel/SolutionInfo.cs index 8b9710081e..dce2f0bdd3 100644 --- a/build/tasks/ProjectModel/SolutionInfo.cs +++ b/build/tasks/ProjectModel/SolutionInfo.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; namespace RepoTasks.ProjectModel { @@ -20,13 +21,20 @@ namespace RepoTasks.ProjectModel throw new ArgumentException(nameof(configName)); } + Directory = Path.GetDirectoryName(fullPath); FullPath = fullPath; ConfigName = configName; Projects = projects ?? throw new ArgumentNullException(nameof(projects)); ShouldBuild = shouldBuild; Shipped = shipped; + + foreach (var proj in Projects) + { + proj.SolutionInfo = this; + } } + public string Directory { get; } public string FullPath { get; } public string ConfigName { get; } public IReadOnlyList Projects { get; } diff --git a/build/tasks/ProjectModel/SolutionInfoFactory.cs b/build/tasks/ProjectModel/SolutionInfoFactory.cs index 91de1c818c..c24475f7c2 100644 --- a/build/tasks/ProjectModel/SolutionInfoFactory.cs +++ b/build/tasks/ProjectModel/SolutionInfoFactory.cs @@ -27,7 +27,7 @@ namespace RepoTasks.ProjectModel _buildEngine = buildEngine; } - public IReadOnlyList Create(IEnumerable solutionItems, IDictionary properties, CancellationToken ct) + public IReadOnlyList Create(IEnumerable solutionItems, IDictionary properties, string defaultConfig, CancellationToken ct) { var timer = Stopwatch.StartNew(); @@ -49,7 +49,7 @@ namespace RepoTasks.ProjectModel if (solutionProps.TryGetValue("Configuration", out var configName)) { - solutionProps["Configuration"] = configName = "Debug"; + solutionProps["Configuration"] = configName = defaultConfig; } var key = $"SlnInfo:{solutionFile}:{configName}"; diff --git a/build/tasks/RepoTasks.tasks b/build/tasks/RepoTasks.tasks index 5410effb57..569b766901 100644 --- a/build/tasks/RepoTasks.tasks +++ b/build/tasks/RepoTasks.tasks @@ -4,6 +4,7 @@ + diff --git a/modules/Identity b/modules/Identity index 8c47b90677..becf1df9c5 160000 --- a/modules/Identity +++ b/modules/Identity @@ -1 +1 @@ -Subproject commit 8c47b90677c0f544844151418ba94f24d9f2a094 +Subproject commit becf1df9c5a3b908e28ec3c1272b072d8801e3dc diff --git a/modules/JavaScriptServices b/modules/JavaScriptServices index 64389a9bbe..d5a664e481 160000 --- a/modules/JavaScriptServices +++ b/modules/JavaScriptServices @@ -1 +1 @@ -Subproject commit 64389a9bbeda7378c80b4c302700ddcb78d4f0aa +Subproject commit d5a664e4817a395cbafec942387162b5afeba7bf diff --git a/modules/Mvc b/modules/Mvc index f8789f5d5c..7cea779b7a 160000 --- a/modules/Mvc +++ b/modules/Mvc @@ -1 +1 @@ -Subproject commit f8789f5d5c4d5869490a05b8f7250b6151f1673e +Subproject commit 7cea779b7a744a3d915aa207a760f6f7b9c6dccc diff --git a/modules/MvcPrecompilation b/modules/MvcPrecompilation index bc58d8495a..f510e70340 160000 --- a/modules/MvcPrecompilation +++ b/modules/MvcPrecompilation @@ -1 +1 @@ -Subproject commit bc58d8495a431d3de606afc52c2987d1ebf1e6ad +Subproject commit f510e7034000e346f6771197d30aaf791ce2bf48 diff --git a/modules/Scaffolding b/modules/Scaffolding index 26822d4c87..069ca26129 160000 --- a/modules/Scaffolding +++ b/modules/Scaffolding @@ -1 +1 @@ -Subproject commit 26822d4c876ee6203014eaf6df398c874c4535ea +Subproject commit 069ca2612999a49e2b19099f21d8196f422c82de diff --git a/modules/Templating b/modules/Templating index 3a4c81dcc9..16c23b846e 160000 --- a/modules/Templating +++ b/modules/Templating @@ -1 +1 @@ -Subproject commit 3a4c81dcc99fbfcb14d73994ac6255f6eced7ac1 +Subproject commit 16c23b846ec57803e83c7e1bffc9c24546fe1de4