use std::sync::Mutex; macro_rules! cargo_script { ( #[env($($env_k:ident=$env_v:expr),* $(,)*)] $($args:expr),* $(,)* ) => { { extern crate tempdir; use std::process::Command; let cargo_lock = ::util::CARGO_MUTEX.lock().expect("could not acquire Cargo mutext"); let temp_dir = tempdir::TempDir::new("cargo-script-test").unwrap(); let cmd_str; let out = { let target_dir = ::std::env::var("CARGO_TARGET_DIR") .unwrap_or_else(|_| String::from("target")); let mut cmd = Command::new(format!("{}/debug/cargo-script", target_dir)); cmd.arg("script"); cmd.arg("--pkg-path").arg(temp_dir.path()); $( cmd.arg($args); )* cmd.env_remove("CARGO_TARGET_DIR"); $(cmd.env(stringify!($env_k), $env_v);)* cmd_str = format!("{:?}", cmd); cmd.output() .map(::util::Output::from) }; if let Ok(out) = out.as_ref() { println!("cargo-script cmd: {}", cmd_str); println!("cargo-script stdout:"); println!("-----"); println!("{}", out.stdout); println!("-----"); println!("cargo-script stderr:"); println!("-----"); println!("{}", out.stderr); println!("-----"); } drop(temp_dir); drop(cargo_lock); out } }; ($($args:expr),* $(,)*) => { cargo_script!(#[env()] $($args),*) }; } macro_rules! with_output_marker { (prelude $p:expr; $e:expr) => { format!(concat!($p, "{}", $e), ::util::OUTPUT_MARKER_CODE) }; ($e:expr) => { format!(concat!("{}", $e), ::util::OUTPUT_MARKER_CODE) }; } lazy_static! { #[doc(hidden)] pub static ref CARGO_MUTEX: Mutex<()> = Mutex::new(()); } pub const OUTPUT_MARKER: &'static str = "--output--"; pub const OUTPUT_MARKER_CODE: &'static str = "println!(\"--output--\");"; pub struct Output { pub status: ::std::process::ExitStatus, pub stdout: String, pub stderr: String, } impl Output { pub fn stdout_output(&self) -> &str { assert!(self.success()); for marker in self.stdout.matches(OUTPUT_MARKER) { let i = subslice_offset(&self.stdout, marker) .expect("couldn't find marker in output"); let before_cp = self.stdout[..i].chars().rev().next() .unwrap_or('\n'); if !(before_cp == '\r' || before_cp == '\n') { continue; } let after = &self.stdout[i+OUTPUT_MARKER.len()..]; let after_cp = after.chars().next() .expect("couldn't find cp after marker"); if !(after_cp == '\r' || after_cp == '\n') { continue; } return after; } panic!("could not find `{}` in script output", OUTPUT_MARKER); } pub fn stderr_raw(&self) -> &str { assert!(self.success()); &self.stderr[..] } pub fn success(&self) -> bool { self.status.success() } } impl From<::std::process::Output> for Output { fn from(v: ::std::process::Output) -> Self { Output { status: v.status, stdout: String::from_utf8(v.stdout).unwrap(), stderr: String::from_utf8(v.stderr).unwrap(), } } } fn subslice_offset(outer: &str, inner: &str) -> Option { let outer_beg = outer.as_ptr() as usize; let inner = inner.as_ptr() as usize; if inner < outer_beg || inner > outer_beg.wrapping_add(outer.len()) { None } else { Some(inner.wrapping_sub(outer_beg)) } }