@@ -13,6 +13,8 @@ use install::{self, InstallMethod};
13
13
use telemetry;
14
14
use telemetry:: { Telemetry , TelemetryEvent } ;
15
15
16
+ use std:: env:: consts:: EXE_SUFFIX ;
17
+ use std:: ffi:: OsString ;
16
18
use std:: process:: Command ;
17
19
use std:: path:: { Path , PathBuf } ;
18
20
use std:: ffi:: OsStr ;
@@ -67,7 +69,16 @@ impl<'a> Toolchain<'a> {
67
69
& self . path
68
70
}
69
71
pub fn exists ( & self ) -> bool {
70
- utils:: is_directory ( & self . path )
72
+ // HACK: linked toolchains are symlinks, and, contrary to what std docs
73
+ // lead me to believe `fs::metadata`, used by `is_directory` does not
74
+ // seem to follow symlinks on windows.
75
+ let is_symlink = if cfg ! ( windows) {
76
+ use std:: fs;
77
+ fs:: symlink_metadata ( & self . path ) . map ( |m| m. file_type ( ) . is_symlink ( ) ) . unwrap_or ( false )
78
+ } else {
79
+ false
80
+ } ;
81
+ utils:: is_directory ( & self . path ) || is_symlink
71
82
}
72
83
pub fn verify ( & self ) -> Result < ( ) > {
73
84
Ok ( try!( utils:: assert_is_directory ( & self . path ) ) )
@@ -277,12 +288,24 @@ impl<'a> Toolchain<'a> {
277
288
return Err ( ErrorKind :: ToolchainNotInstalled ( self . name . to_owned ( ) ) . into ( ) ) ;
278
289
}
279
290
280
- // Assume this binary exists within the current toolchain
281
- let bin_path = self . path . join ( "bin" ) . join ( binary. as_ref ( ) ) ;
291
+ // Create the path to this binary within the current toolchain sysroot
292
+ let binary = if let Some ( binary_str) = binary. as_ref ( ) . to_str ( ) {
293
+ if binary_str. ends_with ( EXE_SUFFIX ) {
294
+ binary. as_ref ( ) . to_owned ( )
295
+ } else {
296
+ OsString :: from ( format ! ( "{}{}" , binary_str, EXE_SUFFIX ) )
297
+ }
298
+ } else {
299
+ // Very weird case. Non-unicode command.
300
+ binary. as_ref ( ) . to_owned ( )
301
+ } ;
302
+
303
+ let bin_path = self . path . join ( "bin" ) . join ( & binary) ;
282
304
let mut cmd = Command :: new ( if utils:: is_file ( & bin_path) {
283
305
& bin_path
284
306
} else {
285
- // If not, let the OS try to resolve it globally for us
307
+ // If the bin doesn't actually exist in the sysroot, let the OS try
308
+ // to resolve it globally for us
286
309
Path :: new ( & binary)
287
310
} ) ;
288
311
self . set_env ( & mut cmd) ;
@@ -293,7 +316,42 @@ impl<'a> Toolchain<'a> {
293
316
// to give custom toolchains access to cargo
294
317
pub fn create_fallback_command < T : AsRef < OsStr > > ( & self , binary : T ,
295
318
primary_toolchain : & Toolchain ) -> Result < Command > {
296
- let mut cmd = try!( self . create_command ( binary) ) ;
319
+ // With the hacks below this only works for cargo atm
320
+ assert ! ( binary. as_ref( ) == "cargo" || binary. as_ref( ) == "cargo.exe" ) ;
321
+
322
+ if !self . exists ( ) {
323
+ return Err ( ErrorKind :: ToolchainNotInstalled ( self . name . to_owned ( ) ) . into ( ) ) ;
324
+ }
325
+ if !primary_toolchain. exists ( ) {
326
+ return Err ( ErrorKind :: ToolchainNotInstalled ( self . name . to_owned ( ) ) . into ( ) ) ;
327
+ }
328
+
329
+ let src_file = self . path . join ( "bin" ) . join ( format ! ( "cargo{}" , EXE_SUFFIX ) ) ;
330
+
331
+ // MAJOR HACKS: Copy cargo.exe to its own directory on windows before
332
+ // running it. This is so that the fallback cargo, when it in turn runs
333
+ // rustc.exe, will run the rustc.exe out of the PATH environment
334
+ // variable, _not_ the rustc.exe sitting in the same directory as the
335
+ // fallback. See the `fallback_cargo_calls_correct_rustc` testcase and
336
+ // PR 812.
337
+ let exe_path = if cfg ! ( windows) {
338
+ use std:: fs;
339
+ let fallback_dir = self . cfg . multirust_dir . join ( "fallback" ) ;
340
+ try!( fs:: create_dir_all ( & fallback_dir)
341
+ . chain_err ( || "unable to create dir to hold fallback exe" ) ) ;
342
+ let fallback_file = fallback_dir. join ( "cargo.exe" ) ;
343
+ if fallback_file. exists ( ) {
344
+ try!( fs:: remove_file ( & fallback_file)
345
+ . chain_err ( || "unable to unlink old fallback exe" ) ) ;
346
+ }
347
+ try!( fs:: hard_link ( & src_file, & fallback_file)
348
+ . chain_err ( || "unable to hard link fallback exe" ) ) ;
349
+ fallback_file
350
+ } else {
351
+ src_file
352
+ } ;
353
+ let mut cmd = Command :: new ( exe_path) ;
354
+ self . set_env ( & mut cmd) ;
297
355
cmd. env ( "RUSTUP_TOOLCHAIN" , & primary_toolchain. name ) ;
298
356
Ok ( cmd)
299
357
}
@@ -328,13 +386,13 @@ impl<'a> Toolchain<'a> {
328
386
}
329
387
env_var:: prepend_path ( sysenv:: LOADER_PATH , & new_path, cmd) ;
330
388
331
- // Prepend first cargo_home, then toolchain/bin to the PATH
332
- let mut path_to_prepend = PathBuf :: from ( "" ) ;
389
+ // Prepend CARGO_HOME/bin to the PATH variable so that we're sure to run
390
+ // cargo/rustc via the proxy bins. There is no fallback case for if the
391
+ // proxy bins don't exist. We'll just be running whatever happens to
392
+ // be on the PATH.
333
393
if let Ok ( cargo_home) = utils:: cargo_home ( ) {
334
- path_to_prepend . push ( cargo_home. join ( "bin" ) ) ;
394
+ env_var :: prepend_path ( "PATH" , & cargo_home. join ( "bin" ) , cmd ) ;
335
395
}
336
- path_to_prepend. push ( self . path . join ( "bin" ) ) ;
337
- env_var:: prepend_path ( "PATH" , path_to_prepend. as_path ( ) , cmd) ;
338
396
}
339
397
340
398
pub fn doc_path ( & self , relative : & str ) -> Result < PathBuf > {
0 commit comments