Remove tasks for analyzing the repo graph (#1469)

As a part of converging repos, we no longer need this code. It is currently buggy and slow, and we can get away without by hard-coding the repo graph (which rarely changes)
This commit is contained in:
Nate McMaster 2018-10-09 12:01:41 -07:00 коммит произвёл GitHub
Родитель 18b61e9217
Коммит eaad1eb3aa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 13 добавлений и 1748 удалений

Просмотреть файл

@ -2,6 +2,11 @@
<configuration>
<packageSources>
<clear />
<!-- Restore sources should be defined in build/sources.props. -->
<!--
Restore sources should be defined in build/sources.props.
The only allowed feed here is myget.org/aspnet-tools which is required to work around
https://github.com/Microsoft/msbuild/issues/2914
-->
<add key="myget.org aspnetcore-tools" value="https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json" />
</packageSources>
</configuration>

Просмотреть файл

@ -1,17 +0,0 @@
<!--
The targets in this file should not be executed from a CI build.
They are meant to be used by developers to update the ASP.NET Core configuration.
-->
<Project>
<!-- Generates a .dgml file representing the repo to repo graph of reach korebuild project -->
<Target Name="GenerateSubmoduleGraph" DependsOnTargets="ResolveRepoInfo;GeneratePropsFiles">
<RepoTasks.GenerateSubmoduleGraph
Solutions="@(Solution)"
Artifacts="@(ArtifactInfo)"
Repositories="@(Repository)"
RepositoryRoot="$(RepositoryRoot)"
Properties="Configuration=$(Configuration);BuildNumber=$(BuildNumber);DotNetPackageVersionPropsPath=$(GeneratedPackageVersionPropsPath)" />
</Target>
</Project>

Просмотреть файл

@ -16,22 +16,22 @@
<RepositoryBuildOrder Include="HttpAbstractions" Order="6" />
<RepositoryBuildOrder Include="HttpClientFactory" Order="6" />
<RepositoryBuildOrder Include="Hosting" Order="7" />
<RepositoryBuildOrder Include="DataProtection" Order="8" />
<RepositoryBuildOrder Include="KestrelHttpServer" Order="8" />
<RepositoryBuildOrder Include="EntityFrameworkCore" Order="8" />
<RepositoryBuildOrder Include="HttpSysServer" Order="8" />
<RepositoryBuildOrder Include="BrowserLink" Order="8" />
<RepositoryBuildOrder Include="DataProtection" Order="9" />
<RepositoryBuildOrder Include="BasicMiddleware" Order="9" />
<RepositoryBuildOrder Include="Antiforgery" Order="9" />
<RepositoryBuildOrder Include="Antiforgery" Order="10" />
<RepositoryBuildOrder Include="IISIntegration" Order="10" />
<RepositoryBuildOrder Include="CORS" Order="11" />
<RepositoryBuildOrder Include="StaticFiles" Order="11" />
<RepositoryBuildOrder Include="Routing" Order="11" />
<RepositoryBuildOrder Include="ResponseCaching" Order="11" />
<RepositoryBuildOrder Include="Session" Order="11" />
<RepositoryBuildOrder Include="ServerTests" Order="11" />
<RepositoryBuildOrder Include="Routing" Order="12" />
<RepositoryBuildOrder Include="Diagnostics" Order="12" />
<RepositoryBuildOrder Include="Localization" Order="12" />
<RepositoryBuildOrder Include="Localization" Order="13" />
<RepositoryBuildOrder Include="WebSockets" Order="13" />
<RepositoryBuildOrder Include="Security" Order="13" />
<RepositoryBuildOrder Include="MetaPackages" Order="13" />

Просмотреть файл

@ -5,10 +5,7 @@
<Import Project="SharedFx.targets" />
<Import Project="SharedFxInstaller.targets" />
<Import Project="Publish.targets" />
<Import Project="GenerateCode.targets" />
<!-- Workaround for #1014 -->
<Import Project="buildorder.props" Condition="$([MSBuild]::IsOSUnixLike())" />
<Import Project="buildorder.props" />
<PropertyGroup>
<GeneratedPackageVersionPropsPath>$(IntermediateDir)dependencies.g.props</GeneratedPackageVersionPropsPath>
@ -226,12 +223,6 @@
</Target>
<Target Name="ComputeGraph" DependsOnTargets="ResolveRepoInfo;GeneratePropsFiles">
<RepoTasks.CheckRepoGraph Condition=" ! $([MSBuild]::IsOSUnixLike())"
Solutions="@(Solution)"
Artifacts="@(ArtifactInfo);@(ShippedArtifactInfo)"
Repositories="@(Repository);@(ShippedRepository)"
Properties="Configuration=$(Configuration);BuildNumber=$(BuildNumber);DotNetPackageVersionPropsPath=$(GeneratedPackageVersionPropsPath);DotNetRestoreSourcePropsPath=$(GeneratedRestoreSourcesPropsPath)" />
<ItemGroup>
<_UndeclaredPackageArtifact Include="%(ArtifactInfo.PackageId)" Condition="'%(ArtifactInfo.ArtifactType)' == 'NuGetPackage'" />
<_UndeclaredPackageArtifact Remove="@(PackageArtifact)" />
@ -240,16 +231,6 @@
<Error Text="Undeclared package artifacts. Add these to artifacts.props:%0A - @(_UndeclaredPackageArtifact, '%0A - ')"
Condition=" @(_UndeclaredPackageArtifact->Count()) != 0 " />
<!-- Skipped to workaround #1014. The order is hardcoded in buildorder.props -->
<RepoTasks.AnalyzeBuildGraph Condition=" ! $([MSBuild]::IsOSUnixLike())"
Solutions="@(Solution)"
Artifacts="@(ArtifactInfo);@(ShippedArtifactInfo)"
Repositories="@(Repository);@(ShippedRepository)"
Dependencies="@(ExternalDependency)"
Properties="Configuration=$(Configuration);BuildNumber=$(BuildNumber);DotNetPackageVersionPropsPath=$(GeneratedPackageVersionPropsPath);DotNetRestoreSourcePropsPath=$(GeneratedRestoreSourcesPropsPath)">
<Output TaskParameter="RepositoryBuildOrder" ItemName="RepositoryBuildOrder" />
</RepoTasks.AnalyzeBuildGraph>
<Message Text="Repository build order:" Importance="high" />
<Message Text="%(RepositoryBuildOrder.Order). @(RepositoryBuildOrder, ', ')" Importance="high" />
</Target>

Просмотреть файл

@ -1,294 +0,0 @@
// 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.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NuGet.Versioning;
using RepoTasks.ProjectModel;
using RepoTasks.Utilities;
using RepoTools.BuildGraph;
namespace RepoTasks
{
public class AnalyzeBuildGraph : Task, ICancelableTask
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
/// <summary>
/// Repositories that we are building new versions of.
/// </summary>
[Required]
public ITaskItem[] Solutions { get; set; }
[Required]
public ITaskItem[] Artifacts { get; set; }
[Required]
public ITaskItem[] Repositories { get; set; }
[Required]
public ITaskItem[] Dependencies { get; set; }
[Required]
public string Properties { get; set; }
/// <summary>
/// The order in which to build repositories
/// </summary>
[Output]
public ITaskItem[] RepositoryBuildOrder { get; set; }
public void Cancel()
{
_cts.Cancel();
}
public override bool Execute()
{
var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
.OfType<ArtifactInfo.Package>()
.Where(p => !p.IsSymbolsArtifact);
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, defaultConfig, _cts.Token);
Log.LogMessage($"Found {solutions.Count} and {solutions.Sum(p => p.Projects.Count)} projects");
var policies = new Dictionary<string, PatchPolicy>();
foreach (var repo in Repositories)
{
policies.Add(repo.ItemSpec, Enum.Parse<PatchPolicy>(repo.GetMetadata("PatchPolicy")));
}
foreach (var solution in solutions)
{
var repoName = Path.GetFileName(solution.Directory);
solution.PatchPolicy = policies[repoName];
}
if (_cts.IsCancellationRequested)
{
return false;
}
EnsureConsistentGraph(packageArtifacts, solutions);
RepositoryBuildOrder = GetRepositoryBuildOrder(packageArtifacts, solutions.Where(s => s.ShouldBuild));
return !Log.HasLoggedErrors;
}
private struct VersionMismatch
{
public SolutionInfo Solution;
public ProjectInfo Project;
public string PackageId;
public string ActualVersion;
public NuGetVersion ExpectedVersion;
}
private void EnsureConsistentGraph(IEnumerable<ArtifactInfo.Package> packages, IEnumerable<SolutionInfo> solutions)
{
// ensure versions cascade
var buildPackageMap = packages.ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);
var dependencyMap = new Dictionary<string, List<ExternalDependency>>(StringComparer.OrdinalIgnoreCase);
foreach (var dep in Dependencies)
{
if (!dependencyMap.TryGetValue(dep.ItemSpec, out var versions))
{
dependencyMap[dep.ItemSpec] = versions = new List<ExternalDependency>();
}
versions.Add(new ExternalDependency
{
PackageId = dep.ItemSpec,
Version = dep.GetMetadata("Version"),
});
}
var inconsistentVersions = new List<VersionMismatch>();
foreach (var solution in solutions)
foreach (var project in solution.Projects)
foreach (var tfm in project.Frameworks)
foreach (var dependency in tfm.Dependencies)
{
var dependencyVersion = dependency.Value.Version;
if (!buildPackageMap.TryGetValue(dependency.Key, out var package))
{
// This dependency is not one of the packages that will be compiled by this run of Universe.
// Must match an external dependency, including its Version.
var idx = -1;
if (!dependencyMap.TryGetValue(dependency.Key, out var externalVersions)
|| (idx = externalVersions.FindIndex(0, externalVersions.Count, i => i.Version == dependencyVersion)) < 0)
{
Log.LogKoreBuildError(
project.FullPath,
KoreBuildErrors.UndefinedExternalDependency,
message: $"Undefined external dependency on {dependency.Key}/{dependencyVersion}");
}
if (idx >= 0)
{
externalVersions[idx].IsReferenced = true;
}
continue;
}
// This package will be created in this Universe run.
var refVersion = VersionRange.Parse(dependencyVersion);
if (refVersion.IsFloating && refVersion.Float.Satisfies(package.PackageInfo.Version))
{
continue;
}
else if (package.PackageInfo.Version.Equals(refVersion.MinVersion))
{
continue;
}
else if (dependencyMap.TryGetValue(dependency.Key, out var externalDependency) &&
externalDependency.Any(ext => ext.Version == dependencyVersion))
{
// Project depends on external version of this package, not the version built in Universe. That's
// fine in benchmark apps for example.
continue;
}
var shouldCascade = (solution.PatchPolicy & PatchPolicy.CascadeVersions) != 0;
if (!solution.ShouldBuild && !solution.IsPatching && shouldCascade)
{
var repoName = Path.GetFileName(Path.GetDirectoryName(solution.FullPath));
Log.LogError($"{repoName} should not be marked 'IsPatching=false'. Version changes in other repositories mean it should be patched to perserve cascading version upgrades.");
}
if (shouldCascade)
{
inconsistentVersions.Add(new VersionMismatch
{
Solution = solution,
Project = project,
PackageId = dependency.Key,
ActualVersion = dependency.Value.Version,
ExpectedVersion = package.PackageInfo.Version,
});
}
}
if (inconsistentVersions.Count != 0)
{
var sb = new StringBuilder();
sb.AppendLine();
sb.AppendLine($"Repos are inconsistent. The following projects have PackageReferences that should be updated");
foreach (var solution in inconsistentVersions.GroupBy(p => p.Solution.FullPath))
{
sb.Append(" - ").AppendLine(Path.GetFileName(solution.Key));
foreach (var project in solution.GroupBy(p => p.Project.FullPath))
{
sb.Append(" - ").AppendLine(Path.GetFileName(project.Key));
foreach (var mismatchedReference in project)
{
sb.AppendLine($" + {mismatchedReference.PackageId}/{{{mismatchedReference.ActualVersion} => {mismatchedReference.ExpectedVersion}}}");
}
}
}
sb.AppendLine();
Log.LogMessage(MessageImportance.High, sb.ToString());
Log.LogError("Package versions are inconsistent. See build log for details.");
}
foreach (var versions in dependencyMap.Values)
{
foreach (var item in versions.Where(i => !i.IsReferenced))
{
// See https://github.com/aspnet/Universe/wiki/Build-warning-and-error-codes#potentially-unused-external-dependency for details
Log.LogMessage(MessageImportance.Normal, $"Potentially unused external dependency: {item.PackageId}/{item.Version}. See https://github.com/aspnet/Universe/wiki/Build-warning-and-error-codes for details.");
}
}
}
private ITaskItem[] GetRepositoryBuildOrder(IEnumerable<ArtifactInfo.Package> artifacts, IEnumerable<SolutionInfo> solutions)
{
var repositories = solutions.Select(s =>
{
var repoName = Path.GetFileName(Path.GetDirectoryName(s.FullPath));
var repo = new Repository(repoName)
{
RootDir = Path.GetDirectoryName(s.FullPath)
};
var packages = artifacts
.Where(a => string.Equals(a.RepoName, repoName, StringComparison.OrdinalIgnoreCase))
.ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);
foreach (var proj in s.Projects)
{
IList<Project> projectGroup;
if (packages.ContainsKey(proj.PackageId))
{
// this project is a package producer and consumer
packages.Remove(proj.PackageId);
projectGroup = repo.Projects;
}
else
{
// this project is a package consumer
projectGroup = repo.SupportProjects;
}
projectGroup.Add(new Project(proj.PackageId)
{
Repository = repo,
PackageReferences = new HashSet<string>(proj
.Frameworks
.SelectMany(f => f.Dependencies.Keys)
.Concat(proj.Tools.Select(t => t.Id)), StringComparer.OrdinalIgnoreCase),
});
}
foreach (var packageId in packages.Keys)
{
// these packages are produced from something besides a csproj. e.g. .Sources packages
repo.Projects.Add(new Project(packageId) { Repository = repo });
}
return repo;
}).ToList();
var graph = GraphBuilder.Generate(repositories, Log);
var repositoriesWithOrder = new List<(ITaskItem repository, int order)>();
foreach (var repository in repositories)
{
var graphNodeRepository = graph.FirstOrDefault(g => g.Repository.Name == repository.Name);
if (graphNodeRepository == null)
{
continue;
}
var order = TopologicalSort.GetOrder(graphNodeRepository);
var repositoryTaskItem = new TaskItem(repository.Name);
repositoryTaskItem.SetMetadata("Order", order.ToString());
repositoryTaskItem.SetMetadata("RootPath", repository.RootDir);
repositoriesWithOrder.Add((repositoryTaskItem, order));
}
return repositoriesWithOrder
.OrderBy(r => r.order)
.Select(r => r.repository)
.ToArray();
}
}
}

Просмотреть файл

@ -1,12 +0,0 @@
// 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.
namespace RepoTools.BuildGraph
{
internal class ExternalDependency
{
public string PackageId { get; set; }
public string Version { get; set; }
public bool IsReferenced { get; set; }
}
}

Просмотреть файл

@ -1,50 +0,0 @@
// 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.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Utilities;
namespace RepoTools.BuildGraph
{
public static class GraphBuilder
{
public static IList<GraphNode> Generate(IList<Repository> repositories, TaskLoggingHelper log)
{
// Build global list of primary projects
var primaryProjects = repositories.SelectMany(c => c.Projects)
.ToDictionary(p => p.Name, StringComparer.OrdinalIgnoreCase);
var graphNodes = repositories.Select(r => new GraphNode { Repository = r })
.ToDictionary(r => r.Repository);
foreach (var project in repositories.SelectMany(r => r.AllProjects))
{
var thisProjectRepositoryNode = graphNodes[project.Repository];
foreach (var packageDependency in project.PackageReferences)
{
if (primaryProjects.TryGetValue(packageDependency, out var dependencyProject))
{
var dependencyRepository = dependencyProject.Repository;
var dependencyNode = graphNodes[dependencyRepository];
if (ReferenceEquals(thisProjectRepositoryNode, dependencyNode))
{
log.LogWarning("{0} has a package reference to a package produced in the same repo. {1} -> {2}", project.Repository.Name, Path.GetFileName(project.Path), packageDependency);
}
else
{
thisProjectRepositoryNode.Incoming.Add(dependencyNode);
}
dependencyNode.Outgoing.Add(thisProjectRepositoryNode);
}
}
}
return graphNodes.Values.ToList();
}
}
}

Просмотреть файл

@ -1,18 +0,0 @@
// 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.Collections.Generic;
using System.Diagnostics;
namespace RepoTools.BuildGraph
{
[DebuggerDisplay("{Repository.Name}")]
public class GraphNode
{
public Repository Repository { get; set; }
public ISet<GraphNode> Incoming { get; } = new HashSet<GraphNode>();
public ISet<GraphNode> Outgoing { get; } = new HashSet<GraphNode>();
}
}

Просмотреть файл

@ -1,28 +0,0 @@
// 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.Generic;
using System.Diagnostics;
namespace RepoTools.BuildGraph
{
[DebuggerDisplay("{Name}")]
public class Project
{
public Project(string name)
{
Name = name;
}
public string Name { get; }
public string Version { get; set; }
public string Path { get; set; }
public Repository Repository { get; set; }
public ISet<string> PackageReferences { get; set; } = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
}
}

Просмотреть файл

@ -1,33 +0,0 @@
// 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.Generic;
using System.Diagnostics;
using System.Linq;
namespace RepoTools.BuildGraph
{
[DebuggerDisplay("{Name}")]
public class Repository : IEquatable<Repository>
{
public Repository(string name)
{
Name = name;
}
public string Name { get; private set; }
public string RootDir { get; set; }
public IList<Project> Projects { get; } = new List<Project>();
public IList<Project> SupportProjects { get; } = new List<Project>();
public IEnumerable<Project> AllProjects => Projects.Concat(SupportProjects);
public bool Equals(Repository other) => string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase);
public override int GetHashCode() => StringComparer.OrdinalIgnoreCase.GetHashCode(Name);
}
}

Просмотреть файл

@ -1,46 +0,0 @@
// 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.Generic;
using System.Linq;
namespace RepoTools.BuildGraph
{
public class TopologicalSort : IComparer<GraphNode>
{
public static readonly TopologicalSort Instance = new TopologicalSort();
public int Compare(GraphNode x, GraphNode y)
{
var xScore = GetOrder(x);
var yScore = GetOrder(y);
return xScore.CompareTo(yScore);
}
public static int GetOrder(GraphNode node)
{
var visited = new List<GraphNode>();
return GetOrder(node, visited);
}
private static int GetOrder(GraphNode node, List<GraphNode> visited)
{
if (visited.Contains(node))
{
var cycle = string.Join(" -> ", visited.Select(v => v.Repository.Name));
throw new Exception($"Cycle detected in the build graph: {cycle} -> {node.Repository.Name}.");
}
var score = 0;
visited.Add(node);
foreach (var dependentNode in node.Incoming)
{
score = Math.Max(score, GetOrder(dependentNode, visited));
}
visited.RemoveAt(visited.Count - 1);
return score + 1;
}
}
}

Просмотреть файл

@ -1,223 +0,0 @@
// 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<ArtifactInfo.Package>()
.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<PackageIdentity, ProjectInfo>();
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<ProjectFrameworkInfo>(),
Array.Empty<DotNetCliReferenceInfo>(),
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];
var policy = Enum.Parse<PatchPolicy>(repo.GetMetadata("PatchPolicy"));
if ((policy & PatchPolicy.AlwaysUpdate) != 0 && !src.IsPatching)
{
Log.LogError($"{repoName} is not currently set to patch, but it should because the policy is set to always include this in servicing updates. Update the configuration in submodule.props.");
continue;
}
var srcShouldCascade = (policy & PatchPolicy.CascadeVersions) != 0;
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 (srcShouldCascade && !src.IsPatching && target.IsPatching)
{
Log.LogError($"{repoName} should be patching because it depend on {targetRepoName} and its patch policy is to cascade version changes. 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;
}
}
}
}

Просмотреть файл

@ -1,42 +0,0 @@
// 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.Xml.Linq;
namespace RepoTasks.CodeGen
{
class DirectedGraphXml
{
private readonly XNamespace _ns = "http://schemas.microsoft.com/vs/2009/dgml";
private readonly XDocument _doc;
private readonly XElement _nodes;
private readonly XElement _links;
public DirectedGraphXml()
{
_doc = new XDocument(new XElement(_ns + "DirectedGraph"));
_nodes = new XElement(_ns + "Nodes");
_links = new XElement(_ns + "Links");
_doc.Root.Add(_nodes);
_doc.Root.Add(_links);
}
public void AddNode(string id)
{
_nodes.Add(new XElement(_ns + "Node", new XAttribute("Id", id), new XAttribute("Label", id)));
}
public void AddLink(string source, string target)
{
_links.Add(new XElement(_ns + "Link",
new XAttribute("Source", source),
new XAttribute("Target", target)));
}
public void Save(string path)
{
_doc.Save(path);
}
}
}

Просмотреть файл

@ -1,226 +0,0 @@
// 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.Versioning;
using RepoTools.BuildGraph;
using RepoTasks.ProjectModel;
using RepoTasks.Utilities;
using RepoTasks.CodeGen;
using NuGet.Packaging.Core;
namespace RepoTasks
{
public class GenerateSubmoduleGraph : Task, ICancelableTask
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
/// <summary>
/// Repositories that we are building new versions of.
/// </summary>
[Required]
public ITaskItem[] Solutions { get; set; }
[Required]
public ITaskItem[] Artifacts { get; set; }
[Required]
public ITaskItem[] Repositories { get; set; }
[Required]
public string RepositoryRoot { get; set; }
[Required]
public string Properties { get; set; }
public void Cancel()
{
_cts.Cancel();
}
public override bool Execute()
{
var packageArtifacts = Artifacts.Select(ArtifactInfo.Parse)
.OfType<ArtifactInfo.Package>()
.Where(p => !p.IsSymbolsArtifact)
.ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);
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, 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;
}
return GenerateGraph(packageArtifacts, solutions);
}
private bool GenerateGraph(IDictionary<string, ArtifactInfo.Package> packageArtifacts, IReadOnlyList<SolutionInfo> solutions)
{
var repoGraph = new AdjacencyMatrix(solutions.Count);
var packageToProjectMap = new Dictionary<PackageIdentity, ProjectInfo>();
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"))
{
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 = Path.GetFileName(dir);
var artifactInfo = packageArtifacts[id];
var sharedSrcProj = new ProjectInfo(dir,
Array.Empty<ProjectFrameworkInfo>(),
Array.Empty<DotNetCliReferenceInfo>(),
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 sln = repoGraph[i];
var deps = from proj in sln.Projects
from tfm in proj.Frameworks
from dep in tfm.Dependencies.Values
select dep;
foreach (var dep in deps)
{
if (packageToProjectMap.TryGetValue(new PackageIdentity(dep.Id, new NuGetVersion(dep.Version)), out var target))
{
var j = repoGraph.FindIndex(target.SolutionInfo);
repoGraph.SetLink(i, j);
}
}
var toolDeps = from proj in sln.Projects
from tool in proj.Tools
select tool;
foreach (var toolDep in toolDeps)
{
if (packageToProjectMap.TryGetValue(new PackageIdentity(toolDep.Id, new NuGetVersion(toolDep.Version)), out var target))
{
var j = repoGraph.FindIndex(target.SolutionInfo);
repoGraph.SetLink(i, j);
}
}
}
CreateDgml(repoGraph);
return !Log.HasLoggedErrors;
}
private void CreateDgml(AdjacencyMatrix repoGraph)
{
var dgml = new DirectedGraphXml();
for (var i = 0; i < repoGraph.Count; i++)
{
var node = repoGraph[i];
var nodeName = Path.GetFileName(node.Directory);
dgml.AddNode(nodeName);
for (var j = 0; j < repoGraph.Count; j++)
{
if (j == i) continue;
if (repoGraph.HasLink(i, j))
{
var target = repoGraph[j];
var targetName = Path.GetFileName(target.Directory);
dgml.AddLink(nodeName, targetName);
}
}
}
dgml.Save(Path.Combine(RepositoryRoot, "modules", "SubmoduleGraph.dgml"));
}
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;
}
}
}
}

Просмотреть файл

@ -1,46 +0,0 @@
// 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.IO;
using System.Text;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
namespace RepoTasks.CodeGen
{
class RepositoryProject
{
private readonly ProjectRootElement _doc;
public RepositoryProject(string repositoryRoot)
{
_doc = ProjectRootElement.Create(NewProjectFileOptions.None);
var import = _doc.CreateImportElement(@"$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props");
var propGroup = _doc.AddPropertyGroup();
if (repositoryRoot[repositoryRoot.Length - 1] != '\\')
{
repositoryRoot += '\\';
}
propGroup.AddProperty("RepositoryRoot", repositoryRoot);
_doc.AddItemGroup();
_doc.PrependChild(import);
_doc.AddImport(@"$(MSBuildToolsPath)\Microsoft.Common.targets");
}
public void AddProjectReference(string path)
{
_doc.AddItem("ProjectReference", path);
}
public void AddProperty(string name, string value)
{
_doc.AddProperty(name, value);
}
public void Save(string filePath)
{
_doc.Save(filePath, Encoding.UTF8);
}
}
}

Просмотреть файл

@ -1,24 +0,0 @@
// 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;
namespace RepoTasks.ProjectModel
{
internal class DotNetCliReferenceInfo
{
public DotNetCliReferenceInfo(string id, string version)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentException(nameof(id));
}
Id = id;
Version = version;
}
public string Id { get; }
public string Version { get; }
}
}

Просмотреть файл

@ -1,44 +0,0 @@
// 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.Generic;
using System.IO;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Versioning;
namespace RepoTasks.ProjectModel
{
internal class PackageInfo
{
public PackageInfo(string id,
NuGetVersion version,
IReadOnlyList<PackageDependencyGroup> dependencyGroups,
string source,
string packageType = "Dependency")
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentException(nameof(id));
}
Id = id;
Version = version ?? throw new ArgumentNullException(nameof(version));
PackageType = packageType;
Source = source;
DependencyGroups = dependencyGroups ?? Array.Empty<PackageDependencyGroup>();
}
public string Id { get; }
public NuGetVersion Version { get; }
public string PackageType { get; }
/// <summary>
/// Can be a https feed or a file path. May be null.
/// </summary>
public string Source { get; }
public IReadOnlyList<PackageDependencyGroup> DependencyGroups { get; }
public override string ToString() => $"{Id}/{Version}";
}
}

Просмотреть файл

@ -1,27 +0,0 @@
// 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.Generic;
namespace RepoTasks.ProjectModel
{
internal class PackageReferenceInfo
{
public PackageReferenceInfo(string id, string version, bool isImplicitlyDefined)
{
if (string.IsNullOrEmpty(id))
{
throw new ArgumentException(nameof(id));
}
Id = id;
Version = version;
IsImplicitlyDefined = isImplicitlyDefined;
}
public string Id { get; }
public string Version { get; }
public bool IsImplicitlyDefined { get; }
}
}

Просмотреть файл

@ -1,31 +0,0 @@
// 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.Generic;
using System.Linq;
using NuGet.Frameworks;
namespace RepoTasks.ProjectModel
{
[Flags]
internal enum PatchPolicy
{
/// <summary>
/// Only produce new package versions if there were changes to product code.
/// </summary>
ProductChangesOnly = 1 << 0,
/// <summary>
/// Packages should update in every patch.
/// </summary>
AlwaysUpdate = 1 << 1,
/// <summary>
/// Produce new package versions if there were changes to product code, or if one of the package dependencies has updated.
/// </summary>
CascadeVersions = 1 << 2,
AlwaysUpdateAndCascadeVersions = CascadeVersions | AlwaysUpdate,
}
}

Просмотреть файл

@ -1,22 +0,0 @@
// 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.Generic;
using System.Linq;
using NuGet.Frameworks;
namespace RepoTasks.ProjectModel
{
internal class ProjectFrameworkInfo
{
public ProjectFrameworkInfo(NuGetFramework targetFramework, IReadOnlyDictionary<string, PackageReferenceInfo> dependencies)
{
TargetFramework = targetFramework ?? throw new ArgumentNullException(nameof(targetFramework));
Dependencies = dependencies ?? throw new ArgumentNullException(nameof(dependencies));
}
public NuGetFramework TargetFramework { get; }
public IReadOnlyDictionary<string, PackageReferenceInfo> Dependencies { get; }
}
}

Просмотреть файл

@ -1,47 +0,0 @@
// 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.Generic;
using System.IO;
namespace RepoTasks.ProjectModel
{
internal class ProjectInfo
{
public ProjectInfo(string fullPath,
IReadOnlyList<ProjectFrameworkInfo> frameworks,
IReadOnlyList<DotNetCliReferenceInfo> tools,
bool isPackable,
string packageId,
string packageVersion)
{
if (!Path.IsPathRooted(fullPath))
{
throw new ArgumentException("Path must be absolute", nameof(fullPath));
}
Frameworks = frameworks ?? throw new ArgumentNullException(nameof(frameworks));
Tools = tools ?? throw new ArgumentNullException(nameof(tools));
FullPath = fullPath;
FileName = Path.GetFileName(fullPath);
Directory = Path.GetDirectoryName(FullPath);
IsPackable = isPackable;
PackageId = packageId;
PackageVersion = packageVersion;
}
public string FullPath { get; }
public string FileName { get; }
public string Directory { get; }
public string PackageId { get; }
public string PackageVersion { get; }
public bool IsPackable { get; }
public SolutionInfo SolutionInfo { get; set; }
public IReadOnlyList<ProjectFrameworkInfo> Frameworks { get; }
public IReadOnlyList<DotNetCliReferenceInfo> Tools { get; }
}
}

Просмотреть файл

@ -1,137 +0,0 @@
// 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.Generic;
using System.IO;
using System.Linq;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using NuGet.Frameworks;
using RepoTasks.Utilities;
using Microsoft.Build.Utilities;
namespace RepoTasks.ProjectModel
{
internal class ProjectInfoFactory
{
private readonly TaskLoggingHelper _logger;
public ProjectInfoFactory(TaskLoggingHelper logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public ProjectInfo Create(string path, ProjectCollection projectCollection)
{
var project = GetProject(path, projectCollection);
var instance = project.CreateProjectInstance(ProjectInstanceSettings.ImmutableWithFastItemLookup);
var targetFrameworks = instance.GetPropertyValue("TargetFrameworks");
var targetFramework = instance.GetPropertyValue("TargetFramework");
var frameworks = new List<ProjectFrameworkInfo>();
if (!string.IsNullOrEmpty(targetFrameworks) && string.IsNullOrEmpty(targetFramework))
{
// multi targeting
foreach (var tfm in targetFrameworks.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
{
project.SetGlobalProperty("TargetFramework", tfm);
var innerBuild = project.CreateProjectInstance(ProjectInstanceSettings.ImmutableWithFastItemLookup);
var tfmInfo = new ProjectFrameworkInfo(NuGetFramework.Parse(tfm), GetDependencies(innerBuild));
frameworks.Add(tfmInfo);
}
project.RemoveGlobalProperty("TargetFramework");
}
else if (!string.IsNullOrEmpty(targetFramework))
{
var tfmInfo = new ProjectFrameworkInfo(NuGetFramework.Parse(targetFramework), GetDependencies(instance));
frameworks.Add(tfmInfo);
}
var projectDir = Path.GetDirectoryName(path);
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,
frameworks,
tools,
isPackable,
packageId,
packageVersion);
}
private static object _projLock = new object();
private static Project GetProject(string path, ProjectCollection projectCollection)
{
var projects = projectCollection.GetLoadedProjects(path);
foreach(var proj in projects)
{
if (proj.GetPropertyValue("DesignTimeBuild") == "true")
{
return proj;
}
}
var xml = ProjectRootElement.Open(path, projectCollection);
var globalProps = new Dictionary<string, string>()
{
["DesignTimeBuild"] = "true",
// Isolate the project from post-restore side effects
["ExcludeRestorePackageImports"] = "true",
};
var project = new Project(xml,
globalProps,
toolsVersion: "15.0",
projectCollection: projectCollection)
{
IsBuildEnabled = false
};
return project;
}
private IReadOnlyDictionary<string, PackageReferenceInfo> GetDependencies(ProjectInstance project)
{
var references = new Dictionary<string, PackageReferenceInfo>(StringComparer.OrdinalIgnoreCase);
foreach (var item in project.GetItems("PackageReference"))
{
bool.TryParse(item.GetMetadataValue("IsImplicitlyDefined"), out var isImplicit);
var info = new PackageReferenceInfo(item.EvaluatedInclude, item.GetMetadataValue("Version"), isImplicit);
if (references.ContainsKey(info.Id))
{
_logger.LogKoreBuildWarning(project.ProjectFileLocation.File, KoreBuildErrors.DuplicatePackageReference, $"Found a duplicate PackageReference for {info.Id}. Restore results may be unpredictable.");
}
references[info.Id] = info;
}
return references;
}
private static IEnumerable<DotNetCliReferenceInfo> GetTools(ProjectInstance project)
{
return project.GetItems("DotNetCliToolReference").Select(item =>
new DotNetCliReferenceInfo(item.EvaluatedInclude, item.GetMetadataValue("Version")));
}
}
}

Просмотреть файл

@ -1,44 +0,0 @@
// 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.Generic;
using System.IO;
namespace RepoTasks.ProjectModel
{
internal class SolutionInfo
{
public SolutionInfo(string fullPath, string configName, IReadOnlyList<ProjectInfo> projects, bool shouldBuild, bool isPatching)
{
if (string.IsNullOrEmpty(fullPath))
{
throw new ArgumentException(nameof(fullPath));
}
if (string.IsNullOrEmpty(configName))
{
throw new ArgumentException(nameof(configName));
}
FullPath = fullPath;
Directory = Path.GetDirectoryName(fullPath);
ConfigName = configName;
Projects = projects ?? throw new ArgumentNullException(nameof(projects));
ShouldBuild = shouldBuild;
IsPatching = isPatching;
foreach (var proj in Projects)
{
proj.SolutionInfo = this;
}
}
public string FullPath { get; }
public string Directory { get; }
public string ConfigName { get; }
public IReadOnlyList<ProjectInfo> Projects { get; }
public bool ShouldBuild { get; }
public bool IsPatching { get; }
public PatchPolicy PatchPolicy { get; set; }
}
}

Просмотреть файл

@ -1,136 +0,0 @@
// 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.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using RepoTasks.Utilities;
namespace RepoTasks.ProjectModel
{
internal class SolutionInfoFactory
{
private readonly TaskLoggingHelper _logger;
private readonly IBuildEngine4 _buildEngine;
public SolutionInfoFactory(TaskLoggingHelper logger, IBuildEngine4 buildEngine)
{
_logger = logger;
_buildEngine = buildEngine;
}
public IReadOnlyList<SolutionInfo> Create(IEnumerable<ITaskItem> solutionItems, IDictionary<string, string> properties, string defaultConfig, CancellationToken ct)
{
var timer = Stopwatch.StartNew();
var solutions = new ConcurrentBag<SolutionInfo>();
Parallel.ForEach(solutionItems, solution =>
{
if (ct.IsCancellationRequested)
{
return;
}
var solutionFile = solution.ItemSpec.Replace('\\', '/');
var solutionProps = new Dictionary<string, string>(properties, StringComparer.OrdinalIgnoreCase);
foreach (var prop in MSBuildListSplitter.GetNamedProperties(solution.GetMetadata("AdditionalProperties")))
{
solutionProps[prop.Key] = prop.Value;
}
if (!solutionProps.TryGetValue("Configuration", out var configName))
{
solutionProps["Configuration"] = configName = defaultConfig;
}
var key = $"SlnInfo:{solutionFile}:{configName}";
var obj = _buildEngine.GetRegisteredTaskObject(key, RegisteredTaskObjectLifetime.Build);
if (obj is SolutionInfo cachedSlnInfo)
{
solutions.Add(cachedSlnInfo);
return;
}
_logger.LogMessage($"Analyzing {solutionFile} ({configName})");
var projects = new ConcurrentBag<ProjectInfo>();
var projectFiles = GetProjectsForSolutionConfig(solutionFile, configName);
using (var projCollection = new ProjectCollection(solutionProps) { IsBuildEnabled = false })
{
Parallel.ForEach(projectFiles, projectFile =>
{
if (ct.IsCancellationRequested)
{
return;
}
try
{
projects.Add(new ProjectInfoFactory(_logger).Create(projectFile, projCollection));
}
catch (Exception ex)
{
_logger.LogErrorFromException(ex);
}
});
}
bool.TryParse(solution.GetMetadata("Build"), out var shouldBuild);
bool.TryParse(solution.GetMetadata("IsPatching"), out var isPatching);
var solutionInfo = new SolutionInfo(
solutionFile,
configName,
projects.ToArray(),
shouldBuild,
isPatching);
_buildEngine.RegisterTaskObject(key, solutionInfo, RegisteredTaskObjectLifetime.Build, allowEarlyCollection: true);
solutions.Add(solutionInfo);
});
timer.Stop();
_logger.LogMessage(MessageImportance.High, $"Finished design-time build in {timer.ElapsedMilliseconds}ms");
return solutions.ToArray();
}
private IList<string> GetProjectsForSolutionConfig(string filePath, string configName)
{
var sln = SolutionFile.Parse(filePath);
if (string.IsNullOrEmpty(configName))
{
configName = sln.GetDefaultConfigurationName();
}
var projects = new List<string>();
var config = sln.SolutionConfigurations.FirstOrDefault(c => c.ConfigurationName == configName);
if (config == null)
{
throw new InvalidOperationException($"A solution configuration by the name of '{configName}' was not found in '{filePath}'");
}
foreach (var project in sln.ProjectsInOrder
.Where(p =>
p.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat // skips solution folders
&& p.ProjectConfigurations.TryGetValue(config.FullName, out var projectConfig)
&& projectConfig.IncludeInBuild))
{
projects.Add(project.AbsolutePath.Replace('\\', '/'));
}
return projects;
}
}
}

Просмотреть файл

@ -4,13 +4,10 @@
</PropertyGroup>
<UsingTask TaskName="RepoTasks.AddMetapackageReferences" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.AnalyzeBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.CheckExpectedPackagesExist" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.CheckRepoGraph" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.CheckVersionOverrides" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.CreateLzma" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.GenerateRestoreSourcesPropsFile" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.GenerateSubmoduleGraph" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.GetDocXmlFiles" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.JoinItems" AssemblyFile="$(_RepoTaskAssembly)" />
<UsingTask TaskName="RepoTasks.OrderBy" AssemblyFile="$(_RepoTaskAssembly)" />

Просмотреть файл

@ -1,70 +0,0 @@
// 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.IO;
using System.Linq;
using NuGet.Frameworks;
using NuGet.Packaging;
using NuGet.Packaging.Core;
using NuGet.Versioning;
using Microsoft.Build.Framework;
using RepoTasks.ProjectModel;
namespace RepoTasks.Utilities
{
internal abstract class ArtifactInfo
{
public static ArtifactInfo Parse(ITaskItem item)
{
ArtifactInfo info;
switch (item.GetMetadata("ArtifactType").ToLowerInvariant())
{
case "nugetpackage":
info = new Package { PackageInfo = GetPackageInfo(item) };
break;
case "nugetsymbolspackage":
info = new Package { PackageInfo = GetPackageInfo(item), IsSymbolsArtifact = true };
break;
default:
return UnknownType.Singleton;
}
info.RepositoryRoot = item.GetMetadata("RepositoryRoot")?.TrimEnd(new [] { '\\', '/' });
if (!string.IsNullOrEmpty(info.RepositoryRoot))
{
info.RepoName = Path.GetFileName(info.RepositoryRoot);
}
return info;
}
public string RepositoryRoot { get; private set; }
public string RepoName { get; private set; }
public class UnknownType : ArtifactInfo
{
private UnknownType() { }
public static UnknownType Singleton { get; } = new UnknownType();
}
public class Package : ArtifactInfo
{
public PackageInfo PackageInfo { get; set; }
public bool IsSymbolsArtifact { get; set; }
}
private static PackageInfo GetPackageInfo(ITaskItem item)
{
return new PackageInfo(
item.GetMetadata("PackageId"),
NuGetVersion.Parse(item.GetMetadata("Version")),
string.IsNullOrEmpty(item.GetMetadata("TargetFramework"))
? MSBuildListSplitter.SplitItemList(item.GetMetadata("TargetFramework")).Select(s => new PackageDependencyGroup(NuGetFramework.Parse(s), Array.Empty<PackageDependency>())).ToArray()
: new [] { new PackageDependencyGroup(NuGetFramework.Parse(item.GetMetadata("TargetFramework")), Array.Empty<PackageDependency>()) },
Path.GetDirectoryName(item.ItemSpec),
item.GetMetadata("PackageType"));
}
}
}

Просмотреть файл

@ -1,31 +0,0 @@
// 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.
namespace RepoTasks.Utilities
{
public static class KoreBuildErrors
{
public const string Prefix = "KRB";
// Typically used in repos in Directory.Build.targets
public const int PackagesHaveNotYetBeenPinned = 1001;
// Warnings
public const int DotNetAssetVersionIsFloating = 2000;
public const int RepoVersionDoesNotMatchProjectVersion = 2001;
public const int RepoPackageVersionDoesNotMatchProjectPackageVersion = 2002;
public const int DuplicatePackageReference = 2003;
// NuGet errors
public const int InvalidNuspecFile = 4001;
public const int PackageReferenceHasVersion = 4002;
public const int DotNetCliReferenceReferenceHasVersion = 4003;
public const int PackageVersionNotFoundInLineup = 4004;
public const int UndefinedExternalDependency = 4005;
public const int EmptyPackageReferenceVersion = 4006;
// Other unknown errors
public const int PolicyFailedToApply = 5000;
public const int UnknownPolicyType = 5001;
}
}

Просмотреть файл

@ -1,26 +0,0 @@
// 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 Microsoft.Build.Utilities;
namespace RepoTasks.Utilities
{
public static class LoggingExtensions
{
public static void LogKoreBuildError(this TaskLoggingHelper logger, int code, string message, params object[] messageArgs)
=> LogKoreBuildError(logger, null, code, message, messageArgs: messageArgs);
public static void LogKoreBuildError(this TaskLoggingHelper logger, string filename, int code, string message, params object[] messageArgs)
{
logger.LogError(null, KoreBuildErrors.Prefix + code, null, filename, 0, 0, 0, 0, message, messageArgs: messageArgs);
}
public static void LogKoreBuildWarning(this TaskLoggingHelper logger, int code, string message, params object[] messageArgs)
=> LogKoreBuildWarning(logger, null, code, message, messageArgs: messageArgs);
public static void LogKoreBuildWarning(this TaskLoggingHelper logger, string filename, int code, string message, params object[] messageArgs)
{
logger.LogWarning(null, KoreBuildErrors.Prefix + code, null, filename, 0, 0, 0, 0, message, messageArgs: messageArgs);
}
}
}

Просмотреть файл

@ -1,45 +0,0 @@
// 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.Generic;
using System.Linq;
namespace RepoTasks.Utilities
{
internal static class MSBuildListSplitter
{
private static readonly char[] SemiColon = { ';' };
public static IEnumerable<string> SplitItemList(string value)
{
return string.IsNullOrEmpty(value)
? Enumerable.Empty<string>()
: value.Split(SemiColon, StringSplitOptions.RemoveEmptyEntries);
}
public static Dictionary<string, string> GetNamedProperties(string input)
{
var values = new Dictionary<string, string>();
if (string.IsNullOrEmpty(input))
{
return values;
}
foreach (var item in input.Split(SemiColon, StringSplitOptions.RemoveEmptyEntries))
{
var splitIdx = item.IndexOf('=');
if (splitIdx <= 0)
{
continue;
}
var key = item.Substring(0, splitIdx).Trim();
var value = item.Substring(splitIdx + 1);
values[key] = value;
}
return values;
}
}
}

Просмотреть файл

@ -3,6 +3,6 @@
"version": "2.2.100-preview2-009404"
},
"msbuild-sdks": {
"Internal.AspNetCore.Sdk": "2.2.0-preview2-20181004.1"
"Internal.AspNetCore.Sdk": "2.2.0-preview2-20181004.6"
}
}

Просмотреть файл

@ -66,6 +66,7 @@ try {
exit 0
}
Invoke-Block { git add "$RepoRoot/global.json" }
Invoke-Block { git add "$RepoRoot/korebuild-lock.txt" }
Invoke-Block { git add "$RepoRoot/build/dependencies.props" }