Procházet zdrojové kódy

Backport improvements from dev branch to repo tasks

 - CalculateBuildGraph
 - PinVersion tool
 - Ensuring sub-repositories build with the same version of KoreBuild executing in Universe
Nate McMaster před 8 roky
rodič
revize
52757943ac

+ 0 - 27
Universe.sln

@@ -1,27 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26228.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PinVersions", "tools\PinVersions\PinVersions.csproj", "{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{085280EC-7055-426A-BF9C-1B692B9599AB}"
-	ProjectSection(SolutionItems) = preProject
-		tools\shared\DependencyGraphSpecProvider.cs = tools\shared\DependencyGraphSpecProvider.cs
-	EndProjectSection
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{DACA9DFB-508E-45EA-A5CF-C0F5C2BA181B}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal

+ 4 - 4
build/Repositories.props

@@ -1,10 +1,7 @@
 <Project>
   <ItemGroup>
     <!-- Repos being patched -->
-    <Repository Include="EntityFrameworkCore" Branch="rel/2.0.1" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
-    <Repository Include="Mvc" Branch="rel/2.0.1" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
-    <Repository Include="Identity" Branch="rel/2.0.1" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
-    <Repository Include="MetaPackages" Branch="rel/2.0.1" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
+    <Repository Include="EntityFrameworkCore" Branch="feature/2.0.1" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
 
     <!-- TODO compute downstream repos that should be updated. -->
 
@@ -13,6 +10,9 @@
     Build tools will *verify* that these repos will be unaffected
     by the patch update and do not need updating.
     -->
+    <VerifyRepository Include="Mvc" Branch="rel/2.0.0" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
+    <VerifyRepository Include="Identity" Branch="rel/2.0.0" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
+    <VerifyRepository Include="MetaPackages" Branch="rel/2.0.0" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
     <VerifyRepository Include="Antiforgery" Branch="rel/2.0.0" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
     <VerifyRepository Include="AzureIntegration" Branch="rel/2.0.0" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />
     <VerifyRepository Include="BasicMiddleware" Branch="rel/2.0.0" Commit="" CloneUrl="[email protected]:aspnet/%(Identity)" />

+ 16 - 27
build/RepositoryBuild.targets

@@ -41,20 +41,14 @@
 
     <Message Text="============ Building $(RepositoryToBuild) ============" Importance="High" />
 
-    <!-- Copy Korebuild folder to individual repos to avoid downloading it again and again and also to
-          prevent from taking in newer Korebuild changes when Universe is building midway -->
-    <ItemGroup>
-      <_KorebuildItems Include="$(RepositoryRoot).build\**\*.*" />
-    </ItemGroup>
-
-    <Message Text="Copying Korebuild from Universe to repository $(BuildRepositoryRoot)"/>
-    <Copy SourceFiles="@(_KorebuildItems)" DestinationFolder="$(BuildRepositoryRoot).build\%(RecursiveDir)" SkipUnchangedFiles="true" UseHardlinksIfPossible="true" />
+    <!-- Copy Korebuild lock file to individual repos to align version if the repo doesn't already have one -->
+    <Message Text="Copying KoreBuild lockfile from Universe to repository $(BuildRepositoryRoot)"/>
+    <Copy SourceFiles="$(RepositoryRoot)korebuild-lock.txt" DestinationFolder="$(BuildRepositoryRoot)" />
 
     <Exec
-      Command="./$(_BuildScriptToExecute) $(BuildArguments)"
-      EnvironmentVariables="KOREBUILD_SKIP_RUNTIME_INSTALL=1"
+      Command="./$(_BuildScriptToExecute) -Path $(BuildRepositoryRoot) $(BuildArguments)"
       IgnoreStandardErrorWarningFormat="true"
-      WorkingDirectory="$(BuildRepositoryRoot)" />
+      WorkingDirectory="$(RepositoryRoot)" />
 
     <ItemGroup>
       <RepositoryArtifacts Include="$(RepositoryArtifactsBuildDirectory)*" />
@@ -71,24 +65,19 @@
        SourceFiles="@(RepositoryMSBuildArtifacts)"
        DestinationFolder="$(ArtifactsDir)msbuild\$(RepositoryToBuild)\%(RecursiveDir)" />
 
-    <Message Text="Publishing the following packages to the volatile feed: @(RepositoryNupkgs -> '%(Filename)%(Extension)', ', ')"
-      Condition="'$(PublishPackages)'=='true' AND '@(RepositoryNupkgs)' != ''" />
-
-    <Exec
-      Command="$(DotNetPath) $(PackagePublisherNetCoreApp) -d $(RepositoryArtifactsBuildDirectory) -f $(NuGetPublishVolatileFeed)"
-      Condition="'$(PublishPackages)'=='true' AND '@(RepositoryNupkgs)' != ''" />
-
     <Message Text="============ Done building $(RepositoryToBuild) ============" Importance="High" />
   </Target>
 
-  <Target Name="_PinVersions" DependsOnTargets="_FindDotNetPath">
-
-    <PropertyGroup>
-      <PinToolBinary>$(RepositoryRoot)tools\PinVersions\bin\$(Configuration)\netcoreapp1.1\PinVersions.dll</PinToolBinary>
-      <PinVersionArgs>$(DotNetPath) $(PinToolBinary) --graph-specs-root &quot;$(_RestoreGraphSpecsDirectory) &quot; -s &quot;$(BuildDir) &quot; &quot;$(BuildRepositoryRoot) &quot;</PinVersionArgs>
-      <PinVersionArgs Condition="Exists('$(_DependencyPackagesDirectory)')">$(PinVersionArgs) -s &quot;$(_DependencyPackagesDirectory) &quot;</PinVersionArgs>
-    </PropertyGroup>
+  <Target Name="_PinVersions">
+    <ItemGroup>
+      <PinPackageSources Include="$(BuildDir)" />
+      <PinPackageSources Include="$(_DependencyPackagesDirectory)" Condition="Exists('$(_DependencyPackagesDirectory)')" />
+    </ItemGroup>
 
-    <Exec Command="$(PinVersionArgs)" />
+    <RepoTasks.PinVersions
+      GraphSpecsRoot="$(_RestoreGraphSpecsDirectory)"
+      BuildRepositoryRoot="$(BuildRepositoryRoot)"
+      PackageSources="@(PinPackageSources)" />
   </Target>
-</Project>
+
+</Project>

+ 6 - 0
build/repo.props

@@ -0,0 +1,6 @@
+<Project>
+  <PropertyGroup>
+    <!-- This repo does not have solutions to build -->
+    <DisableDefaultTargets>true</DisableDefaultTargets>
+  </PropertyGroup>
+</Project>

+ 2 - 11
build/repo.targets

@@ -23,10 +23,6 @@
     <BuildDependsOn>$(BuildDependsOn);CloneRepositories;BuildRepositories</BuildDependsOn>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="PackagePublisher" Version="2.0.0-*" />
-  </ItemGroup>
-
   <Import Project="$(_RepositoryListToImport)" />
 
   <Target Name="CleanUniverseArtifacts">
@@ -57,7 +53,7 @@
 
   <Target Name="CloneRepositories" DependsOnTargets="_PrepareRepositories">
     <ItemGroup>
-      <_CloneRepositories Include="@(Repository);@(VerifyRepositories)" />
+      <_CloneRepositories Include="@(Repository);@(VerifyRepository)" />
       <_CloneRepository Include="$(MSBuildProjectFullPath)">
         <AdditionalProperties>
           CloneRepository=%(_CloneRepositories.Identity);
@@ -122,7 +118,7 @@
   </Target>
 
   <Target Name="BuildRepositories"
-     DependsOnTargets="_PrepareRepositories;_FindDotNetPath;_CreateRepositoriesListWithCommits;_UpdateNuGetConfig;_GenerateBuildGraph;_BuildRepositories" />
+     DependsOnTargets="_PrepareRepositories;_CreateRepositoriesListWithCommits;_UpdateNuGetConfig;_GenerateBuildGraph;_BuildRepositories" />
 
   <Target Name="_PrepareRestoreGraphSpecs" DependsOnTargets="_PrepareRepositories">
     <ItemGroup>
@@ -213,9 +209,4 @@
     </PropertyGroup>
   </Target>
 
-  <Target Name="_FindDotNetPath">
-    <GetDotNetHost>
-      <Output TaskParameter="ExecutablePath" PropertyName="DotNetPath" />
-    </GetDotNetHost>
-  </Target>
 </Project>

+ 9 - 0
build/tasks/BuildGraph/DependencyGraphSpecProvider.cs

@@ -1,3 +1,6 @@
+// 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.IO;
 using NuGet.ProjectModel;
 
@@ -15,6 +18,12 @@ namespace RepoTools.BuildGraph
         public DependencyGraphSpec GetDependencyGraphSpec(string repositoryName, string solutionPath)
         {
             var outputFile = Path.Combine(_packageSpecDirectory, repositoryName, Path.GetFileName(solutionPath) + ".json");
+
+            if (!File.Exists(outputFile))
+            {
+                return null;
+            }
+
             return DependencyGraphSpec.Load(outputFile);
         }
     }

+ 16 - 3
build/tasks/BuildGraph/GraphBuilder.cs

@@ -1,12 +1,17 @@
-using System;
+// 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, string root)
+        public static IList<GraphNode> Generate(IList<Repository> repositories, string root, TaskLoggingHelper log)
         {
             // Build global list of primary projects
             var primaryProjects = repositories.SelectMany(c => c.Projects)
@@ -31,7 +36,15 @@ namespace RepoTools.BuildGraph
                         var dependencyRepository = dependencyProject.Repository;
                         var dependencyNode = graphNodes[dependencyRepository];
 
-                        thisProjectRepositoryNode.Incoming.Add(dependencyNode);
+                        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);
                     }
                 }

+ 4 - 1
build/tasks/BuildGraph/GraphNode.cs

@@ -1,4 +1,7 @@
-using System.Collections.Generic;
+// 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

+ 6 - 1
build/tasks/BuildGraph/Project.cs

@@ -1,4 +1,7 @@
-using System;
+// 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;
 
@@ -14,6 +17,8 @@ namespace RepoTools.BuildGraph
 
         public string Name { get; }
 
+        public string Version { get; set; }
+
         public string Path { get; set; }
 
         public Repository Repository { get; set; }

+ 11 - 1
build/tasks/BuildGraph/Repository.cs

@@ -1,4 +1,7 @@
-using System;
+// 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.IO;
@@ -49,6 +52,7 @@ namespace RepoTools.BuildGraph
             var repository = new Repository(name);
 
             ReadSharedSourceProjects(Path.Combine(repositoryPath, "shared"), repository, repository.Projects);
+
             var srcDirectory = Path.GetFullPath(Path.Combine(repositoryPath, "src"))
                 .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
 
@@ -56,6 +60,11 @@ namespace RepoTools.BuildGraph
             foreach (var file in solutionFiles)
             {
                 var spec = provider.GetDependencyGraphSpec(name, file);
+                if (spec == null)
+                {
+                    continue;
+                }
+
                 var projects = spec.Projects.OrderBy(p => p.RestoreMetadata.ProjectStyle == ProjectStyle.PackageReference ? 0 : 1);
                 foreach (var specProject in projects)
                 {
@@ -73,6 +82,7 @@ namespace RepoTools.BuildGraph
                         {
                             Repository = repository,
                             Path = specProject.FilePath,
+                            Version = specProject.Version?.ToString(),
                         };
 
                         projectGroup.Add(project);

+ 4 - 1
build/tasks/BuildGraph/TopologicalSort.cs

@@ -1,4 +1,7 @@
-using System;
+// 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;
 

+ 15 - 9
build/tasks/BuildGraph/CalculateBuildGraph.cs → build/tasks/CalculateBuildGraph.cs

@@ -15,19 +15,19 @@ namespace RepoTasks
         [Required]
         public ITaskItem[] Repositories { get; set; }
 
-        [Output]
-        public ITaskItem[] RepositoriesToBuildInOrder { get; set; }
+        /// <summary>
+        /// Directory that contains the package spec files.
+        /// </summary>
+        [Required]
+        public string PackageSpecsDirectory { get; set; }
 
         /// <summary>
         /// The repository at which to root the graph at
         /// </summary>
         public string StartGraphAt { get; set; }
 
-        /// <summary>
-        /// Directory that contains the package spec files.
-        /// </summary>
-        [Required]
-        public string PackageSpecsDirectory { get; set; }
+        [Output]
+        public ITaskItem[] RepositoriesToBuildInOrder { get; set; }
 
         public override bool Execute()
         {
@@ -36,12 +36,18 @@ namespace RepoTasks
             var repositoryPaths = Repositories.Select(r => r.GetMetadata("RepositoryPath")).ToList();
             var repositories = Repository.ReadAllRepositories(repositoryPaths, graphSpecProvider);
 
-            var graph = GraphBuilder.Generate(repositories, StartGraphAt);
+            var graph = GraphBuilder.Generate(repositories, StartGraphAt, Log);
             var repositoriesWithOrder = new List<(ITaskItem repository, int order)>();
             foreach (var repositoryTaskItem in Repositories)
             {
                 var repositoryName = repositoryTaskItem.ItemSpec;
-                var graphNodeRepository = graph.First(g => g.Repository.Name == repositoryName);
+                var graphNodeRepository = graph.FirstOrDefault(g => g.Repository.Name == repositoryName);
+                if (graphNodeRepository == null)
+                {
+                    // StartGraphAt was specified so the graph is incomplete.
+                    continue;
+                }
+
                 var order = TopologicalSort.GetOrder(graphNodeRepository);
                 repositoryTaskItem.SetMetadata("Order", order.ToString());
                 repositoriesWithOrder.Add((repositoryTaskItem, order));

+ 61 - 0
build/tasks/Logger/FlowLogger.cs

@@ -0,0 +1,61 @@
+// 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.Logging;
+
+namespace RepoTasks
+{
+    public class FlowLogger : ConsoleLogger
+    {
+        private volatile bool _initialized;
+
+        public FlowLogger()
+        {
+        }
+
+        public override void Initialize(IEventSource eventSource, int nodeCount)
+        {
+            PreInit(eventSource);
+            base.Initialize(eventSource, nodeCount);
+        }
+
+        public override void Initialize(IEventSource eventSource)
+        {
+            PreInit(eventSource);
+            base.Initialize(eventSource);
+        }
+
+        private void PreInit(IEventSource eventSource)
+        {
+            if (_initialized) return;
+            _initialized = true;
+
+            var flowId = GetFlowId();
+            var prefix = $"{flowId,-22}| ";
+            var write = WriteHandler;
+            WriteHandler = msg => write(prefix + msg);
+
+            eventSource.BuildStarted += (o, e) =>
+            {
+                WriteHandler(e.Message + Environment.NewLine);
+            };
+        }
+
+        private string GetFlowId()
+        {
+            var parameters = Parameters?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
+            if (parameters == null || parameters.Length == 0)
+            {
+                return null;
+            }
+
+            const string flowIdParamName = "FlowId=";
+            return parameters
+                .FirstOrDefault(p => p.StartsWith(flowIdParamName, StringComparison.Ordinal))
+                ?.Substring(flowIdParamName.Length);
+        }
+    }
+}

+ 46 - 0
build/tasks/PinVersions.cs

@@ -0,0 +1,46 @@
+// 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 RepoTasks.VersionPinning;
+
+namespace RepoTasks
+{
+    public class PinVersions : Microsoft.Build.Utilities.Task
+    {
+        [Required]
+        public string BuildRepositoryRoot { get; set; }
+
+        [Required]
+        public ITaskItem[] PackageSources { get; set; }
+
+        public string GraphSpecsRoot { get; set; }
+
+        public override bool Execute()
+        {
+            if (PackageSources?.Length == 0)
+            {
+                Log.LogError($"Missing PackageSources. At least one item source must be specified.");
+                return false;
+            }
+
+            var graphSpecProvider = !string.IsNullOrEmpty(GraphSpecsRoot)
+                ? new DependencyGraphSpecProvider(GraphSpecsRoot)
+                : DependencyGraphSpecProvider.Default;
+
+            using (graphSpecProvider)
+            {
+                var pinVersionUtility = new PinVersionUtility(
+                    BuildRepositoryRoot,
+                    PackageSources.Select(i => i.ItemSpec).ToList(),
+                    graphSpecProvider,
+                    Log);
+                pinVersionUtility.Execute();
+            }
+
+            return true;
+        }
+    }
+}

+ 1 - 1
build/tasks/RepoTasks.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="NuGet.ProjectModel" Version="4.0.0" />
+    <PackageReference Include="NuGet.ProjectModel" Version="4.3.0" PrivateAssets="All" />
   </ItemGroup>
 
   <Import Project="$(RepoTasksSdkPath)\Sdk.targets" Condition="'$(RepoTasksSdkPath)' != '' "/>

+ 1 - 0
build/tasks/RepoTasks.tasks

@@ -5,4 +5,5 @@
 
   <UsingTask TaskName="RepoTasks.CalculateBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" />
   <UsingTask TaskName="RepoTasks.VerifyBuildGraph" AssemblyFile="$(_RepoTaskAssembly)" />
+  <UsingTask TaskName="RepoTasks.PinVersions" AssemblyFile="$(_RepoTaskAssembly)" />
 </Project>

+ 9 - 6
tools/shared/DependencyGraphSpecProvider.cs → build/tasks/VersionPinning/DependencyGraphSpecProvider.cs

@@ -1,17 +1,19 @@
-using System;
+// 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.IO;
-using Microsoft.DotNet.Cli.Utils;
 using NuGet.ProjectModel;
 
-namespace UniverseTools
+namespace RepoTasks.VersionPinning
 {
     public class DependencyGraphSpecProvider : IDisposable
     {
         private readonly string _packageSpecDirectory;
         private readonly bool _deleteSpecDirectoryOnDispose;
-        private readonly string _muxerPath;
+        private readonly string _dotnetPath;
 
         public DependencyGraphSpecProvider(string packageSpecDirectory)
             : this(packageSpecDirectory, deleteSpecDirectoryOnDispose: false)
@@ -22,7 +24,7 @@ namespace UniverseTools
         {
             _packageSpecDirectory = packageSpecDirectory;
             _deleteSpecDirectoryOnDispose = deleteSpecDirectoryOnDispose;
-            _muxerPath = new Muxer().MuxerPath;
+            _dotnetPath = Process.GetCurrentProcess().MainModule.FileName;
         }
 
         public static DependencyGraphSpecProvider Default { get; } =
@@ -42,7 +44,7 @@ namespace UniverseTools
 
         private void RunMSBuild(string solutionPath, string outputFile)
         {
-            var psi = new ProcessStartInfo(_muxerPath);
+            var psi = new ProcessStartInfo(_dotnetPath);
 
             var arguments = new List<string>
             {
@@ -53,6 +55,7 @@ namespace UniverseTools
                 "/v:q",
                 "/p:BuildProjectReferences=false",
                 $"/p:RestoreGraphOutputPath=\"{outputFile}\"",
+                "/p:KoreBuildRestoreTargetsImported=true",
             };
 
             psi.Arguments = string.Join(" ", arguments);

+ 58 - 24
tools/PinVersions/PinVersionUtility.cs → build/tasks/VersionPinning/PinVersionUtility.cs

@@ -1,4 +1,7 @@
-using System;
+// 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.IO;
@@ -6,6 +9,8 @@ using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml.Linq;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
 using NuGet.Common;
 using NuGet.Frameworks;
 using NuGet.LibraryModel;
@@ -13,19 +18,23 @@ using NuGet.ProjectModel;
 using NuGet.Protocol;
 using NuGet.Protocol.Core.Types;
 using NuGet.Versioning;
-using UniverseTools;
 
-namespace PinVersions
+namespace RepoTasks.VersionPinning
 {
-    class PinVersionUtility
+    internal class PinVersionUtility
     {
         private readonly string _repositoryRoot;
         private readonly FindPackageByIdResource[] _findPackageResources;
         private readonly ConcurrentDictionary<string, Task<NuGetVersion>> _exactMatches = new ConcurrentDictionary<string, Task<NuGetVersion>>(StringComparer.OrdinalIgnoreCase);
         private readonly DependencyGraphSpecProvider _provider;
         private readonly SourceCacheContext _sourceCacheContext;
+        private readonly TaskLoggingHelper _logger;
 
-        public PinVersionUtility(string repositoryRoot, List<string> pinSources, DependencyGraphSpecProvider provider)
+        public PinVersionUtility(
+            string repositoryRoot,
+            List<string> pinSources,
+            DependencyGraphSpecProvider provider,
+            TaskLoggingHelper logger)
         {
             _repositoryRoot = repositoryRoot;
             _findPackageResources = new FindPackageByIdResource[pinSources.Count];
@@ -36,19 +45,27 @@ namespace PinVersions
             }
             _provider = provider;
             _sourceCacheContext = new SourceCacheContext();
+            _logger = logger;
         }
 
         public void Execute()
         {
-            var solutionPinMetadata = GetPinVersionMetadata();
-            foreach (var item in solutionPinMetadata)
+            _logger.LogMessage(MessageImportance.High, $"Pinning package references for projects in {_repositoryRoot}");
+
+            var solutionPinMetadata = GetProjectPinVersionMetadata();
+            foreach (var cliToolReference in solutionPinMetadata.CLIToolReferences)
+            {
+                _logger.LogMessage(MessageImportance.Normal, $"Pinning CLI Tool {cliToolReference.Item1.Name}({cliToolReference.Item1.VersionRange} to {cliToolReference.Item2} for all projects in {_repositoryRoot}.");
+            }
+
+            foreach (var item in solutionPinMetadata.PinVersionLookup)
             {
                 var projectPinMetadata = item.Value;
                 var specProject = projectPinMetadata.PackageSpec;
 
-                if (!(projectPinMetadata.Packages.Any() || projectPinMetadata.CLIToolReferences.Any()))
+                if (!(projectPinMetadata.Packages.Any() || solutionPinMetadata.CLIToolReferences.Any()))
                 {
-                    Console.WriteLine($"No package or tool references to pin for {specProject.FilePath}.");
+                    _logger.LogMessage(MessageImportance.Normal, $"No package or tool references to pin for {specProject.FilePath}.");
                     continue;
                 }
 
@@ -59,12 +76,16 @@ namespace PinVersions
 
                 Directory.CreateDirectory(Path.GetDirectoryName(pinnedReferencesFile));
 
-                Console.WriteLine($"Pinning package versions for {specProject.FilePath}.");
-                var pinnedReferences = new XElement("ItemGroup");
+                if (projectPinMetadata.Packages.Any())
+                {
+                    _logger.LogMessage(MessageImportance.Normal, $"Pinning package versions for {specProject.FilePath}.");
+                }
+
+                var pinnedReferences = new XElement("ItemGroup", new XAttribute("Condition", "'$(PolicyDesignTimeBuild)' != 'true' AND !Exists('$(MSBuildThisFileDirectory)$(MSBuildProjectFile).nugetpolicy.g.targets')"));
                 foreach (var packageReference in projectPinMetadata.Packages)
                 {
                     (var tfm, var libraryRange, var exactVersion) = packageReference;
-                    Console.WriteLine($"Pinning reference {libraryRange.Name}({libraryRange.VersionRange} to {exactVersion}.");
+                    _logger.LogMessage(MessageImportance.Normal, $"Pinning reference {libraryRange.Name}({libraryRange.VersionRange} to {exactVersion}.");
                     var metadata = new List<XAttribute>
                     {
                         new XAttribute("Update", libraryRange.Name),
@@ -79,10 +100,10 @@ namespace PinVersions
                     pinnedReferences.Add(new XElement("PackageReference", metadata));
                 }
 
-                foreach (var toolReference in projectPinMetadata.CLIToolReferences)
+                // CLI Tool references are specified at solution level.
+                foreach (var toolReference in solutionPinMetadata.CLIToolReferences)
                 {
                     (var libraryRange, var exactVersion) = toolReference;
-                    Console.WriteLine($"Pinning CLI Tool {libraryRange.Name}({libraryRange.VersionRange} to {exactVersion}.");
                     var metadata = new List<XAttribute>
                     {
                         new XAttribute("Update", libraryRange.Name),
@@ -97,10 +118,11 @@ namespace PinVersions
             }
         }
 
-        private IDictionary<string, PinVersionMetadata> GetPinVersionMetadata()
+        private SolutionPinVersionMetadata GetProjectPinVersionMetadata()
         {
             var repositoryDirectoryInfo = new DirectoryInfo(_repositoryRoot);
-            var projects = new Dictionary<string, PinVersionMetadata>(StringComparer.OrdinalIgnoreCase);
+            var projects = new Dictionary<string, ProjectPinVersionMetadata>(StringComparer.OrdinalIgnoreCase);
+            var cliToolReferences = new List<(LibraryRange, NuGetVersion)>();
 
             foreach (var slnFile in repositoryDirectoryInfo.EnumerateFiles("*.sln"))
             {
@@ -109,7 +131,7 @@ namespace PinVersions
                 {
                     if (!projects.TryGetValue(specProject.FilePath, out var pinMetadata))
                     {
-                        pinMetadata = new PinVersionMetadata(specProject);
+                        pinMetadata = new ProjectPinVersionMetadata(specProject);
                         projects[specProject.FilePath] = pinMetadata;
                     }
 
@@ -139,7 +161,7 @@ namespace PinVersions
                         }
                         else if (projectStyle == ProjectStyle.DotnetCliTool)
                         {
-                            pinMetadata.CLIToolReferences.Add((reference.LibraryRange, exactVersion));
+                            cliToolReferences.Add((reference.LibraryRange, exactVersion));
                         }
                         else
                         {
@@ -149,7 +171,7 @@ namespace PinVersions
                 }
             }
 
-            return projects;
+            return new SolutionPinVersionMetadata(projects, cliToolReferences);
         }
 
         private NuGetVersion GetExactVersion(string name, VersionRange range)
@@ -188,20 +210,32 @@ namespace PinVersions
             return null;
         }
 
-        private struct PinVersionMetadata
+        private struct SolutionPinVersionMetadata
+        {
+            public SolutionPinVersionMetadata(
+                IDictionary<string, ProjectPinVersionMetadata> pinVersionLookup,
+                List<(LibraryRange, NuGetVersion)> cliToolReferences)
+            {
+                PinVersionLookup = pinVersionLookup;
+                CLIToolReferences = cliToolReferences;
+            }
+
+            public IDictionary<string, ProjectPinVersionMetadata> PinVersionLookup { get; }
+
+            public List<(LibraryRange, NuGetVersion)> CLIToolReferences { get; }
+        }
+
+        private struct ProjectPinVersionMetadata
         {
-            public PinVersionMetadata(PackageSpec packageSpec)
+            public ProjectPinVersionMetadata(PackageSpec packageSpec)
             {
                 PackageSpec = packageSpec;
                 Packages = new List<(NuGetFramework, LibraryRange, NuGetVersion)>();
-                CLIToolReferences = new List<(LibraryRange, NuGetVersion)>();
             }
 
             public PackageSpec PackageSpec { get; }
 
             public List<(NuGetFramework, LibraryRange, NuGetVersion)> Packages { get; }
-
-            public List<(LibraryRange, NuGetVersion)> CLIToolReferences { get; }
         }
     }
 }

+ 0 - 17
tools/PinVersions/PinVersions.csproj

@@ -1,17 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp1.1</TargetFramework>
-  </PropertyGroup>
-
-  <ItemGroup>
-    <Compile Include="..\shared\*.cs" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <PackageReference Include="Microsoft.DotNet.Cli.Utils" Version="1.0.1" />
-    <PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.0" />
-    <PackageReference Include="System.ValueTuple" Version="4.3.0" />
-  </ItemGroup>
-</Project>

+ 0 - 53
tools/PinVersions/Program.cs

@@ -1,53 +0,0 @@
-using System;
-using Microsoft.Extensions.CommandLineUtils;
-using UniverseTools;
-
-namespace PinVersions
-{
-    class Program
-    {
-        static int Main(string[] args)
-        {
-            var app = new CommandLineApplication();
-
-            var pinSourceOption = app.Option("-s|--source",
-                "Feed containing packages to pin.",
-                CommandOptionType.MultipleValue);
-
-            var packageSpecsDirectoryOption = app.Option("--graph-specs-root",
-               "Directory containing package specs. (Optional)",
-               CommandOptionType.SingleValue);
-
-            var repositoryArgument = app.Argument("Repository", "Repository directory");
-
-            app.OnExecute(() =>
-            {
-                if (!pinSourceOption.HasValue())
-                {
-                    Console.Error.WriteLine($"Option {pinSourceOption.Template} must have a value.");
-                    return 1;
-                }
-
-                if (string.IsNullOrEmpty(repositoryArgument.Value))
-                {
-                    Console.Error.WriteLine($"Repository argument must be specified.");
-                    return 1;
-                }
-
-                var graphSpecProvider = packageSpecsDirectoryOption.HasValue() ?
-                    new DependencyGraphSpecProvider(packageSpecsDirectoryOption.Value().Trim()) :
-                    DependencyGraphSpecProvider.Default;
-
-                using (graphSpecProvider)
-                {
-                    var pinVersionUtility = new PinVersionUtility(repositoryArgument.Value.Trim(), pinSourceOption.Values, graphSpecProvider);
-                    pinVersionUtility.Execute();
-                }
-
-                return 0;
-            });
-
-            return app.Execute(args);
-        }
-    }
-}