1
1
using System ;
2
2
using System . Diagnostics ;
3
+ using System . Diagnostics . Eventing . Reader ;
3
4
using System . IO ;
5
+ using System . Reflection ;
4
6
using System . Text ;
7
+ using System . Text . RegularExpressions ;
5
8
using System . Xml ;
6
9
using Microsoft . MIDebugEngine ;
7
10
using Microsoft . VisualStudio ;
@@ -13,46 +16,53 @@ namespace VisualRust.Project
13
16
{
14
17
sealed class DefaultRustLauncher : IProjectLauncher
15
18
{
16
- private readonly RustProjectNode _project ;
17
- private RustProjectConfig _projectConfig ;
18
- private readonly Configuration . Debug _debugConfig ;
19
+ private enum BuildArchitecture
20
+ {
21
+ Unknown ,
22
+ i686 ,
23
+ x86_64 ,
24
+ }
25
+
26
+ private readonly RustProjectNode project ;
27
+ private readonly Configuration . Debug debugConfig ;
28
+ private readonly RustProjectConfig projectConfig ;
19
29
20
30
public DefaultRustLauncher ( RustProjectNode project )
21
31
{
22
32
Utilities . ArgumentNotNull ( "project" , project ) ;
23
- _project = project ;
24
- string currConfig = _project . GetProjectProperty ( ProjectFileConstants . Configuration ) ;
25
- _projectConfig = ( RustProjectConfig ) _project . ConfigProvider . GetProjectConfiguration ( currConfig ) ;
26
- _debugConfig = Configuration . Debug . LoadFrom ( new [ ] { _projectConfig . UserCfg } ) ;
33
+ this . project = project ;
34
+ string currConfig = this . project . GetProjectProperty ( ProjectFileConstants . Configuration ) ;
35
+ projectConfig = ( RustProjectConfig ) this . project . ConfigProvider . GetProjectConfiguration ( currConfig ) ;
36
+ debugConfig = Configuration . Debug . LoadFrom ( new [ ] { projectConfig . UserCfg } ) ;
27
37
}
28
38
29
39
public int LaunchProject ( bool debug )
30
40
{
31
- if ( _debugConfig . StartAction == Configuration . StartAction . Project &&
32
- _project . GetProjectProperty ( "OutputType" ) != "exe" )
41
+ if ( debugConfig . StartAction == Configuration . StartAction . Project &&
42
+ project . GetProjectProperty ( "OutputType" ) != "exe" )
33
43
{
34
44
throw new InvalidOperationException ( "A project with an Output Type of Library cannot be started directly." ) ;
35
45
}
36
46
37
47
string startupFilePath ;
38
- if ( _debugConfig . StartAction == Configuration . StartAction . Project )
48
+ if ( debugConfig . StartAction == Configuration . StartAction . Project )
39
49
startupFilePath = GetProjectStartupFile ( ) ;
40
50
else
41
- startupFilePath = _debugConfig . ExternalProgram ;
51
+ startupFilePath = debugConfig . ExternalProgram ;
42
52
43
53
return LaunchFile ( startupFilePath , debug ) ;
44
54
}
45
55
46
56
private string GetProjectStartupFile ( )
47
57
{
48
- var startupFilePath = Path . Combine ( _project . GetProjectProperty ( "TargetDir" ) , _project . GetProjectProperty ( "TargetFileName" ) ) ;
58
+ var startupFilePath = Path . Combine ( project . GetProjectProperty ( "TargetDir" ) , project . GetProjectProperty ( "TargetFileName" ) ) ;
49
59
if ( string . IsNullOrEmpty ( startupFilePath ) )
50
60
{
51
61
throw new ApplicationException ( "Visual Rust could not resolve path for your executable. Your installation of Visual Rust or .rsproj file might be corrupted." ) ;
52
62
}
53
63
54
64
if ( ! File . Exists ( startupFilePath ) )
55
- _project . Build ( "Build" ) ;
65
+ project . Build ( "Build" ) ;
56
66
57
67
return startupFilePath ;
58
68
}
@@ -65,7 +75,7 @@ public int LaunchFile(string file, bool debug)
65
75
}
66
76
else
67
77
{
68
- var processStartInfo = CreateProcessStartInfo ( file , debug ) ;
78
+ var processStartInfo = CreateProcessStartInfo ( file ) ;
69
79
Process . Start ( processStartInfo ) ;
70
80
}
71
81
@@ -75,21 +85,29 @@ public int LaunchFile(string file, bool debug)
75
85
76
86
private void LaunchInGdbDebugger ( string file )
77
87
{
78
- VsDebugTargetInfo4 [ ] targets = new VsDebugTargetInfo4 [ 1 ] ;
88
+ var targets = new VsDebugTargetInfo4 [ 1 ] ;
79
89
targets [ 0 ] . dlo = ( uint ) DEBUG_LAUNCH_OPERATION . DLO_CreateProcess ;
80
90
targets [ 0 ] . bstrExe = file ;
81
91
targets [ 0 ] . guidLaunchDebugEngine = new Guid ( EngineConstants . EngineId ) ;
82
-
83
- string gdbPath = GetDebuggingProperty < string > ( "DebuggerLocation" ) ;
84
- if ( string . IsNullOrWhiteSpace ( gdbPath ) )
92
+
93
+ bool useCustomPath = GetDebugProperty < bool > ( "UseCustomGdbPath" ) ;
94
+ string gdbPath ;
95
+ if ( useCustomPath )
85
96
{
86
- gdbPath = "gdb.exe" ;
97
+ gdbPath = GetDebugProperty < string > ( "DebuggerLocation" ) ;
98
+ }
99
+ else
100
+ {
101
+ gdbPath = Path . Combine (
102
+ Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) ,
103
+ "gdb" ,
104
+ GuessArchitecture ( ) ,
105
+ "bin\\ gdb" ) ;
87
106
}
88
-
89
107
string gdbArgs =
90
108
"-q " + // quiet
91
109
"-interpreter=mi " + // use machine interface
92
- GetDebuggingProperty < string > ( "ExtraArgs" ) ; // add extra options from Visual Rust/Debugging options page
110
+ GetDebugProperty < string > ( "ExtraArgs" ) ; // add extra options from Visual Rust/Debugging options page
93
111
94
112
var options = new StringBuilder ( ) ;
95
113
using ( var writer = XmlWriter . Create ( options , new XmlWriterSettings { OmitXmlDeclaration = true } ) )
@@ -98,15 +116,15 @@ private void LaunchInGdbDebugger(string file)
98
116
writer . WriteAttributeString ( "PipePath" , gdbPath ) ;
99
117
writer . WriteAttributeString ( "PipeArguments" , gdbArgs ) ;
100
118
writer . WriteAttributeString ( "ExePath" , EscapePath ( file ) ) ;
101
- if ( ! string . IsNullOrEmpty ( _debugConfig . CommandLineArgs ) )
119
+ if ( ! string . IsNullOrEmpty ( debugConfig . CommandLineArgs ) )
102
120
{
103
- writer . WriteAttributeString ( "ExeArguments" , _debugConfig . CommandLineArgs ) ;
121
+ writer . WriteAttributeString ( "ExeArguments" , debugConfig . CommandLineArgs ) ;
104
122
}
105
- if ( ! string . IsNullOrEmpty ( _debugConfig . WorkingDir ) )
123
+ if ( ! string . IsNullOrEmpty ( debugConfig . WorkingDir ) )
106
124
{
107
- writer . WriteAttributeString ( "WorkingDirectory" , EscapePath ( _debugConfig . WorkingDir ) ) ;
125
+ writer . WriteAttributeString ( "WorkingDirectory" , EscapePath ( debugConfig . WorkingDir ) ) ;
108
126
// GDB won't search working directory by default, but this is expected on Windows.
109
- writer . WriteAttributeString ( "AdditionalSOLibSearchPath" , _debugConfig . WorkingDir ) ;
127
+ writer . WriteAttributeString ( "AdditionalSOLibSearchPath" , debugConfig . WorkingDir ) ;
110
128
}
111
129
else
112
130
{
@@ -122,9 +140,9 @@ private void LaunchInGdbDebugger(string file)
122
140
writer . WriteElementString ( "Command" , "alias -a gdb=echo" ) ;
123
141
// launch debuggee in a new console window
124
142
writer . WriteElementString ( "Command" , "set new-console on" ) ;
125
- if ( ! string . IsNullOrEmpty ( _debugConfig . DebuggerScript ) )
143
+ if ( ! string . IsNullOrEmpty ( debugConfig . DebuggerScript ) )
126
144
{
127
- foreach ( string cmd in _debugConfig . DebuggerScript . Split ( '\r ' , '\n ' ) )
145
+ foreach ( string cmd in debugConfig . DebuggerScript . Split ( '\r ' , '\n ' ) )
128
146
if ( ! string . IsNullOrEmpty ( cmd ) )
129
147
writer . WriteElementString ( "Command" , cmd ) ;
130
148
}
@@ -135,49 +153,109 @@ private void LaunchInGdbDebugger(string file)
135
153
136
154
VsDebugTargetProcessInfo [ ] results = new VsDebugTargetProcessInfo [ targets . Length ] ;
137
155
138
- IVsDebugger4 vsDebugger = ( IVsDebugger4 ) _project . GetService ( typeof ( SVsShellDebugger ) ) ;
156
+ IVsDebugger4 vsDebugger = ( IVsDebugger4 ) project . GetService ( typeof ( SVsShellDebugger ) ) ;
139
157
vsDebugger . LaunchDebugTargets4 ( ( uint ) targets . Length , targets , results ) ;
140
158
141
159
// Type "gdb <command>" in the VS Command Window
142
- var commandWnd = ( IVsCommandWindow ) _project . GetService ( typeof ( SVsCommandWindow ) ) ;
160
+ var commandWnd = ( IVsCommandWindow ) project . GetService ( typeof ( SVsCommandWindow ) ) ;
143
161
commandWnd . ExecuteCommand ( "alias gdb Debug.GDBExec" ) ;
144
162
}
145
163
146
- private string EscapePath ( string path )
164
+ private string GuessArchitecture ( )
147
165
{
148
- return String . Format ( "\" {0}\" " , path ) ;
166
+ BuildArchitecture configArch = GetArchFromConfiguration ( ) ;
167
+ if ( configArch != BuildArchitecture . Unknown )
168
+ return ArchitectureToString ( configArch ) ;
169
+ BuildArchitecture rustcArch = GetArchFromRustc ( ) ;
170
+ if ( rustcArch != BuildArchitecture . Unknown )
171
+ return ArchitectureToString ( rustcArch ) ;
172
+ if ( Environment . Is64BitOperatingSystem )
173
+ return "x86_64" ;
174
+ return "i686" ;
175
+ }
176
+
177
+ private BuildArchitecture GetArchFromConfiguration ( )
178
+ {
179
+ string configuredTarget = Configuration . Build . LoadFrom ( new ProjectConfig [ ] { this . projectConfig } ) . PlatformTarget ;
180
+ return ArchitectureFromTargetTriple ( configuredTarget ) ;
181
+ }
182
+
183
+ private static BuildArchitecture ArchitectureFromTargetTriple ( string configuredTarget )
184
+ {
185
+ int platformNameLength = configuredTarget . IndexOf ( '-' ) ;
186
+ if ( platformNameLength == - 1 )
187
+ return BuildArchitecture . Unknown ;
188
+ return ParseArchitecture ( configuredTarget . Substring ( 0 , platformNameLength ) ) ;
189
+ }
190
+
191
+ private BuildArchitecture GetArchFromRustc ( )
192
+ {
193
+ string defaultInstallPath = Shared . Environment . FindInstallPath ( "default" ) ;
194
+ if ( defaultInstallPath == null )
195
+ return BuildArchitecture . Unknown ;
196
+ string rustcPath = Path . Combine ( defaultInstallPath , "rustc.exe" ) ;
197
+ string rustcHost = GetRustcHost ( rustcPath ) ;
198
+ return ArchitectureFromTargetTriple ( rustcHost ) ;
149
199
}
150
200
151
- private T GetDebuggingProperty < T > ( string key )
201
+ private static string ArchitectureToString ( BuildArchitecture b )
152
202
{
153
- var env = ( EnvDTE . DTE ) _project . GetService ( typeof ( EnvDTE . DTE ) ) ;
154
- return ( T ) env . get_Properties ( "Visual Rust" , "Debugging" ) . Item ( key ) . Value ;
203
+ switch ( b )
204
+ {
205
+ case BuildArchitecture . i686 :
206
+ return "i686" ;
207
+ case BuildArchitecture . x86_64 :
208
+ return "x86_64" ;
209
+ default :
210
+ throw new ArgumentException ( null , "b" ) ;
211
+ }
155
212
}
156
213
157
- private ProcessStartInfo CreateProcessStartInfo ( string startupFile , bool debug )
214
+ private static BuildArchitecture ParseArchitecture ( string name )
158
215
{
159
- var commandLineArgs = _debugConfig . CommandLineArgs ;
160
- if ( ! debug )
216
+ switch ( name )
161
217
{
162
- commandLineArgs = String . Format ( @"/c """"{0}"" {1} & pause""" , startupFile , commandLineArgs ) ;
163
- startupFile = Path . Combine ( System . Environment . SystemDirectory , "cmd.exe" ) ;
218
+ case "i686" :
219
+ return BuildArchitecture . i686 ;
220
+ case "x86_64" :
221
+ return BuildArchitecture . x86_64 ;
222
+ default :
223
+ return BuildArchitecture . Unknown ;
164
224
}
225
+ }
226
+
227
+ private static string EscapePath ( string path )
228
+ {
229
+ return String . Format ( "\" {0}\" " , path ) ;
230
+ }
231
+
232
+ private T GetDebugProperty < T > ( string key )
233
+ {
234
+ var env = ( EnvDTE . DTE ) project . GetService ( typeof ( EnvDTE . DTE ) ) ;
235
+ return ( T ) env . Properties [ "Visual Rust" , "Debugging" ] . Item ( key ) . Value ;
236
+ }
237
+
238
+ private ProcessStartInfo CreateProcessStartInfo ( string startupFile )
239
+ {
240
+ var commandLineArgs = debugConfig . CommandLineArgs ;
241
+ commandLineArgs = String . Format ( @"/c """"{0}"" {1} & pause""" , startupFile , commandLineArgs ) ;
242
+ startupFile = Path . Combine ( System . Environment . SystemDirectory , "cmd.exe" ) ;
165
243
var startInfo = new ProcessStartInfo ( startupFile , commandLineArgs ) ;
166
- startInfo . WorkingDirectory = _debugConfig . WorkingDir ;
244
+ startInfo . WorkingDirectory = debugConfig . WorkingDir ;
167
245
startInfo . UseShellExecute = false ;
168
246
InjectRustBinPath ( startInfo ) ;
169
247
return startInfo ;
170
248
}
171
249
172
250
private void InjectRustBinPath ( ProcessStartInfo startInfo )
173
251
{
174
- EnvDTE . Project proj = _project . GetAutomationObject ( ) as EnvDTE . Project ;
252
+ EnvDTE . Project proj = project . GetAutomationObject ( ) as EnvDTE . Project ;
175
253
if ( proj == null )
176
254
return ;
177
255
string currentConfigName = Utilities . GetActiveConfigurationName ( proj ) ;
178
256
if ( currentConfigName == null )
179
257
return ;
180
- ProjectConfig currentConfig = _project . ConfigProvider . GetProjectConfiguration ( currentConfigName ) ;
258
+ ProjectConfig currentConfig = project . ConfigProvider . GetProjectConfiguration ( currentConfigName ) ;
181
259
if ( currentConfig == null )
182
260
return ;
183
261
string currentTarget = currentConfig . GetConfigurationProperty ( "PlatformTarget" , true ) ;
@@ -190,5 +268,23 @@ private void InjectRustBinPath(ProcessStartInfo startInfo)
190
268
string newEnvPath = String . Format ( "{0};{1}" , envPath , installPath ) ;
191
269
startInfo . EnvironmentVariables [ "PATH" ] = newEnvPath ;
192
270
}
271
+
272
+ public static string GetRustcHost ( string exePath )
273
+ {
274
+ ProcessStartInfo psi = new ProcessStartInfo
275
+ {
276
+ UseShellExecute = false ,
277
+ CreateNoWindow = true ,
278
+ FileName = exePath ,
279
+ RedirectStandardOutput = true ,
280
+ Arguments = "-Vv"
281
+ } ;
282
+ Process proc = Process . Start ( psi ) ;
283
+ string verboseVersion = proc . StandardOutput . ReadToEnd ( ) ;
284
+ Match hostMatch = Regex . Match ( verboseVersion , "^host: (.+)$" , RegexOptions . Multiline ) ;
285
+ if ( hostMatch . Groups . Count == 1 )
286
+ return null ;
287
+ return hostMatch . Groups [ 1 ] . Value ;
288
+ }
193
289
}
194
290
}
0 commit comments