Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deal cleanly with malformed default-host #1578

Merged
merged 7 commits into from
Jan 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/rustup-cli/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 16,10 @@ macro_rules! verbose {
( $ ( $ arg : tt ) * ) => ( $crate::log::verbose_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
}

macro_rules! debug {
( $ ( $ arg : tt ) * ) => ( $crate::log::debug_fmt ( format_args ! ( $ ( $ arg ) * ) ) )
}

pub fn warn_fmt(args: fmt::Arguments) {
let mut t = term2::stderr();
let _ = t.fg(term2::color::BRIGHT_YELLOW);
Expand Down Expand Up @@ -54,3 58,15 @@ pub fn verbose_fmt(args: fmt::Arguments) {
let _ = t.write_fmt(args);
let _ = write!(t, "\n");
}

pub fn debug_fmt(args: fmt::Arguments) {
if std::env::var("RUSTUP_DEBUG").is_ok() {
let mut t = term2::stderr();
let _ = t.fg(term2::color::BRIGHT_BLUE);
let _ = t.attr(term2::Attr::Bold);
let _ = write!(t, "verbose: ");
let _ = t.reset();
let _ = t.write_fmt(args);
let _ = write!(t, "\n");
}
}
32 changes: 31 additions & 1 deletion src/rustup-cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 223,7 @@ fn canonical_cargo_home() -> Result<String> {
/// and adding `CARGO_HOME`/bin to PATH.
pub fn install(no_prompt: bool, verbose: bool, mut opts: InstallOpts) -> Result<()> {
do_pre_install_sanity_checks()?;
do_pre_install_options_sanity_checks(&opts)?;
check_existence_of_rustc_or_cargo_in_path(no_prompt)?;
do_anti_sudo_check(no_prompt)?;

Expand Down Expand Up @@ -454,7 455,7 @@ fn do_pre_install_sanity_checks() -> Result<()> {
"delete `{}` to remove rustup.sh",
rustup_sh_path.expect("").display()
);
warn!("or, if you already rustup installed, you can run");
warn!("or, if you already have rustup installed, you can run");
warn!("`rustup self update` and `rustup toolchain list` to upgrade");
warn!("your directory structure");
return Err("cannot install while rustup.sh is installed".into());
Expand All @@ -463,6 464,35 @@ fn do_pre_install_sanity_checks() -> Result<()> {
Ok(())
}

fn do_pre_install_options_sanity_checks(opts: &InstallOpts) -> Result<()> {
// Verify that the installation options are vaguely sane
(|| {
let host_triple = dist::TargetTriple::from_str(&opts.default_host_triple);
let toolchain_to_use = if opts.default_toolchain == "none" {
"stable"
} else {
&opts.default_toolchain
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like it is duplicating behaviour defined elsewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To my knowledge it's not per-se. This is simply because using PartialToolchainDesc::resolve needs a valid toolchain name or it won't work (and none isn't valid IIRC).

It looks similar to other checks, but this time we're resolving from options and don't have a Cfg struct so I can't just call set_default_host et al, and deal with things that way. Also the debug!() is meant to be useful to tell us that it's behaving right and picking the correct fully qualified toolchain name.

If I've missed something you know of, please do let me know, but I've had a fairly good grep around I think.

let partial_channel = dist::PartialToolchainDesc::from_str(toolchain_to_use)?;
let resolved = partial_channel.resolve(&host_triple)?.to_string();
debug!(
"Successfully resolved installation toolchain as: {}",
resolved
);
Ok(())
})()
.map_err(|e: Box<std::error::Error>| {
format!(
"Pre-checks for host and toolchain failed: {}\n\
If you are unsure of suitable values, the 'stable' toolchain is the default.\n\
Valid host triples look something like: {}",
e,
dist::TargetTriple::from_host_or_build()
)
})?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should move this checking to its own function - do_pre_install_sanity_checks is about checking the user's system, rather than the installation options

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I've created a function called do_pre_install_options_sanity_checks() and moved the content into there, though for now I'm calling it from the same spot as do_pre_install_sanity_checks() unless I find something better.

Ok(())
}

// If the user is trying to install with sudo, on some systems this will
// result in writing root-owned files to the user's home directory, because
// sudo is configured not to change $HOME. Don't let that bogosity happen.
Expand Down
28 changes: 21 additions & 7 deletions src/rustup-dist/src/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,11 291,25 @@ impl PartialToolchainDesc {
}
}

pub fn resolve(self, host: &TargetTriple) -> ToolchainDesc {
let host = PartialTargetTriple::from_str(&host.0)
.expect("host triple couldn't be converted to partial triple");
let host_arch = host.arch.expect("");
let host_os = host.os.expect("");
pub fn resolve(self, input_host: &TargetTriple) -> Result<ToolchainDesc> {
let host = PartialTargetTriple::from_str(&input_host.0).ok_or_else(|| {
format!(
"Provided host '{}' couldn't be converted to partial triple",
input_host.0
)
})?;
let host_arch = host.arch.ok_or_else(|| {
format!(
"Provided host '{}' did not specify a CPU architecture",
input_host.0
)
})?;
let host_os = host.os.ok_or_else(|| {
format!(
"Provided host '{}' did not specify an operating system",
input_host.0
)
})?;
let host_env = host.env;

// If OS was specified, don't default to host environment, even if the OS matches
Expand All @@ -314,11 328,11 @@ impl PartialToolchainDesc {
format!("{}-{}", arch, os)
};

ToolchainDesc {
Ok(ToolchainDesc {
channel: self.channel,
date: self.date,
target: TargetTriple(trip),
}
})
}

pub fn has_triple(&self) -> bool {
Expand Down
22 changes: 16 additions & 6 deletions src/rustup/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 99,7 @@ impl Cfg {
);
let dist_root = dist_root_server.clone() "/dist";

Ok(Cfg {
let cfg = Cfg {
rustup_dir: rustup_dir,
settings_file: settings_file,
toolchains_dir: toolchains_dir,
Expand All @@ -111,7 111,15 @@ impl Cfg {
env_override: env_override,
dist_root_url: dist_root,
dist_root_server: dist_root_server,
})
};

// Run some basic checks against the constructed configuration
// For now, that means simply checking that 'stable' can resolve
// for the current configuration.
cfg.resolve_toolchain("stable")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we need this check. Could you explain please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. By doing this check here we verify that later attempts to resolve the toolchain are more likely to succeed. It means we report the error about bad host types etc earlier in the sequence of operations. This leads to, in my opinion, a slightly better user experience.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. I wonder if this will ever give a false negatve? If we have some toolchain which is nightly-only perhaps?

.map_err(|e| format!("Unable parse configuration: {}", e))?;

Ok(cfg)
}

pub fn set_default(&self, toolchain: &str) -> Result<()> {
Expand Down Expand Up @@ -470,9 478,11 @@ impl Cfg {
}

pub fn set_default_host_triple(&self, host_triple: &str) -> Result<()> {
if dist::PartialTargetTriple::from_str(host_triple).is_none() {
return Err("Invalid host triple".into());
}
// Ensure that the provided host_triple is capable of resolving
// against the 'stable' toolchain. This provides early errors
// if the supplied triple is insufficient / bad.
dist::PartialToolchainDesc::from_str("stable")?
.resolve(&dist::TargetTriple::from_str(host_triple))?;
self.settings_file.with_mut(|s| {
s.default_host_triple = Some(host_triple.to_owned());
Ok(())
Expand All @@ -493,7 503,7 @@ impl Cfg {
pub fn resolve_toolchain(&self, name: &str) -> Result<String> {
if let Ok(desc) = dist::PartialToolchainDesc::from_str(name) {
let host = self.get_default_host_triple()?;
Ok(desc.resolve(&host).to_string())
Ok(desc.resolve(&host)?.to_string())
} else {
Ok(name.to_owned())
}
Expand Down
14 changes: 13 additions & 1 deletion tests/cli-rustup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 900,19 @@ fn set_default_host_invalid_triple() {
expect_err(
config,
&["rustup", "set", "default-host", "foo"],
"Invalid host triple",
"error: Provided host 'foo' couldn't be converted to partial triple",
);
});
}

// #745
#[test]
fn set_default_host_invalid_triple_valid_partial() {
setup(&|config| {
expect_err(
config,
&["rustup", "set", "default-host", "x86_64-msvc"],
"error: Provided host 'x86_64-msvc' did not specify an operating system",
);
});
}
Expand Down