diff --git a/.travis.yml b/.travis.yml index 52e0f8012..18fbe57c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: rust cache: cargo rust: - - 1.13.0 + - 1.16.0 - stable - beta - nightly diff --git a/Cargo.lock b/Cargo.lock index a17b67371..eb463a7ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,7 +19,7 @@ dependencies = [ "local-encoding 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-disk-cache 0.1.0", - "named_pipe 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "protobuf 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.1.73 (registry+https://github.com/rust-lang/crates.io-index)", @@ -483,15 +483,6 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "named_pipe" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "native-tls" version = "0.1.1" @@ -1134,7 +1125,6 @@ dependencies = [ "checksum miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3e690c5df6b2f60acd45d56378981e827ff8295562fc8d34f573deb267a59cd1" "checksum miow 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a78d2605eb97302c10cf944b8d96b0a2a890c52957caf92fcd1f24f69049579" "checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8" -"checksum named_pipe 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57495d00ae2cf44c03e8cd9246f9a00eb803e63e9664ff61b5cc288c328ca9b8" "checksum native-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b805ee0e8fa268f67a4e5c7f4f80adb8af1fc4428ea0ce5b0ecab1430ef17ec0" "checksum net2 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "1dd775c6de972a1f57a34016f3b2bdc9e086e948f870b38675d1db410a21566b" "checksum num 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "d2ee34a0338c16ae67afb55824aaf8852700eb0f77ccd977807ccb7606b295f6" diff --git a/Cargo.toml b/Cargo.toml index 763f92910..ccb110a7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,8 +47,8 @@ tokio-uds = "0.1" [target.'cfg(windows)'.dependencies] kernel32-sys = "0.2.2" -named_pipe = "0.2.2" winapi = "0.2" +mio-named-pipes = "0.1" [features] default = [] diff --git a/README.md b/README.md index 646ea572b..9446cf354 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Sccache can also be used with local storage instead of remote. Requirements ------------ -sccache is a [Rust](https://www.rust-lang.org/) program. Building it requires `cargo` (and thus `rustc`). sccache currently requires **Rust 1.13**. +sccache is a [Rust](https://www.rust-lang.org/) program. Building it requires `cargo` (and thus `rustc`). sccache currently requires **Rust 1.16**. We recommend you install Rust via [Rustup](https://rustup.rs/). The generated binaries can be built so that they are very portable, see [scripts/build-release.sh](scripts/build-release.sh). diff --git a/src/commands.rs b/src/commands.rs index ae706f31c..5ae7bd067 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -192,87 +192,57 @@ fn redirect_error_log() -> Result<()> { } /// Re-execute the current executable as a background server. -/// -/// `std::process::Command` doesn't expose a way to create a -/// detatched process on Windows, so we have to roll our own. -/// TODO: remove this all when `CommandExt::creation_flags` hits stable: -/// https://github.com/rust-lang/rust/issues/37827 #[cfg(windows)] fn run_server_process() -> Result { - use kernel32; - use named_pipe::PipeOptions; - use std::io::{Read, Error}; - use std::os::windows::ffi::OsStrExt; - use std::mem; - use std::ptr; + use futures::Future; + use mio_named_pipes::NamedPipe; + use std::os::windows::process::CommandExt; + use std::time::Duration; + use tokio_core::io::read_exact; + use tokio_core::reactor::{Core, Timeout, PollEvented}; use uuid::Uuid; - use winapi::minwindef::{TRUE,FALSE,LPVOID,DWORD}; - use winapi::processthreadsapi::{PROCESS_INFORMATION,STARTUPINFOW}; - use winapi::winbase::{CREATE_UNICODE_ENVIRONMENT,DETACHED_PROCESS,CREATE_NEW_PROCESS_GROUP}; + use winapi::{DETACHED_PROCESS, CREATE_NEW_PROCESS_GROUP}; trace!("run_server_process"); - // Create a pipe to get startup status back from the server. + + // Create a mini event loop and register our named pipe server + let mut core = Core::new()?; + let handle = core.handle(); let pipe_name = format!(r"\\.\pipe\{}", Uuid::new_v4().simple()); - let server = PipeOptions::new(&pipe_name).single()?; + let server = NamedPipe::new(&pipe_name)?; + let server = PollEvented::new(server, &handle)?; + + // Connect a client to our server, and we'll wait below if it's still in + // progress. + match server.get_ref().connect() { + Ok(()) => {} + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {} + Err(e) => return Err(e.into()), + } + + // Spawn a server which should come back and connect to us let exe_path = env::current_exe()?; - let mut exe = OsStr::new(&exe_path) - .encode_wide() - .chain(Some(0u16)) - .collect::>(); - // Collect existing env vars + extra into an environment block. - let mut envp = { - let mut v = vec!(); - let extra_vars = vec![ - (OsString::from("SCCACHE_START_SERVER"), OsString::from("1")), - (OsString::from("SCCACHE_STARTUP_NOTIFY"), OsString::from(&pipe_name)), - (OsString::from("RUST_BACKTRACE"), OsString::from("1")), - ]; - for (key, val) in env::vars_os().chain(extra_vars) { - v.extend(key.encode_wide().chain(Some('=' as u16)).chain(val.encode_wide()).chain(Some(0))); - } - v.push(0); - v - }; - let mut pi = PROCESS_INFORMATION { - hProcess: ptr::null_mut(), - hThread: ptr::null_mut(), - dwProcessId: 0, - dwThreadId: 0, - }; - let mut si: STARTUPINFOW = unsafe { mem::zeroed() }; - si.cb = mem::size_of::() as DWORD; - if unsafe { kernel32::CreateProcessW(exe.as_mut_ptr(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - FALSE, - CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP, - envp.as_mut_ptr() as LPVOID, - ptr::null(), - &mut si, - &mut pi) == TRUE } { - unsafe { - kernel32::CloseHandle(pi.hProcess); - kernel32::CloseHandle(pi.hThread); + let _child = process::Command::new(exe_path) + .env("SCCACHE_START_SERVER", "1") + .env("SCCACHE_STARTUP_NOTIFY", &pipe_name) + .env("RUST_BACKTRACE", "1") + .creation_flags(DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP) + .spawn()?; + + let result = read_exact(server, [0u8]).map(|(_socket, byte)| { + if byte[0] == 0 { + ServerStartup::Ok + } else { + let err = format!("Server startup failed: {}", byte[0]).into(); + ServerStartup::Err(err) } - } else { - return Err(Error::last_os_error().into()) - } - // Wait for a connection on the pipe. - let mut pipe = match server.wait_ms(SERVER_STARTUP_TIMEOUT_MS)? { - Ok(pipe) => pipe, - Err(_) => return Ok(ServerStartup::TimedOut), - }; - // It would be nice to have a read timeout here. - let mut buffer = [0; 1]; - pipe.read_exact(&mut buffer)?; - if buffer[0] == 0 { - info!("Server started up successfully"); - Ok(ServerStartup::Ok) - } else { - //TODO: send error messages over the socket as well. - error!("Server startup failed: {}", buffer[0]); - Ok(ServerStartup::Err(format!("Server startup failed: {}", buffer[0]).into())) + }); + + let timeout = Duration::from_millis(SERVER_STARTUP_TIMEOUT_MS.into()); + let timeout = Timeout::new(timeout, &handle)?.map(|()| ServerStartup::TimedOut); + match core.run(result.select(timeout)) { + Ok((e, _other)) => Ok(e), + Err((e, _other)) => Err(e).chain_err(|| "failed waiting for server to start"), } } diff --git a/src/main.rs b/src/main.rs index 790d164d8..cfca9e2f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![cfg_attr(feature = "unstable", feature(windows_process_extensions))] - extern crate app_dirs; extern crate chrono; extern crate clap; @@ -38,7 +36,7 @@ extern crate lru_disk_cache; extern crate fern; extern crate libc; #[cfg(windows)] -extern crate named_pipe; +extern crate mio_named_pipes; extern crate number_prefix; extern crate protobuf; extern crate regex; diff --git a/src/mock_command.rs b/src/mock_command.rs index 8a35635be..b52d6b372 100644 --- a/src/mock_command.rs +++ b/src/mock_command.rs @@ -192,7 +192,7 @@ impl RunCommand for AsyncCommand { self } - #[cfg(all(windows, feature = "unstable"))] + #[cfg(windows)] fn no_console(&mut self) -> &mut AsyncCommand { use std::os::windows::process::CommandExt; const CREATE_NO_WINDOW: u32 = 0x08000000; @@ -200,7 +200,7 @@ impl RunCommand for AsyncCommand { self } - #[cfg(not(all(windows, feature = "unstable")))] + #[cfg(unix)] fn no_console(&mut self) -> &mut AsyncCommand { self } diff --git a/src/server.rs b/src/server.rs index eacc8949b..2a509f3f9 100644 --- a/src/server.rs +++ b/src/server.rs @@ -98,12 +98,13 @@ fn notify_server_startup(name: &Option, success: bool) -> io::Result<( #[cfg(windows)] fn notify_server_startup(name: &Option, success: bool) -> io::Result<()> { - use named_pipe::PipeClient; + use std::fs::OpenOptions; + let name = match *name { Some(ref s) => s, None => return Ok(()), }; - let pipe = try!(PipeClient::connect(name)); + let pipe = try!(OpenOptions::new().write(true).read(true).open(name)); notify_server_startup_internal(pipe, success) }