@@ -10,6 +10,7 @@ use std::process::Stdio;
10
10
use std:: thread;
11
11
use std:: time:: SystemTime ;
12
12
13
+ use super :: death;
13
14
use cargo_test_support:: paths:: { self , CargoPathExt } ;
14
15
use cargo_test_support:: registry:: Package ;
15
16
use cargo_test_support:: { basic_manifest, is_coarse_mtime, project, rustc_host, sleep_ms} ;
@@ -2318,8 +2319,12 @@ fn linking_interrupted() {
2318
2319
2319
2320
// This is used to detect when linking starts, then to pause the linker so
2320
2321
// that the test can kill cargo.
2321
- let listener = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
2322
- let addr = listener. local_addr ( ) . unwrap ( ) ;
2322
+ let link_listener = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
2323
+ let link_addr = link_listener. local_addr ( ) . unwrap ( ) ;
2324
+
2325
+ // This is used to detect when rustc exits.
2326
+ let rustc_listener = TcpListener :: bind ( "127.0.0.1:0" ) . unwrap ( ) ;
2327
+ let rustc_addr = rustc_listener. local_addr ( ) . unwrap ( ) ;
2323
2328
2324
2329
// Create a linker that we can interrupt.
2325
2330
let linker = project ( )
@@ -2328,8 +2333,6 @@ fn linking_interrupted() {
2328
2333
. file (
2329
2334
"src/main.rs" ,
2330
2335
& r#"
2331
- use std::io::Read;
2332
-
2333
2336
fn main() {
2334
2337
// Figure out the output filename.
2335
2338
let output = match std::env::args().find(|a| a.starts_with("/OUT:")) {
@@ -2348,58 +2351,79 @@ fn linking_interrupted() {
2348
2351
std::fs::write(&output, "").unwrap();
2349
2352
// Tell the test that we are ready to be interrupted.
2350
2353
let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap();
2351
- // Wait for the test to tell us to exit .
2352
- let _ = socket.read(&mut [0; 1] );
2354
+ // Wait for the test to kill us.
2355
+ std::thread::sleep(std::time::Duration::new(60, 0) );
2353
2356
}
2354
2357
"#
2355
- . replace ( "__ADDR__" , & addr . to_string ( ) ) ,
2358
+ . replace ( "__ADDR__" , & link_addr . to_string ( ) ) ,
2356
2359
)
2357
2360
. build ( ) ;
2358
2361
linker. cargo ( "build" ) . run ( ) ;
2359
2362
2363
+ // Create a wrapper around rustc that will tell us when rustc is finished.
2364
+ let rustc = project ( )
2365
+ . at ( "rustc-waiter" )
2366
+ . file ( "Cargo.toml" , & basic_manifest ( "rustc-waiter" , "1.0.0" ) )
2367
+ . file (
2368
+ "src/main.rs" ,
2369
+ & r#"
2370
+ fn main() {
2371
+ let mut conn = None;
2372
+ // Check for a normal build (not -vV or --print).
2373
+ if std::env::args().any(|arg| arg == "t1") {
2374
+ // Tell the test that rustc has started.
2375
+ conn = Some(std::net::TcpStream::connect("__ADDR__").unwrap());
2376
+ }
2377
+ let status = std::process::Command::new("rustc")
2378
+ .args(std::env::args().skip(1))
2379
+ .status()
2380
+ .expect("rustc to run");
2381
+ std::process::exit(status.code().unwrap_or(1));
2382
+ }
2383
+ "#
2384
+ . replace ( "__ADDR__" , & rustc_addr. to_string ( ) ) ,
2385
+ )
2386
+ . build ( ) ;
2387
+ rustc. cargo ( "build" ) . run ( ) ;
2388
+
2360
2389
// Build it once so that the fingerprint gets saved to disk.
2361
2390
let p = project ( )
2362
2391
. file ( "src/lib.rs" , "" )
2363
2392
. file ( "tests/t1.rs" , "" )
2364
2393
. build ( ) ;
2365
2394
p. cargo ( "test --test t1 --no-run" ) . run ( ) ;
2395
+
2366
2396
// Make a change, start a build, then interrupt it.
2367
2397
p. change_file ( "src/lib.rs" , "// modified" ) ;
2368
2398
let linker_env = format ! (
2369
2399
"CARGO_TARGET_{}_LINKER" ,
2370
2400
rustc_host( ) . to_uppercase( ) . replace( '-' , "_" )
2371
2401
) ;
2372
- // NOTE: This assumes that the path to the linker is not in the
2373
- // fingerprint. But maybe it should be?
2402
+ // NOTE: This assumes that the paths to the linker or rustc are not in the
2403
+ // fingerprint. But maybe they should be?
2374
2404
let mut cmd = p
2375
2405
. cargo ( "test --test t1 --no-run" )
2376
2406
. env ( & linker_env, linker. bin ( "linker" ) )
2407
+ . env ( "RUSTC" , rustc. bin ( "rustc-waiter" ) )
2377
2408
. build_command ( ) ;
2378
2409
let mut child = cmd
2379
2410
. stdout ( Stdio :: null ( ) )
2380
2411
. stderr ( Stdio :: null ( ) )
2412
+ . env ( "__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE" , "1" )
2381
2413
. spawn ( )
2382
2414
. unwrap ( ) ;
2415
+ // Wait for rustc to start.
2416
+ let mut rustc_conn = rustc_listener. accept ( ) . unwrap ( ) . 0 ;
2383
2417
// Wait for linking to start.
2384
- let mut conn = listener . accept ( ) . unwrap ( ) . 0 ;
2418
+ drop ( link_listener . accept ( ) . unwrap ( ) ) ;
2385
2419
2386
2420
// Interrupt the child.
2387
- child. kill ( ) . unwrap ( ) ;
2388
- child. wait ( ) . unwrap ( ) ;
2389
- // Note: rustc and the linker may still be running because we didn't kill
2390
- // the entire process group. Normally, when a user hits Ctrl-C, everything
2391
- // is killed. However, setting up process groups in a cross-platform test
2392
- // is a pain, and there's no easy way to know when everything has been
2393
- // killed. This write will tell them to exit, pretending that they died
2394
- // before finishing. Ignore the error, because (sometimes?) on Windows
2395
- // everything is already killed.
2396
- let _ = conn. write ( b"X" ) ;
2397
- // Sleep a bit to allow rustc to clean up and exit. I have seen some race
2398
- // conditions on macOS where clang dies with `no such
2399
- // file...t1-HASH.rcgu.o`. I think what is happening is that the old rustc
2400
- // process is still running, and deletes the `*.o` files while the command
2401
- // below is trying to write them. Not sure if that is macOS-specific.
2402
- std:: thread:: sleep ( std:: time:: Duration :: new ( 2 , 0 ) ) ;
2421
+ death:: ctrl_c ( & mut child) ;
2422
+ assert ! ( !child. wait( ) . unwrap( ) . success( ) ) ;
2423
+ // Wait for rustc to exit. If we don't wait, then the command below could
2424
+ // start while rustc is still being torn down.
2425
+ let mut buf = [ 0 ] ;
2426
+ drop ( rustc_conn. read_exact ( & mut buf) ) ;
2403
2427
2404
2428
// Build again, shouldn't be fresh.
2405
2429
p. cargo ( "test --test t1" )
0 commit comments