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 +}