Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
use std::env;
use std::io::prelude::*;
use std::io::BufReader;
use std::process::{Command, Stdio};
use std::sync::mpsc;
use std::thread;
use jobserver::Client;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}
fn main() {
if env::var("I_AM_THE_CLIENT").is_ok() {
client();
} else {
server();
}
}
fn server() {
let me = t!(env::current_exe());
let client = t!(Client::new(1));
let mut cmd = Command::new(me);
cmd.env("I_AM_THE_CLIENT", "1").stdout(Stdio::piped());
client.configure(&mut cmd);
let acq = client.acquire().unwrap();
let mut child = t!(cmd.spawn());
let stdout = child.stdout.take().unwrap();
let (tx, rx) = mpsc::channel();
let t = thread::spawn(move || {
for line in BufReader::new(stdout).lines() {
tx.send(t!(line)).unwrap();
}
});
for _ in 0..100 {
assert!(rx.try_recv().is_err());
}
drop(acq);
assert_eq!(rx.recv().unwrap(), "hello!");
t.join().unwrap();
assert!(rx.recv().is_err());
client.acquire().unwrap();
}
fn client() {
let client = unsafe { Client::from_env().unwrap() };
let acq = client.acquire().unwrap();
println!("hello!");
drop(acq);
}

192
vendor/jobserver/tests/client.rs vendored Normal file
View File

@@ -0,0 +1,192 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use jobserver::Client;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}
struct Test {
name: &'static str,
f: &'static (dyn Fn() + Send + Sync),
make_args: &'static [&'static str],
rule: &'static (dyn Fn(&str) -> String + Send + Sync),
}
const TESTS: &[Test] = &[
Test {
name: "no j args",
make_args: &[],
rule: &|me| me.to_string(),
f: &|| {
assert!(unsafe { Client::from_env().is_none() });
},
},
Test {
name: "no j args with plus",
make_args: &[],
rule: &|me| format!("+{}", me),
f: &|| {
assert!(unsafe { Client::from_env().is_none() });
},
},
Test {
name: "j args with plus",
make_args: &["-j2"],
rule: &|me| format!("+{}", me),
f: &|| {
assert!(unsafe { Client::from_env().is_some() });
},
},
Test {
name: "acquire",
make_args: &["-j2"],
rule: &|me| format!("+{}", me),
f: &|| {
let c = unsafe { Client::from_env().unwrap() };
drop(c.acquire().unwrap());
drop(c.acquire().unwrap());
},
},
Test {
name: "acquire3",
make_args: &["-j3"],
rule: &|me| format!("+{}", me),
f: &|| {
let c = unsafe { Client::from_env().unwrap() };
let a = c.acquire().unwrap();
let b = c.acquire().unwrap();
drop((a, b));
},
},
Test {
name: "acquire blocks",
make_args: &["-j2"],
rule: &|me| format!("+{}", me),
f: &|| {
let c = unsafe { Client::from_env().unwrap() };
let a = c.acquire().unwrap();
let hit = Arc::new(AtomicBool::new(false));
let hit2 = hit.clone();
let (tx, rx) = mpsc::channel();
let t = thread::spawn(move || {
tx.send(()).unwrap();
let _b = c.acquire().unwrap();
hit2.store(true, Ordering::SeqCst);
});
rx.recv().unwrap();
assert!(!hit.load(Ordering::SeqCst));
drop(a);
t.join().unwrap();
assert!(hit.load(Ordering::SeqCst));
},
},
Test {
name: "acquire_raw",
make_args: &["-j2"],
rule: &|me| format!("+{}", me),
f: &|| {
let c = unsafe { Client::from_env().unwrap() };
c.acquire_raw().unwrap();
c.release_raw().unwrap();
},
},
];
fn main() {
if let Ok(test) = env::var("TEST_TO_RUN") {
return (TESTS.iter().find(|t| t.name == test).unwrap().f)();
}
let me = t!(env::current_exe());
let me = me.to_str().unwrap();
let filter = env::args().nth(1);
let join_handles = TESTS
.iter()
.filter(|test| match filter {
Some(ref s) => test.name.contains(s),
None => true,
})
.map(|test| {
let td = t!(tempfile::tempdir());
let makefile = format!(
"\
all: export TEST_TO_RUN={}
all:
\t{}
",
test.name,
(test.rule)(me)
);
t!(t!(File::create(td.path().join("Makefile"))).write_all(makefile.as_bytes()));
thread::spawn(move || {
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
let mut cmd = Command::new(prog);
cmd.args(test.make_args);
cmd.current_dir(td.path());
(test, cmd.output().unwrap())
})
})
.collect::<Vec<_>>();
println!("\nrunning {} tests\n", join_handles.len());
let failures = join_handles
.into_iter()
.filter_map(|join_handle| {
let (test, output) = join_handle.join().unwrap();
if output.status.success() {
println!("test {} ... ok", test.name);
None
} else {
println!("test {} ... FAIL", test.name);
Some((test, output))
}
})
.collect::<Vec<_>>();
if failures.is_empty() {
println!("\ntest result: ok\n");
return;
}
println!("\n----------- failures");
for (test, output) in failures {
println!("test {}", test.name);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("\texit status: {}", output.status);
if !stdout.is_empty() {
println!("\tstdout ===");
for line in stdout.lines() {
println!("\t\t{}", line);
}
}
if !stderr.is_empty() {
println!("\tstderr ===");
for line in stderr.lines() {
println!("\t\t{}", line);
}
}
}
std::process::exit(4);
}

76
vendor/jobserver/tests/helper.rs vendored Normal file
View File

@@ -0,0 +1,76 @@
use jobserver::Client;
use std::sync::atomic::*;
use std::sync::*;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}
#[test]
fn helper_smoke() {
let client = t!(Client::new(1));
drop(client.clone().into_helper_thread(|_| ()).unwrap());
drop(client.clone().into_helper_thread(|_| ()).unwrap());
drop(client.clone().into_helper_thread(|_| ()).unwrap());
drop(client.clone().into_helper_thread(|_| ()).unwrap());
drop(client.clone().into_helper_thread(|_| ()).unwrap());
drop(client.into_helper_thread(|_| ()).unwrap());
}
#[test]
fn acquire() {
let (tx, rx) = mpsc::channel();
let client = t!(Client::new(1));
let helper = client
.into_helper_thread(move |a| drop(tx.send(a)))
.unwrap();
assert!(rx.try_recv().is_err());
helper.request_token();
rx.recv().unwrap().unwrap();
helper.request_token();
rx.recv().unwrap().unwrap();
helper.request_token();
helper.request_token();
rx.recv().unwrap().unwrap();
rx.recv().unwrap().unwrap();
helper.request_token();
helper.request_token();
drop(helper);
}
#[test]
fn prompt_shutdown() {
for _ in 0..100 {
let client = jobserver::Client::new(4).unwrap();
let count = Arc::new(AtomicU32::new(0));
let count2 = count.clone();
let tokens = Arc::new(Mutex::new(Vec::new()));
let helper = client
.into_helper_thread(move |token| {
tokens.lock().unwrap().push(token);
count2.fetch_add(1, Ordering::SeqCst);
})
.unwrap();
// Request more tokens than what are available.
for _ in 0..5 {
helper.request_token();
}
// Wait for at least some of the requests to finish.
while count.load(Ordering::SeqCst) < 3 {
std::thread::yield_now();
}
// Drop helper
let t = std::time::Instant::now();
drop(helper);
let d = t.elapsed();
assert!(d.as_secs_f64() < 0.5);
}
}

View File

@@ -0,0 +1,81 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::process::Command;
use jobserver::Client;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}
fn main() {
if env::var("_DO_THE_TEST").is_ok() {
std::process::exit(
Command::new(env::var_os("MAKE").unwrap())
.env("MAKEFLAGS", env::var_os("CARGO_MAKEFLAGS").unwrap())
.env_remove("_DO_THE_TEST")
.args(&env::args_os().skip(1).collect::<Vec<_>>())
.status()
.unwrap()
.code()
.unwrap_or(1),
);
}
if let Ok(s) = env::var("TEST_ADDR") {
let mut contents = Vec::new();
t!(t!(TcpStream::connect(&s)).read_to_end(&mut contents));
return;
}
let c = t!(Client::new(1));
let td = tempfile::tempdir().unwrap();
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
let me = t!(env::current_exe());
let me = me.to_str().unwrap();
let mut cmd = Command::new(&me);
cmd.current_dir(td.path());
cmd.env("MAKE", prog);
cmd.env("_DO_THE_TEST", "1");
t!(t!(File::create(td.path().join("Makefile"))).write_all(
format!(
"\
all: foo bar
foo:
\t{0}
bar:
\t{0}
",
me
)
.as_bytes()
));
// We're leaking one extra token to `make` sort of violating the makefile
// jobserver protocol. It has the desired effect though.
c.configure(&mut cmd);
let listener = t!(TcpListener::bind("127.0.0.1:0"));
let addr = t!(listener.local_addr());
cmd.env("TEST_ADDR", addr.to_string());
let mut child = t!(cmd.spawn());
// We should get both connections as the two programs should be run
// concurrently.
let a = t!(listener.accept());
let b = t!(listener.accept());
drop((a, b));
assert!(t!(child.wait()).success());
}

181
vendor/jobserver/tests/server.rs vendored Normal file
View File

@@ -0,0 +1,181 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::process::Command;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
use jobserver::Client;
macro_rules! t {
($e:expr) => {
match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
}
};
}
#[test]
fn server_smoke() {
let c = t!(Client::new(1));
drop(c.acquire().unwrap());
drop(c.acquire().unwrap());
}
#[test]
fn server_multiple() {
let c = t!(Client::new(2));
let a = c.acquire().unwrap();
let b = c.acquire().unwrap();
drop((a, b));
}
#[test]
fn server_available() {
let c = t!(Client::new(10));
assert_eq!(c.available().unwrap(), 10);
let a = c.acquire().unwrap();
assert_eq!(c.available().unwrap(), 9);
drop(a);
assert_eq!(c.available().unwrap(), 10);
}
#[test]
fn server_none_available() {
let c = t!(Client::new(2));
assert_eq!(c.available().unwrap(), 2);
let a = c.acquire().unwrap();
assert_eq!(c.available().unwrap(), 1);
let b = c.acquire().unwrap();
assert_eq!(c.available().unwrap(), 0);
drop(a);
assert_eq!(c.available().unwrap(), 1);
drop(b);
assert_eq!(c.available().unwrap(), 2);
}
#[test]
fn server_blocks() {
let c = t!(Client::new(1));
let a = c.acquire().unwrap();
let hit = Arc::new(AtomicBool::new(false));
let hit2 = hit.clone();
let (tx, rx) = mpsc::channel();
let t = thread::spawn(move || {
tx.send(()).unwrap();
let _b = c.acquire().unwrap();
hit2.store(true, Ordering::SeqCst);
});
rx.recv().unwrap();
assert!(!hit.load(Ordering::SeqCst));
drop(a);
t.join().unwrap();
assert!(hit.load(Ordering::SeqCst));
}
#[test]
fn make_as_a_single_thread_client() {
let c = t!(Client::new(1));
let td = tempfile::tempdir().unwrap();
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
let mut cmd = Command::new(prog);
cmd.current_dir(td.path());
t!(t!(File::create(td.path().join("Makefile"))).write_all(
b"
all: foo bar
foo:
\techo foo
bar:
\techo bar
"
));
// The jobserver protocol means that the `make` process itself "runs with a
// token", so we acquire our one token to drain the jobserver, and this
// should mean that `make` itself never has a second token available to it.
let _a = c.acquire();
c.configure(&mut cmd);
let output = t!(cmd.output());
println!(
"\n\t=== stderr\n\t\t{}",
String::from_utf8_lossy(&output.stderr).replace("\n", "\n\t\t")
);
println!(
"\t=== stdout\n\t\t{}",
String::from_utf8_lossy(&output.stdout).replace("\n", "\n\t\t")
);
assert!(output.status.success());
assert!(output.stderr.is_empty());
let stdout = String::from_utf8_lossy(&output.stdout).replace("\r\n", "\n");
let a = "\
echo foo
foo
echo bar
bar
";
let b = "\
echo bar
bar
echo foo
foo
";
assert!(stdout == a || stdout == b);
}
#[test]
fn make_as_a_multi_thread_client() {
let c = t!(Client::new(1));
let td = tempfile::tempdir().unwrap();
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
let mut cmd = Command::new(prog);
cmd.current_dir(td.path());
t!(t!(File::create(td.path().join("Makefile"))).write_all(
b"
all: foo bar
foo:
\techo foo
bar:
\techo bar
"
));
// We're leaking one extra token to `make` sort of violating the makefile
// jobserver protocol. It has the desired effect though.
c.configure(&mut cmd);
let output = t!(cmd.output());
println!(
"\n\t=== stderr\n\t\t{}",
String::from_utf8_lossy(&output.stderr).replace("\n", "\n\t\t")
);
println!(
"\t=== stdout\n\t\t{}",
String::from_utf8_lossy(&output.stdout).replace("\n", "\n\t\t")
);
assert!(output.status.success());
}
#[test]
fn zero_client() {
let client = t!(Client::new(0));
let (tx, rx) = mpsc::channel();
let helper = client
.into_helper_thread(move |a| drop(tx.send(a)))
.unwrap();
helper.request_token();
helper.request_token();
for _ in 0..1000 {
assert!(rx.try_recv().is_err());
}
}