Add `x.py setup`

- Suggest `x.py setup` if config.toml doesn't exist yet (twice, once
before and once after the build)
- Prompt for a profile if not given on the command line
- Print the configuration file that will be used
- Print helpful starting commands after setup
- Link to the dev-guide after finishing
- Note that distro maintainers will see the changelog warning
This commit is contained in:
Joshua Nelson 2020-09-12 02:32:43 -04:00
parent cbc5e4d4d5
commit 9baa601afd
8 changed files with 152 additions and 8 deletions

View File

@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Non-breaking changes since the last major version]
- Add `x.py setup` [#76631](https://github.com/rust-lang/rust/pull/76631)
- Add a changelog for x.py [#76626](https://github.com/rust-lang/rust/pull/76626)
- Optionally, download LLVM from CI on Linux and NixOS
+ [#76439](https://github.com/rust-lang/rust/pull/76349)

View File

@ -7,21 +7,34 @@
use std::env;
use bootstrap::{Build, Config};
use bootstrap::{Build, Config, Subcommand};
fn main() {
let args = env::args().skip(1).collect::<Vec<_>>();
let config = Config::parse(&args);
let changelog_suggestion = check_version(&config);
if let Some(suggestion) = &changelog_suggestion {
// NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the
// changelog warning, not the `x.py setup` message.
let suggest_setup = !config.config.exists() && !matches!(config.cmd, Subcommand::Setup { .. });
if suggest_setup {
println!("warning: you have not made a `config.toml`");
println!("help: consider running `x.py setup` or copying `config.toml.example`");
} else if let Some(suggestion) = &changelog_suggestion {
println!("{}", suggestion);
}
Build::new(config).build();
if let Some(suggestion) = changelog_suggestion {
if suggest_setup {
println!("warning: you have not made a `config.toml`");
println!("help: consider running `x.py setup` or copying `config.toml.example`");
} else if let Some(suggestion) = &changelog_suggestion {
println!("{}", suggestion);
}
if suggest_setup || changelog_suggestion.is_some() {
println!("note: this message was printed twice to make it more likely to be seen");
}
}

View File

@ -549,7 +549,9 @@ impl<'a> Builder<'a> {
Subcommand::Dist { ref paths } => (Kind::Dist, &paths[..]),
Subcommand::Install { ref paths } => (Kind::Install, &paths[..]),
Subcommand::Run { ref paths } => (Kind::Run, &paths[..]),
Subcommand::Format { .. } | Subcommand::Clean { .. } => panic!(),
Subcommand::Format { .. } | Subcommand::Clean { .. } | Subcommand::Setup { .. } => {
panic!()
}
};
Self::new_internal(build, kind, paths.to_owned())

View File

@ -72,6 +72,8 @@ pub struct Config {
pub stage: u32,
pub keep_stage: Vec<u32>,
pub src: PathBuf,
// defaults to `config.toml`
pub config: PathBuf,
pub jobs: Option<u32>,
pub cmd: Subcommand,
pub incremental: bool,
@ -512,6 +514,7 @@ impl Config {
config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")];
config.deny_warnings = true;
config.missing_tools = false;
config.config = PathBuf::from("config.toml");
// set by bootstrap.py
config.build = TargetSelection::from_user(&env!("BUILD_TRIPLE"));
@ -556,7 +559,7 @@ impl Config {
let get_toml = |file: PathBuf| {
use std::process;
let contents = t!(fs::read_to_string(&file), "configuration file did not exist");
let contents = t!(fs::read_to_string(&file), "`include` config not found");
match toml::from_str(&contents) {
Ok(table) => table,
Err(err) => {
@ -642,6 +645,7 @@ impl Config {
| Subcommand::Clippy { .. }
| Subcommand::Fix { .. }
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. } => flags.stage.unwrap_or(0),
};
@ -666,6 +670,7 @@ impl Config {
| Subcommand::Clippy { .. }
| Subcommand::Fix { .. }
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. } => {}
}
}

View File

@ -7,6 +7,7 @@ use std::env;
use std::path::PathBuf;
use std::process;
use build_helper::t;
use getopts::Options;
use crate::builder::Builder;
@ -88,6 +89,9 @@ pub enum Subcommand {
Run {
paths: Vec<PathBuf>,
},
Setup {
path: String,
},
}
impl Default for Subcommand {
@ -191,6 +195,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
|| (s == "install")
|| (s == "run")
|| (s == "r")
|| (s == "setup")
});
let subcommand = match subcommand {
Some(s) => s,
@ -445,10 +450,21 @@ Arguments:
At least a tool needs to be called.",
);
}
"setup" => {
subcommand_help.push_str(
"\n
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.",
);
}
_ => {}
};
// Get any optional paths which occur after the subcommand
let paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
let verbose = matches.opt_present("verbose");
@ -500,6 +516,20 @@ Arguments:
}
Subcommand::Run { paths }
}
"setup" => {
let path = if paths.len() > 1 {
println!("\nat most one profile can be passed to setup\n");
usage(1, &opts, verbose, &subcommand_help)
} else if let Some(path) = paths.pop() {
t!(path.into_os_string().into_string().map_err(|path| format!(
"{} is not a valid UTF8 string",
path.to_string_lossy()
)))
} else {
t!(crate::setup::interactive_path())
};
Subcommand::Setup { path }
}
_ => {
usage(1, &opts, verbose, &subcommand_help);
}

View File

@ -141,6 +141,7 @@ mod metadata;
mod native;
mod run;
mod sanity;
mod setup;
mod test;
mod tool;
mod toolstate;
@ -165,7 +166,7 @@ mod job {
use crate::cache::{Interned, INTERNER};
pub use crate::config::Config;
use crate::flags::Subcommand;
pub use crate::flags::Subcommand;
const LLVM_TOOLS: &[&str] = &[
"llvm-nm", // used to inspect binaries; it shows symbol names, their sizes and visibility
@ -470,6 +471,10 @@ impl Build {
return clean::clean(self, all);
}
if let Subcommand::Setup { path: include_name } = &self.config.cmd {
return setup::setup(&self.config.src, include_name);
}
{
let builder = builder::Builder::new(&self);
if let Some(path) = builder.paths.get(0) {

View File

@ -10,7 +10,7 @@ impl Step for ExpandYamlAnchors {
/// Runs the `expand-yaml_anchors` tool.
///
/// This tool in `src/tools` read the CI configuration files written in YAML and expands the
/// This tool in `src/tools` reads the CI configuration files written in YAML and expands the
/// anchors in them, since GitHub Actions doesn't support them.
fn run(self, builder: &Builder<'_>) {
builder.info("Expanding YAML anchors in the GitHub Actions configuration");

88
src/bootstrap/setup.rs Normal file
View File

@ -0,0 +1,88 @@
use crate::t;
use std::path::{Path, PathBuf};
use std::{
env, fs,
io::{self, Write},
};
pub fn setup(src_path: &Path, include_name: &str) {
let cfg_file = env::var_os("BOOTSTRAP_CONFIG").map(PathBuf::from);
if cfg_file.as_ref().map_or(false, |f| f.exists()) {
let file = cfg_file.unwrap();
println!(
"error: you asked `x.py` to setup a new config file, but one already exists at `{}`",
file.display()
);
println!(
"help: try adding `profile = \"{}\"` at the top of {}",
include_name,
file.display()
);
println!(
"note: this will use the configuration in {}/src/bootstrap/defaults/config.toml.{}",
src_path.display(),
include_name
);
std::process::exit(1);
}
let path = cfg_file.unwrap_or_else(|| src_path.join("config.toml"));
let settings = format!(
"# Includes one of the default files in src/bootstrap/defaults\n\
profile = \"{}\"\n",
include_name
);
t!(fs::write(path, settings));
let include_path =
format!("{}/src/bootstrap/defaults/config.toml.{}", src_path.display(), include_name);
println!("`x.py` will now use the configuration at {}", include_path);
let suggestions = match include_name {
"codegen" | "compiler" => &["check", "build", "test"][..],
"library" => &["check", "build", "test library/std", "doc"],
"user" => &["dist", "build"],
_ => return,
};
println!("To get started, try one of the following commands:");
for cmd in suggestions {
println!("- `x.py {}`", cmd);
}
if include_name != "user" {
println!(
"For more suggestions, see https://rustc-dev-guide.rust-lang.org/building/suggested.html"
);
}
}
// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<String> {
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
c) Contribute to the compiler, and also modify LLVM or codegen
d) Install Rust from source"
);
let template = loop {
print!("Please choose one (a/b/c/d): ");
io::stdout().flush()?;
io::stdin().read_line(&mut input)?;
break match input.trim().to_lowercase().as_str() {
"a" | "lib" | "library" => "library",
"b" | "compiler" => "compiler",
"c" | "llvm" => "llvm",
"d" | "user" | "maintainer" => "maintainer",
_ => {
println!("error: unrecognized option '{}'", input.trim());
println!("note: press Ctrl+C to exit");
continue;
}
};
};
Ok(template.to_owned())
}