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:
Родитель
18b61e9217
Коммит
eaad1eb3aa
|
@ -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" }
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче