mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Add a wrapper to wait for rustc to exit.
This commit is contained in:
parent
ca085e9723
commit
01648b942b
@ -88,7 +88,7 @@ fn ctrl_c_kills_everyone() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn ctrl_c(child: &mut Child) {
|
pub fn ctrl_c(child: &mut Child) {
|
||||||
let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
|
let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
|
||||||
if r < 0 {
|
if r < 0 {
|
||||||
panic!("failed to kill: {}", io::Error::last_os_error());
|
panic!("failed to kill: {}", io::Error::last_os_error());
|
||||||
@ -96,6 +96,6 @@ fn ctrl_c(child: &mut Child) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn ctrl_c(child: &mut Child) {
|
pub fn ctrl_c(child: &mut Child) {
|
||||||
child.kill().unwrap();
|
child.kill().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use std::process::Stdio;
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
|
|
||||||
|
use super::death;
|
||||||
use cargo_test_support::paths::{self, CargoPathExt};
|
use cargo_test_support::paths::{self, CargoPathExt};
|
||||||
use cargo_test_support::registry::Package;
|
use cargo_test_support::registry::Package;
|
||||||
use cargo_test_support::{basic_manifest, is_coarse_mtime, project, rustc_host, sleep_ms};
|
use cargo_test_support::{basic_manifest, is_coarse_mtime, project, rustc_host, sleep_ms};
|
||||||
@ -2318,8 +2319,12 @@ fn linking_interrupted() {
|
|||||||
|
|
||||||
// This is used to detect when linking starts, then to pause the linker so
|
// This is used to detect when linking starts, then to pause the linker so
|
||||||
// that the test can kill cargo.
|
// that the test can kill cargo.
|
||||||
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
let link_listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
let addr = listener.local_addr().unwrap();
|
let link_addr = link_listener.local_addr().unwrap();
|
||||||
|
|
||||||
|
// This is used to detect when rustc exits.
|
||||||
|
let rustc_listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let rustc_addr = rustc_listener.local_addr().unwrap();
|
||||||
|
|
||||||
// Create a linker that we can interrupt.
|
// Create a linker that we can interrupt.
|
||||||
let linker = project()
|
let linker = project()
|
||||||
@ -2328,8 +2333,6 @@ fn linking_interrupted() {
|
|||||||
.file(
|
.file(
|
||||||
"src/main.rs",
|
"src/main.rs",
|
||||||
&r#"
|
&r#"
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Figure out the output filename.
|
// Figure out the output filename.
|
||||||
let output = match std::env::args().find(|a| a.starts_with("/OUT:")) {
|
let output = match std::env::args().find(|a| a.starts_with("/OUT:")) {
|
||||||
@ -2348,58 +2351,79 @@ fn linking_interrupted() {
|
|||||||
std::fs::write(&output, "").unwrap();
|
std::fs::write(&output, "").unwrap();
|
||||||
// Tell the test that we are ready to be interrupted.
|
// Tell the test that we are ready to be interrupted.
|
||||||
let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap();
|
let mut socket = std::net::TcpStream::connect("__ADDR__").unwrap();
|
||||||
// Wait for the test to tell us to exit.
|
// Wait for the test to kill us.
|
||||||
let _ = socket.read(&mut [0; 1]);
|
std::thread::sleep(std::time::Duration::new(60, 0));
|
||||||
}
|
}
|
||||||
"#
|
"#
|
||||||
.replace("__ADDR__", &addr.to_string()),
|
.replace("__ADDR__", &link_addr.to_string()),
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
linker.cargo("build").run();
|
linker.cargo("build").run();
|
||||||
|
|
||||||
|
// Create a wrapper around rustc that will tell us when rustc is finished.
|
||||||
|
let rustc = project()
|
||||||
|
.at("rustc-waiter")
|
||||||
|
.file("Cargo.toml", &basic_manifest("rustc-waiter", "1.0.0"))
|
||||||
|
.file(
|
||||||
|
"src/main.rs",
|
||||||
|
&r#"
|
||||||
|
fn main() {
|
||||||
|
let mut conn = None;
|
||||||
|
// Check for a normal build (not -vV or --print).
|
||||||
|
if std::env::args().any(|arg| arg == "t1") {
|
||||||
|
// Tell the test that rustc has started.
|
||||||
|
conn = Some(std::net::TcpStream::connect("__ADDR__").unwrap());
|
||||||
|
}
|
||||||
|
let status = std::process::Command::new("rustc")
|
||||||
|
.args(std::env::args().skip(1))
|
||||||
|
.status()
|
||||||
|
.expect("rustc to run");
|
||||||
|
std::process::exit(status.code().unwrap_or(1));
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
.replace("__ADDR__", &rustc_addr.to_string()),
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
rustc.cargo("build").run();
|
||||||
|
|
||||||
// Build it once so that the fingerprint gets saved to disk.
|
// Build it once so that the fingerprint gets saved to disk.
|
||||||
let p = project()
|
let p = project()
|
||||||
.file("src/lib.rs", "")
|
.file("src/lib.rs", "")
|
||||||
.file("tests/t1.rs", "")
|
.file("tests/t1.rs", "")
|
||||||
.build();
|
.build();
|
||||||
p.cargo("test --test t1 --no-run").run();
|
p.cargo("test --test t1 --no-run").run();
|
||||||
|
|
||||||
// Make a change, start a build, then interrupt it.
|
// Make a change, start a build, then interrupt it.
|
||||||
p.change_file("src/lib.rs", "// modified");
|
p.change_file("src/lib.rs", "// modified");
|
||||||
let linker_env = format!(
|
let linker_env = format!(
|
||||||
"CARGO_TARGET_{}_LINKER",
|
"CARGO_TARGET_{}_LINKER",
|
||||||
rustc_host().to_uppercase().replace('-', "_")
|
rustc_host().to_uppercase().replace('-', "_")
|
||||||
);
|
);
|
||||||
// NOTE: This assumes that the path to the linker is not in the
|
// NOTE: This assumes that the paths to the linker or rustc are not in the
|
||||||
// fingerprint. But maybe it should be?
|
// fingerprint. But maybe they should be?
|
||||||
let mut cmd = p
|
let mut cmd = p
|
||||||
.cargo("test --test t1 --no-run")
|
.cargo("test --test t1 --no-run")
|
||||||
.env(&linker_env, linker.bin("linker"))
|
.env(&linker_env, linker.bin("linker"))
|
||||||
|
.env("RUSTC", rustc.bin("rustc-waiter"))
|
||||||
.build_command();
|
.build_command();
|
||||||
let mut child = cmd
|
let mut child = cmd
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
|
.env("__CARGO_TEST_SETSID_PLEASE_DONT_USE_ELSEWHERE", "1")
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
// Wait for rustc to start.
|
||||||
|
let mut rustc_conn = rustc_listener.accept().unwrap().0;
|
||||||
// Wait for linking to start.
|
// Wait for linking to start.
|
||||||
let mut conn = listener.accept().unwrap().0;
|
drop(link_listener.accept().unwrap());
|
||||||
|
|
||||||
// Interrupt the child.
|
// Interrupt the child.
|
||||||
child.kill().unwrap();
|
death::ctrl_c(&mut child);
|
||||||
child.wait().unwrap();
|
assert!(!child.wait().unwrap().success());
|
||||||
// Note: rustc and the linker may still be running because we didn't kill
|
// Wait for rustc to exit. If we don't wait, then the command below could
|
||||||
// the entire process group. Normally, when a user hits Ctrl-C, everything
|
// start while rustc is still being torn down.
|
||||||
// is killed. However, setting up process groups in a cross-platform test
|
let mut buf = [0];
|
||||||
// is a pain, and there's no easy way to know when everything has been
|
drop(rustc_conn.read_exact(&mut buf));
|
||||||
// killed. This write will tell them to exit, pretending that they died
|
|
||||||
// before finishing. Ignore the error, because (sometimes?) on Windows
|
|
||||||
// everything is already killed.
|
|
||||||
let _ = conn.write(b"X");
|
|
||||||
// Sleep a bit to allow rustc to clean up and exit. I have seen some race
|
|
||||||
// conditions on macOS where clang dies with `no such
|
|
||||||
// file...t1-HASH.rcgu.o`. I think what is happening is that the old rustc
|
|
||||||
// process is still running, and deletes the `*.o` files while the command
|
|
||||||
// below is trying to write them. Not sure if that is macOS-specific.
|
|
||||||
std::thread::sleep(std::time::Duration::new(2, 0));
|
|
||||||
|
|
||||||
// Build again, shouldn't be fresh.
|
// Build again, shouldn't be fresh.
|
||||||
p.cargo("test --test t1")
|
p.cargo("test --test t1")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user