22using System . Collections . Generic ;
33using System . Diagnostics ;
44using System . IO ;
5+ using System . Linq ;
6+ using System . Text . RegularExpressions ;
57using Semmle . Util ;
68
79namespace Semmle . Extraction . CSharp . DependencyFetching
810{
911 /// <summary>
1012 /// Utilities to run the "dotnet" command.
1113 /// </summary>
12- internal class DotNet : IDotNet
14+ internal partial class DotNet : IDotNet
1315 {
1416 private readonly ProgressMonitor progressMonitor ;
1517 private readonly string dotnet ;
@@ -31,17 +33,22 @@ private void Info()
3133 }
3234 }
3335
34- private ProcessStartInfo MakeDotnetStartInfo ( string args , bool redirectStandardOutput ) =>
35- new ProcessStartInfo ( dotnet , args )
36+ private ProcessStartInfo MakeDotnetStartInfo ( string args , bool redirectStandardOutput )
37+ {
38+ var startInfo = new ProcessStartInfo ( dotnet , args )
3639 {
3740 UseShellExecute = false ,
3841 RedirectStandardOutput = redirectStandardOutput
3942 } ;
43+ // Set the .NET CLI language to English to avoid localized output.
44+ startInfo . EnvironmentVariables [ "DOTNET_CLI_UI_LANGUAGE" ] = "en" ;
45+ return startInfo ;
46+ }
4047
4148 private bool RunCommand ( string args )
4249 {
4350 progressMonitor . RunningProcess ( $ "{ dotnet } { args } ") ;
44- using var proc = Process . Start ( this . MakeDotnetStartInfo ( args , redirectStandardOutput : false ) ) ;
51+ using var proc = Process . Start ( MakeDotnetStartInfo ( args , redirectStandardOutput : false ) ) ;
4552 proc ? . WaitForExit ( ) ;
4653 var exitCode = proc ? . ExitCode ?? - 1 ;
4754 if ( exitCode != 0 )
@@ -52,12 +59,50 @@ private bool RunCommand(string args)
5259 return true ;
5360 }
5461
55- public bool RestoreToDirectory ( string projectOrSolutionFile , string packageDirectory , string ? pathToNugetConfig = null )
62+ private bool RunCommand ( string args , out IList < string > output )
63+ {
64+ progressMonitor . RunningProcess ( $ "{ dotnet } { args } ") ;
65+ var pi = MakeDotnetStartInfo ( args , redirectStandardOutput : true ) ;
66+ var exitCode = pi . ReadOutput ( out output ) ;
67+ if ( exitCode != 0 )
68+ {
69+ progressMonitor . CommandFailed ( dotnet , args , exitCode ) ;
70+ return false ;
71+ }
72+ return true ;
73+ }
74+
75+ private static string GetRestoreArgs ( string projectOrSolutionFile , string packageDirectory ) =>
76+ $ "restore --no-dependencies \" { projectOrSolutionFile } \" --packages \" { packageDirectory } \" /p:DisableImplicitNuGetFallbackFolder=true";
77+
78+ public bool RestoreProjectToDirectory ( string projectFile , string packageDirectory , out string stdout , string ? pathToNugetConfig = null )
5679 {
57- var args = $ "restore --no-dependencies \" { projectOrSolutionFile } \" --packages \" { packageDirectory } \" /p:DisableImplicitNuGetFallbackFolder=true" ;
80+ var args = GetRestoreArgs ( projectFile , packageDirectory ) ;
5881 if ( pathToNugetConfig != null )
82+ {
5983 args += $ " --configfile \" { pathToNugetConfig } \" ";
60- return RunCommand ( args ) ;
84+ }
85+ var success = RunCommand ( args , out var output ) ;
86+ stdout = string . Join ( "\n " , output ) ;
87+ return success ;
88+ }
89+
90+ public bool RestoreSolutionToDirectory ( string solutionFile , string packageDirectory , out IEnumerable < string > projects )
91+ {
92+ var args = GetRestoreArgs ( solutionFile , packageDirectory ) ;
93+ args += " --verbosity normal" ;
94+ if ( RunCommand ( args , out var output ) )
95+ {
96+ var regex = RestoreProjectRegex ( ) ;
97+ projects = output
98+ . Select ( line => regex . Match ( line ) )
99+ . Where ( match => match . Success )
100+ . Select ( match => match . Groups [ 1 ] . Value ) ;
101+ return true ;
102+ }
103+
104+ projects = Array . Empty < string > ( ) ;
105+ return false ;
61106 }
62107
63108 public bool New ( string folder )
@@ -78,22 +123,21 @@ public bool AddPackage(string folder, string package)
78123
79124 private IList < string > GetListed ( string args , string artifact )
80125 {
81- progressMonitor . RunningProcess ( $ "{ dotnet } { args } ") ;
82- var pi = this . MakeDotnetStartInfo ( args , redirectStandardOutput : true ) ;
83- var exitCode = pi . ReadOutput ( out var artifacts ) ;
84- if ( exitCode != 0 )
126+ if ( RunCommand ( args , out var artifacts ) )
85127 {
86- progressMonitor . CommandFailed ( dotnet , args , exitCode ) ;
87- return new List < string > ( ) ;
128+ progressMonitor . LogInfo ( $ "Found { artifact } s: { string . Join ( " \n " , artifacts ) } " ) ;
129+ return artifacts ;
88130 }
89- progressMonitor . LogInfo ( $ "Found { artifact } s: { string . Join ( "\n " , artifacts ) } ") ;
90- return artifacts ;
131+ return new List < string > ( ) ;
91132 }
92133
93134 public bool Exec ( string execArgs )
94135 {
95136 var args = $ "exec { execArgs } ";
96137 return RunCommand ( args ) ;
97138 }
139+
140+ [ GeneratedRegex ( "Restored\\ s+(.+\\ .csproj)" , RegexOptions . Compiled ) ]
141+ private static partial Regex RestoreProjectRegex ( ) ;
98142 }
99143}
0 commit comments