diff --git a/src/rustup-cli/self_update.rs b/src/rustup-cli/self_update.rs index e3abe8f550e..3f34d9974aa 100644 --- a/src/rustup-cli/self_update.rs +++ b/src/rustup-cli/self_update.rs @@ -191,7 +191,7 @@ tools, but otherwise, install the C++ build tools before proceeding. "#; static TOOLS: &'static [&'static str] - = &["rustc", "rustdoc", "cargo", "rust-lldb", "rust-gdb"]; + = &["rustc", "rustdoc", "cargo", "rust-lldb", "rust-gdb", "rls"]; static UPDATE_ROOT: &'static str = "https://static.rust-lang.org/rustup"; @@ -594,9 +594,7 @@ fn install_bins() -> Result<()> { // like Android, does not support hardlinks, so we fallback to symlinks. for tool in TOOLS { let ref tool_path = bin_path.join(&format!("{}{}", tool, EXE_SUFFIX)); - if utils::hardlink_file(rustup_path, tool_path).is_err() { - try!(utils::symlink_file(rustup_path, tool_path)) - } + try!(utils::hard_or_symlink_file(rustup_path, tool_path)); } Ok(()) diff --git a/src/rustup-mock/src/clitools.rs b/src/rustup-mock/src/clitools.rs index 6e6b5d53247..02a1cd60cf1 100644 --- a/src/rustup-mock/src/clitools.rs +++ b/src/rustup-mock/src/clitools.rs @@ -99,6 +99,7 @@ pub fn setup(s: Scenario, f: &Fn(&Config)) { let setup_path = config.exedir.join(format!("rustup-init{}", EXE_SUFFIX)); let rustc_path = config.exedir.join(format!("rustc{}", EXE_SUFFIX)); let cargo_path = config.exedir.join(format!("cargo{}", EXE_SUFFIX)); + let rls_path = config.exedir.join(format!("rls{}", EXE_SUFFIX)); // Don't copy an executable via `fs::copy` on Unix because that'll require // opening up the destination for writing. If one thread in our process then @@ -119,6 +120,7 @@ pub fn setup(s: Scenario, f: &Fn(&Config)) { fs::hard_link(rustup_path, setup_path).unwrap(); fs::hard_link(rustup_path, rustc_path).unwrap(); fs::hard_link(rustup_path, cargo_path).unwrap(); + fs::hard_link(rustup_path, rls_path).unwrap(); // Make sure the host triple matches the build triple. Otherwise testing a 32-bit build of // rustup on a 64-bit machine will fail, because the tests do not have the host detection @@ -290,7 +292,7 @@ pub fn run(config: &Config, name: &str, args: &[&str], env: &[(&str, &str)]) -> for env in env { cmd.env(env.0, env.1); } - let out = cmd.output().unwrap(); + let out = cmd.output().expect("failed to run test command"); SanitizedOutput { ok: out.status.success(), @@ -365,6 +367,7 @@ fn build_mock_channel(s: Scenario, channel: &str, date: &str, let std = build_mock_std_installer(host_triple); let rustc = build_mock_rustc_installer(host_triple, version, version_hash); let cargo = build_mock_cargo_installer(version, version_hash); + let rls = build_mock_rls_installer(version, version_hash); let rust_docs = build_mock_rust_doc_installer(); let rust = build_combined_installer(&[&std, &rustc, &cargo, &rust_docs]); let cross_std1 = build_mock_cross_std_installer(CROSS_ARCH1, date); @@ -378,6 +381,7 @@ fn build_mock_channel(s: Scenario, channel: &str, date: &str, (cross_std2, CROSS_ARCH2.to_string())]), ("rustc", vec![(rustc, host_triple.clone())]), ("cargo", vec![(cargo, host_triple.clone())]), + ("rls", vec![(rls, host_triple.clone())]), ("rust-docs", vec![(rust_docs, host_triple.clone())]), ("rust-src", vec![(rust_src, "*".to_string())]), ("rust", vec![(rust, host_triple.clone())])]; @@ -386,6 +390,7 @@ fn build_mock_channel(s: Scenario, channel: &str, date: &str, let std = build_mock_std_installer(MULTI_ARCH1); let rustc = build_mock_rustc_installer(MULTI_ARCH1, version, version_hash); let cargo = build_mock_cargo_installer(version, version_hash); + let rls = build_mock_rls_installer(version, version_hash); let rust_docs = build_mock_rust_doc_installer(); let rust = build_combined_installer(&[&std, &rustc, &cargo, &rust_docs]); let cross_std1 = build_mock_cross_std_installer(CROSS_ARCH1, date); @@ -398,6 +403,7 @@ fn build_mock_channel(s: Scenario, channel: &str, date: &str, (cross_std2, CROSS_ARCH2.to_string())]), ("rustc", vec![(rustc, triple.clone())]), ("cargo", vec![(cargo, triple.clone())]), + ("rls", vec![(rls, triple.clone())]), ("rust-docs", vec![(rust_docs, triple.clone())]), ("rust-src", vec![(rust_src, "*".to_string())]), ("rust", vec![(rust, triple.clone())])]; @@ -445,6 +451,10 @@ fn build_mock_channel(s: Scenario, channel: &str, date: &str, name: "rust-docs".to_string(), target: target.to_string() }); + target_pkg.extensions.push(MockComponent { + name: "rls".to_string(), + target: target.to_string() + }); target_pkg.extensions.push(MockComponent { name: "rust-std".to_string(), target: CROSS_ARCH1.to_string(), @@ -544,6 +554,17 @@ fn build_mock_cargo_installer(version: &str, version_hash: &str) -> MockInstalle } } +fn build_mock_rls_installer(version: &str, version_hash: &str) -> MockInstallerBuilder { + let cargo = format!("bin/rls{}", EXE_SUFFIX); + MockInstallerBuilder { + components: vec![ + ("rls".to_string(), + vec![MockCommand::File(cargo.clone())], + vec![(cargo, mock_bin("rls", version, version_hash))]) + ] + } +} + fn build_mock_rust_doc_installer() -> MockInstallerBuilder { MockInstallerBuilder { components: vec![ diff --git a/src/rustup-utils/src/utils.rs b/src/rustup-utils/src/utils.rs index b505b726897..cefec8ef514 100644 --- a/src/rustup-utils/src/utils.rs +++ b/src/rustup-utils/src/utils.rs @@ -263,6 +263,13 @@ pub fn symlink_dir(src: &Path, dest: &Path, notify_handler: &Fn(Notification)) - }) } +pub fn hard_or_symlink_file(src: &Path, dest: &Path) -> Result<()> { + if hardlink_file(src, dest).is_err() { + symlink_file(src, dest)?; + } + Ok(()) +} + pub fn hardlink_file(src: &Path, dest: &Path) -> Result<()> { raw::hardlink(src, dest).chain_err(|| { ErrorKind::LinkingFile { diff --git a/tests/cli-misc.rs b/tests/cli-misc.rs index 9c9facf3d08..07cb1600948 100644 --- a/tests/cli-misc.rs +++ b/tests/cli-misc.rs @@ -15,6 +15,7 @@ use time::Duration; use std::ops::Sub; use std::ops::Add; use std::time::Duration as StdDuration; +use std::env::consts::EXE_SUFFIX; macro_rules! for_host { ($s: expr) => (&format!($s, this_host_triple())) } @@ -405,3 +406,25 @@ fn telemetry_cleanup_removes_old_files() { assert_eq!(count, 100); }); } + +#[test] +fn rls_exists_in_toolchain() { + setup(&|config| { + expect_ok(config, &["rustup", "default", "stable"]); + expect_ok(config, &["rustup", "component", "add", "rls"]); + assert!(config.exedir.join(format!("rls{}", EXE_SUFFIX)).exists()); + expect_ok(config, &["rls", "--version"]); + }); +} + +#[test] +fn rls_does_not_exist_in_toolchain() { + setup(&|config| { + // FIXME: If rls exists in the toolchain, this should suggest a command + // to run to install it + expect_ok(config, &["rustup", "default", "stable"]); + expect_err(config, &["rls", "--version"], + &format!("toolchain 'stable-{}' does not have the binary `rls{}`", + this_host_triple(), EXE_SUFFIX)); + }); +} diff --git a/tests/cli-self-upd.rs b/tests/cli-self-upd.rs index 6359007e3bb..c8cde87e335 100644 --- a/tests/cli-self-upd.rs +++ b/tests/cli-self-upd.rs @@ -1158,3 +1158,26 @@ fn uninstall_removes_legacy_home_symlink() { assert!(!multirust_dir.exists()); }); } + +#[test] +fn rls_proxy_set_up_after_install() { + setup(&|config| { + expect_ok(config, &["rustup-init", "-y"]); + expect_err(config, &["rls", "--version"], + &format!("toolchain 'stable-{}' does not have the binary `rls{}`", + this_host_triple(), EXE_SUFFIX)); + expect_ok(config, &["rustup", "component", "add", "rls"]); + expect_ok(config, &["rls", "--version"]); + }); +} + +#[test] +fn rls_proxy_set_up_after_update() { + update_setup(&|config, _| { + let ref rls_path = config.cargodir.join(format!("bin/rls{}", EXE_SUFFIX)); + expect_ok(config, &["rustup-init", "-y"]); + fs::remove_file(rls_path).unwrap(); + expect_ok(config, &["rustup", "self", "update"]); + assert!(rls_path.exists()); + }); +}