diff --git a/Cargo.lock b/Cargo.lock index d62263c7baa..c9771c096b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,7 +80,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cargo" version = "0.25.0" -source = "git+https://github.com/rust-lang/cargo#7f0583bd400dcb6ad59ca4b172c2fd41a9c1e5d9" +source = "git+https://github.com/rust-lang/cargo#e10c651ea142f0a8c077bc717462b2ea233e7e1c" dependencies = [ "atty 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -90,7 +90,7 @@ dependencies = [ "curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -213,10 +213,10 @@ dependencies = [ [[package]] name = "crates-io" version = "0.14.0" -source = "git+https://github.com/rust-lang/cargo#7f0583bd400dcb6ad59ca4b172c2fd41a9c1e5d9" +source = "git+https://github.com/rust-lang/cargo#e10c651ea142f0a8c077bc717462b2ea233e7e1c" dependencies = [ "curl 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.23 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -356,6 +356,25 @@ dependencies = [ "backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "failure" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "failure_derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "filetime" version = "0.1.14" @@ -855,6 +874,7 @@ version = "0.124.0" dependencies = [ "cargo 0.25.0 (git+https://github.com/rust-lang/cargo)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "json 0.11.12 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "languageserver-types 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1076,6 +1096,15 @@ dependencies = [ "unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "synstructure" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syntex_errors" version = "0.52.0" @@ -1378,6 +1407,8 @@ dependencies = [ "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" +"checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" +"checksum failure_derive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7cdda555bb90c9bb67a3b670a0f42de8e73f5981524123ad8578aafec8ddb8b" "checksum filetime 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "aa75ec8f7927063335a9583e7fa87b0110bb888cf766dc01b54c0ff70d760c8e" "checksum flate2 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "e6234dd4468ae5d1e2dbb06fe2b058696fdc50a339c68a393aefbf00bc81e423" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" @@ -1460,6 +1491,7 @@ dependencies = [ "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +"checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e52bffe6202cfb67587784cf23e0ec5bf26d331eef4922a16d5c42e12aa1e9b" "checksum syntex_pos 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "955ef4b16af4c468e4680d1497f873ff288f557d338180649e18f915af5e15ac" "checksum syntex_syntax 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76a302e717e348aa372ff577791c3832395650073b8d8432f8b3cb170b34afde" diff --git a/Cargo.toml b/Cargo.toml index dfb91b82374..fc6b28b5f24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ build = "build.rs" [dependencies] cargo = { git = "https://github.com/rust-lang/cargo" } env_logger = "0.4" +failure = "0.1.1" jsonrpc-core = "7.0.1" languageserver-types = "0.16" lazy_static = "0.2" diff --git a/src/actions/post_build.rs b/src/actions/post_build.rs index 60a9b4b490a..73a0c9277c4 100644 --- a/src/actions/post_build.rs +++ b/src/actions/post_build.rs @@ -56,19 +56,19 @@ impl PostBuildHandler { self.notifier.notify_begin(); match result { - BuildResult::Success(messages, new_analysis) => { + BuildResult::Success(cwd, messages, new_analysis) => { thread::spawn(move || { trace!("build - Success"); // Emit appropriate diagnostics using the ones from build. - self.handle_messages(messages); + self.handle_messages(&cwd, messages); // Reload the analysis data. debug!("reload analysis: {:?}", self.project_path); if new_analysis.is_empty() { - self.reload_analysis_from_disk(); + self.reload_analysis_from_disk(&cwd); } else { - self.reload_analysis_from_memory(new_analysis); + self.reload_analysis_from_memory(&cwd, new_analysis); } // Wake up any threads blocked on this analysis. @@ -90,7 +90,7 @@ impl PostBuildHandler { } } - fn handle_messages(&self, messages: Vec) { + fn handle_messages(&self, cwd: &Path, messages: Vec) { // These notifications will include empty sets of errors for files // which had errors, but now don't. This instructs the IDE to clear // errors for those files. @@ -101,8 +101,6 @@ impl PostBuildHandler { v.clear(); } - let cwd = ::std::env::current_dir().unwrap(); - for msg in &messages { if let Some(FileDiagnostic { file_path, @@ -119,8 +117,7 @@ impl PostBuildHandler { self.emit_notifications(&results); } - fn reload_analysis_from_disk(&self) { - let cwd = ::std::env::current_dir().unwrap(); + fn reload_analysis_from_disk(&self, cwd: &Path) { if self.use_black_list { self.analysis .reload_with_blacklist(&self.project_path, &cwd, &CRATE_BLACKLIST) @@ -130,8 +127,7 @@ impl PostBuildHandler { } } - fn reload_analysis_from_memory(&self, analysis: Vec) { - let cwd = ::std::env::current_dir().unwrap(); + fn reload_analysis_from_memory(&self, cwd: &Path, analysis: Vec) { if self.use_black_list { self.analysis .reload_from_analysis(analysis, &self.project_path, &cwd, &CRATE_BLACKLIST) diff --git a/src/build/cargo.rs b/src/build/cargo.rs index 62f98da11f9..8e97aa76843 100644 --- a/src/build/cargo.rs +++ b/src/build/cargo.rs @@ -25,7 +25,7 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::env; use std::ffi::OsString; use std::fs::{read_dir, remove_file}; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::{Arc, Mutex}; use std::thread; @@ -64,10 +64,10 @@ pub(super) fn cargo(internals: &Internals) -> BuildResult { match handle .join() - .map_err(|_| "thread panicked".into()) + .map_err(|_| format_err!("thread panicked")) .and_then(|res| res) { - Ok(_) if workspace_mode => { + Ok(ref cwd) if workspace_mode => { let diagnostics = Arc::try_unwrap(diagnostics_clone) .unwrap() .into_inner() @@ -76,9 +76,9 @@ pub(super) fn cargo(internals: &Internals) -> BuildResult { .unwrap() .into_inner() .unwrap(); - BuildResult::Success(diagnostics, analysis) + BuildResult::Success(cwd.clone(), diagnostics, analysis) } - Ok(_) => BuildResult::Success(vec![], vec![]), + Ok(cwd) => BuildResult::Success(cwd, vec![], vec![]), Err(err) => { let stdout = String::from_utf8(out_clone.lock().unwrap().to_owned()).unwrap(); info!("cargo failed\ncause: {}\nstdout: {}", err, stdout); @@ -95,13 +95,15 @@ fn run_cargo( compiler_messages: Arc>>, analysis: Arc>>, out: Arc>>, -) -> CargoResult<()> { +) -> CargoResult { // Lock early to guarantee synchronized access to env var for the scope of Cargo routine. // Additionally we need to pass inner lock to RlsExecutor, since it needs to hand it down // during exec() callback when calling linked compiler in parallel, for which we need to // guarantee consistent environment variables. let (lock_guard, inner_lock) = env_lock.lock(); + let mut restore_env = Environment::push_with_lock(&HashMap::new(), None, lock_guard); + let build_dir = { let mut compilation_cx = compilation_cx.lock().unwrap(); // Since Cargo build routine will try to regenerate the unit dep graph, @@ -123,7 +125,7 @@ fn run_cargo( let rls_config = rls_config.lock().unwrap(); let target_dir = rls_config.target_dir.as_ref().map(|p| p as &Path); - make_cargo_config(manifest_dir, target_dir, shell) + make_cargo_config(manifest_dir, target_dir, restore_env.get_old_cwd(), shell) }; let ws = Workspace::new(&manifest_path, &config)?; @@ -186,16 +188,14 @@ fn run_cargo( ..CompileOptions::default(&config, CompileMode::Check { test: false }) }; - // Create a custom environment for running cargo, the environment is reset afterwards automatically - let mut env: HashMap> = HashMap::new(); - env.insert("RUSTFLAGS".to_owned(), Some(rustflags.into())); + // Create a custom environment for running cargo, the environment is reset + // afterwards automatically + restore_env.push_var("RUSTFLAGS", &Some(rustflags.into())); if clear_env_rust_log { - env.insert("RUST_LOG".to_owned(), None); + restore_env.push_var("RUST_LOG", &None); } - let _restore_env = Environment::push_with_lock(&env, lock_guard); - let exec = RlsExecutor::new( &ws, compilation_cx.clone(), @@ -213,7 +213,9 @@ fn run_cargo( compilation_cx.lock().unwrap().build_plan ); - Ok(()) + Ok(compilation_cx.lock().unwrap().cwd.clone().unwrap_or_else(|| { + restore_env.get_old_cwd().to_path_buf() + })) } struct RlsExecutor { @@ -475,11 +477,12 @@ impl Executor for RlsExecutor { &self.vfs, &args, &envs, + cargo_cmd.get_cwd(), &build_dir, self.config.clone(), env_lock, ) { - BuildResult::Success(mut messages, mut analysis) => { + BuildResult::Success(_, mut messages, mut analysis) => { self.compiler_messages.lock().unwrap().append(&mut messages); self.analysis.lock().unwrap().append(&mut analysis); } @@ -493,6 +496,7 @@ impl Executor for RlsExecutor { let mut compilation_cx = self.compilation_cx.lock().unwrap(); compilation_cx.args = args; compilation_cx.envs = envs; + compilation_cx.cwd = cargo_cmd.get_cwd().map(|p| p.to_path_buf()); Ok(()) } @@ -596,13 +600,13 @@ fn prepare_cargo_rustflags(config: &Config) -> String { /// Construct a cargo configuration for the given build and target directories /// and shell. -pub fn make_cargo_config(build_dir: &Path, target_dir: Option<&Path>, shell: Shell) -> CargoConfig { +pub fn make_cargo_config(build_dir: &Path, + target_dir: Option<&Path>, + cwd: &Path, + shell: Shell) -> CargoConfig { let config = CargoConfig::new( shell, - // This is Cargo's cwd. We're using the actual cwd, - // because Cargo will generate relative paths based - // on this to source files it wants to compile - env::current_dir().unwrap(), + cwd.to_path_buf(), homedir(&build_dir).unwrap(), ); diff --git a/src/build/environment.rs b/src/build/environment.rs index ba21d7b9728..ec67b9dee20 100644 --- a/src/build/environment.rs +++ b/src/build/environment.rs @@ -8,9 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::collections::HashMap; use std::env; use std::ffi::OsString; -use std::collections::HashMap; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex, MutexGuard}; // Ensures we don't race on the env vars. This is only also important in tests, @@ -23,19 +24,26 @@ lazy_static! { /// Requires supplying an external lock guard to guarantee env var consistency across multiple threads. pub struct Environment<'a> { old_vars: HashMap>, + old_cwd: PathBuf, _guard: MutexGuard<'a, ()>, } impl<'a> Environment<'a> { pub fn push_with_lock( envs: &HashMap>, + cwd: Option<&Path>, lock: MutexGuard<'a, ()>, ) -> Environment<'a> { let mut result = Environment { old_vars: HashMap::new(), + old_cwd: env::current_dir().expect("failed to read cwd"), _guard: lock, }; + if let Some(cwd) = cwd { + env::set_current_dir(cwd).expect("failed to change cwd"); + } + for (k, v) in envs { result.push_var(k, v); } @@ -49,10 +57,15 @@ impl<'a> Environment<'a> { None => env::remove_var(key), } } + + pub fn get_old_cwd(&self) -> &Path { + &self.old_cwd + } } impl<'a> Drop for Environment<'a> { fn drop(&mut self) { + drop(env::set_current_dir(&self.old_cwd)); for (k, v) in &self.old_vars { match *v { Some(ref v) => env::set_var(k, v), diff --git a/src/build/mod.rs b/src/build/mod.rs index a99d6c3bea2..55468bee48b 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -29,7 +29,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use std::time::Duration; -mod environment; +pub mod environment; mod cargo; mod rustc; mod plan; @@ -96,8 +96,9 @@ struct Internals { #[derive(Debug)] pub enum BuildResult { /// Build was performed without any internal errors. The payload - /// contains emitted raw diagnostics and Analysis data. - Success(Vec, Vec), + /// contains current directory at the time, emitted raw diagnostics, and + /// Analysis data. + Success(PathBuf, Vec, Vec), /// Build was coalesced with another build. Squashed, /// There was an error attempting to build. @@ -121,6 +122,7 @@ struct CompilationContext { /// args and envs are saved from Cargo and passed to rustc. args: Vec, envs: HashMap>, + cwd: Option, /// The build directory is supplied by the client and passed to Cargo. build_dir: Option, /// Build plan, which should know all the inter-package/target dependencies @@ -135,6 +137,7 @@ impl CompilationContext { envs: HashMap::new(), build_dir: None, build_plan: BuildPlan::new(), + cwd: None, } } } @@ -447,7 +450,7 @@ impl Internals { // now. It's possible that a build was scheduled with given files, but // user later changed them. These should still be left as dirty (not built). match *&result { - BuildResult::Success(_, _) => { + BuildResult::Success(..) => { let mut dirty_files = self.dirty_files.lock().unwrap(); dirty_files.retain(|file, dirty_version| { built_files @@ -517,6 +520,7 @@ impl Internals { &self.vfs, args, envs, + compile_cx.cwd.as_ref().map(|x| &**x), build_dir, self.config.clone(), env_lock, diff --git a/src/build/plan.rs b/src/build/plan.rs index e584957b704..2aaa2657371 100644 --- a/src/build/plan.rs +++ b/src/build/plan.rs @@ -26,7 +26,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt; -use std::path::Path; +use std::path::{Path, PathBuf}; use cargo::core::{PackageId, Profile, Target, TargetKind}; use cargo::ops::{Context, Kind, Unit}; @@ -341,6 +341,7 @@ impl JobQueue { let mut compiler_messages = vec![]; let mut analyses = vec![]; + let mut cwd = PathBuf::from("."); // Go through cached compiler invocations sequentially, collecting each // invocation's compiler messages for diagnostics and analysis data while let Some(job) = self.dequeue() { @@ -357,20 +358,22 @@ impl JobQueue { &internals.vfs, &args, job.get_envs(), + None, &build_dir, internals.config.clone(), internals.env_lock.as_facade(), ) { - BuildResult::Success(mut messages, mut analysis) => { + BuildResult::Success(c, mut messages, mut analysis) => { compiler_messages.append(&mut messages); analyses.append(&mut analysis); + cwd = c; } BuildResult::Err => return BuildResult::Err, _ => {} } } - BuildResult::Success(compiler_messages, analyses) + BuildResult::Success(cwd, compiler_messages, analyses) } } diff --git a/src/build/rustc.rs b/src/build/rustc.rs index 18dd74b271c..add9ad32265 100644 --- a/src/build/rustc.rs +++ b/src/build/rustc.rs @@ -38,12 +38,12 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; - // Runs a single instance of rustc. Runs in-process. pub fn rustc( vfs: &Vfs, args: &[String], envs: &HashMap>, + cwd: Option<&Path>, build_dir: &Path, rls_config: Arc>, env_lock: EnvironmentLockFacade, @@ -64,7 +64,7 @@ pub fn rustc( } let (guard, _) = env_lock.lock(); - let _restore_env = Environment::push_with_lock(&local_envs, guard); + let restore_env = Environment::push_with_lock(&local_envs, cwd, guard); let buf = Arc::new(Mutex::new(vec![])); let err_buf = buf.clone(); @@ -99,7 +99,8 @@ pub fn rustc( let analysis = analysis.lock().unwrap().clone(); let analysis = analysis.map(|analysis| vec![analysis]).unwrap_or(vec![]); - BuildResult::Success(stderr_json_msgs, analysis) + let cwd = cwd.unwrap_or(restore_env.get_old_cwd()).to_path_buf(); + BuildResult::Success(cwd, stderr_json_msgs, analysis) } // Our compiler controller. We mostly delegate to the default rustc diff --git a/src/config.rs b/src/config.rs index 198dca8a54a..644a15a6d04 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,7 @@ use build; +use std::env; use std::fmt::Debug; use std::io::sink; use std::path::{Path, PathBuf}; @@ -208,7 +209,8 @@ impl Config { // Cargo constructs relative paths from the manifest dir, so we have to pop "Cargo.toml" let manifest_dir = manifest_path.parent().unwrap(); let shell = Shell::from_write(Box::new(sink())); - let cargo_config = build::make_cargo_config(manifest_dir, None, shell); + let cwd = env::current_dir().expect("failed to get cwd"); + let cargo_config = build::make_cargo_config(manifest_dir, None, &cwd, shell); let ws = Workspace::new(&manifest_path, &cargo_config)?; @@ -226,7 +228,7 @@ impl Config { ws.members() .find(move |x| x.name() == package_name) - .ok_or(format!( + .ok_or(format_err!( "Couldn't find specified `{}` package via \ `analyze_package` in the workspace", package_name @@ -248,7 +250,7 @@ impl Config { // No `lib` detected, but also can't find any `bin` target - there's // no sensible target here, so just Err out let first = bins.nth(0) - .ok_or("No `bin` or `lib` targets in the package")?; + .ok_or(format_err!("No `bin` or `lib` targets in the package"))?; let mut bins = targets.iter().filter(|x| x.is_bin()); let target = match bins.find(|x| x.src_path().ends_with("main.rs")) { diff --git a/src/main.rs b/src/main.rs index 2442a77a636..5b8e68e7f82 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,8 @@ extern crate cargo; extern crate env_logger; +#[macro_use] +extern crate failure; extern crate languageserver_types as ls_types; #[macro_use] extern crate lazy_static; diff --git a/src/test/harness.rs b/src/test/harness.rs index de55912ea57..0330a9c1711 100644 --- a/src/test/harness.rs +++ b/src/test/harness.rs @@ -42,7 +42,16 @@ impl Environment { env::set_var("RUSTC", "rustc"); } - let cur_dir = env::current_dir().expect("Could not find current working directory"); + // Acquire the current directory, but this is changing when tests are + // running so we need to be sure to access it in a synchronized fashion. + let cur_dir = { + use build::environment::{EnvironmentLock, Environment}; + let env = EnvironmentLock::get(); + let (guard, _other) = env.lock(); + Environment::push_with_lock(&HashMap::new(), None, guard) + .get_old_cwd() + .to_path_buf() + }; let project_path = cur_dir.join("test_data").join(project_dir); let target_path = cur_dir .join("target")