From c4c5653a48fa6871bf2a07dd56cb7f2801115bfb Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 13 Oct 2020 00:07:52 +0100 Subject: [PATCH 1/5] x.py: setup: Refactor to centralise list of profiles Put all()'s otuput in the order we want to print things in, and add a comment about why they are in this order. Provide purpose() and all_for_help(). Use these things everywhere. Move all the abbrev character ("a", "b", etc.) processing into interactive_path. No functional change. Signed-off-by: Ian Jackson --- src/bootstrap/flags.rs | 4 +-- src/bootstrap/setup.rs | 64 +++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index c10188875fb..f38c6876747 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -551,9 +551,7 @@ Arguments: profile_string.parse().unwrap_or_else(|err| { eprintln!("error: {}", err); eprintln!("help: the available profiles are:"); - for choice in Profile::all() { - eprintln!("- {}", choice); - } + eprint!("{}", Profile::all_for_help("- ")); std::process::exit(1); }) } else { diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 2a9507cfc4c..85b361e16a3 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -1,4 +1,5 @@ use crate::{t, VERSION}; +use std::fmt::Write as _; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::{ @@ -20,7 +21,28 @@ impl Profile { } pub fn all() -> impl Iterator { - [Profile::Compiler, Profile::Codegen, Profile::Library, Profile::User].iter().copied() + use Profile::*; + // N.B. these are ordered by how they are displayed, not alphabetically + [Library, Compiler, Codegen, User].iter().copied() + } + + pub fn purpose(&self) -> String { + use Profile::*; + match self { + Library => "Contribute to the standard library", + Compiler => "Contribute to the compiler or rustdoc", + Codegen => "Contribute to the compiler, and also modify LLVM or codegen", + User => "Install Rust from source", + } + .to_string() + } + + pub fn all_for_help(indent: &str) -> String { + let mut out = String::new(); + for choice in Profile::all() { + writeln!(&mut out, "{}{}", indent, choice).unwrap(); + } + out } } @@ -29,10 +51,10 @@ impl FromStr for Profile { fn from_str(s: &str) -> Result { match s { - "a" | "lib" | "library" => Ok(Profile::Library), - "b" | "compiler" | "rustdoc" => Ok(Profile::Compiler), - "c" | "llvm" | "codegen" => Ok(Profile::Codegen), - "d" | "maintainer" | "user" => Ok(Profile::User), + "lib" | "library" => Ok(Profile::Library), + "compiler" | "rustdoc" => Ok(Profile::Compiler), + "llvm" | "codegen" => Ok(Profile::Codegen), + "maintainer" | "user" => Ok(Profile::User), _ => Err(format!("unknown profile: '{}'", s)), } } @@ -104,19 +126,33 @@ pub fn setup(src_path: &Path, profile: Profile) { // Used to get the path for `Subcommand::Setup` pub fn interactive_path() -> io::Result { + fn abbrev_all() -> impl Iterator { + ('a'..).map(|c| c.to_string()).zip(Profile::all()) + } + + fn parse_with_abbrev(input: &str) -> Result { + let input = input.trim().to_lowercase(); + for (letter, profile) in abbrev_all() { + if input == letter { + return Ok(profile); + } + } + input.parse() + } + let mut input = String::new(); - println!( - "Welcome to the Rust project! What do you want to do with x.py? -a) Contribute to the standard library -b) Contribute to the compiler or rustdoc -c) Contribute to the compiler, and also modify LLVM or codegen -d) Install Rust from source" - ); + println!("Welcome to the Rust project! What do you want to do with x.py?"); + for (letter, profile) in abbrev_all() { + println!("{}) {}", letter, profile.purpose()); + } let template = loop { - print!("Please choose one (a/b/c/d): "); + print!( + "Please choose one ({}): ", + abbrev_all().map(|(l, _)| l).collect::>().join("/") + ); io::stdout().flush()?; io::stdin().read_line(&mut input)?; - break match input.trim().to_lowercase().parse() { + break match parse_with_abbrev(&input) { Ok(profile) => profile, Err(err) => { println!("error: {}", err); From b7c6041df1489b866a3f71a74afc180a8458a324 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 12 Oct 2020 23:54:49 +0100 Subject: [PATCH 2/5] x.py: setup: Provide a description of what it does Co-authored-by: Joshua Nelson --- src/bootstrap/flags.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index f38c6876747..22cfd0c5643 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -125,6 +125,7 @@ Subcommands: dist Build distribution artifacts install Install distribution artifacts run, r Run tools contained in this repository + setup Create a config.toml (making it easier to use `x.py` itself) To learn more about a subcommand, run `./x.py -h`", ); @@ -472,15 +473,21 @@ Arguments: ); } "setup" => { - subcommand_help.push_str( + subcommand_help.push_str(&format!( "\n +x.py setup creates a `config.toml` which changes the defaults for x.py itself. + Arguments: This subcommand accepts a 'profile' to use for builds. For example: ./x.py setup library - The profile is optional and you will be prompted interactively if it is not given.", - ); + The profile is optional and you will be prompted interactively if it is not given. + The following profiles are available: + +{}", + Profile::all_for_help(" ").trim_end() + )); } _ => {} }; From 6e9e040bf85dd27e503a8c7f70b3e4d360c40d58 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 13 Oct 2020 00:55:05 +0100 Subject: [PATCH 3/5] x.py: setup: Offer keywords in interactive prompt We understand these profile names because we use .to_str(). Mention them in the question. Signed-off-by: Ian Jackson --- src/bootstrap/setup.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 85b361e16a3..2119747d9e1 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -40,7 +40,7 @@ impl Profile { pub fn all_for_help(indent: &str) -> String { let mut out = String::new(); for choice in Profile::all() { - writeln!(&mut out, "{}{}", indent, choice).unwrap(); + writeln!(&mut out, "{}{}: {}", indent, choice, choice.purpose()).unwrap(); } out } @@ -143,7 +143,7 @@ pub fn interactive_path() -> io::Result { let mut input = String::new(); println!("Welcome to the Rust project! What do you want to do with x.py?"); for (letter, profile) in abbrev_all() { - println!("{}) {}", letter, profile.purpose()); + println!("{}) {}: {}", letter, profile, profile.purpose()); } let template = loop { print!( From e9058571ce97dc8f3e5551ad38d1d59c2caa1c48 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 13 Oct 2020 12:44:23 +0100 Subject: [PATCH 4/5] x.py setup: Fix handling of wrong interactive input We need a fresh input buffer each time, or we reuse the previous data (since `read_line` appends). Signed-off-by: Ian Jackson --- src/bootstrap/setup.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 2119747d9e1..6811a376ac0 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -140,7 +140,6 @@ pub fn interactive_path() -> io::Result { input.parse() } - let mut input = String::new(); println!("Welcome to the Rust project! What do you want to do with x.py?"); for (letter, profile) in abbrev_all() { println!("{}) {}: {}", letter, profile, profile.purpose()); @@ -151,6 +150,7 @@ pub fn interactive_path() -> io::Result { abbrev_all().map(|(l, _)| l).collect::>().join("/") ); io::stdout().flush()?; + let mut input = String::new(); io::stdin().read_line(&mut input)?; break match parse_with_abbrev(&input) { Ok(profile) => profile, From 636728e39464b678646bdedfc47327898b464a66 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Tue, 13 Oct 2020 18:25:17 +0100 Subject: [PATCH 5/5] x.py setup: Avoid infinite loop if stdin is /dev/null EOF is not an error; it just causes read_line to produce "". Signed-off-by: Ian Jackson --- src/bootstrap/setup.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs index 6811a376ac0..a281061ace1 100644 --- a/src/bootstrap/setup.rs +++ b/src/bootstrap/setup.rs @@ -152,6 +152,10 @@ pub fn interactive_path() -> io::Result { io::stdout().flush()?; let mut input = String::new(); io::stdin().read_line(&mut input)?; + if input == "" { + eprintln!("EOF on stdin, when expecting answer to question. Giving up."); + std::process::exit(1); + } break match parse_with_abbrev(&input) { Ok(profile) => profile, Err(err) => {