diff --git a/src/build/cargo.rs b/src/build/cargo.rs index 77b2df1d0a4..402f8d7ebf1 100644 --- a/src/build/cargo.rs +++ b/src/build/cargo.rs @@ -20,7 +20,7 @@ use std::collections::{HashMap, BTreeMap}; 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; @@ -29,231 +29,233 @@ use std::thread; impl Internals { // Runs an in-process instance of Cargo. pub fn cargo(&self) -> BuildResult { - struct RlsExecutor { - compilation_cx: Arc>, - cur_package_id: Mutex>, - config: Arc>, - } - - impl RlsExecutor { - fn new(compilation_cx: Arc>, - config: Arc>) -> RlsExecutor { - RlsExecutor { - compilation_cx: compilation_cx, - cur_package_id: Mutex::new(None), - config, - } - } + let exec = RlsExecutor::new(self.compilation_cx.clone(), self.config.clone()); - fn is_primary_crate(&self, id: &PackageId) -> bool { - let cur_package_id = self.cur_package_id.lock().unwrap(); - id == cur_package_id.as_ref().expect("Executor has not been initialised") - } - } + let out = Arc::new(Mutex::new(vec![])); + let out_clone = out.clone(); + let rls_config = self.config.clone(); + let build_dir = { + let compilation_cx = self.compilation_cx.lock().unwrap(); + compilation_cx.build_dir.as_ref().unwrap().clone() + }; - impl Executor for RlsExecutor { - fn init(&self, cx: &Context) { - let mut cur_package_id = self.cur_package_id.lock().unwrap(); - *cur_package_id = Some(cx.ws - .current_opt() - .expect("No current package in Cargo") - .package_id() - .clone()); - } + // Cargo may or may not spawn threads to run the various builds, since + // we may be in separate threads we need to block and wait our thread. + // However, if Cargo doesn't run a separate thread, then we'll just wait + // forever. Therefore, we spawn an extra thread here to be safe. + let handle = thread::spawn(move || run_cargo(exec, rls_config, build_dir, out)); - fn force_rebuild(&self, unit: &Unit) -> bool { - // We only do a cargo build if we want to force rebuild the last - // crate (e.g., because some args changed). Therefore we should - // always force rebuild the primary crate. - let id = unit.pkg.package_id(); - // FIXME build scripts - this will force rebuild build scripts as - // well as the primary crate. But this is not too bad - it means - // we will rarely rebuild more than we have to. - self.is_primary_crate(id) + match handle.join() { + Ok(_) => BuildResult::Success(vec![], None), + Err(_) => { + info!("cargo stdout {}", String::from_utf8(out_clone.lock().unwrap().to_owned()).unwrap()); + BuildResult::Err } + } + } +} - fn exec(&self, cargo_cmd: ProcessBuilder, id: &PackageId) -> CargoResult<()> { - trace!("exec"); - // Delete any stale data. We try and remove any json files with - // the same crate name as Cargo would emit. This includes files - // with the same crate name but different hashes, e.g., those - // made with a different compiler. - let cargo_args = cargo_cmd.get_args(); - let crate_name = parse_arg(cargo_args, "--crate-name").expect("no crate-name in rustc command line"); - let out_dir = parse_arg(cargo_args, "--out-dir").expect("no out-dir in rustc command line"); - let analysis_dir = Path::new(&out_dir).join("save-analysis"); - if let Ok(dir_contents) = read_dir(&analysis_dir) { - for entry in dir_contents { - let entry = entry.expect("unexpected error reading save-analysis directory"); - let name = entry.file_name(); - let name = name.to_str().unwrap(); - if name.starts_with(&crate_name) && name.ends_with(".json") { - debug!("removing: `{:?}`", name); - remove_file(entry.path()).expect("could not remove file"); - } +fn run_cargo(exec: RlsExecutor, rls_config: Arc>, build_dir: PathBuf, out: Arc>>) { + let mut flags = "-Zunstable-options -Zsave-analysis --error-format=json \ + -Zcontinue-parse-after-error".to_owned(); + + let mut shell = Shell::from_write(Box::new(BufWriter(out.clone()))); + shell.set_verbosity(Verbosity::Quiet); + let mut manifest_path = build_dir; + let config = make_cargo_config(&manifest_path, shell); + manifest_path.push("Cargo.toml"); + trace!("manifest_path: {:?}", manifest_path); + // TODO: Add support for virtual manifests and multiple packages + let ws = Workspace::new(&manifest_path, &config).expect("could not create cargo workspace"); + let current_package = ws.current().unwrap(); + let targets = current_package.targets(); + let bins; + let target_string; + + let opts = { + let rls_config = rls_config.lock().unwrap(); + if let Some(ref sysroot) = rls_config.sysroot { + flags.push_str(&format!(" --sysroot {}", sysroot)); + } + let rustflags = format!("{} {} {}", + env::var("RUSTFLAGS").unwrap_or(String::new()), + rls_config.rustflags.as_ref().unwrap_or(&String::new()), + flags); + let rustflags = dedup_flags(&rustflags); + env::set_var("RUSTFLAGS", &rustflags); + + bins = { + if let Some(ref build_bin) = rls_config.build_bin { + let mut bins = targets.iter().filter(|x| x.is_bin()); + let bin = bins.find(|x| x.name() == build_bin); + match bin { + Some(bin) => vec![bin.name().to_owned()], + None => { + debug!("cargo - couldn't find binary `{}` (specified in rls toml file)", build_bin); + vec![] } } + } else { + vec![] + } + }; - // We only want to intercept rustc call targeting current crate to cache - // args/envs generated by cargo so we can run only rustc later ourselves - // Currently we don't cache nor modify build script args - let is_build_script = crate_name == "build_script_build"; - if !self.is_primary_crate(id) || is_build_script { - let build_script_notice = if is_build_script {" (build script)"} else {""}; - trace!("rustc not intercepted - {}{}", id.name(), build_script_notice); + let mut opts = CompileOptions::default(&config, CompileMode::Check); + if rls_config.build_lib { + opts.filter = CompileFilter::new(true, &[], false, &[], false, &[], false, &[], false); + } else if !bins.is_empty() { + opts.filter = CompileFilter::new(false, &bins, false, &[], false, &[], false, &[], false); + } + if let Some(ref target) = rls_config.target { + target_string = target.clone(); + opts.target = Some(&target_string); + } + opts + }; + compile_with_exec(&ws, &opts, Arc::new(exec)).expect("could not run cargo"); +} - return cargo_cmd.exec(); - } +struct RlsExecutor { + compilation_cx: Arc>, + cur_package_id: Mutex>, + config: Arc>, +} - trace!("rustc intercepted - args: {:?} envs: {:?}", cargo_cmd.get_args(), cargo_cmd.get_envs()); - - let rustc_exe = env::var("RUSTC").unwrap_or("rustc".to_owned()); - let mut cmd = Command::new(&rustc_exe); - let mut args: Vec<_> = - cargo_cmd.get_args().iter().map(|a| a.clone().into_string().unwrap()).collect(); - - { - let config = self.config.lock().unwrap(); - let crate_type = parse_arg(cargo_args, "--crate-type"); - // Becase we only try to emulate `cargo test` using `cargo check`, so for now - // assume crate_type arg (i.e. in `cargo test` it isn't specified for --test targets) - // and build test harness only for final crate type - let crate_type = crate_type.expect("no crate-type in rustc command line"); - let is_final_crate_type = crate_type == "bin" || (crate_type == "lib" && config.build_lib); - - if config.cfg_test { - // FIXME(#351) allow passing --test to lib crate-type when building a dependency - if is_final_crate_type { - args.push("--test".to_owned()); - } else { - args.push("--cfg".to_owned()); - args.push("test".to_owned()); - } - } - if config.sysroot.is_none() { - let sysroot = current_sysroot() - .expect("need to specify SYSROOT env var or use rustup or multirust"); - args.push("--sysroot".to_owned()); - args.push(sysroot); - } +impl RlsExecutor { + fn new(compilation_cx: Arc>, + config: Arc>) -> RlsExecutor { + RlsExecutor { + compilation_cx: compilation_cx, + cur_package_id: Mutex::new(None), + config, + } + } - // We can't omit compilation here, because Cargo is going to expect to get - // dep-info for this crate, so we shell out to rustc to get that. - // This is not really ideal, because we are going to - // compute this info anyway when we run rustc ourselves, but we don't do - // that before we return to Cargo. - // FIXME Don't do this. Start our build here rather than on another thread - // so the dep-info is ready by the time we return from this callback. - // Since ProcessBuilder doesn't allow to modify args, we need to create - // our own command here from scratch here. - for a in &args { - // Emitting only dep-info is possible only for final crate type, as - // as others may emit required metadata for dependent crate types - if a.starts_with("--emit") && is_final_crate_type { - cmd.arg("--emit=dep-info"); - } else { - cmd.arg(a); - } - } - cmd.envs(cargo_cmd.get_envs().iter().filter_map(|(k, v)| v.as_ref().map(|v| (k, v)))); - if let Some(cwd) = cargo_cmd.get_cwd() { - cmd.current_dir(cwd); - } - } + fn is_primary_crate(&self, id: &PackageId) -> bool { + let cur_package_id = self.cur_package_id.lock().unwrap(); + id == cur_package_id.as_ref().expect("Executor has not been initialised") + } +} - cmd.status().expect("Couldn't execute rustc"); +impl Executor for RlsExecutor { + fn init(&self, cx: &Context) { + let mut cur_package_id = self.cur_package_id.lock().unwrap(); + *cur_package_id = Some(cx.ws + .current_opt() + .expect("No current package in Cargo") + .package_id() + .clone()); + } - // Finally, store the modified cargo-generated args/envs for future rustc calls - args.insert(0, rustc_exe); - let mut compilation_cx = self.compilation_cx.lock().unwrap(); - compilation_cx.args = args; - compilation_cx.envs = cargo_cmd.get_envs().clone(); + fn force_rebuild(&self, unit: &Unit) -> bool { + // We only do a cargo build if we want to force rebuild the last + // crate (e.g., because some args changed). Therefore we should + // always force rebuild the primary crate. + let id = unit.pkg.package_id(); + // FIXME build scripts - this will force rebuild build scripts as + // well as the primary crate. But this is not too bad - it means + // we will rarely rebuild more than we have to. + self.is_primary_crate(id) + } - Ok(()) + fn exec(&self, cargo_cmd: ProcessBuilder, id: &PackageId) -> CargoResult<()> { + trace!("exec"); + // Delete any stale data. We try and remove any json files with + // the same crate name as Cargo would emit. This includes files + // with the same crate name but different hashes, e.g., those + // made with a different compiler. + let cargo_args = cargo_cmd.get_args(); + let crate_name = parse_arg(cargo_args, "--crate-name").expect("no crate-name in rustc command line"); + let out_dir = parse_arg(cargo_args, "--out-dir").expect("no out-dir in rustc command line"); + let analysis_dir = Path::new(&out_dir).join("save-analysis"); + if let Ok(dir_contents) = read_dir(&analysis_dir) { + for entry in dir_contents { + let entry = entry.expect("unexpected error reading save-analysis directory"); + let name = entry.file_name(); + let name = name.to_str().unwrap(); + if name.starts_with(&crate_name) && name.ends_with(".json") { + debug!("removing: `{:?}`", name); + remove_file(entry.path()).expect("could not remove file"); + } } } - let exec = RlsExecutor::new(self.compilation_cx.clone(), self.config.clone()); + // We only want to intercept rustc call targeting current crate to cache + // args/envs generated by cargo so we can run only rustc later ourselves + // Currently we don't cache nor modify build script args + let is_build_script = crate_name == "build_script_build"; + if !self.is_primary_crate(id) || is_build_script { + let build_script_notice = if is_build_script {" (build script)"} else {""}; + trace!("rustc not intercepted - {}{}", id.name(), build_script_notice); - let out = Arc::new(Mutex::new(vec![])); - let out_clone = out.clone(); - let rls_config = self.config.clone(); - let build_dir = { - let compilation_cx = self.compilation_cx.lock().unwrap(); - compilation_cx.build_dir.as_ref().unwrap().clone() - }; + return cargo_cmd.exec(); + } - // Cargo may or may not spawn threads to run the various builds, since - // we may be in separate threads we need to block and wait our thread. - // However, if Cargo doesn't run a separate thread, then we'll just wait - // forever. Therefore, we spawn an extra thread here to be safe. - let handle = thread::spawn(move || { - let mut flags = "-Zunstable-options -Zsave-analysis --error-format=json \ - -Zcontinue-parse-after-error".to_owned(); - - let mut shell = Shell::from_write(Box::new(BufWriter(out.clone()))); - shell.set_verbosity(Verbosity::Quiet); - let mut manifest_path = build_dir; - let config = make_cargo_config(&manifest_path, shell); - manifest_path.push("Cargo.toml"); - trace!("manifest_path: {:?}", manifest_path); - // TODO: Add support for virtual manifests and multiple packages - let ws = Workspace::new(&manifest_path, &config).expect("could not create cargo workspace"); - let current_package = ws.current().unwrap(); - let targets = current_package.targets(); - let bins; - let target_string; - - let opts = { - let rls_config = rls_config.lock().unwrap(); - if let Some(ref sysroot) = rls_config.sysroot { - flags.push_str(&format!(" --sysroot {}", sysroot)); - } - let rustflags = format!("{} {} {}", - env::var("RUSTFLAGS").unwrap_or(String::new()), - rls_config.rustflags.as_ref().unwrap_or(&String::new()), - flags); - let rustflags = dedup_flags(&rustflags); - env::set_var("RUSTFLAGS", &rustflags); - - bins = { - if let Some(ref build_bin) = rls_config.build_bin { - let mut bins = targets.iter().filter(|x| x.is_bin()); - let bin = bins.find(|x| x.name() == build_bin); - match bin { - Some(bin) => vec![bin.name().to_owned()], - None => { - debug!("cargo - couldn't find binary `{}` (specified in rls toml file)", build_bin); - vec![] - } - } - } else { - vec![] - } - }; + trace!("rustc intercepted - args: {:?} envs: {:?}", cargo_cmd.get_args(), cargo_cmd.get_envs()); - let mut opts = CompileOptions::default(&config, CompileMode::Check); - if rls_config.build_lib { - opts.filter = CompileFilter::new(true, &[], false, &[], false, &[], false, &[], false); - } else if !bins.is_empty() { - opts.filter = CompileFilter::new(false, &bins, false, &[], false, &[], false, &[], false); - } - if let Some(ref target) = rls_config.target { - target_string = target.clone(); - opts.target = Some(&target_string); + let rustc_exe = env::var("RUSTC").unwrap_or("rustc".to_owned()); + let mut cmd = Command::new(&rustc_exe); + let mut args: Vec<_> = + cargo_cmd.get_args().iter().map(|a| a.clone().into_string().unwrap()).collect(); + + { + let config = self.config.lock().unwrap(); + let crate_type = parse_arg(cargo_args, "--crate-type"); + // Becase we only try to emulate `cargo test` using `cargo check`, so for now + // assume crate_type arg (i.e. in `cargo test` it isn't specified for --test targets) + // and build test harness only for final crate type + let crate_type = crate_type.expect("no crate-type in rustc command line"); + let is_final_crate_type = crate_type == "bin" || (crate_type == "lib" && config.build_lib); + + if config.cfg_test { + // FIXME(#351) allow passing --test to lib crate-type when building a dependency + if is_final_crate_type { + args.push("--test".to_owned()); + } else { + args.push("--cfg".to_owned()); + args.push("test".to_owned()); } - opts - }; - compile_with_exec(&ws, &opts, Arc::new(exec)).expect("could not run cargo"); - }); + } + if config.sysroot.is_none() { + let sysroot = current_sysroot() + .expect("need to specify SYSROOT env var or use rustup or multirust"); + args.push("--sysroot".to_owned()); + args.push(sysroot); + } - match handle.join() { - Ok(_) => BuildResult::Success(vec![], None), - Err(_) => { - info!("cargo stdout {}", String::from_utf8(out_clone.lock().unwrap().to_owned()).unwrap()); - BuildResult::Err + // We can't omit compilation here, because Cargo is going to expect to get + // dep-info for this crate, so we shell out to rustc to get that. + // This is not really ideal, because we are going to + // compute this info anyway when we run rustc ourselves, but we don't do + // that before we return to Cargo. + // FIXME Don't do this. Start our build here rather than on another thread + // so the dep-info is ready by the time we return from this callback. + // Since ProcessBuilder doesn't allow to modify args, we need to create + // our own command here from scratch here. + for a in &args { + // Emitting only dep-info is possible only for final crate type, as + // as others may emit required metadata for dependent crate types + if a.starts_with("--emit") && is_final_crate_type { + cmd.arg("--emit=dep-info"); + } else { + cmd.arg(a); + } + } + cmd.envs(cargo_cmd.get_envs().iter().filter_map(|(k, v)| v.as_ref().map(|v| (k, v)))); + if let Some(cwd) = cargo_cmd.get_cwd() { + cmd.current_dir(cwd); } } + + cmd.status().expect("Couldn't execute rustc"); + + // Finally, store the modified cargo-generated args/envs for future rustc calls + args.insert(0, rustc_exe); + let mut compilation_cx = self.compilation_cx.lock().unwrap(); + compilation_cx.args = args; + compilation_cx.envs = cargo_cmd.get_envs().clone(); + + Ok(()) } } diff --git a/src/build/mod.rs b/src/build/mod.rs index f0dcd2ff869..f24c85856f3 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -386,7 +386,7 @@ impl Internals { assert!(!args.is_empty()); let envs = &compile_cx.envs; let build_dir = compile_cx.build_dir.as_ref().unwrap(); - self.rustc(args, envs, build_dir) + rustc::rustc(&self.vfs, args, envs, build_dir) } } diff --git a/src/build/rustc.rs b/src/build/rustc.rs index 188cd01c0bc..b0d763e74f5 100644 --- a/src/build/rustc.rs +++ b/src/build/rustc.rs @@ -25,8 +25,9 @@ use self::rustc_save_analysis::CallbackHandler; use self::syntax::ast; use self::syntax::codemap::{FileLoader, RealFileLoader}; -use build::{Internals, BufWriter, BuildResult}; +use build::{BufWriter, BuildResult}; use data::Analysis; +use vfs::Vfs; use std::collections::HashMap; use std::env; @@ -35,135 +36,133 @@ use std::io; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; -impl Internals { - // Runs a single instance of rustc. Runs in-process. - pub fn rustc(&self, args: &[String], envs: &HashMap>, build_dir: &Path) -> BuildResult { - trace!("rustc - args: `{:?}`, envs: {:?}, build dir: {:?}", args, envs, build_dir); - - let changed = self.vfs.get_cached_files(); - - let _restore_env = Environment::push(envs); - let buf = Arc::new(Mutex::new(vec![])); - let err_buf = buf.clone(); - let args = args.to_owned(); - - let analysis = Arc::new(Mutex::new(None)); - - let mut controller = RlsRustcCalls::new(analysis.clone()); - - let exit_code = ::std::panic::catch_unwind(|| { - run(move || { - // Replace stderr so we catch most errors. - run_compiler(&args, - &mut controller, - Some(Box::new(ReplacedFileLoader::new(changed))), - Some(Box::new(BufWriter(buf)))) - }) - }); +// Runs a single instance of rustc. Runs in-process. +pub fn rustc(vfs: &Vfs, args: &[String], envs: &HashMap>, build_dir: &Path) -> BuildResult { + trace!("rustc - args: `{:?}`, envs: {:?}, build dir: {:?}", args, envs, build_dir); + + let changed = vfs.get_cached_files(); + + let _restore_env = Environment::push(envs); + let buf = Arc::new(Mutex::new(vec![])); + let err_buf = buf.clone(); + let args = args.to_owned(); + + let analysis = Arc::new(Mutex::new(None)); + + let mut controller = RlsRustcCalls::new(analysis.clone()); + + let exit_code = ::std::panic::catch_unwind(|| { + run(move || { + // Replace stderr so we catch most errors. + run_compiler(&args, + &mut controller, + Some(Box::new(ReplacedFileLoader::new(changed))), + Some(Box::new(BufWriter(buf)))) + }) + }); + + // FIXME(#25) given that we are running the compiler directly, there is no need + // to serialise the error messages - we should pass them in memory. + let stderr_json_msg = convert_message_to_json_strings(Arc::try_unwrap(err_buf) + .unwrap() + .into_inner() + .unwrap()); + + let analysis = analysis.lock().unwrap().clone(); + match exit_code { + Ok(0) => BuildResult::Success(stderr_json_msg, analysis), + _ => BuildResult::Failure(stderr_json_msg, analysis), + } +} - // FIXME(#25) given that we are running the compiler directly, there is no need - // to serialise the error messages - we should pass them in memory. - let stderr_json_msg = convert_message_to_json_strings(Arc::try_unwrap(err_buf) - .unwrap() - .into_inner() - .unwrap()); - - let analysis = analysis.lock().unwrap().clone(); - return match exit_code { - Ok(0) => BuildResult::Success(stderr_json_msg, analysis), - _ => BuildResult::Failure(stderr_json_msg, analysis), - }; +// Our compiler controller. We mostly delegate to the default rustc +// controller, but use our own callback for save-analysis. +#[derive(Clone)] +struct RlsRustcCalls { + default_calls: RustcDefaultCalls, + analysis: Arc>>, +} - // Our compiler controller. We mostly delegate to the default rustc - // controller, but use our own callback for save-analysis. - #[derive(Clone)] - struct RlsRustcCalls { +impl RlsRustcCalls { + fn new(analysis: Arc>>) -> RlsRustcCalls { + RlsRustcCalls { default_calls: RustcDefaultCalls, - analysis: Arc>>, + analysis: analysis, } + } +} - impl RlsRustcCalls { - fn new(analysis: Arc>>) -> RlsRustcCalls { - RlsRustcCalls { - default_calls: RustcDefaultCalls, - analysis: analysis, - } - } - } +impl<'a> CompilerCalls<'a> for RlsRustcCalls { + fn early_callback(&mut self, + matches: &getopts::Matches, + sopts: &config::Options, + cfg: &ast::CrateConfig, + descriptions: &errors::registry::Registry, + output: ErrorOutputType) + -> Compilation { + self.default_calls.early_callback(matches, sopts, cfg, descriptions, output) + } - impl<'a> CompilerCalls<'a> for RlsRustcCalls { - fn early_callback(&mut self, - matches: &getopts::Matches, - sopts: &config::Options, - cfg: &ast::CrateConfig, - descriptions: &errors::registry::Registry, - output: ErrorOutputType) - -> Compilation { - self.default_calls.early_callback(matches, sopts, cfg, descriptions, output) - } + fn no_input(&mut self, + matches: &getopts::Matches, + sopts: &config::Options, + cfg: &ast::CrateConfig, + odir: &Option, + ofile: &Option, + descriptions: &errors::registry::Registry) + -> Option<(Input, Option)> { + self.default_calls.no_input(matches, sopts, cfg, odir, ofile, descriptions) + } - fn no_input(&mut self, - matches: &getopts::Matches, - sopts: &config::Options, - cfg: &ast::CrateConfig, - odir: &Option, - ofile: &Option, - descriptions: &errors::registry::Registry) - -> Option<(Input, Option)> { - self.default_calls.no_input(matches, sopts, cfg, odir, ofile, descriptions) - } + fn late_callback(&mut self, + matches: &getopts::Matches, + sess: &Session, + input: &Input, + odir: &Option, + ofile: &Option) + -> Compilation { + self.default_calls.late_callback(matches, sess, input, odir, ofile) + } - fn late_callback(&mut self, - matches: &getopts::Matches, - sess: &Session, - input: &Input, - odir: &Option, - ofile: &Option) - -> Compilation { - self.default_calls.late_callback(matches, sess, input, odir, ofile) - } + fn build_controller(&mut self, + sess: &Session, + matches: &getopts::Matches) + -> CompileController<'a> { + let mut result = self.default_calls.build_controller(sess, matches); + let analysis = self.analysis.clone(); + + result.after_analysis.callback = Box::new(move |state| { + // There are two ways to move the data from rustc to the RLS, either + // directly or by serialising and deserialising. We only want to do + // the latter when there are compatibility issues between crates. + + // This version passes via JSON, it is more easily backwards compatible. + // save::process_crate(state.tcx.unwrap(), + // state.expanded_crate.unwrap(), + // state.analysis.unwrap(), + // state.crate_name.unwrap(), + // save::DumpHandler::new(save::Format::Json, + // state.out_dir, + // state.crate_name.unwrap())); + // This version passes directly, it is more efficient. + save::process_crate(state.tcx.unwrap(), + state.expanded_crate.unwrap(), + state.analysis.unwrap(), + state.crate_name.unwrap(), + CallbackHandler { + callback: &mut |a| { + let mut analysis = analysis.lock().unwrap(); + let a = unsafe { + ::std::mem::transmute(a.clone()) + }; + *analysis = Some(a); + } + }); + }); + result.after_analysis.run_callback_on_error = true; + result.make_glob_map = rustc_resolve::MakeGlobMap::Yes; - fn build_controller(&mut self, - sess: &Session, - matches: &getopts::Matches) - -> CompileController<'a> { - let mut result = self.default_calls.build_controller(sess, matches); - let analysis = self.analysis.clone(); - - result.after_analysis.callback = Box::new(move |state| { - // There are two ways to move the data from rustc to the RLS, either - // directly or by serialising and deserialising. We only want to do - // the latter when there are compatibility issues between crates. - - // This version passes via JSON, it is more easily backwards compatible. - // save::process_crate(state.tcx.unwrap(), - // state.expanded_crate.unwrap(), - // state.analysis.unwrap(), - // state.crate_name.unwrap(), - // save::DumpHandler::new(save::Format::Json, - // state.out_dir, - // state.crate_name.unwrap())); - // This version passes directly, it is more efficient. - save::process_crate(state.tcx.unwrap(), - state.expanded_crate.unwrap(), - state.analysis.unwrap(), - state.crate_name.unwrap(), - CallbackHandler { - callback: &mut |a| { - let mut analysis = analysis.lock().unwrap(); - let a = unsafe { - ::std::mem::transmute(a.clone()) - }; - *analysis = Some(a); - } - }); - }); - result.after_analysis.run_callback_on_error = true; - result.make_glob_map = rustc_resolve::MakeGlobMap::Yes; - - result - } - } + result } }