diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..4eb7559fce
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,29 @@
+; EditorConfig to support per-solution formatting.
+; Use the EditorConfig VS add-in to make this work.
+; http://editorconfig.org/
+
+; This is the default for the codeline.
+root = true
+
+[*]
+indent_style = space
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{cs}]
+indent_size = 4
+dotnet_sort_system_directives_first = true:warning
+
+[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}]
+indent_size = 2
+
+[*.json]
+indent_size = 2
+
+[*.{ps1,psm1}]
+indent_size = 4
+
+[*.sh]
+indent_size = 4
+end_of_line = lf
diff --git a/.gitignore b/.gitignore
index a0129822f5..9efbb61c7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ node_modules
.deps
global.json
korebuild-lock.txt
+*.binlog
diff --git a/build/Repositories.props b/build/Repositories.props
index 25e5478021..866f06c5ae 100644
--- a/build/Repositories.props
+++ b/build/Repositories.props
@@ -2,6 +2,7 @@
dev
+ true
diff --git a/build/RepositoryBuild.targets b/build/RepositoryBuild.targets
index 660030d6c8..636e016708 100644
--- a/build/RepositoryBuild.targets
+++ b/build/RepositoryBuild.targets
@@ -30,7 +30,7 @@
$(RepositoryBuildArguments) '/p:Universe_Version=$(Version)'
$(RepositoryBuildArguments) '/p:Universe_LineupBuildDir=$(LineupBuildDir)'
- $(RepositoryBuildArguments) '/p:Universe_DependencyLineupDir=$(_DependencyLineupDir)'
+ $(RepositoryBuildArguments) '/p:Universe_AdditionalRestoreSources=$(IntermediateMirrorPackageDir)'
$(RepositoryBuildArguments) '/p:Universe_IntermediateDir=$(IntermediateDir)'
$(RepositoryBuildArguments) '/p:CustomBeforeKoreBuildProps=$(MSBuildThisFileDirectory)repobuild\BeforeKoreBuild.props'
$(RepositoryBuildArguments) '/p:CustomAfterKoreBuildTargets=$(MSBuildThisFileDirectory)repobuild\AfterKoreBuild.targets'
@@ -59,8 +59,6 @@
-
-
@@ -72,15 +70,6 @@
SourceFiles="@(RepositoryMSBuildArtifacts)"
DestinationFolder="$(ArtifactsDir)msbuild\$(RepositoryToBuild)\%(RecursiveDir)" />
-
-
-
-
diff --git a/build/artifacts.props b/build/artifacts.props
new file mode 100644
index 0000000000..799feba584
--- /dev/null
+++ b/build/artifacts.props
@@ -0,0 +1,239 @@
+
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build/dependencies.props b/build/dependencies.props
index 6e179d808c..6e3273a926 100644
--- a/build/dependencies.props
+++ b/build/dependencies.props
@@ -1,12 +1,75 @@
-
+
+
+
+
+
+
+
+
+
+ false
+
+ false
+
+ true
+
+
+
+
- https://dotnet.myget.org/F/aspnetcore-ci-dev/api/v3/index.json
+ https://dotnet.myget.org/f/dotnet-core/api/v3/index.json
-
+
+
+
+
+
+ https://dotnet.myget.org/f/dotnet-corefxlab/api/v3/index.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ https://dotnet.myget.org/F/roslyn/api/v3/index.json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ https://dotnet.myget.org/F/aspnetcoremodule/api/v3/index.json
+
+
+
+
@@ -15,13 +78,14 @@
-
-
-
-
+
+
+
+
+
-
+
@@ -43,32 +107,32 @@
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -130,7 +194,7 @@
-
+
diff --git a/build/lineups/Internal.AspNetCore.Partners.Lineup.nuspec b/build/lineups/Internal.AspNetCore.Partners.Lineup.nuspec
new file mode 100644
index 0000000000..66ebb614e0
--- /dev/null
+++ b/build/lineups/Internal.AspNetCore.Partners.Lineup.nuspec
@@ -0,0 +1,17 @@
+
+
+
+ Internal.AspNetCore.Partners.Lineup
+ $version$
+ Microsoft
+ This package used to unify external dependency versions. Internal use only.
+
+
+
+
+
+
+
+
diff --git a/lineups/Internal.AspNetCore.Universe.Lineup.nuspec b/build/lineups/Internal.AspNetCore.Universe.Lineup.nuspec
similarity index 78%
rename from lineups/Internal.AspNetCore.Universe.Lineup.nuspec
rename to build/lineups/Internal.AspNetCore.Universe.Lineup.nuspec
index 2ca7e0c78f..7ffbde205a 100644
--- a/lineups/Internal.AspNetCore.Universe.Lineup.nuspec
+++ b/build/lineups/Internal.AspNetCore.Universe.Lineup.nuspec
@@ -4,7 +4,7 @@
Internal.AspNetCore.Universe.Lineup
$version$
Microsoft
- This package used to unify dependency versions across all Universe repos. Internal use only.
+ This package used to unify ASP.NET Core package versions across all Universe repos. Internal use only.
diff --git a/build/push.targets b/build/push.targets
new file mode 100644
index 0000000000..a766f7d43c
--- /dev/null
+++ b/build/push.targets
@@ -0,0 +1,23 @@
+
+
+
+
+
+ <_PackagesToPush Include="$(BuildDir)*.nupkg" />
+ <_PackagesToPush Include="$(ArtifactsDir)mirror\*.nupkg" />
+ <_LineupPackagesToPush Include="$(BuildDir)*.nupkg" />
+
+
+
+
+
+
+
diff --git a/build/repo.props b/build/repo.props
index 2e230882b2..40b83474b9 100644
--- a/build/repo.props
+++ b/build/repo.props
@@ -1,13 +1,10 @@
-
+
true
- $(VersionPrefix)
- $(Version)-$(VersionSuffix)
- $(Version)-$(BuildNumber)
diff --git a/build/repo.targets b/build/repo.targets
index f02189a663..d2858b0671 100644
--- a/build/repo.targets
+++ b/build/repo.targets
@@ -2,8 +2,6 @@
- https://dotnet.myget.org/F/aspnetcore-volatile-dev/api/v2/package
-
$(ArtifactsDir)lineups\
<_CloneRepositoryRoot>$(RepositoryRoot).r\
<_DependencyBuildDirectory>$(RepositoryRoot).deps\build\
@@ -20,75 +18,84 @@
<_RepositoryBuildTargets Condition="'$(_RepositoryBuildTargets)'=='' AND '$(CompileOnly)'=='true'">/t:Package /t:VerifyPackages
<_RepositoryBuildTargets Condition="'$(_RepositoryBuildTargets)'==''">/t:Verify
- $(PrepareDependsOn);CleanUniverseArtifacts
- $(CleanDependsOn);CleanUniverseArtifacts
- $(BuildDependsOn);CloneRepositories;BuildRepositories
+
+ $(IntermediateDir)mirror\
+
+ $(IntermediateDir)ext\
+
+ $(PrepareDependsOn);VerifyPackageArtifactConfig;CleanArtifacts;CleanUniverseArtifacts
+ $(RestoreDependsOn);RestoreExternalDependencies
+ $(CleanDependsOn);CleanArtifacts;CleanUniverseArtifacts
+ $(CompileDependsOn);CloneRepositories;BuildRepositories
+ $(PackageDependsOn);CopyPackagesByCategory
+ $(VerifyDependsOn);VerifyCoherentVersions
-
-
+
+
-
-
- <_DependencyPackageFiles Include="$(_DependencyPackagesDirectory)*.nupkg"
- Exclude="$(_DependencyPackagesDirectory)*.symbols.nupkg"
- Condition=" '$(_DependencyPackagesDirectory)' != '' " />
- <_DependencyLineupFiles Include="$(_DependencyLineupDir)*.nupkg"
- Exclude="$(_DependencyLineupDir)Internal.AspNetCore.Universe.Lineup.*.nupkg"
- Condition=" '$(_DependencyLineupDir)' != '' " />
-
-
-
-
-
-
-
-
-
+
+
-
-
- <_Dependency Remove="@(_Dependency)" />
+
+
+
+
-
- <_Dependency Include="@(PackagesProduced)" />
- <_Dependency Include="@(ExternalDependency)" Exclude="@(_Dependency)" />
-
- <_Dependency Include="@(ArtifactDependency)" Exclude="@(_Dependency);@(DependencyLineupDependency)" />
-
+
+
+
-
+
+
+
+
+ Dependencies="@(ExternalDependency->WithMetadataValue('Lineup', 'true'))">
-
-
-
+
+
+
+
+
+
+
<_RepositoriesToInclude Include="$(KOREBUILD_REPOSITORY_INCLUDE)" />
+
+ Update="@(Repository)"
+ Condition="'@(Repository)'=='@(_RepositoriesToInclude)' AND '%(Identity)'!=''"
+ Build="true" />
-
+
@@ -166,12 +175,14 @@
Condition="'$(GateBranchExitCode)'!='0'"
WorkingDirectory="$(_CloneRepositoryRoot)"
EnvironmentVariables="GIT_TERMINAL_PROMPT=0"
+ IgnoreStandardErrorWarningFormat="true"
Timeout="180000" />
@@ -183,7 +194,8 @@
+ ContinueOnError="WarnAndContinue"
+ Condition="%(Repository.Build)">
@@ -213,12 +225,11 @@
-
@@ -227,16 +238,46 @@
NuGetConfigPath="$(_CloneRepositoryRoot)%(Repository.Identity)\NuGet.config"
SourceName="Dependencies"
SourceUri="$(_DependencyPackagesDirectory)"
- Condition="Exists('$(_DependencyPackagesDirectory)')" />
+ Condition="Exists('$(_DependencyPackagesDirectory)') AND %(Repository.Build)" />
+
+
+
+
+ <_MirroredPackageFiles Include="$(IntermediateMirrorPackageDir)*.nupkg" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
$(BuildDir)$(_RepositoryListFileName)
diff --git a/build/repobuild/AfterKoreBuild.targets b/build/repobuild/AfterKoreBuild.targets
index 6db507685d..c482209e13 100644
--- a/build/repobuild/AfterKoreBuild.targets
+++ b/build/repobuild/AfterKoreBuild.targets
@@ -2,5 +2,6 @@
+
diff --git a/build/repobuild/BeforeKoreBuild.props b/build/repobuild/BeforeKoreBuild.props
index 26bd1c6b92..f7fbcf8abd 100644
--- a/build/repobuild/BeforeKoreBuild.props
+++ b/build/repobuild/BeforeKoreBuild.props
@@ -5,6 +5,9 @@
$([MSBuild]::NormalizeDirectory($(Universe_IntermediateDir)))packages\
$(PolicyRestoreAdditionalSources);$(Universe_LineupBuildDir)
- $(PolicyRestoreAdditionalSources);$(Universe_DependencyLineupDir)
+
+
+
+
diff --git a/build/tasks/CalculateBuildGraph.cs b/build/tasks/CalculateBuildGraph.cs
index 1dc3abfecf..0c574f41dd 100644
--- a/build/tasks/CalculateBuildGraph.cs
+++ b/build/tasks/CalculateBuildGraph.cs
@@ -35,9 +35,6 @@ namespace RepoTasks
[Output]
public ITaskItem[] RepositoriesToBuildInOrder { get; set; }
- [Output]
- public ITaskItem[] PackagesProduced { get; set; }
-
public override bool Execute()
{
var graphSpecProvider = new DependencyGraphSpecProvider(PackageSpecsDirectory.Trim());
@@ -74,17 +71,6 @@ namespace RepoTasks
.Select(r => r.repository)
.ToArray();
- var packages = new List();
- foreach (var project in repositories.SelectMany(p => p.Projects))
- {
- var pkg = new TaskItem(project.Name);
- var version = project.Version ?? DefaultPackageVersion;
- pkg.SetMetadata("Version", version);
- packages.Add(pkg);
- }
-
- PackagesProduced = packages.ToArray();
-
return true;
}
}
diff --git a/build/tasks/CopyPackagesToSplitFolders.cs b/build/tasks/CopyPackagesToSplitFolders.cs
new file mode 100644
index 0000000000..682e50806c
--- /dev/null
+++ b/build/tasks/CopyPackagesToSplitFolders.cs
@@ -0,0 +1,110 @@
+// 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.Framework;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+using RepoTasks.ProjectModel;
+
+namespace RepoTasks
+{
+ public class CopyPackagesToSplitFolders : Microsoft.Build.Utilities.Task
+ {
+ ///
+ /// The item group containing the nuget packages to split in different folders.
+ ///
+ [Required]
+ public ITaskItem[] Packages { get; set; }
+
+ [Required]
+ public ITaskItem[] Files { get; set; }
+
+ ///
+ /// The folder where packages should be copied. Subfolders will be created based on package category.
+ ///
+ [Required]
+ public string DestinationFolder { get; set; }
+
+ public bool Overwrite { get; set; }
+
+ public override bool Execute()
+ {
+ if (Files?.Length == 0)
+ {
+ Log.LogError("No packages were found.");
+ return false;
+ }
+
+ var expectedPackages = PackageCollection.FromItemGroup(Packages);
+
+ Directory.CreateDirectory(DestinationFolder);
+
+ foreach (var file in Files)
+ {
+ PackageIdentity identity;
+ using (var reader = new PackageArchiveReader(file.ItemSpec))
+ {
+ identity = reader.GetIdentity();
+ }
+
+ if (!expectedPackages.TryGetCategory(identity.Id, out var category))
+ {
+ Log.LogError($"Unexpected package artifact with id: {identity.Id}");
+ continue;
+ }
+
+ string destDir;
+ switch (category)
+ {
+ case PackageCategory.Unknown:
+ throw new InvalidOperationException($"Package {identity} does not have a recognized package category.");
+ case PackageCategory.Shipping:
+ destDir = Path.Combine(DestinationFolder, "ship");
+ break;
+ case PackageCategory.NoShip:
+ destDir = Path.Combine(DestinationFolder, "noship");
+ break;
+ case PackageCategory.ShipOob:
+ destDir = Path.Combine(DestinationFolder, "shipoob");
+ break;
+ case PackageCategory.Mirror:
+ destDir = Path.Combine(DestinationFolder, "mirror");
+ break;
+ default:
+ throw new NotImplementedException();
+ }
+
+ Directory.CreateDirectory(destDir);
+
+ var destFile = Path.Combine(destDir, Path.GetFileName(file.ItemSpec));
+
+ if (!Overwrite && File.Exists(destFile))
+ {
+ Log.LogError($"File already exists in {destFile}");
+ continue;
+ }
+
+ Log.LogMessage($"Copying {file.ItemSpec} to {destFile}");
+ File.Copy(file.ItemSpec, destFile, Overwrite);
+ expectedPackages.Remove(identity.Id);
+ }
+
+ if (expectedPackages.Count != 0)
+ {
+ var error = new StringBuilder();
+ foreach (var key in expectedPackages.Keys)
+ {
+ error.Append(" - ").AppendLine(key);
+ }
+
+ Log.LogError($"Expected the following packages, but they were not found:" + error.ToString());
+ return false;
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/build/tasks/ProjectModel/PackageCategory.cs b/build/tasks/ProjectModel/PackageCategory.cs
new file mode 100644
index 0000000000..e3869319f0
--- /dev/null
+++ b/build/tasks/ProjectModel/PackageCategory.cs
@@ -0,0 +1,15 @@
+// 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.ProjectModel
+{
+ public enum PackageCategory
+ {
+ Unknown = 0,
+ Shipping,
+ NoShip,
+ ShipOob,
+ Mirror,
+ }
+}
diff --git a/build/tasks/ProjectModel/PackageCollection.cs b/build/tasks/ProjectModel/PackageCollection.cs
new file mode 100644
index 0000000000..56b998969a
--- /dev/null
+++ b/build/tasks/ProjectModel/PackageCollection.cs
@@ -0,0 +1,69 @@
+// 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.Framework;
+
+namespace RepoTasks.ProjectModel
+{
+ public class PackageCollection
+ {
+ private readonly IDictionary _packages = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ private PackageCollection()
+ {
+ }
+
+ public bool TryGetCategory(string packageId, out PackageCategory category) => _packages.TryGetValue(packageId, out category);
+
+ public void Remove(string packageId) => _packages.Remove(packageId);
+
+ public int Count => _packages.Count;
+
+ public IEnumerable Keys => _packages.Keys;
+
+ public static PackageCollection FromItemGroup(ITaskItem[] items)
+ {
+ var list = new PackageCollection();
+ if (items == null)
+ {
+ return list;
+ }
+
+ foreach (var item in items)
+ {
+ PackageCategory category;
+ switch (item.GetMetadata("Category")?.ToLowerInvariant())
+ {
+ case "ship":
+ category = PackageCategory.Shipping;
+ break;
+ case "noship":
+ category = PackageCategory.NoShip;
+ break;
+ case "shipoob":
+ category = PackageCategory.ShipOob;
+ break;
+ case "mirror":
+ category = PackageCategory.Mirror;
+ break;
+ default:
+ category = PackageCategory.Unknown;
+ break;
+ }
+
+ if (list._packages.ContainsKey(item.ItemSpec))
+ {
+ throw new InvalidDataException($"Duplicate package id detected: {item.ItemSpec}");
+ }
+
+ list._packages.Add(item.ItemSpec, category);
+ }
+
+ return list;
+ }
+ }
+}
diff --git a/build/tasks/ProjectModel/PackageInfo.cs b/build/tasks/ProjectModel/PackageInfo.cs
new file mode 100644
index 0000000000..463fab690a
--- /dev/null
+++ b/build/tasks/ProjectModel/PackageInfo.cs
@@ -0,0 +1,42 @@
+// 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 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();
+ }
+
+ public string Id { get; }
+ public NuGetVersion Version { get; }
+ public string PackageType { get; }
+ ///
+ /// Can be a https feed or a file path. May be null.
+ ///
+ public string Source { get; }
+ public IReadOnlyList DependencyGroups { get; }
+ }
+}
diff --git a/build/tasks/ReadPackageDependencies.cs b/build/tasks/ReadPackageDependencies.cs
deleted file mode 100644
index 315cb45271..0000000000
--- a/build/tasks/ReadPackageDependencies.cs
+++ /dev/null
@@ -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;
-using System.Linq;
-using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
-using NuGet.Packaging;
-
-namespace RepoTasks
-{
- public class ReadPackageDependencies : Task
- {
- [Required]
- public ITaskItem[] PackageFiles { get; set; }
-
- [Output]
- public ITaskItem[] PackageDefinitions { get; set; }
-
- public override bool Execute()
- {
- PackageDefinitions = PackageFiles.SelectMany(item =>
- {
- using (var package = new PackageArchiveReader(item.ItemSpec))
- {
- var identity = package.GetIdentity();
- var metadata = new NuspecReader(package.GetNuspec());
- var groups = metadata.GetDependencyGroups()?.ToList();
- if (groups == null)
- {
- return Enumerable.Empty();
- }
-
- return groups.SelectMany(g =>
- g.Packages.Select(p => new TaskItem(p.Id, new Hashtable { ["Version"] = p.VersionRange.MinVersion.ToString() })));
- }
- })
- .ToArray();
-
- return true;
- }
- }
-}
diff --git a/build/tasks/ReadPackageIdentity.cs b/build/tasks/ReadPackageIdentity.cs
deleted file mode 100644
index 354a226422..0000000000
--- a/build/tasks/ReadPackageIdentity.cs
+++ /dev/null
@@ -1,37 +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.Linq;
-using Microsoft.Build.Framework;
-using Microsoft.Build.Utilities;
-using NuGet.Packaging;
-
-namespace RepoTasks
-{
- public class ReadPackageIdentity : Task
- {
- [Required]
- public ITaskItem[] PackageFiles { get; set; }
-
- [Output]
- public ITaskItem[] PackageDefinitions { get; set; }
-
- public override bool Execute()
- {
- PackageDefinitions = PackageFiles.Select(item =>
- {
- using (var package = new PackageArchiveReader(item.ItemSpec))
- {
- var identity = package.GetIdentity();
- var packageItem = new TaskItem(identity.Id);
- packageItem.SetMetadata("Version", identity.Version.ToString());
- return packageItem;
- }
- })
- .ToArray();
-
- return true;
- }
- }
-}
diff --git a/build/tasks/RepoTasks.tasks b/build/tasks/RepoTasks.tasks
index d4759ca827..7d33d0ad09 100644
--- a/build/tasks/RepoTasks.tasks
+++ b/build/tasks/RepoTasks.tasks
@@ -4,7 +4,7 @@
+
-
-
+
diff --git a/build/tasks/VerifyCoherentVersions.cs b/build/tasks/VerifyCoherentVersions.cs
new file mode 100644
index 0000000000..f0e92a7d47
--- /dev/null
+++ b/build/tasks/VerifyCoherentVersions.cs
@@ -0,0 +1,137 @@
+// 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 Microsoft.Build.Framework;
+using NuGet.Frameworks;
+using NuGet.Packaging;
+using NuGet.Packaging.Core;
+using NuGet.Versioning;
+using RepoTasks.ProjectModel;
+
+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 list))
+ {
+ dependencyMap[dep.ItemSpec] = list = new List();
+ }
+ var externalDep = new ExternalDependency
+ {
+ Version = dep.GetMetadata("Version"),
+ IsPrivate = bool.TryParse(dep.GetMetadata("Private"), out var isPrivate) && isPrivate,
+ };
+ list.Add(externalDep);
+ }
+
+ foreach (var file in PackageFiles)
+ {
+ PackageInfo package;
+ using (var reader = new PackageArchiveReader(file.ItemSpec))
+ {
+ var identity = reader.GetIdentity();
+ var metadata = new PackageBuilder(reader.GetNuspec(), basePath: null);
+ package = new PackageInfo(identity.Id, identity.Version,
+ source: Path.GetDirectoryName(file.ItemSpec),
+ dependencyGroups: metadata.DependencyGroups.ToArray());
+ }
+
+ if (packageLookup.TryGetValue(package.Id, out var existingPackage))
+ {
+ throw new Exception("Multiple copies of the following package were found: " +
+ Environment.NewLine +
+ existingPackage +
+ Environment.NewLine +
+ package);
+ }
+
+ packageLookup[package.Id] = package;
+ }
+
+ foreach (var packageInfo in packageLookup.Values)
+ {
+ Visit(packageLookup, dependencyMap, packageInfo);
+ }
+
+ Log.LogMessage(MessageImportance.High, $"Verified {PackageFiles.Length} package(s) have coherent versions");
+ return !Log.HasLoggedErrors;
+ }
+
+ private class ExternalDependency
+ {
+ public string Version { get; set; }
+ public bool IsPrivate { get; set; }
+ }
+
+ private void Visit(
+ IReadOnlyDictionary packageLookup,
+ IReadOnlyDictionary> dependencyMap,
+ PackageInfo packageInfo)
+ {
+ Log.LogMessage(MessageImportance.Low, $"Processing package {packageInfo.Id}");
+ try
+ {
+ foreach (var dependencySet in packageInfo.DependencyGroups)
+ {
+ foreach (var dependency in dependencySet.Packages)
+ {
+ PackageInfo dependencyPackageInfo;
+ var depVersion = dependency.VersionRange.MinVersion.ToString();
+ if (dependencyMap.TryGetValue(dependency.Id, out var externalDependencies))
+ {
+ var matchedVersion = externalDependencies.FirstOrDefault(d => depVersion.Equals(d.Version));
+
+ if (matchedVersion == null)
+ {
+ var versions = string.Join(" or ", externalDependencies.Select(d => d.Version));
+ Log.LogError($"Package {packageInfo.Id} has an external dependency on the wrong version of {dependency.Id}. "
+ + $"It uses {depVersion} but only {versions} is allowed.");
+ }
+ else if (matchedVersion.IsPrivate)
+ {
+ Log.LogError($"Package {packageInfo.Id} has an external dependency on {dependency.Id}/{depVersion} which is marked as Private=true.");
+ }
+ continue;
+ }
+ else if (!packageLookup.TryGetValue(dependency.Id, out dependencyPackageInfo))
+ {
+ Log.LogError($"Package {packageInfo.Id} has an undefined external dependency on {dependency.Id}/{depVersion}");
+ continue;
+ }
+
+ if (dependencyPackageInfo.Version != dependency.VersionRange.MinVersion)
+ {
+ // For any dependency in the universe
+ // Add a mismatch if the min version doesn't work out
+ // (we only really care about >= minVersion)
+ Log.LogError($"{packageInfo.Id} depends on {dependency.Id} " +
+ $"{dependency.VersionRange} ({dependencySet.TargetFramework}) when the latest build is {depVersion}.");
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Log.LogError($"Unexpected error while attempting to verify package {packageInfo.Id}.\r\n{ex}");
+ }
+ }
+ }
+}
diff --git a/version.xml b/version.props
similarity index 51%
rename from version.xml
rename to version.props
index 3c05022b7d..834077241d 100644
--- a/version.xml
+++ b/version.props
@@ -1,8 +1,7 @@
-
- dev
2.1.0
preview1
+ $(VersionSuffix)-$(BuildNumber)