diff --git a/build/dependencies.props b/build/dependencies.props
new file mode 100644
index 0000000000..7094a9050b
--- /dev/null
+++ b/build/dependencies.props
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+ false
+
+
+
+
+
+ https://dotnet.myget.org/F/roslyn/api/v3/index.json
+
+
+
+
+
+ KRB2004
+
+
+
+ KRB2004
+
+
+
+
+
+ KRB2004
+
+
+
+
+
+
+
+ https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json
+
+
+
+
+
+
+
+
+ https://dotnet.myget.org/F/aspnetcoremodule/api/v3/index.json
+
+
+
+
+
+
+
+
+ https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json
+
+
+
+
+
+
+
+
+
+ https://api.nuget.org/v3/index.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ KRB2004
+
+
+
+
+ KRB2004
+
+
+
+ KRB2004
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ KRB2004
+
+
+
+ KRB2004
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ KRB2004
+
+
+
+ KRB2004
+
+
+
+
+
+ KRB2004
+
+
+
+ KRB2004
+
+
+
diff --git a/build/repo.beforecommon.props b/build/repo.beforecommon.props
new file mode 100644
index 0000000000..0550358981
--- /dev/null
+++ b/build/repo.beforecommon.props
@@ -0,0 +1,10 @@
+
+
+
+ true
+
+
diff --git a/build/repo.props b/build/repo.props
index a98645c000..63a8d665d0 100644
--- a/build/repo.props
+++ b/build/repo.props
@@ -5,4 +5,5 @@
+
diff --git a/build/repo.targets b/build/repo.targets
index f6fb26a5c9..2ddd3f45e9 100644
--- a/build/repo.targets
+++ b/build/repo.targets
@@ -75,9 +75,9 @@
-
+ BuildInParallel="$(BuildInParallel)" />
@@ -161,6 +161,7 @@
@@ -199,13 +200,15 @@
DestinationFolder="$(ArtifactsDir)" />
-
+
+
+ PackageFiles="@(ShippingPackageFiles)"
+ ExternalDependencies="@(ExternalDependency);@(ShippedExternalDependency)" />
diff --git a/build/tasks/AnalyzeBuildGraph.cs b/build/tasks/AnalyzeBuildGraph.cs
index 7776f07319..1119b21dcf 100644
--- a/build/tasks/AnalyzeBuildGraph.cs
+++ b/build/tasks/AnalyzeBuildGraph.cs
@@ -31,6 +31,9 @@ namespace RepoTasks
[Required]
public ITaskItem[] Artifacts { get; set; }
+ [Required]
+ public ITaskItem[] Dependencies { get; set; }
+
// Artifacts that already shipped from repos
[Required]
public ITaskItem[] ShippedArtifacts { get; set; }
@@ -95,6 +98,21 @@ namespace RepoTasks
// ensure versions cascade
var buildPackageMap = packages.ToDictionary(p => p.PackageInfo.Id, p => p, StringComparer.OrdinalIgnoreCase);
+ var dependencyMap = new Dictionary>(StringComparer.OrdinalIgnoreCase);
+ foreach (var dep in Dependencies)
+ {
+ if (!dependencyMap.TryGetValue(dep.ItemSpec, out var versions))
+ {
+ dependencyMap[dep.ItemSpec] = versions = new List();
+ }
+ else if (dep.GetMetadata("NoWarn") == null || dep.GetMetadata("NoWarn").IndexOf("KRB" + KoreBuildErrors.MultipleExternalDependencyVersions) < 0)
+ {
+ Log.LogKoreBuildWarning(
+ KoreBuildErrors.MultipleExternalDependencyVersions,
+ message: $"Multiple versions of external dependency '{dep.ItemSpec}' are defined. In most cases, there should only be one version of external dependencies.");
+ }
+ versions.Add(dep.GetMetadata("Version"));
+ }
var inconsistentVersions = new List();
var reposThatShouldPatch = new HashSet();
@@ -107,7 +125,25 @@ namespace RepoTasks
{
if (!buildPackageMap.TryGetValue(dependency.Key, out var package))
{
- // this dependency is not a PackageReference to something that we build in Universe
+ // This dependency is not one of the packages that will be compiled by this run of Universe.
+
+ var matchesExternalDependency = false;
+ if (shippedPackageMap.TryGetValue(dependency.Key, out var shippedPackage))
+ {
+ matchesExternalDependency = shippedPackage.PackageInfo.Version.Equals(NuGetVersion.Parse(dependency.Value.Version));
+ }
+ else if (dependencyMap.TryGetValue(dependency.Key, out var externalVersions))
+ {
+ matchesExternalDependency = externalVersions.Contains(dependency.Value.Version);
+ }
+
+ if (!matchesExternalDependency)
+ {
+ Log.LogKoreBuildError(
+ project.FullPath,
+ KoreBuildErrors.UndefinedExternalDependency,
+ message: $"Undefined external dependency on {dependency.Key}/{dependency.Value.Version}");
+ }
continue;
}
diff --git a/build/tasks/Utilities/KoreBuildErrors.cs b/build/tasks/Utilities/KoreBuildErrors.cs
index 7751985c6f..b3693e5011 100644
--- a/build/tasks/Utilities/KoreBuildErrors.cs
+++ b/build/tasks/Utilities/KoreBuildErrors.cs
@@ -15,12 +15,14 @@ namespace RepoTasks.Utilities
public const int RepoVersionDoesNotMatchProjectVersion = 2001;
public const int RepoPackageVersionDoesNotMatchProjectPackageVersion = 2002;
public const int DuplicatePackageReference = 2003;
+ public const int MultipleExternalDependencyVersions = 2004;
// 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;
// Other unknown errors
public const int PolicyFailedToApply = 5000;
diff --git a/build/tasks/VerifyCoherentVersions.cs b/build/tasks/VerifyCoherentVersions.cs
index 6001041d0e..1ea4afcfce 100644
--- a/build/tasks/VerifyCoherentVersions.cs
+++ b/build/tasks/VerifyCoherentVersions.cs
@@ -18,11 +18,27 @@ namespace RepoTasks
{
public class VerifyCoherentVersions : Microsoft.Build.Utilities.Task
{
+ [Required]
public ITaskItem[] PackageFiles { get; set; }
+ [Required]
+ public ITaskItem[] ExternalDependencies { get; set; }
+
public override bool Execute()
{
var packageLookup = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ var dependencyMap = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var dep in ExternalDependencies)
+ {
+ if (!dependencyMap.TryGetValue(dep.ItemSpec, out var externalDep))
+ {
+ dependencyMap[dep.ItemSpec] = externalDep = new ExternalDependency();
+ }
+ externalDep.IsPrivate = bool.TryParse(dep.GetMetadata("Private"), out var isPrivate) && isPrivate;
+ externalDep.AllowedVersions.Add(dep.GetMetadata("Version"));
+ }
+
foreach (var file in PackageFiles)
{
PackageInfo package;
@@ -47,51 +63,51 @@ namespace RepoTasks
packageLookup[package.Id] = package;
}
- var dependencyIssues = new List();
foreach (var packageInfo in packageLookup.Values)
{
- dependencyIssues.AddRange(Visit(packageLookup, packageInfo));
- }
-
- var success = true;
- foreach (var mismatch in dependencyIssues)
- {
- var message = $"{mismatch.Info.Id} depends on {mismatch.Dependency.Id} " +
- $"v{mismatch.Dependency.VersionRange} ({mismatch.TargetFramework}) when the latest build is v{mismatch.Info.Version}.";
- Log.LogError(message);
- success = false;
+ Visit(packageLookup, dependencyMap, packageInfo);
}
Log.LogMessage(MessageImportance.High, $"Verified {PackageFiles.Length} package(s) have coherent versions");
- return success;
+ return !Log.HasLoggedErrors;
}
- private class DependencyWithIssue
+ private class ExternalDependency
{
- public PackageDependency Dependency { get; set; }
- public PackageInfo Info { get; set; }
- public NuGetFramework TargetFramework { get; set; }
+ public List AllowedVersions { get; } = new List();
+ public bool IsPrivate { get; set; }
}
- private IEnumerable Visit(IDictionary packageLookup, PackageInfo packageInfo)
+ private void Visit(
+ IReadOnlyDictionary packageLookup,
+ IReadOnlyDictionary dependencyMap,
+ PackageInfo packageInfo)
{
Log.LogMessage(MessageImportance.Low, $"Processing package {packageInfo.Id}");
try
{
- var issues = new List();
foreach (var dependencySet in packageInfo.DependencyGroups)
{
- // If the package doens't target any frameworks, just accept it
- if (dependencySet.TargetFramework == null)
- {
- continue;
- }
-
foreach (var dependency in dependencySet.Packages)
{
- if (!packageLookup.TryGetValue(dependency.Id, out var dependencyPackageInfo))
+ PackageInfo dependencyPackageInfo;
+ var depVersion = dependency.VersionRange.MinVersion.ToString();
+ if (dependencyMap.TryGetValue(dependency.Id, out var externalDepInfo))
{
- // External dependency
+ if (externalDepInfo.IsPrivate)
+ {
+ Log.LogError($"Package {packageInfo.Id} has an external dependency on {dependency.Id}/{depVersion} which is marked as Private=true.");
+ }
+ else if (!externalDepInfo.AllowedVersions.Any(a => depVersion.Equals(a)))
+ {
+ Log.LogError($"Package {packageInfo.Id} has an external dependency on the wrong version of {dependency.Id}. "
+ + $"It uses {depVersion} but only {string.Join(" or ", externalDepInfo.AllowedVersions)} is allowed.");
+ }
+ continue;
+ }
+ else if (!packageLookup.TryGetValue(dependency.Id, out dependencyPackageInfo))
+ {
+ Log.LogError($"Package {packageInfo.Id} has an undefined external dependency on {dependency.Id}/{depVersion}");
continue;
}
@@ -100,21 +116,15 @@ namespace RepoTasks
// For any dependency in the universe
// Add a mismatch if the min version doesn't work out
// (we only really care about >= minVersion)
- issues.Add(new DependencyWithIssue
- {
- Dependency = dependency,
- TargetFramework = dependencySet.TargetFramework,
- Info = dependencyPackageInfo
- });
+ Log.LogError($"{packageInfo.Id} depends on {dependency.Id} " +
+ $"{dependency.VersionRange} ({dependencySet.TargetFramework}) when the latest build is {depVersion}.");
}
}
}
- return issues;
}
- catch
+ catch (Exception ex)
{
- Log.LogError($"Unable to verify package {packageInfo.Id}");
- throw;
+ Log.LogError($"Unexpected error while attempting to verify package {packageInfo.Id}.\r\n{ex}");
}
}
}