From d6848d8579a18a7e8c128eb63a1e4a1dc2725ab0 Mon Sep 17 00:00:00 2001 From: Stuart Lang Date: Sun, 11 Jan 2026 23:59:26 +0000 Subject: [PATCH] Upgrade System.CommandLine to 2.0.1 and remove hosting package Update System.CommandLine from 2.0.0-beta4 to 2.0.1 and remove the System.CommandLine.Hosting dependency. Refactor the command structure to use the new SetAction API instead of CommandHandler.Create with builder hosting. Remove IHost dependency injection and manage logging and workspace creation directly in Main and handler methods. Add explicit package references for Microsoft.Extensions.Logging, Microsoft.Extensions.Logging.Console, and Microsoft.Extensions.FileSystemGlobbing that were previously transitive. Co-Authored-By: Claude Haiku 4.5 --- ScipDotnet/IndexCommandHandler.cs | 24 ++--- ScipDotnet/Program.cs | 168 ++++++++++++++++++------------ ScipDotnet/ScipDotnet.csproj | 6 +- ScipDotnet/ScipProjectIndexer.cs | 16 ++- 4 files changed, 124 insertions(+), 90 deletions(-) diff --git a/ScipDotnet/IndexCommandHandler.cs b/ScipDotnet/IndexCommandHandler.cs index 63cdb74..1c92a8e 100644 --- a/ScipDotnet/IndexCommandHandler.cs +++ b/ScipDotnet/IndexCommandHandler.cs @@ -2,9 +2,7 @@ using Google.Protobuf; using Microsoft.CodeAnalysis.Elfie.Extensions; using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileSystemGlobbing; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Scip; @@ -13,7 +11,8 @@ namespace ScipDotnet; public static class IndexCommandHandler { public static async Task Process( - IHost host, + ILoggerFactory loggerFactory, + MSBuildWorkspace workspace, List projects, string output, FileInfo workingDirectory, @@ -25,7 +24,7 @@ public static async Task Process( FileInfo? nugetConfigPath ) { - var logger = host.Services.GetRequiredService>(); + var logger = loggerFactory.CreateLogger(); var matcher = new Matcher(); matcher.AddIncludePatterns(include.Count == 0 ? new[] { "**" } : include); matcher.AddExcludePatterns(exclude); @@ -49,18 +48,17 @@ public static async Task Process( skipDotnetRestore, nugetConfigPath ); - await ScipIndex(host, options); + await ScipIndex(loggerFactory, workspace, options); // Log msbuild workspace diagnostic information after the index command finishes // We log the MSBuild failures as error since they are often blocking issues // preventing indexing. However, we log msbuild warnings as debug since they // do not block indexing usually and are much noisier - var workspaceLogger = host.Services.GetRequiredService>(); - var workspaceService = host.Services.GetRequiredService(); - if (workspaceService.Diagnostics.Any()) + var workspaceLogger = loggerFactory.CreateLogger(); + if (workspace.Diagnostics.Any()) { - var diagnosticGroups = workspaceService.Diagnostics + var diagnosticGroups = workspace.Diagnostics .GroupBy(d => new { d.Kind, d.Message }) .Select(g => new { g.Key.Kind, g.Key.Message, Count = g.Count() }); foreach (var diagnostic in diagnosticGroups) @@ -83,10 +81,10 @@ public static async Task Process( private static FileInfo OutputFile(FileInfo workingDirectory, string output) => Path.IsPathRooted(output) ? new FileInfo(output) : new FileInfo(Path.Join(workingDirectory.FullName, output)); - private static async Task ScipIndex(IHost host, IndexCommandOptions options) + private static async Task ScipIndex(ILoggerFactory loggerFactory, MSBuildWorkspace workspace, IndexCommandOptions options) { var stopwatch = Stopwatch.StartNew(); - var indexer = host.Services.GetRequiredService(); + var indexer = new ScipProjectIndexer(loggerFactory.CreateLogger()); var index = new Scip.Index { Metadata = new Metadata @@ -100,7 +98,7 @@ private static async Task ScipIndex(IHost host, IndexCommandOptions options) TextDocumentEncoding = TextEncoding.Utf8, } }; - await foreach (var document in indexer.IndexDocuments(host, options)) + await foreach (var document in indexer.IndexDocuments(workspace, options)) { index.Documents.Add(document); } @@ -137,4 +135,4 @@ private static List FindSolutionOrProjectFile(FileInfo workingDirector workingDirectory.FullName, FixThisProblem("SOLUTION_FILE")); return new List(); } -} \ No newline at end of file +} diff --git a/ScipDotnet/Program.cs b/ScipDotnet/Program.cs index ec6ab57..cb47306 100644 --- a/ScipDotnet/Program.cs +++ b/ScipDotnet/Program.cs @@ -1,14 +1,6 @@ -using System.CommandLine; -using System.CommandLine.Builder; -using System.CommandLine.Hosting; -using System.CommandLine.NamingConventionBinder; -using System.CommandLine.Parsing; +using System.CommandLine; using Microsoft.Build.Locator; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace ScipDotnet; @@ -19,69 +11,115 @@ public static class Program public static async Task Main(string[] args) { + var projectsArgument = new Argument>("projects") + { + Arity = ArgumentArity.ZeroOrMore, + Description = "Path to the .sln (solution) or .csproj/.vbproj file" + }; + var outputOption = new Option("--output") + { + Description = "Path to the output SCIP index file", + DefaultValueFactory = _ => "index.scip" + }; + var workingDirectoryOption = new Option("--working-directory") + { + Description = "The working directory", + DefaultValueFactory = _ => new FileInfo(Directory.GetCurrentDirectory()) + }; + var includeOption = new Option>("--include") + { + Description = "Only index files that match the given file glob pattern", + Arity = ArgumentArity.ZeroOrMore, + DefaultValueFactory = _ => new List() + }; + var excludeOption = new Option>("--exclude") + { + Description = "Only index files that match the given file glob pattern", + Arity = ArgumentArity.ZeroOrMore, + DefaultValueFactory = _ => new List() + }; + var allowGlobalSymbolDefinitionsOption = new Option("--allow-global-symbol-definitions") + { + Description = "If enabled, allow public symbol definitions to be accessible from other SCIP indexes. " + + "If disabled, then public symbols will only be visible within the index.", + DefaultValueFactory = _ => false + }; + var dotnetRestoreTimeoutOption = new Option("--dotnet-restore-timeout") + { + Description = @"The timeout (in ms) for the ""dotnet restore"" command", + DefaultValueFactory = _ => DotnetRestoreTimeout + }; + var skipDotnetRestoreOption = new Option("--skip-dotnet-restore") + { + Description = @"Skip executing ""dotnet restore"" and assume it has been run externally.", + DefaultValueFactory = _ => false + }; + var nugetConfigPathOption = new Option("--nuget-config-path") + { + Description = @"Provide a case sensitive custom path for ""dotnet restore"" to find the NuGet.config file. " + + @"If not provided, ""dotnet restore"" will search for the NuGet.config file recursively up the folder hierarchy " + + @"and in the default user and system config locations." + }; + var indexCommand = new Command("index", "Index a solution file") { - new Argument("projects", "Path to the .sln (solution) or .csproj/.vbproj file") - { Arity = ArgumentArity.ZeroOrMore }, - new Option("--output", () => "index.scip", - "Path to the output SCIP index file"), - new Option("--working-directory", - () => new FileInfo(Directory.GetCurrentDirectory()), - "The working directory"), - new Option>("--include", () => new List(), - "Only index files that match the given file glob pattern") - { - Arity = ArgumentArity.ZeroOrMore, - }, - new Option>("--exclude", () => new List(), - "Only index files that match the given file glob pattern") - { - Arity = ArgumentArity.ZeroOrMore - }, - new Option("--allow-global-symbol-definitions", () => false, - "If enabled, allow public symbol definitions to be accessible from other SCIP indexes. " + - "If disabled, then public symbols will only be visible within the index."), - new Option("--dotnet-restore-timeout", () => DotnetRestoreTimeout, - @"The timeout (in ms) for the ""dotnet restore"" command"), - new Option("--skip-dotnet-restore", () => false, - @"Skip executing ""dotnet restore"" and assume it has been run externally."), - new Option("--nuget-config-path", () => null, - @"Provide a case sensitive custom path for ""dotnet restore"" to find the NuGet.config file. " + - @"If not provided, ""dotnet restore"" will search for the NuGet.config file recursively up the folder hierarchy " + - @"and in the default user and system config locations."), + projectsArgument, + outputOption, + workingDirectoryOption, + includeOption, + excludeOption, + allowGlobalSymbolDefinitionsOption, + dotnetRestoreTimeoutOption, + skipDotnetRestoreOption, + nugetConfigPathOption }; - indexCommand.Handler = CommandHandler.Create(IndexCommandHandler.Process); - var rootCommand = - new RootCommand( - "SCIP indexer for the C# and Visual basic programming languages. Built with the Roslyn .NET compiler. Supports MSBuild.") - { - indexCommand, - }; - var builder = new CommandLineBuilder(rootCommand); - return await builder.UseHost(_ => Host.CreateDefaultBuilder(), host => + + indexCommand.SetAction(async (parseResult, cancellationToken) => + { + var projects = parseResult.GetValue(projectsArgument) ?? new List(); + var output = parseResult.GetValue(outputOption) ?? "index.scip"; + var workingDirectory = parseResult.GetValue(workingDirectoryOption) ?? new FileInfo(Directory.GetCurrentDirectory()); + var include = parseResult.GetValue(includeOption) ?? new List(); + var exclude = parseResult.GetValue(excludeOption) ?? new List(); + var allowGlobalSymbolDefinitions = parseResult.GetValue(allowGlobalSymbolDefinitionsOption); + var dotnetRestoreTimeout = parseResult.GetValue(dotnetRestoreTimeoutOption); + var skipDotnetRestore = parseResult.GetValue(skipDotnetRestoreOption); + var nugetConfigPath = parseResult.GetValue(nugetConfigPathOption); + + using var loggerFactory = LoggerFactory.Create(builder => { - host.ConfigureAppConfiguration(b => b.AddInMemoryCollection()); - host.ConfigureLogging(b => b.AddSimpleConsole(options => + builder.AddSimpleConsole(options => { options.IncludeScopes = true; options.SingleLine = true; options.TimestampFormat = "HH:mm:ss "; - }).AddFilter("Microsoft.Hosting.Lifetime", LogLevel.None)); - host.ConfigureServices((_, collection) => - collection - .AddSingleton(_ => CreateWorkspace()) - .AddTransient() - .AddTransient(services => (Workspace)services.GetRequiredService()) - ); - }) - .UseDefaults() - .Build() - .InvokeAsync(args); - } + }); + builder.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.None); + }); - private static MSBuildWorkspace CreateWorkspace() - { - MSBuildLocator.RegisterDefaults(); - return MSBuildWorkspace.Create(); + MSBuildLocator.RegisterDefaults(); + var workspace = MSBuildWorkspace.Create(); + + return await IndexCommandHandler.Process( + loggerFactory, + workspace, + projects, + output, + workingDirectory, + include, + exclude, + allowGlobalSymbolDefinitions, + dotnetRestoreTimeout, + skipDotnetRestore, + nugetConfigPath); + }); + + var rootCommand = new RootCommand( + "SCIP indexer for the C# and Visual basic programming languages. Built with the Roslyn .NET compiler. Supports MSBuild.") + { + indexCommand + }; + + return await rootCommand.Parse(args).InvokeAsync(); } -} \ No newline at end of file +} diff --git a/ScipDotnet/ScipDotnet.csproj b/ScipDotnet/ScipDotnet.csproj index 6816cb1..8c206cc 100644 --- a/ScipDotnet/ScipDotnet.csproj +++ b/ScipDotnet/ScipDotnet.csproj @@ -24,8 +24,10 @@ - - + + + + diff --git a/ScipDotnet/ScipProjectIndexer.cs b/ScipDotnet/ScipProjectIndexer.cs index 00306f0..a58bb88 100644 --- a/ScipDotnet/ScipProjectIndexer.cs +++ b/ScipDotnet/ScipProjectIndexer.cs @@ -1,9 +1,7 @@ using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.MSBuild; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.FileSystemGlobbing; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace ScipDotnet; @@ -42,19 +40,19 @@ private void Restore(IndexCommandOptions options, FileInfo project) } } - public async IAsyncEnumerable IndexDocuments(IHost host, IndexCommandOptions options) + public async IAsyncEnumerable IndexDocuments(MSBuildWorkspace workspace, IndexCommandOptions options) { var indexedProjects = new HashSet(); foreach (var project in options.ProjectsFile) { - await foreach (var document in IndexProject(host, options, project, indexedProjects)) + await foreach (var document in IndexProject(workspace, options, project, indexedProjects)) { yield return document; } } } - private async IAsyncEnumerable IndexProject(IHost host, + private async IAsyncEnumerable IndexProject(MSBuildWorkspace workspace, IndexCommandOptions options, FileInfo rootProject, HashSet indexedProjects) @@ -67,11 +65,9 @@ private void Restore(IndexCommandOptions options, FileInfo project) var projects = (string.Equals(rootProject.Extension, ".csproj") || string.Equals(rootProject.Extension, ".vbproj") ? new[] { - await host.Services.GetRequiredService() - .OpenProjectAsync(rootProject.FullName) + await workspace.OpenProjectAsync(rootProject.FullName) } - : (await host.Services.GetRequiredService() - .OpenSolutionAsync(rootProject.FullName)).Projects).ToList(); + : (await workspace.OpenSolutionAsync(rootProject.FullName)).Projects).ToList(); options.Logger.LogDebug($"Found {projects.Count()} projects"); @@ -156,4 +152,4 @@ await host.Services.GetRequiredService() return doc; } -} \ No newline at end of file +}