From 6287e62574681a76fcaa5c43e7128607860277b2 Mon Sep 17 00:00:00 2001 From: Chidozie Ononiwu <31145988+chidozieononiwu@users.noreply.github.com> Date: Mon, 8 Jul 2024 10:25:10 -0700 Subject: [PATCH] Update Parser Logic (#8565) --- .../Languages/CSharpLanguageService.cs | 45 ------------ .../CSharpAPIParser/CSharpAPIParser.csproj | 1 + .../CSharpAPIParser/Program.cs | 68 +++++++++++++++++-- 3 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/dotnet/APIView/APIViewWeb/Languages/CSharpLanguageService.cs b/src/dotnet/APIView/APIViewWeb/Languages/CSharpLanguageService.cs index 4c4f3a0d5e1..11da3e8b0cf 100644 --- a/src/dotnet/APIView/APIViewWeb/Languages/CSharpLanguageService.cs +++ b/src/dotnet/APIView/APIViewWeb/Languages/CSharpLanguageService.cs @@ -86,50 +86,5 @@ private CodeFile GetDummyReviewCodeFile(string originalName, List - /// Resolves the NuGet package dependencies and extracts them to a temporary folder. It is the responsibility of teh caller to clean up the folder. - /// - /// The dependency infos - /// A temporary path where the dependency files were extracted. - private async Task ExtractNugetDependencies(List dependencyInfos) - { - string tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - SourceCacheContext cache = new SourceCacheContext(); - SourceRepository repository = NuGet.Protocol.Core.Types.Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); - try - { - FindPackageByIdResource resource = await repository.GetResourceAsync().ConfigureAwait(false); - foreach (var dep in dependencyInfos) - { - using (MemoryStream packageStream = new MemoryStream()) - { - if (await resource.CopyNupkgToStreamAsync( - dep.Name, - new NuGetVersion(dep.Version), - packageStream, - cache, - NullLogger.Instance, - CancellationToken.None)) - { - using PackageArchiveReader reader = new PackageArchiveReader(packageStream); - NuspecReader nuspec = reader.NuspecReader; - var file = reader.GetFiles().FirstOrDefault(f => f.EndsWith(dep.Name + ".dll")); - if (file != null) - { - var fileInfo = new FileInfo(file); - var path = Path.Combine(tempFolder, dep.Name, fileInfo.Name); - var tmp = reader.ExtractFile(file, path, NullLogger.Instance); - } - } - } - } - } - finally - { - cache.Dispose(); - } - return tempFolder; - } } } diff --git a/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/CSharpAPIParser.csproj b/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/CSharpAPIParser.csproj index 46e67f8e934..2fa0ca2222c 100644 --- a/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/CSharpAPIParser.csproj +++ b/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/CSharpAPIParser.csproj @@ -11,6 +11,7 @@ + diff --git a/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/Program.cs b/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/Program.cs index 375bd05fd4b..b210b13fcee 100644 --- a/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/Program.cs +++ b/tools/apiview/parsers/csharp-api-parser/CSharpAPIParser/Program.cs @@ -4,6 +4,11 @@ using System.Text.Json.Serialization; using System.Xml.Linq; using ApiView; +using NuGet.Common; +using NuGet.Packaging; +using NuGet.Protocol; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; var inputOption = new Option("--packageFilePath", "C# Package (.nupkg) file").ExistingOnly(); inputOption.IsRequired = true; @@ -21,13 +26,13 @@ runAnalysis }; -rootCommand.SetHandler((FileInfo packageFilePath, DirectoryInfo outputDirectory, string outputFileName, bool runAnalysis) => +rootCommand.SetHandler(async (FileInfo packageFilePath, DirectoryInfo outputDirectory, string outputFileName, bool runAnalysis) => { try { using (var stream = packageFilePath.OpenRead()) { - HandlePackageFileParsing(stream, packageFilePath, outputDirectory, outputFileName, runAnalysis); + await HandlePackageFileParsing(stream, packageFilePath, outputDirectory, outputFileName, runAnalysis); } } catch (Exception ex) @@ -39,12 +44,13 @@ return rootCommand.InvokeAsync(args).Result; -static void HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, DirectoryInfo OutputDirectory, string outputFileName, bool runAnalysis) +static async Task HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, DirectoryInfo OutputDirectory, string outputFileName, bool runAnalysis) { ZipArchive? zipArchive = null; Stream? dllStream = stream; Stream? docStream = null; List? dependencies = null; + string dependencyFilesTempDir = null; try { @@ -94,12 +100,16 @@ static void HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, Di } } - var assemblySymbol = CompilationFactory.GetCompilation(dllStream, docStream); + dependencyFilesTempDir = await ExtractNugetDependencies(dependencies).ConfigureAwait(false); + var dependencyFilePaths = Directory.EnumerateFiles(dependencyFilesTempDir, "*.dll", SearchOption.AllDirectories); + var assemblySymbol = CompilationFactory.GetCompilation(dllStream, docStream, dependencyFilePaths); + if (assemblySymbol == null) { Console.Error.WriteLine($"PackageFile {packageFilePath.FullName} contains no Assembly Symbol."); return; } + var parsedFileName = string.IsNullOrEmpty(outputFileName) ? assemblySymbol.Name : outputFileName; var treeTokenCodeFile = new CSharpAPIParser.TreeToken.CodeFileBuilder().Build(assemblySymbol, runAnalysis, dependencies); var gzipJsonTokenFilePath = Path.Combine(OutputDirectory.FullName, $"{parsedFileName}.json.tgz"); @@ -126,8 +136,11 @@ static void HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, Di finally { zipArchive?.Dispose(); + if (dependencyFilesTempDir != null && Directory.Exists(dependencyFilesTempDir)) + { + Directory.Delete(dependencyFilesTempDir, true); + } } - } static bool IsNuget(string name) @@ -144,3 +157,48 @@ static bool IsDll(string name) { return name.EndsWith(".dll", StringComparison.OrdinalIgnoreCase); } + +/// +/// Resolves the NuGet package dependencies and extracts them to a temporary folder. It is the responsibility of teh caller to clean up the folder. +/// +/// The dependency infos +/// A temporary path where the dependency files were extracted. +static async Task ExtractNugetDependencies(List dependencyInfos) +{ + string tempFolder = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + SourceCacheContext cache = new SourceCacheContext(); + SourceRepository repository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json"); + try + { + FindPackageByIdResource resource = await repository.GetResourceAsync().ConfigureAwait(false); + foreach (var dep in dependencyInfos) + { + using (MemoryStream packageStream = new MemoryStream()) + { + if (await resource.CopyNupkgToStreamAsync( + dep.Name, + new NuGetVersion(dep.Version), + packageStream, + cache, + NullLogger.Instance, + CancellationToken.None)) + { + using PackageArchiveReader reader = new PackageArchiveReader(packageStream); + NuspecReader nuspec = reader.NuspecReader; + var file = reader.GetFiles().FirstOrDefault(f => f.EndsWith(dep.Name + ".dll")); + if (file != null) + { + var fileInfo = new FileInfo(file); + var path = Path.Combine(tempFolder, dep.Name, fileInfo.Name); + var tmp = reader.ExtractFile(file, path, NullLogger.Instance); + } + } + } + } + } + finally + { + cache.Dispose(); + } + return tempFolder; +}