Repository: paritytech/substrate
File: utils/wasm-builder/src/lib.rs (Rust)

Code before:
		// the prequisities check fail.
		get_rustup_command(None).unwrap_or(default_cargo)
	}
}

/// Get the first rustup command that supports our Substrate wasm env. If `selected` is `Some(_)`, a
/// `CargoCommand` using the given nightly is returned.
fn get_rustup_command(selected: Option<String>) -> Option<CargoCommand> {
	let host = format!("-{}", env::var("HOST").expect("`HOST` is always set by cargo"));

	let version = match selected {
		Some(selected) => selected,
		None => {
			let output = Command::new("rustup").args(&["toolchain", "list"]).output().ok()?.stdout;
			let lines = output.as_slice().lines();

			let mut found_version = None;
			for line in lines.filter_map(|l| l.ok()) {
				let version = line.trim_end_matches(&host);

				if CargoCommand::new_with_args("rustup", &["run", &version, "cargo"])
					.supports_substrate_wasm_env()
				{
					found_version = Some(version.into());
					break
				}
			}

			found_version?
		},
	};

	Some(CargoCommand::new_with_args("rustup", &["run", &version, "cargo"]))
}

/// Wraps a specific command which represents a cargo invocation.
#[derive(Debug)]
struct CargoCommand {
	program: String,
	args: Vec<String>,
}

impl CargoCommand {
	fn new(program: &str) -> Self {
		CargoCommand { program: program.into(), args: Vec::new() }
	}

	fn new_with_args(program: &str, args: &[&str]) -> Self {
		CargoCommand {
			program: program.into(),
			args: args.iter().map(ToString::to_string).collect(),

Review comment (suggestion):
Previously this just picked the newest nightly and now it'll pick the first toolchain which rustup returns, which (based on the order in which rustup returns the toolchains) it means that it will prioritize:

1) the non-versioned "stable" toolchain or "beta" toolchain, if compatible (and this won't necessarily be the newest stable that the user has installed),
2) the oldest compatible installed nightly,
3) the oldest compatible installed stable.

Besides the fact that this changes the previous behavior (e.g. it'll break the build on my machine as I have a lot of old nightlies installed :P) considering that we aggressively use new Rust features potentially picking the oldest installed toolchain is probably not what we want?

Maybe a better approach would be to do something like this:

1) parse the date from every installed cargo/rustc's version string (note: stable can be installed either as an explicitly versioned toolchain or just as "stable", so parsing rustup's output is not enough here),
2) pick the newest stable and the newest nightly,
3) if the stable is new enough - use that, otherwise use nightly

This would also make it not depend on the order in which `rustup` returns the toolchains (which I'm not sure if it's officially "stable").

Code after:
		default_cargo
	} else {
		// If no command before provided us with a cargo that supports our Substrate wasm env, we
		// try to search one with rustup. If that fails as well, we return the default cargo and let
		// the prequisities check fail.
		get_rustup_command().unwrap_or(default_cargo)
	}
}

/// Get the newest rustup command that supports our Substrate wasm env.
///
/// Stable versions are always favored over nightly versions even if the nightly versions are
/// newer.
fn get_rustup_command() -> Option<CargoCommand> {
	let host = format!("-{}", env::var("HOST").expect("`HOST` is always set by cargo"));

	let output = Command::new("rustup").args(&["toolchain", "list"]).output().ok()?.stdout;
	let lines = output.as_slice().lines();

	let mut versions = Vec::new();
	for line in lines.filter_map(|l| l.ok()) {
		let rustup_version = line.trim_end_matches(&host);

		let cmd = CargoCommand::new_with_args("rustup", &["run", &rustup_version, "cargo"]);

		if !cmd.supports_substrate_wasm_env() {
			continue
		}

		let Some(cargo_version) = cmd.version() else { continue; };

		versions.push((cargo_version, rustup_version.to_string()));
	}

	// Sort by the parsed version to get the latest version (greatest version) at the end of the
	// vec.
	versions.sort_by_key(|v| v.0);
	let version = &versions.last()?.1;

	Some(CargoCommand::new_with_args("rustup", &["run", &version, "cargo"]))
}

/// Wraps a specific command which represents a cargo invocation.
#[derive(Debug)]
struct CargoCommand {
	program: String,
	args: Vec<String>,
	version: Option<Version>,
}

impl CargoCommand {