Skip to content

Commit

Permalink
Add option to use MSBuild's graph to discover projects (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
dfederm authored Aug 16, 2024
1 parent 87c4b36 commit 8c31de5
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 3 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<ItemGroup>
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Microsoft.Build" Version="17.10.4" />
<PackageVersion Include="Microsoft.Build.Locator" Version="1.7.8" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageVersion Include="NuGet.Commands" Version="6.10.1" />
Expand Down
3 changes: 2 additions & 1 deletion src/PackagesConfigConverter/PackagesConfigConverter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CommandLineParser" />
<PackageReference Include="Microsoft.Build" />
<PackageReference Include="Microsoft.Build" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Locator" />
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="NuGet.Commands" />
<PackageReference Include="Serilog" />
Expand Down
11 changes: 11 additions & 0 deletions src/PackagesConfigConverter/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.IO;
using System.Threading;
using CommandLine;
using Microsoft.Build.Locator;
using Microsoft.Extensions.Logging;
using NuGet.Frameworks;
using Serilog;
Expand Down Expand Up @@ -62,15 +63,24 @@ public static void Run(ProgramArguments arguments)
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddSerilog());
ILogger logger = factory.CreateLogger("PackagesConfigConverter");

MSBuildLocator.RegisterDefaults();

ProjectConverterSettings settings = new ProjectConverterSettings
{
RepositoryRoot = arguments.RepoRoot,
Include = arguments.Include.ToRegex(),
Exclude = arguments.Exclude.ToRegex(),
Graph = arguments.Graph,
Log = logger,
TrimPackages = arguments.Trim,
};

if (settings.Graph && settings.Include != null)
{
logger.LogError("Cannot specify using graph discovery and specific project inclusion.");
return;
}

if (arguments.DefaultTargetFramework != null)
{
settings.DefaultTargetFramework = NuGetFramework.Parse(arguments.DefaultTargetFramework);
Expand All @@ -79,6 +89,7 @@ public static void Run(ProgramArguments arguments)
logger.LogInformation($" RepositoryRoot: '{settings.RepositoryRoot}'");
logger.LogInformation($" Include regex: '{settings.Include}'");
logger.LogInformation($" Exclude regex: '{settings.Exclude}'");
logger.LogInformation($" Graph discovery: '{settings.Graph}'");
logger.LogInformation(string.Empty);

if (!arguments.Yes)
Expand Down
3 changes: 3 additions & 0 deletions src/PackagesConfigConverter/ProgramArguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ public class ProgramArguments
[Option('i', HelpText = "Regex for project files to include", MetaValue = "regex")]
public string Include { get; set; }

[Option('g', HelpText = "Whether to use the MSBuild graph to discover project files")]
public bool Graph { get; set; }

[Option('l', HelpText = "Log file to write to", MetaValue = "log")]
public string LogFile { get; set; }

Expand Down
66 changes: 64 additions & 2 deletions src/PackagesConfigConverter/ProjectConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using System.Xml.Linq;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Graph;
using Microsoft.Extensions.Logging;
using NuGet.Commands;
using NuGet.Common;
Expand Down Expand Up @@ -87,9 +88,13 @@ public void ConvertRepository(CancellationToken cancellationToken)

Log.LogInformation($" NuGet configuration file : \"{_converterSettings.NuGetConfigPath}\"");

foreach (string file in Directory.EnumerateFiles(_converterSettings.RepositoryRoot, "*.csproj", SearchOption.AllDirectories)
.TakeWhile(_ => !cancellationToken.IsCancellationRequested))
foreach (string file in GetProjectFiles())
{
if (cancellationToken.IsCancellationRequested)
{
break;
}

if (_converterSettings.Exclude != null && _converterSettings.Exclude.IsMatch(file))
{
Log.LogDebug($" Excluding file \"{file}\"");
Expand Down Expand Up @@ -142,6 +147,63 @@ private static ISettings GetNuGetSettings(ProjectConverterSettings converterSett
return Settings.LoadDefaultSettings(converterSettings.RepositoryRoot, Settings.DefaultSettingsFileName, new XPlatMachineWideSetting());
}

private IEnumerable<string> GetProjectFiles()
{
if (_converterSettings.Graph)
{
string entryProjectFile;

string[] rootSolutionFiles = Directory.GetFiles(_converterSettings.RepositoryRoot, "*.sln", SearchOption.TopDirectoryOnly);
if (rootSolutionFiles.Length > 1)
{
Log.LogError("Found more than one sln file in the repo root. Cannot use graph discovery");
return Array.Empty<string>();
}

if (rootSolutionFiles.Length == 1)
{
entryProjectFile = rootSolutionFiles[0];
}
else
{
string[] rootProjectFiles = Directory.GetFiles(_converterSettings.RepositoryRoot, "*.*proj", SearchOption.TopDirectoryOnly);
if (rootProjectFiles.Length > 1)
{
Log.LogError("Found more than one project file in the repo root. Cannot use graph discovery");
return Array.Empty<string>();
}

if (rootProjectFiles.Length == 1)
{
entryProjectFile = rootProjectFiles[0];
}
else
{
Log.LogError("Did not find any solutions or projects in the repo root. Cannot use graph discovery");
return Array.Empty<string>();
}
}

// Using a set to avoid duplicates due to inner/outer builds. Sorting for a consistent ordering.
SortedSet<string> projectFiles = new(StringComparer.OrdinalIgnoreCase);
ProjectGraph graph = new(entryProjectFile);
foreach (ProjectGraphNode node in graph.ProjectNodes)
{
string projectFile = node.ProjectInstance.FullPath;
if (projectFile.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase))
{
projectFiles.Add(projectFile);
}
}

return projectFiles;
}
else
{
return Directory.EnumerateFiles(_converterSettings.RepositoryRoot, "*.csproj", SearchOption.AllDirectories);
}
}

private ProjectItemElement AddPackageReference(ProjectItemGroupElement itemGroupElement, PackageUsage package, LockFileTargetLibrary lockFileTargetLibrary, bool isDevelopmentDependency)
{
LibraryIncludeFlags existingAssets = LibraryIncludeFlags.None;
Expand Down
2 changes: 2 additions & 0 deletions src/PackagesConfigConverter/ProjectConverterSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public sealed class ProjectConverterSettings

public Regex Include { get; set; }

public bool Graph { get; set; }

public ILogger Log { get; set; }

public string NuGetConfigPath { get; set; } = "(Default)";
Expand Down

0 comments on commit 8c31de5

Please sign in to comment.