Skip to content

Commit 379918d

Browse files
committed
Tests passing
1 parent 23133bd commit 379918d

File tree

6 files changed

+127
-79
lines changed

6 files changed

+127
-79
lines changed

src/config.rs

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ use crate::notifications::*;
2323
use crate::process;
2424
use crate::settings::{Settings, SettingsFile, DEFAULT_METADATA_VERSION};
2525
use crate::toolchain::{
26-
LocalToolchainName, PathBasedToolchain, ResolvableToolchainName, ToolchainName,
26+
LocalToolchainName, PathBasedToolchain, ResolvableLocalToolchainName, ResolvableToolchainName,
27+
ToolchainName,
2728
};
2829
use crate::toolchain_old::{
2930
InstalledToolchain, OwnedCustomToolchain, OwnedDistributableToolchain, OwnedInstalled,
@@ -177,7 +178,7 @@ pub(crate) struct Cfg {
177178
pub download_dir: PathBuf,
178179
pub temp_cfg: temp::Cfg,
179180
pub toolchain_override: Option<ResolvableToolchainName>,
180-
pub env_override: Option<String>,
181+
pub env_override: Option<LocalToolchainName>,
181182
pub dist_root_url: String,
182183
pub notify_handler: Arc<dyn Fn(Notification<'_>)>,
183184
}
@@ -209,11 +210,17 @@ impl Cfg {
209210
let update_hash_dir = rustup_dir.join("update-hashes");
210211
let download_dir = rustup_dir.join("downloads");
211212

213+
// Figure out get_default_host_triple before Config is populated
214+
let default_host_triple = settings_file.with(|s| Ok(get_default_host_triple(s)))?;
212215
// Environment override
213216
let env_override = process()
214217
.var("RUSTUP_TOOLCHAIN")
215218
.ok()
216-
.and_then(utils::if_not_empty);
219+
.and_then(utils::if_not_empty)
220+
.map(|s| ResolvableLocalToolchainName::try_from(s))
221+
.transpose()?
222+
.map(|t| t.resolve(&default_host_triple))
223+
.transpose()?;
217224

218225
let dist_root_server = match process().var("RUSTUP_DIST_SERVER") {
219226
Ok(ref s) if !s.is_empty() => s.clone(),
@@ -458,10 +465,7 @@ impl Cfg {
458465
// custom, distributable, and absolute path toolchains otherwise
459466
// rustup's export of a RUSTUP_TOOLCHAIN when running a process will
460467
// error when a nested rustup invocation occurs
461-
override_ = Some((
462-
LocalToolchainName::try_from(name)?.to_string().into(),
463-
OverrideReason::Environment,
464-
));
468+
override_ = Some((name.to_string().into(), OverrideReason::Environment));
465469
}
466470

467471
// Then walk up the directory tree from 'path' looking for either the
@@ -574,25 +578,29 @@ impl Cfg {
574578
// internal only safe struct
575579
let override_file = Cfg::parse_override_file(contents, parse_mode)
576580
.with_context(add_file_context)?;
577-
if let Some(toolchain_name) = &override_file.toolchain.channel {
578-
let toolchain_name =
579-
ResolvableToolchainName::try_from(&toolchain_name as &str)?;
581+
if let Some(toolchain_name_str) = &override_file.toolchain.channel {
582+
let toolchain_name = ResolvableToolchainName::try_from(toolchain_name_str)?;
583+
let default_host_triple = get_default_host_triple(settings);
580584
// Do not permit architecture/os selection in channels as
581585
// these are host specific and toolchain files are portable.
582586
if let ResolvableToolchainName::Official(ref name) = toolchain_name {
583587
if name.has_triple() {
584-
return Err(anyhow!(format!("target triple in channel name '{name}'")));
588+
// Permit fully qualified names IFF the toolchain is installed. TODO(robertc): consider
589+
// disabling this and backing out https://github.com/rust-lang/rustup/pull/2141 (but provide
590+
// the base name in the error to help users)
591+
let resolved_name = &ToolchainName::try_from(toolchain_name_str)?;
592+
let ts = self.list_toolchains()?;
593+
eprintln!("{resolved_name:?} {ts:?}");
594+
if !self.list_toolchains()?.iter().any(|s| s == resolved_name) {
595+
return Err(anyhow!(format!(
596+
"target triple in channel name '{name}'"
597+
)));
598+
}
585599
}
586600
}
587601

588602
// XXX: this awkwardness deals with settings file being locked already
589-
let toolchain_name = toolchain_name.resolve(
590-
&settings
591-
.default_host_triple
592-
.as_ref()
593-
.map(|s| dist::TargetTriple::new(s))
594-
.unwrap_or_else(dist::TargetTriple::from_host_or_build),
595-
)?;
603+
let toolchain_name = toolchain_name.resolve(&default_host_triple)?;
596604
let toolchain = Toolchain::from_name(self, &(&toolchain_name).into());
597605
if !toolchain.exists() && matches!(toolchain_name, ToolchainName::Custom(_)) {
598606
bail!(
@@ -759,23 +767,19 @@ impl Cfg {
759767
/// List all the installed toolchains: that is paths in the toolchain dir
760768
/// that are:
761769
/// - not files
762-
/// - named with a valid toolchain name
770+
/// - named with a valid resolved toolchain name
771+
/// Currently no notification of incorrect names or entry type is done.
763772
#[cfg_attr(feature = "otel", tracing::instrument(skip_all))]
764773
pub(crate) fn list_toolchains(&self) -> Result<Vec<ToolchainName>> {
765774
if utils::is_directory(&self.toolchains_dir) {
766-
let default_host_triple = &self.get_default_host_triple()?;
767775
let mut toolchains: Vec<_> = utils::read_dir("toolchains", &self.toolchains_dir)?
768776
// TODO: this discards errors reading the directory, is that
769777
// correct? could we get a short-read and report less toolchains
770778
// than exist?
771779
.filter_map(io::Result::ok)
772780
.filter(|e| e.file_type().map(|f| !f.is_file()).unwrap_or(false))
773781
.filter_map(|e| e.file_name().into_string().ok())
774-
.filter_map(|n| {
775-
ResolvableToolchainName::try_from(n)
776-
.ok()
777-
.and_then(|n| n.resolve(default_host_triple).ok())
778-
})
782+
.filter_map(|n| ToolchainName::try_from(&n).ok())
779783
.collect();
780784

781785
crate::toolchain::toolchain_sort(&mut toolchains);
@@ -954,12 +958,7 @@ impl Cfg {
954958
pub(crate) fn get_default_host_triple(&self) -> Result<dist::TargetTriple> {
955959
Ok(self
956960
.settings_file
957-
.with(|s| {
958-
Ok(s.default_host_triple
959-
.as_ref()
960-
.map(|s| dist::TargetTriple::new(s)))
961-
})?
962-
.unwrap_or_else(dist::TargetTriple::from_host_or_build))
961+
.with(|s| Ok(get_default_host_triple(s)))?)
963962
}
964963

965964
pub(crate) fn toolchain_path(&self, toolchain: &LocalToolchainName) -> PathBuf {
@@ -970,6 +969,13 @@ impl Cfg {
970969
}
971970
}
972971

972+
fn get_default_host_triple(s: &Settings) -> dist::TargetTriple {
973+
s.default_host_triple
974+
.as_ref()
975+
.map(|s| dist::TargetTriple::new(s))
976+
.unwrap_or_else(dist::TargetTriple::from_host_or_build)
977+
}
978+
973979
/// Specifies how a `rust-toolchain`/`rust-toolchain.toml` configuration file should be parsed.
974980
enum ParseMode {
975981
/// Only permit TOML format in a configuration file.

src/dist/dist.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ struct ParsedToolchainDesc {
105105
// 'stable-msvc' to work. Partial target triples though are parsed
106106
// from a hardcoded set of known triples, whereas target triples
107107
// are nearly-arbitrary strings.
108-
#[derive(Debug, Clone)]
108+
#[derive(Debug, Clone, Eq, PartialEq, PartialOrd, Ord)]
109109
pub struct PartialToolchainDesc {
110110
// Either "nightly", "stable", "beta", or an explicit version number
111111
pub channel: String,

src/dist/triple.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ static LIST_ENVS: &[&str] = &[
4747
"musl",
4848
];
4949

50-
#[derive(Debug, Clone, PartialEq, Eq)]
50+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
5151
pub struct PartialTargetTriple {
5252
pub arch: Option<String>,
5353
pub os: Option<String>,

src/toolchain.rs

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
//!
33
//! From the user (including config files, toolchain files and manifests) we get
44
//! a String. Strings are convertable into `MaybeOfficialToolchainName`,
5-
//! `ResolvableToolchainName`, and `LocalToolchainName`.
5+
//! `ResolvableToolchainName`, and `ResolvableLocalToolchainName`.
66
//!
77
//! `MaybeOfficialToolchainName` represents a toolchain passed to rustup-init:
88
//! 'none' to select no toolchain to install, and otherwise a partial toolchain
@@ -15,11 +15,15 @@
1515
//! for both custom and official names.
1616
//!
1717
//! `ToolchainName` is the result of resolving `ResolvableToolchainName` with a
18-
//! host triple.
18+
//! host triple, or parsing an installed toolchain name directly.
19+
//!
20+
//! `ResolvableLocalToolchainName` represents the values permittable in
21+
//! `RUSTUP_TOOLCHAIN`: resolved or not resolved official names, custom names,
22+
//! and absolute paths.
1923
//!
2024
//! `LocalToolchainName` represents all the toolchain names that can make sense
21-
//! for referring to locally present toolchains. One of a
22-
//! `ResolvedToolchainName` or an absolute path.
25+
//! for referring to actually present toolchains. One of a `ToolchainName` or an
26+
//! absolute path.
2327
//!
2428
//! From the toolchains directory we can iterate directly over
2529
//! `ResolvedToolchainName`.
@@ -123,7 +127,7 @@ pub(crate) fn partial_toolchain_desc_parser(
123127
}
124128

125129
/// A toolchain name from user input.
126-
#[derive(Debug, Clone)]
130+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
127131
pub(crate) enum ResolvableToolchainName {
128132
Custom(CustomToolchainName),
129133
Official(PartialToolchainDesc),
@@ -338,29 +342,60 @@ pub(crate) fn toolchain_sort(v: &mut [ToolchainName]) {
338342
});
339343
}
340344

341-
/// LocalToolchainName can be used in calls to Cfg that alter configuration,
342-
/// like setting overrides, or that depend on configuration, like calculating
343-
/// the toolchain directory. It is also used to model the RUSTUP_TOOLCHAIN
344-
/// variable, as that can take any possible resolved toolchain value.
345+
/// ResolvableLocalToolchainName is used to process values set in
346+
/// RUSTUP_TOOLCHAIN: resolvable and resolved official names, custom names and
347+
/// absolute paths.
345348
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
346-
pub(crate) enum LocalToolchainName {
347-
Named(ToolchainName),
349+
pub(crate) enum ResolvableLocalToolchainName {
350+
Named(ResolvableToolchainName),
348351
Path(PathBasedToolchain),
349352
}
350353

351-
impl LocalToolchainName {
352-
/// Validates if the string is a concrete toolchain, or a custom toolchain, or a path based toolchain.
353-
fn validate(candidate: &str) -> Result<LocalToolchainName, Error> {
354+
impl ResolvableLocalToolchainName {
355+
/// Resolve to a concrete toolchain name
356+
pub fn resolve(&self, host: &TargetTriple) -> Result<LocalToolchainName, anyhow::Error> {
357+
match self.clone() {
358+
ResolvableLocalToolchainName::Named(t) => {
359+
Ok(LocalToolchainName::Named(t.resolve(host)?))
360+
}
361+
ResolvableLocalToolchainName::Path(t) => Ok(LocalToolchainName::Path(t)),
362+
}
363+
}
364+
365+
/// Validates if the string is a resolvable toolchain, or a path based toolchain.
366+
fn validate(candidate: &str) -> Result<Self, Error> {
354367
let candidate = validate(candidate)?;
355-
ToolchainName::try_from(candidate)
356-
.map(LocalToolchainName::Named)
368+
ResolvableToolchainName::try_from(candidate)
369+
.map(ResolvableLocalToolchainName::Named)
357370
.or_else(|_| {
358371
PathBasedToolchain::try_from(&PathBuf::from(candidate) as &Path)
359-
.map(LocalToolchainName::Path)
372+
.map(ResolvableLocalToolchainName::Path)
360373
})
361374
}
362375
}
363376

377+
try_from_str!(ResolvableLocalToolchainName);
378+
379+
impl Display for ResolvableLocalToolchainName {
380+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
381+
match self {
382+
ResolvableLocalToolchainName::Named(t) => write!(f, "{t}"),
383+
ResolvableLocalToolchainName::Path(t) => write!(f, "{t}"),
384+
}
385+
}
386+
}
387+
388+
/// LocalToolchainName can be used in calls to Cfg that alter configuration,
389+
/// like setting overrides, or that depend on configuration, like calculating
390+
/// the toolchain directory. It is not used to model the RUSTUP_TOOLCHAIN
391+
/// variable, because that can take unresolved toolchain values that are not
392+
/// invalid for referring to an installed toolchain.
393+
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
394+
pub(crate) enum LocalToolchainName {
395+
Named(ToolchainName),
396+
Path(PathBasedToolchain),
397+
}
398+
364399
impl From<&ToolchainDesc> for LocalToolchainName {
365400
fn from(value: &ToolchainDesc) -> Self {
366401
ToolchainName::Official(value.to_owned()).into()
@@ -385,7 +420,6 @@ from_variant!(
385420
LocalToolchainName,
386421
LocalToolchainName::Path
387422
);
388-
try_from_str!(LocalToolchainName);
389423

390424
impl Display for LocalToolchainName {
391425
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

tests/suite/cli_rustup.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,14 +1069,10 @@ fn show_toolchain_env() {
10691069
test(&|config| {
10701070
config.with_scenario(Scenario::SimpleV2, &|config| {
10711071
config.expect_ok(&["rustup", "default", "nightly"]);
1072-
let mut cmd = clitools::cmd(config, "rustup", ["show"]);
1073-
clitools::env(config, &mut cmd);
1074-
cmd.env("RUSTUP_TOOLCHAIN", "nightly");
1075-
let out = cmd.output().unwrap();
1076-
assert!(out.status.success());
1077-
let stdout = String::from_utf8(out.stdout).unwrap();
1072+
let out = config.run("rustup", ["show"], &[("RUSTUP_TOOLCHAIN", "nightly")]);
1073+
assert!(out.ok);
10781074
assert_eq!(
1079-
&stdout,
1075+
&out.stdout,
10801076
for_host_and_home!(
10811077
config,
10821078
r"Default host: {0}
@@ -2017,6 +2013,7 @@ fn valid_override_settings() {
20172013
config.expect_ok(&["rustup", "default", "nightly"]);
20182014
raw::write_file(&toolchain_file, "nightly").unwrap();
20192015
config.expect_ok(&["rustc", "--version"]);
2016+
// Special case: same version as is installed is permitted.
20202017
raw::write_file(&toolchain_file, for_host!("nightly-{}")).unwrap();
20212018
config.expect_ok(&["rustc", "--version"]);
20222019
let fullpath = config
@@ -2042,16 +2039,14 @@ fn file_override_with_target_info() {
20422039
// Target info is not portable between machines, so we reject toolchain
20432040
// files that include it.
20442041
test(&|config| {
2045-
config.with_scenario(Scenario::SimpleV2, &|config| {
2046-
let cwd = config.current_dir();
2047-
let toolchain_file = cwd.join("rust-toolchain");
2048-
raw::write_file(&toolchain_file, "nightly-x86_64-unknown-linux-gnu").unwrap();
2042+
let cwd = config.current_dir();
2043+
let toolchain_file = cwd.join("rust-toolchain");
2044+
raw::write_file(&toolchain_file, "nightly-x86_64-unknown-linux-gnu").unwrap();
20492045

2050-
config.expect_err(
2051-
&["rustc", "--version"],
2052-
"target triple in channel name 'nightly-x86_64-unknown-linux-gnu'",
2053-
);
2054-
})
2046+
config.expect_err(
2047+
&["rustc", "--version"],
2048+
"target triple in channel name 'nightly-x86_64-unknown-linux-gnu'",
2049+
);
20552050
});
20562051
}
20572052

tests/suite/cli_self_upd.rs

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ use std::process::Command;
88

99
use remove_dir_all::remove_dir_all;
1010

11+
use retry::{
12+
delay::{jitter, Fibonacci},
13+
retry, OperationResult,
14+
};
1115
use rustup::test::{
1216
mock::{
1317
clitools::{self, output_release_file, self_update_setup, Config, Scenario},
@@ -255,21 +259,30 @@ fn uninstall_self_delete_works() {
255259
// file in CONFIG.CARGODIR/.. ; check that it doesn't exist.
256260
#[test]
257261
fn uninstall_doesnt_leave_gc_file() {
258-
use std::thread;
259-
use std::time::Duration;
260-
261262
setup_empty_installed(&|config| {
262263
config.expect_ok(&["rustup", "self", "uninstall", "-y"]);
263-
264-
// The gc removal happens after rustup terminates. Give it a moment.
265-
thread::sleep(Duration::from_millis(100));
266-
267264
let parent = config.cargodir.parent().unwrap();
268-
// Actually, there just shouldn't be any files here
269-
for dirent in fs::read_dir(parent).unwrap() {
270-
let dirent = dirent.unwrap();
271-
println!("residual garbage '{}'", dirent.path().display());
272-
panic!();
265+
266+
// The gc removal happens after rustup terminates. Typically under
267+
// 100ms, but during the contention of test suites can be substantially
268+
// longer while still succeeding.
269+
270+
#[derive(thiserror::Error, Debug)]
271+
#[error("{:?}", .0)]
272+
struct GcErr(Vec<String>);
273+
274+
match retry(Fibonacci::from_millis(1).map(jitter).take(10), || {
275+
let garbage = fs::read_dir(parent)
276+
.unwrap()
277+
.map(|d| d.unwrap().path().to_string_lossy().to_string())
278+
.collect::<Vec<_>>();
279+
match garbage.len() {
280+
0 => OperationResult::Ok(()),
281+
_ => OperationResult::Err(GcErr(garbage)),
282+
}
283+
}) {
284+
Ok(_) => (),
285+
Err(e) => panic!("{e}"),
273286
}
274287
})
275288
}

0 commit comments

Comments
 (0)