44using System . Text . Json . Serialization ;
55using System . Xml . Linq ;
66using ApiView ;
7+ using NuGet . Common ;
8+ using NuGet . Packaging ;
9+ using NuGet . Protocol ;
10+ using NuGet . Protocol . Core . Types ;
11+ using NuGet . Versioning ;
712
813var inputOption = new Option < FileInfo > ( "--packageFilePath" , "C# Package (.nupkg) file" ) . ExistingOnly ( ) ;
914inputOption . IsRequired = true ;
2126 runAnalysis
2227} ;
2328
24- rootCommand . SetHandler ( ( FileInfo packageFilePath , DirectoryInfo outputDirectory , string outputFileName , bool runAnalysis ) =>
29+ rootCommand . SetHandler ( async ( FileInfo packageFilePath , DirectoryInfo outputDirectory , string outputFileName , bool runAnalysis ) =>
2530{
2631 try
2732 {
2833 using ( var stream = packageFilePath . OpenRead ( ) )
2934 {
30- HandlePackageFileParsing ( stream , packageFilePath , outputDirectory , outputFileName , runAnalysis ) ;
35+ await HandlePackageFileParsing ( stream , packageFilePath , outputDirectory , outputFileName , runAnalysis ) ;
3136 }
3237 }
3338 catch ( Exception ex )
3944return rootCommand . InvokeAsync ( args ) . Result ;
4045
4146
42- static void HandlePackageFileParsing ( Stream stream , FileInfo packageFilePath , DirectoryInfo OutputDirectory , string outputFileName , bool runAnalysis )
47+ static async Task HandlePackageFileParsing ( Stream stream , FileInfo packageFilePath , DirectoryInfo OutputDirectory , string outputFileName , bool runAnalysis )
4348{
4449 ZipArchive ? zipArchive = null ;
4550 Stream ? dllStream = stream ;
4651 Stream ? docStream = null ;
4752 List < DependencyInfo > ? dependencies = null ;
53+ string dependencyFilesTempDir = null ;
4854
4955 try
5056 {
@@ -94,12 +100,16 @@ static void HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, Di
94100 }
95101 }
96102
97- var assemblySymbol = CompilationFactory . GetCompilation ( dllStream , docStream ) ;
103+ dependencyFilesTempDir = await ExtractNugetDependencies ( dependencies ) . ConfigureAwait ( false ) ;
104+ var dependencyFilePaths = Directory . EnumerateFiles ( dependencyFilesTempDir , "*.dll" , SearchOption . AllDirectories ) ;
105+ var assemblySymbol = CompilationFactory . GetCompilation ( dllStream , docStream , dependencyFilePaths ) ;
106+
98107 if ( assemblySymbol == null )
99108 {
100109 Console . Error . WriteLine ( $ "PackageFile { packageFilePath . FullName } contains no Assembly Symbol.") ;
101110 return ;
102111 }
112+
103113 var parsedFileName = string . IsNullOrEmpty ( outputFileName ) ? assemblySymbol . Name : outputFileName ;
104114 var treeTokenCodeFile = new CSharpAPIParser . TreeToken . CodeFileBuilder ( ) . Build ( assemblySymbol , runAnalysis , dependencies ) ;
105115 var gzipJsonTokenFilePath = Path . Combine ( OutputDirectory . FullName , $ "{ parsedFileName } .json.tgz") ;
@@ -126,8 +136,11 @@ static void HandlePackageFileParsing(Stream stream, FileInfo packageFilePath, Di
126136 finally
127137 {
128138 zipArchive ? . Dispose ( ) ;
139+ if ( dependencyFilesTempDir != null && Directory . Exists ( dependencyFilesTempDir ) )
140+ {
141+ Directory . Delete ( dependencyFilesTempDir , true ) ;
142+ }
129143 }
130-
131144}
132145
133146static bool IsNuget ( string name )
@@ -144,3 +157,48 @@ static bool IsDll(string name)
144157{
145158 return name . EndsWith ( ".dll" , StringComparison . OrdinalIgnoreCase ) ;
146159}
160+
161+ /// <summary>
162+ /// Resolves the NuGet package dependencies and extracts them to a temporary folder. It is the responsibility of teh caller to clean up the folder.
163+ /// </summary>
164+ /// <param name="dependencyInfos">The dependency infos</param>
165+ /// <returns>A temporary path where the dependency files were extracted.</returns>
166+ static async Task < string > ExtractNugetDependencies ( List < DependencyInfo > dependencyInfos )
167+ {
168+ string tempFolder = Path . Combine ( Path . GetTempPath ( ) , Guid . NewGuid ( ) . ToString ( ) ) ;
169+ SourceCacheContext cache = new SourceCacheContext ( ) ;
170+ SourceRepository repository = Repository . Factory . GetCoreV3 ( "https://api.nuget.org/v3/index.json" ) ;
171+ try
172+ {
173+ FindPackageByIdResource resource = await repository . GetResourceAsync < FindPackageByIdResource > ( ) . ConfigureAwait ( false ) ;
174+ foreach ( var dep in dependencyInfos )
175+ {
176+ using ( MemoryStream packageStream = new MemoryStream ( ) )
177+ {
178+ if ( await resource . CopyNupkgToStreamAsync (
179+ dep . Name ,
180+ new NuGetVersion ( dep . Version ) ,
181+ packageStream ,
182+ cache ,
183+ NullLogger . Instance ,
184+ CancellationToken . None ) )
185+ {
186+ using PackageArchiveReader reader = new PackageArchiveReader ( packageStream ) ;
187+ NuspecReader nuspec = reader . NuspecReader ;
188+ var file = reader . GetFiles ( ) . FirstOrDefault ( f => f . EndsWith ( dep . Name + ".dll" ) ) ;
189+ if ( file != null )
190+ {
191+ var fileInfo = new FileInfo ( file ) ;
192+ var path = Path . Combine ( tempFolder , dep . Name , fileInfo . Name ) ;
193+ var tmp = reader . ExtractFile ( file , path , NullLogger . Instance ) ;
194+ }
195+ }
196+ }
197+ }
198+ }
199+ finally
200+ {
201+ cache . Dispose ( ) ;
202+ }
203+ return tempFolder ;
204+ }
0 commit comments