@@ -10,6 +10,7 @@ use std::process::Stdio;
1010use std:: thread;
1111use std:: time:: SystemTime ;
1212
13+ use super :: death;
1314use cargo_test_support:: paths:: { self , CargoPathExt } ;
1415use cargo_test_support:: registry:: Package ;
1516use cargo_test_support:: { basic_manifest, is_coarse_mtime, project, rustc_host, sleep_ms} ;
@@ -2318,8 +2319,12 @@ fn linking_interrupted() {
23182319
23192320 // This is used to detect when linking starts, then to pause the linker so
23202321 // 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 ( ) ;
23232328
23242329 // Create a linker that we can interrupt.
23252330 let linker = project ( )
@@ -2328,8 +2333,6 @@ fn linking_interrupted() {
23282333 . file (
23292334 "src/main.rs" ,
23302335 & r#"
2331- use std::io::Read;
2332-
23332336 fn main() {
23342337 // Figure out the output filename.
23352338 let output = match std::env::args().find(|a| a.starts_with("/OUT:")) {
@@ -2348,58 +2351,79 @@ fn linking_interrupted() {
23482351 std::fs::write(&output, "").unwrap();
23492352 // Tell the test that we are ready to be interrupted.
23502353 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) );
23532356 }
23542357 "#
2355- . replace ( "__ADDR__" , & addr . to_string ( ) ) ,
2358+ . replace ( "__ADDR__" , & link_addr . to_string ( ) ) ,
23562359 )
23572360 . build ( ) ;
23582361 linker. cargo ( "build" ) . run ( ) ;
23592362
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+
23602389 // Build it once so that the fingerprint gets saved to disk.
23612390 let p = project ( )
23622391 . file ( "src/lib.rs" , "" )
23632392 . file ( "tests/t1.rs" , "" )
23642393 . build ( ) ;
23652394 p. cargo ( "test --test t1 --no-run" ) . run ( ) ;
2395+
23662396 // Make a change, start a build, then interrupt it.
23672397 p. change_file ( "src/lib.rs" , "// modified" ) ;
23682398 let linker_env = format ! (
23692399 "CARGO_TARGET_{}_LINKER" ,
23702400 rustc_host( ) . to_uppercase( ) . replace( '-' , "_" )
23712401 ) ;
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?
23742404 let mut cmd = p
23752405 . cargo ( "test --test t1 --no-run" )
23762406 . env ( & linker_env, linker. bin ( "linker" ) )
2407+ . env ( "RUSTC" , rustc. bin ( "rustc-waiter" ) )
23772408 . build_command ( ) ;
23782409 let mut child = cmd
23792410 . stdout ( Stdio :: null ( ) )
23802411 . stderr ( Stdio :: null ( ) )
2412+ . env ( "__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE" , "1" )
23812413 . spawn ( )
23822414 . unwrap ( ) ;
2415+ // Wait for rustc to start.
2416+ let mut rustc_conn = rustc_listener. accept ( ) . unwrap ( ) . 0 ;
23832417 // Wait for linking to start.
2384- let mut conn = listener . accept ( ) . unwrap ( ) . 0 ;
2418+ drop ( link_listener . accept ( ) . unwrap ( ) ) ;
23852419
23862420 // 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) ) ;
24032427
24042428 // Build again, shouldn't be fresh.
24052429 p. cargo ( "test --test t1" )
0 commit comments